From 2355523af92baf01db0b2bb347d32fa19187eebc Mon Sep 17 00:00:00 2001 From: Kyle Wood Date: Sun, 29 Jan 2023 21:46:05 -0600 Subject: [PATCH] Create obfuscation-explorer project This is a separate plugin to support all different Java mapping formats. This plugin will provide a extension points for the Minecraft Development plugin to hook into and provide mapping information. It will also provide a generic settings page to allow users to configure their own mappings for the project. --- build.gradle.kts | 138 +----- buildSrc/build.gradle.kts | 39 +- buildSrc/settings.gradle.kts | 8 + buildSrc/src/main/kotlin/ParserExec.kt | 1 + ...mcdev.gradle.kts => mcdev-core.gradle.kts} | 125 ++++- .../src/main/kotlin/mcdev-parsing.gradle.kts | 46 ++ .../main/kotlin/mcdev-publishing.gradle.kts | 34 ++ buildSrc/src/main/kotlin/util.kt | 10 +- gradle.properties | 3 - gradle/libs.versions.toml | 36 +- obfuscation-explorer/.gitignore | 3 + obfuscation-explorer/build.gradle.kts | 128 ++++++ obfuscation-explorer/gradle.properties | 22 + obfuscation-explorer/icons/csrg.svg | 78 ++++ obfuscation-explorer/icons/enigma.svg | 78 ++++ obfuscation-explorer/icons/jam.svg | 78 ++++ obfuscation-explorer/icons/pluginIcon.svg | 138 ++++++ obfuscation-explorer/icons/proguard.svg | 78 ++++ obfuscation-explorer/icons/srg.svg | 78 ++++ obfuscation-explorer/icons/tinyv1.svg | 78 ++++ obfuscation-explorer/icons/tinyv2.svg | 78 ++++ obfuscation-explorer/icons/tsrg.svg | 78 ++++ obfuscation-explorer/icons/tsrg2.svg | 81 ++++ obfuscation-explorer/icons/xsrg.svg | 78 ++++ .../src/main/grammars/CSrgLexer.flex | 80 ++++ .../src/main/grammars/CSrgParser.bnf | 79 ++++ .../src/main/grammars/EnigmaLexer.flex | 97 ++++ .../src/main/grammars/EnigmaParser.bnf | 78 ++++ .../src/main/grammars/JamLexer.flex | 90 ++++ .../src/main/grammars/JamParser.bnf | 93 ++++ .../src/main/grammars/ProGuardLexer.flex | 78 ++++ .../src/main/grammars/ProGuardParser.bnf | 69 +++ .../src/main/grammars/SrgLexer.flex | 96 ++++ .../src/main/grammars/SrgParser.bnf | 111 +++++ .../src/main/grammars/TSrg2Lexer.flex | 101 +++++ .../src/main/grammars/TSrg2Parser.bnf | 81 ++++ .../src/main/grammars/TSrgLexer.flex | 83 ++++ .../src/main/grammars/TSrgParser.bnf | 70 +++ .../src/main/grammars/TinyV1Lexer.flex | 93 ++++ .../src/main/grammars/TinyV1Parser.bnf | 67 +++ .../src/main/grammars/TinyV2Lexer.flex | 154 +++++++ .../src/main/grammars/TinyV2Parser.bnf | 94 ++++ .../src/main/kotlin/ObfIcons.kt | 44 ++ .../src/main/kotlin/facet/ObfExFacet.kt | 44 ++ .../kotlin/facet/ObfExFacetConfiguration.kt | 66 +++ .../src/main/kotlin/facet/ObfExFacetType.kt | 47 ++ .../main/kotlin/facet/config/MappingsTable.kt | 313 +++++++++++++ .../kotlin/facet/config/MappingsTableCell.kt | 39 ++ .../kotlin/facet/config/MappingsTableRow.kt | 33 ++ .../facet/config/ObfExFacetEditorTab.kt | 55 +++ .../main/kotlin/formats/MappingsFormatType.kt | 41 ++ .../formats/MappingsFormatTypeManager.kt | 43 ++ .../formats/csrg/CSrgMappingsFormatParser.kt | 109 +++++ .../formats/csrg/CSrgMappingsFormatType.kt | 31 ++ .../main/kotlin/formats/csrg/lang/CSrgFile.kt | 30 ++ .../kotlin/formats/csrg/lang/CSrgFileType.kt | 33 ++ .../kotlin/formats/csrg/lang/CSrgLanguage.kt | 25 + .../formats/csrg/lang/CSrgParserDefinition.kt | 50 ++ .../formats/csrg/lang/colors/CSrgAnnotator.kt | 50 ++ .../csrg/lang/colors/CSrgSyntaxHighlighter.kt | 71 +++ .../colors/CSrgSyntaxHighlighterFactory.kt | 29 ++ .../formats/csrg/lang/psi/CSrgElementType.kt | 27 ++ .../formats/csrg/lang/psi/CSrgLexerAdapter.kt | 26 ++ .../formats/csrg/lang/psi/CSrgTokenType.kt | 27 ++ ...EnigmaLanguageCodeStyleSettingsProvider.kt | 46 ++ .../enigma/EnigmaMappingsFormatParser.kt | 218 +++++++++ .../enigma/EnigmaMappingsFormatType.kt | 31 ++ .../kotlin/formats/enigma/lang/EnigmaFile.kt | 31 ++ .../formats/enigma/lang/EnigmaFileType.kt | 33 ++ .../formats/enigma/lang/EnigmaLanguage.kt | 25 + .../formats/enigma/lang/EnigmaLayoutLexer.kt | 35 ++ .../enigma/lang/EnigmaParserDefinition.kt | 49 ++ .../enigma/lang/colors/EnigmaAnnotator.kt | 50 ++ .../lang/colors/EnigmaSyntaxHighlighter.kt | 95 ++++ .../colors/EnigmaSyntaxHighlighterFactory.kt | 29 ++ .../enigma/lang/psi/EnigmaElementType.kt | 27 ++ .../enigma/lang/psi/EnigmaLexerAdapter.kt | 26 ++ .../enigma/lang/psi/EnigmaParserUtil.kt | 42 ++ .../enigma/lang/psi/EnigmaTokenType.kt | 27 ++ .../formats/jam/JamMappingsFormatParser.kt | 146 ++++++ .../formats/jam/JamMappingsFormatType.kt | 31 ++ .../main/kotlin/formats/jam/lang/JamFile.kt | 30 ++ .../kotlin/formats/jam/lang/JamFileType.kt | 33 ++ .../kotlin/formats/jam/lang/JamLanguage.kt | 25 + .../formats/jam/lang/JamParserDefinition.kt | 50 ++ .../formats/jam/lang/colors/JamAnnotator.kt | 61 +++ .../jam/lang/colors/JamSyntaxHighlighter.kt | 86 ++++ .../colors/JamSyntaxHighlighterFactory.kt | 31 ++ .../formats/jam/lang/psi/JamElementType.kt | 27 ++ .../formats/jam/lang/psi/JamLexerAdapter.kt | 26 ++ .../formats/jam/lang/psi/JamTokenType.kt | 27 ++ .../proguard/ProGuardMappingsFormatParser.kt | 217 +++++++++ .../proguard/ProGuardMappingsFormatType.kt | 31 ++ .../formats/proguard/lang/ProGuardFile.kt | 30 ++ .../formats/proguard/lang/ProGuardFileType.kt | 33 ++ .../proguard/lang/ProGuardFileTypeDetector.kt | 73 +++ .../formats/proguard/lang/ProGuardLanguage.kt | 25 + .../proguard/lang/ProGuardParserDefinition.kt | 48 ++ .../proguard/lang/colors/ProGuardAnnotator.kt | 54 +++ .../lang/colors/ProGuardSyntaxHighlighter.kt | 98 ++++ .../ProGuardSyntaxHighlighterFactory.kt | 31 ++ .../proguard/lang/psi/ProGuardElementType.kt | 27 ++ .../proguard/lang/psi/ProGuardLexerAdapter.kt | 26 ++ .../proguard/lang/psi/ProGuardTokenType.kt | 27 ++ .../formats/srg/SrgMappingsFormatParser.kt | 180 ++++++++ .../formats/srg/SrgMappingsFormatType.kt | 31 ++ .../main/kotlin/formats/srg/lang/SrgFile.kt | 30 ++ .../kotlin/formats/srg/lang/SrgFileType.kt | 33 ++ .../kotlin/formats/srg/lang/SrgLanguage.kt | 25 + .../formats/srg/lang/SrgParserDefinition.kt | 50 ++ .../formats/srg/lang/colors/SrgAnnotator.kt | 72 +++ .../srg/lang/colors/SrgSyntaxHighlighter.kt | 78 ++++ .../colors/SrgSyntaxHighlighterFactory.kt | 31 ++ .../formats/srg/lang/psi/SrgElementType.kt | 27 ++ .../formats/srg/lang/psi/SrgLexerAdapter.kt | 26 ++ .../formats/srg/lang/psi/SrgTokenType.kt | 27 ++ .../lang/psi/mixins/SrgFieldMappingMixin.kt | 36 ++ .../mixins/impl/SrgFieldMappingImplMixin.kt | 38 ++ .../tinyv1/TinyV1MappingsFormatParser.kt | 193 ++++++++ .../tinyv1/TinyV1MappingsFormatType.kt | 31 ++ .../tinyv1/lang/TinyFileTypeDetector.kt | 48 ++ .../kotlin/formats/tinyv1/lang/TinyV1File.kt | 30 ++ .../formats/tinyv1/lang/TinyV1FileType.kt | 33 ++ .../formats/tinyv1/lang/TinyV1Language.kt | 25 + .../tinyv1/lang/TinyV1ParserDefinition.kt | 50 ++ .../tinyv1/lang/colors/TinyV1Annotator.kt | 51 +++ .../lang/colors/TinyV1RainbowVisitor.kt | 94 ++++ .../lang/colors/TinyV1SyntaxHighlighter.kt | 87 ++++ .../colors/TinyV1SyntaxHighlighterFactory.kt | 31 ++ .../tinyv1/lang/psi/TinyV1ElementType.kt | 27 ++ .../tinyv1/lang/psi/TinyV1LexerAdapter.kt | 26 ++ .../tinyv1/lang/psi/TinyV1TokenType.kt | 27 ++ ...TinyV2LanguageCodeStyleSettingsProvider.kt | 43 ++ .../tinyv2/TinyV2MappingsFormatParser.kt | 325 +++++++++++++ .../tinyv2/TinyV2MappingsFormatType.kt | 31 ++ .../kotlin/formats/tinyv2/lang/TinyV2File.kt | 30 ++ .../formats/tinyv2/lang/TinyV2FileType.kt | 33 ++ .../formats/tinyv2/lang/TinyV2Language.kt | 25 + .../tinyv2/lang/TinyV2ParserDefinition.kt | 50 ++ .../tinyv2/lang/colors/TinyV2Annotator.kt | 66 +++ .../lang/colors/TinyV2RainbowVisitor.kt | 75 +++ .../lang/colors/TinyV2SyntaxHighlighter.kt | 127 ++++++ .../colors/TinyV2SyntaxHighlighterFactory.kt | 31 ++ .../tinyv2/lang/psi/TinyV2ElementType.kt | 27 ++ .../tinyv2/lang/psi/TinyV2LexerAdapter.kt | 26 ++ .../tinyv2/lang/psi/TinyV2TokenType.kt | 27 ++ .../TSrgLanguageCodeStyleSettingsProvider.kt | 40 ++ .../formats/tsrg/TSrgMappingsFormatParser.kt | 120 +++++ .../formats/tsrg/TSrgMappingsFormatType.kt | 31 ++ .../main/kotlin/formats/tsrg/lang/TSrgFile.kt | 30 ++ .../kotlin/formats/tsrg/lang/TSrgFileType.kt | 33 ++ .../formats/tsrg/lang/TSrgFileTypeDetector.kt | 45 ++ .../kotlin/formats/tsrg/lang/TSrgLanguage.kt | 25 + .../formats/tsrg/lang/TSrgParserDefinition.kt | 50 ++ .../formats/tsrg/lang/colors/TSrgAnnotator.kt | 50 ++ .../tsrg/lang/colors/TSrgSyntaxHighlighter.kt | 71 +++ .../colors/TSrgSyntaxHighlighterFactory.kt | 31 ++ .../formats/tsrg/lang/psi/TSrgElementType.kt | 27 ++ .../formats/tsrg/lang/psi/TSrgLexerAdapter.kt | 26 ++ .../formats/tsrg/lang/psi/TSrgTokenType.kt | 27 ++ .../TSrg2LanguageCodeStyleSettingsProvider.kt | 44 ++ .../tsrg2/TSrg2MappingsFormatParser.kt | 231 ++++++++++ .../formats/tsrg2/TSrg2MappingsFormatType.kt | 31 ++ .../kotlin/formats/tsrg2/lang/TSrg2File.kt | 30 ++ .../formats/tsrg2/lang/TSrg2FileType.kt | 33 ++ .../formats/tsrg2/lang/TSrg2Language.kt | 25 + .../formats/tsrg2/lang/TSrg2LayoutLexer.kt | 35 ++ .../tsrg2/lang/TSrg2ParserDefinition.kt | 51 +++ .../tsrg2/lang/colors/TSrg2Annotator.kt | 55 +++ .../tsrg2/lang/colors/TSrg2RainbowVisitor.kt | 78 ++++ .../lang/colors/TSrg2SyntaxHighlighter.kt | 95 ++++ .../colors/TSrg2SyntaxHighlighterFactory.kt | 31 ++ .../tsrg2/lang/psi/TSrg2ElementType.kt | 27 ++ .../tsrg2/lang/psi/TSrg2LexerAdapter.kt | 26 ++ .../formats/tsrg2/lang/psi/TSrg2TokenType.kt | 27 ++ .../formats/util/NamespaceRainbowVisitor.kt | 137 ++++++ .../util/TabCodeStyleSettingsProvider.kt | 36 ++ .../util/sigws/SignificantWhitespaceLexer.kt | 144 ++++++ .../sigws/SignificantWhitespaceParserUtil.kt | 54 +++ .../src/main/kotlin/formats/util/util.kt | 84 ++++ .../formats/xsrg/XSrgMappingsFormatParser.kt | 224 +++++++++ .../formats/xsrg/XSrgMappingsFormatType.kt | 31 ++ .../main/kotlin/formats/xsrg/lang/XSrgFile.kt | 30 ++ .../kotlin/formats/xsrg/lang/XSrgFileType.kt | 33 ++ .../kotlin/formats/xsrg/lang/XSrgLanguage.kt | 25 + .../formats/xsrg/lang/XSrgParserDefinition.kt | 50 ++ .../src/main/kotlin/lookup/HashLookupIndex.kt | 87 ++++ .../src/main/kotlin/lookup/HashLookupTable.kt | 117 +++++ .../src/main/kotlin/lookup/LookupIndex.kt | 36 ++ .../main/kotlin/lookup/MultiLookupTable.kt | 96 ++++ .../main/kotlin/mappings/MappingElement.kt | 394 ++++++++++++++++ .../main/kotlin/mappings/MappingLocation.kt | 72 +++ .../main/kotlin/mappings/MappingNamespace.kt | 39 ++ .../src/main/kotlin/mappings/MappingSet.kt | 234 ++++++++++ .../main/kotlin/mappings/MappingSetBuilder.kt | 412 +++++++++++++++++ .../main/kotlin/mappings/MappingSetSource.kt | 69 +++ .../kotlin/mappings/MappingsDefinition.kt | 54 +++ .../kotlin/mappings/MappingsFormatParser.kt | 88 ++++ .../mappings/store/MappingLookupIndex.kt | 44 ++ .../mappings/store/MappingLookupTable.kt | 75 +++ .../kotlin/mappings/store/NamespaceLookup.kt | 25 + .../src/main/kotlin/ref/ref.kt | 224 +++++++++ .../src/main/kotlin/ref/types.kt | 199 ++++++++ obfuscation-explorer/src/main/kotlin/util.kt | 310 +++++++++++++ .../src/main/resources/META-INF/plugin.xml | 110 +++++ .../main/resources/META-INF/pluginIcon.svg | 9 + .../src/main/resources/fileTypes/csrg.svg | 8 + .../src/main/resources/fileTypes/enigma.svg | 8 + .../src/main/resources/fileTypes/jam.svg | 8 + .../src/main/resources/fileTypes/obfex.svg | 9 + .../src/main/resources/fileTypes/proguard.svg | 8 + .../src/main/resources/fileTypes/srg.svg | 8 + .../src/main/resources/fileTypes/tinyv1.svg | 8 + .../src/main/resources/fileTypes/tinyv2.svg | 8 + .../src/main/resources/fileTypes/tsrg.svg | 8 + .../src/main/resources/fileTypes/tsrg2.svg | 8 + .../src/main/resources/fileTypes/xsrg.svg | 8 + .../src/test/kotlin/TypeParsingTest.kt | 221 +++++++++ .../src/test/kotlin/formats/ParserFixture.kt | 60 +++ .../csrg/CSrgMappingsFormatParserTest.kt | 153 +++++++ .../kotlin/formats/enigma/EnigmaLexerTest.kt | 46 ++ .../enigma/EnigmaMappingsFormatParserTest.kt | 195 ++++++++ .../jam/JamMappingsFormatParserTest.kt | 112 +++++ .../ProGuardMappingsFormatParserTest.kt | 105 +++++ .../test/kotlin/formats/srg/SrgLexerTest.kt | 47 ++ .../srg/SrgMappingsFormatParserTest.kt | 124 +++++ .../tinyv1/TinyV1MappingsFormatParserTest.kt | 90 ++++ .../kotlin/formats/tinyv2/TinyV2LexerTest.kt | 46 ++ .../tinyv2/TinyV2MappingsFormatParserTest.kt | 124 +++++ .../tsrg/TSrgMappingsFormatParserTest.kt | 133 ++++++ .../kotlin/formats/tsrg2/TSrg2LexerTest.kt | 46 ++ .../tsrg2/TSrg2MappingsFormatParserTest.kt | 161 +++++++ .../xsrg/XSrgMappingsFormatParserTest.kt | 128 ++++++ .../enigma/lexer/fixtures/test.mapping | 34 ++ .../formats/enigma/lexer/fixtures/test.txt | 347 ++++++++++++++ .../obfex/formats/srg/lexer/fixtures/test.srg | 10 + .../obfex/formats/srg/lexer/fixtures/test.txt | 124 +++++ .../formats/tinyv2/lexer/fixtures/test.tiny | 15 + .../formats/tinyv2/lexer/fixtures/test.txt | 135 ++++++ .../formats/tsrg2/lexer/fixtures/test.tsrg2 | 26 ++ .../formats/tsrg2/lexer/fixtures/test.txt | 228 ++++++++++ settings.gradle.kts | 1 + src/main/grammars/AtParser.bnf | 2 +- src/main/kotlin/facet/MinecraftFacet.kt | 2 +- .../nbt/filetype/NbtFileTypeDetector.kt | 4 + .../nbt/lang/colors/NbttSyntaxHighlighter.kt | 3 +- src/main/kotlin/platform/mcp/at/AtFile.kt | 2 +- src/main/kotlin/platform/mcp/at/AtFileType.kt | 2 +- .../platform/mcp/at/AtParserDefinition.kt | 3 - .../platform/mcp/at/AtSyntaxHighlighter.kt | 5 +- src/main/kotlin/util/psi-utils.kt | 6 +- src/main/resources/META-INF/plugin.xml | 4 +- src/test/kotlin/framework/test-util.kt | 60 +-- .../at/lexer/fixtures/spigot_mappings_at.txt | 426 +++++++++--------- .../mcp/at/parser/fixtures/asterisks_at.txt | 4 +- .../mcp/at/parser/fixtures/comments_at.txt | 12 +- .../mcp/at/parser/fixtures/fields_at.txt | 18 +- .../mcp/at/parser/fixtures/funcs_at.txt | 40 +- .../mcp/at/parser/fixtures/keywords_at.txt | 24 +- .../mcp/at/parser/fixtures/no_value_at.txt | 6 +- 260 files changed, 16708 insertions(+), 453 deletions(-) rename buildSrc/src/main/kotlin/{mcdev.gradle.kts => mcdev-core.gradle.kts} (56%) create mode 100644 buildSrc/src/main/kotlin/mcdev-parsing.gradle.kts create mode 100644 buildSrc/src/main/kotlin/mcdev-publishing.gradle.kts create mode 100644 obfuscation-explorer/.gitignore create mode 100644 obfuscation-explorer/build.gradle.kts create mode 100644 obfuscation-explorer/gradle.properties create mode 100644 obfuscation-explorer/icons/csrg.svg create mode 100644 obfuscation-explorer/icons/enigma.svg create mode 100644 obfuscation-explorer/icons/jam.svg create mode 100644 obfuscation-explorer/icons/pluginIcon.svg create mode 100644 obfuscation-explorer/icons/proguard.svg create mode 100644 obfuscation-explorer/icons/srg.svg create mode 100644 obfuscation-explorer/icons/tinyv1.svg create mode 100644 obfuscation-explorer/icons/tinyv2.svg create mode 100644 obfuscation-explorer/icons/tsrg.svg create mode 100644 obfuscation-explorer/icons/tsrg2.svg create mode 100644 obfuscation-explorer/icons/xsrg.svg create mode 100644 obfuscation-explorer/src/main/grammars/CSrgLexer.flex create mode 100644 obfuscation-explorer/src/main/grammars/CSrgParser.bnf create mode 100644 obfuscation-explorer/src/main/grammars/EnigmaLexer.flex create mode 100644 obfuscation-explorer/src/main/grammars/EnigmaParser.bnf create mode 100644 obfuscation-explorer/src/main/grammars/JamLexer.flex create mode 100644 obfuscation-explorer/src/main/grammars/JamParser.bnf create mode 100644 obfuscation-explorer/src/main/grammars/ProGuardLexer.flex create mode 100644 obfuscation-explorer/src/main/grammars/ProGuardParser.bnf create mode 100644 obfuscation-explorer/src/main/grammars/SrgLexer.flex create mode 100644 obfuscation-explorer/src/main/grammars/SrgParser.bnf create mode 100644 obfuscation-explorer/src/main/grammars/TSrg2Lexer.flex create mode 100644 obfuscation-explorer/src/main/grammars/TSrg2Parser.bnf create mode 100644 obfuscation-explorer/src/main/grammars/TSrgLexer.flex create mode 100644 obfuscation-explorer/src/main/grammars/TSrgParser.bnf create mode 100644 obfuscation-explorer/src/main/grammars/TinyV1Lexer.flex create mode 100644 obfuscation-explorer/src/main/grammars/TinyV1Parser.bnf create mode 100644 obfuscation-explorer/src/main/grammars/TinyV2Lexer.flex create mode 100644 obfuscation-explorer/src/main/grammars/TinyV2Parser.bnf create mode 100644 obfuscation-explorer/src/main/kotlin/ObfIcons.kt create mode 100644 obfuscation-explorer/src/main/kotlin/facet/ObfExFacet.kt create mode 100644 obfuscation-explorer/src/main/kotlin/facet/ObfExFacetConfiguration.kt create mode 100644 obfuscation-explorer/src/main/kotlin/facet/ObfExFacetType.kt create mode 100644 obfuscation-explorer/src/main/kotlin/facet/config/MappingsTable.kt create mode 100644 obfuscation-explorer/src/main/kotlin/facet/config/MappingsTableCell.kt create mode 100644 obfuscation-explorer/src/main/kotlin/facet/config/MappingsTableRow.kt create mode 100644 obfuscation-explorer/src/main/kotlin/facet/config/ObfExFacetEditorTab.kt create mode 100644 obfuscation-explorer/src/main/kotlin/formats/MappingsFormatType.kt create mode 100644 obfuscation-explorer/src/main/kotlin/formats/MappingsFormatTypeManager.kt create mode 100644 obfuscation-explorer/src/main/kotlin/formats/csrg/CSrgMappingsFormatParser.kt create mode 100644 obfuscation-explorer/src/main/kotlin/formats/csrg/CSrgMappingsFormatType.kt create mode 100644 obfuscation-explorer/src/main/kotlin/formats/csrg/lang/CSrgFile.kt create mode 100644 obfuscation-explorer/src/main/kotlin/formats/csrg/lang/CSrgFileType.kt create mode 100644 obfuscation-explorer/src/main/kotlin/formats/csrg/lang/CSrgLanguage.kt create mode 100644 obfuscation-explorer/src/main/kotlin/formats/csrg/lang/CSrgParserDefinition.kt create mode 100644 obfuscation-explorer/src/main/kotlin/formats/csrg/lang/colors/CSrgAnnotator.kt create mode 100644 obfuscation-explorer/src/main/kotlin/formats/csrg/lang/colors/CSrgSyntaxHighlighter.kt create mode 100644 obfuscation-explorer/src/main/kotlin/formats/csrg/lang/colors/CSrgSyntaxHighlighterFactory.kt create mode 100644 obfuscation-explorer/src/main/kotlin/formats/csrg/lang/psi/CSrgElementType.kt create mode 100644 obfuscation-explorer/src/main/kotlin/formats/csrg/lang/psi/CSrgLexerAdapter.kt create mode 100644 obfuscation-explorer/src/main/kotlin/formats/csrg/lang/psi/CSrgTokenType.kt create mode 100644 obfuscation-explorer/src/main/kotlin/formats/enigma/EnigmaLanguageCodeStyleSettingsProvider.kt create mode 100644 obfuscation-explorer/src/main/kotlin/formats/enigma/EnigmaMappingsFormatParser.kt create mode 100644 obfuscation-explorer/src/main/kotlin/formats/enigma/EnigmaMappingsFormatType.kt create mode 100644 obfuscation-explorer/src/main/kotlin/formats/enigma/lang/EnigmaFile.kt create mode 100644 obfuscation-explorer/src/main/kotlin/formats/enigma/lang/EnigmaFileType.kt create mode 100644 obfuscation-explorer/src/main/kotlin/formats/enigma/lang/EnigmaLanguage.kt create mode 100644 obfuscation-explorer/src/main/kotlin/formats/enigma/lang/EnigmaLayoutLexer.kt create mode 100644 obfuscation-explorer/src/main/kotlin/formats/enigma/lang/EnigmaParserDefinition.kt create mode 100644 obfuscation-explorer/src/main/kotlin/formats/enigma/lang/colors/EnigmaAnnotator.kt create mode 100644 obfuscation-explorer/src/main/kotlin/formats/enigma/lang/colors/EnigmaSyntaxHighlighter.kt create mode 100644 obfuscation-explorer/src/main/kotlin/formats/enigma/lang/colors/EnigmaSyntaxHighlighterFactory.kt create mode 100644 obfuscation-explorer/src/main/kotlin/formats/enigma/lang/psi/EnigmaElementType.kt create mode 100644 obfuscation-explorer/src/main/kotlin/formats/enigma/lang/psi/EnigmaLexerAdapter.kt create mode 100644 obfuscation-explorer/src/main/kotlin/formats/enigma/lang/psi/EnigmaParserUtil.kt create mode 100644 obfuscation-explorer/src/main/kotlin/formats/enigma/lang/psi/EnigmaTokenType.kt create mode 100644 obfuscation-explorer/src/main/kotlin/formats/jam/JamMappingsFormatParser.kt create mode 100644 obfuscation-explorer/src/main/kotlin/formats/jam/JamMappingsFormatType.kt create mode 100644 obfuscation-explorer/src/main/kotlin/formats/jam/lang/JamFile.kt create mode 100644 obfuscation-explorer/src/main/kotlin/formats/jam/lang/JamFileType.kt create mode 100644 obfuscation-explorer/src/main/kotlin/formats/jam/lang/JamLanguage.kt create mode 100644 obfuscation-explorer/src/main/kotlin/formats/jam/lang/JamParserDefinition.kt create mode 100644 obfuscation-explorer/src/main/kotlin/formats/jam/lang/colors/JamAnnotator.kt create mode 100644 obfuscation-explorer/src/main/kotlin/formats/jam/lang/colors/JamSyntaxHighlighter.kt create mode 100644 obfuscation-explorer/src/main/kotlin/formats/jam/lang/colors/JamSyntaxHighlighterFactory.kt create mode 100644 obfuscation-explorer/src/main/kotlin/formats/jam/lang/psi/JamElementType.kt create mode 100644 obfuscation-explorer/src/main/kotlin/formats/jam/lang/psi/JamLexerAdapter.kt create mode 100644 obfuscation-explorer/src/main/kotlin/formats/jam/lang/psi/JamTokenType.kt create mode 100644 obfuscation-explorer/src/main/kotlin/formats/proguard/ProGuardMappingsFormatParser.kt create mode 100644 obfuscation-explorer/src/main/kotlin/formats/proguard/ProGuardMappingsFormatType.kt create mode 100644 obfuscation-explorer/src/main/kotlin/formats/proguard/lang/ProGuardFile.kt create mode 100644 obfuscation-explorer/src/main/kotlin/formats/proguard/lang/ProGuardFileType.kt create mode 100644 obfuscation-explorer/src/main/kotlin/formats/proguard/lang/ProGuardFileTypeDetector.kt create mode 100644 obfuscation-explorer/src/main/kotlin/formats/proguard/lang/ProGuardLanguage.kt create mode 100644 obfuscation-explorer/src/main/kotlin/formats/proguard/lang/ProGuardParserDefinition.kt create mode 100644 obfuscation-explorer/src/main/kotlin/formats/proguard/lang/colors/ProGuardAnnotator.kt create mode 100644 obfuscation-explorer/src/main/kotlin/formats/proguard/lang/colors/ProGuardSyntaxHighlighter.kt create mode 100644 obfuscation-explorer/src/main/kotlin/formats/proguard/lang/colors/ProGuardSyntaxHighlighterFactory.kt create mode 100644 obfuscation-explorer/src/main/kotlin/formats/proguard/lang/psi/ProGuardElementType.kt create mode 100644 obfuscation-explorer/src/main/kotlin/formats/proguard/lang/psi/ProGuardLexerAdapter.kt create mode 100644 obfuscation-explorer/src/main/kotlin/formats/proguard/lang/psi/ProGuardTokenType.kt create mode 100644 obfuscation-explorer/src/main/kotlin/formats/srg/SrgMappingsFormatParser.kt create mode 100644 obfuscation-explorer/src/main/kotlin/formats/srg/SrgMappingsFormatType.kt create mode 100644 obfuscation-explorer/src/main/kotlin/formats/srg/lang/SrgFile.kt create mode 100644 obfuscation-explorer/src/main/kotlin/formats/srg/lang/SrgFileType.kt create mode 100644 obfuscation-explorer/src/main/kotlin/formats/srg/lang/SrgLanguage.kt create mode 100644 obfuscation-explorer/src/main/kotlin/formats/srg/lang/SrgParserDefinition.kt create mode 100644 obfuscation-explorer/src/main/kotlin/formats/srg/lang/colors/SrgAnnotator.kt create mode 100644 obfuscation-explorer/src/main/kotlin/formats/srg/lang/colors/SrgSyntaxHighlighter.kt create mode 100644 obfuscation-explorer/src/main/kotlin/formats/srg/lang/colors/SrgSyntaxHighlighterFactory.kt create mode 100644 obfuscation-explorer/src/main/kotlin/formats/srg/lang/psi/SrgElementType.kt create mode 100644 obfuscation-explorer/src/main/kotlin/formats/srg/lang/psi/SrgLexerAdapter.kt create mode 100644 obfuscation-explorer/src/main/kotlin/formats/srg/lang/psi/SrgTokenType.kt create mode 100644 obfuscation-explorer/src/main/kotlin/formats/srg/lang/psi/mixins/SrgFieldMappingMixin.kt create mode 100644 obfuscation-explorer/src/main/kotlin/formats/srg/lang/psi/mixins/impl/SrgFieldMappingImplMixin.kt create mode 100644 obfuscation-explorer/src/main/kotlin/formats/tinyv1/TinyV1MappingsFormatParser.kt create mode 100644 obfuscation-explorer/src/main/kotlin/formats/tinyv1/TinyV1MappingsFormatType.kt create mode 100644 obfuscation-explorer/src/main/kotlin/formats/tinyv1/lang/TinyFileTypeDetector.kt create mode 100644 obfuscation-explorer/src/main/kotlin/formats/tinyv1/lang/TinyV1File.kt create mode 100644 obfuscation-explorer/src/main/kotlin/formats/tinyv1/lang/TinyV1FileType.kt create mode 100644 obfuscation-explorer/src/main/kotlin/formats/tinyv1/lang/TinyV1Language.kt create mode 100644 obfuscation-explorer/src/main/kotlin/formats/tinyv1/lang/TinyV1ParserDefinition.kt create mode 100644 obfuscation-explorer/src/main/kotlin/formats/tinyv1/lang/colors/TinyV1Annotator.kt create mode 100644 obfuscation-explorer/src/main/kotlin/formats/tinyv1/lang/colors/TinyV1RainbowVisitor.kt create mode 100644 obfuscation-explorer/src/main/kotlin/formats/tinyv1/lang/colors/TinyV1SyntaxHighlighter.kt create mode 100644 obfuscation-explorer/src/main/kotlin/formats/tinyv1/lang/colors/TinyV1SyntaxHighlighterFactory.kt create mode 100644 obfuscation-explorer/src/main/kotlin/formats/tinyv1/lang/psi/TinyV1ElementType.kt create mode 100644 obfuscation-explorer/src/main/kotlin/formats/tinyv1/lang/psi/TinyV1LexerAdapter.kt create mode 100644 obfuscation-explorer/src/main/kotlin/formats/tinyv1/lang/psi/TinyV1TokenType.kt create mode 100644 obfuscation-explorer/src/main/kotlin/formats/tinyv2/TinyV2LanguageCodeStyleSettingsProvider.kt create mode 100644 obfuscation-explorer/src/main/kotlin/formats/tinyv2/TinyV2MappingsFormatParser.kt create mode 100644 obfuscation-explorer/src/main/kotlin/formats/tinyv2/TinyV2MappingsFormatType.kt create mode 100644 obfuscation-explorer/src/main/kotlin/formats/tinyv2/lang/TinyV2File.kt create mode 100644 obfuscation-explorer/src/main/kotlin/formats/tinyv2/lang/TinyV2FileType.kt create mode 100644 obfuscation-explorer/src/main/kotlin/formats/tinyv2/lang/TinyV2Language.kt create mode 100644 obfuscation-explorer/src/main/kotlin/formats/tinyv2/lang/TinyV2ParserDefinition.kt create mode 100644 obfuscation-explorer/src/main/kotlin/formats/tinyv2/lang/colors/TinyV2Annotator.kt create mode 100644 obfuscation-explorer/src/main/kotlin/formats/tinyv2/lang/colors/TinyV2RainbowVisitor.kt create mode 100644 obfuscation-explorer/src/main/kotlin/formats/tinyv2/lang/colors/TinyV2SyntaxHighlighter.kt create mode 100644 obfuscation-explorer/src/main/kotlin/formats/tinyv2/lang/colors/TinyV2SyntaxHighlighterFactory.kt create mode 100644 obfuscation-explorer/src/main/kotlin/formats/tinyv2/lang/psi/TinyV2ElementType.kt create mode 100644 obfuscation-explorer/src/main/kotlin/formats/tinyv2/lang/psi/TinyV2LexerAdapter.kt create mode 100644 obfuscation-explorer/src/main/kotlin/formats/tinyv2/lang/psi/TinyV2TokenType.kt create mode 100644 obfuscation-explorer/src/main/kotlin/formats/tsrg/TSrgLanguageCodeStyleSettingsProvider.kt create mode 100644 obfuscation-explorer/src/main/kotlin/formats/tsrg/TSrgMappingsFormatParser.kt create mode 100644 obfuscation-explorer/src/main/kotlin/formats/tsrg/TSrgMappingsFormatType.kt create mode 100644 obfuscation-explorer/src/main/kotlin/formats/tsrg/lang/TSrgFile.kt create mode 100644 obfuscation-explorer/src/main/kotlin/formats/tsrg/lang/TSrgFileType.kt create mode 100644 obfuscation-explorer/src/main/kotlin/formats/tsrg/lang/TSrgFileTypeDetector.kt create mode 100644 obfuscation-explorer/src/main/kotlin/formats/tsrg/lang/TSrgLanguage.kt create mode 100644 obfuscation-explorer/src/main/kotlin/formats/tsrg/lang/TSrgParserDefinition.kt create mode 100644 obfuscation-explorer/src/main/kotlin/formats/tsrg/lang/colors/TSrgAnnotator.kt create mode 100644 obfuscation-explorer/src/main/kotlin/formats/tsrg/lang/colors/TSrgSyntaxHighlighter.kt create mode 100644 obfuscation-explorer/src/main/kotlin/formats/tsrg/lang/colors/TSrgSyntaxHighlighterFactory.kt create mode 100644 obfuscation-explorer/src/main/kotlin/formats/tsrg/lang/psi/TSrgElementType.kt create mode 100644 obfuscation-explorer/src/main/kotlin/formats/tsrg/lang/psi/TSrgLexerAdapter.kt create mode 100644 obfuscation-explorer/src/main/kotlin/formats/tsrg/lang/psi/TSrgTokenType.kt create mode 100644 obfuscation-explorer/src/main/kotlin/formats/tsrg2/TSrg2LanguageCodeStyleSettingsProvider.kt create mode 100644 obfuscation-explorer/src/main/kotlin/formats/tsrg2/TSrg2MappingsFormatParser.kt create mode 100644 obfuscation-explorer/src/main/kotlin/formats/tsrg2/TSrg2MappingsFormatType.kt create mode 100644 obfuscation-explorer/src/main/kotlin/formats/tsrg2/lang/TSrg2File.kt create mode 100644 obfuscation-explorer/src/main/kotlin/formats/tsrg2/lang/TSrg2FileType.kt create mode 100644 obfuscation-explorer/src/main/kotlin/formats/tsrg2/lang/TSrg2Language.kt create mode 100644 obfuscation-explorer/src/main/kotlin/formats/tsrg2/lang/TSrg2LayoutLexer.kt create mode 100644 obfuscation-explorer/src/main/kotlin/formats/tsrg2/lang/TSrg2ParserDefinition.kt create mode 100644 obfuscation-explorer/src/main/kotlin/formats/tsrg2/lang/colors/TSrg2Annotator.kt create mode 100644 obfuscation-explorer/src/main/kotlin/formats/tsrg2/lang/colors/TSrg2RainbowVisitor.kt create mode 100644 obfuscation-explorer/src/main/kotlin/formats/tsrg2/lang/colors/TSrg2SyntaxHighlighter.kt create mode 100644 obfuscation-explorer/src/main/kotlin/formats/tsrg2/lang/colors/TSrg2SyntaxHighlighterFactory.kt create mode 100644 obfuscation-explorer/src/main/kotlin/formats/tsrg2/lang/psi/TSrg2ElementType.kt create mode 100644 obfuscation-explorer/src/main/kotlin/formats/tsrg2/lang/psi/TSrg2LexerAdapter.kt create mode 100644 obfuscation-explorer/src/main/kotlin/formats/tsrg2/lang/psi/TSrg2TokenType.kt create mode 100644 obfuscation-explorer/src/main/kotlin/formats/util/NamespaceRainbowVisitor.kt create mode 100644 obfuscation-explorer/src/main/kotlin/formats/util/TabCodeStyleSettingsProvider.kt create mode 100644 obfuscation-explorer/src/main/kotlin/formats/util/sigws/SignificantWhitespaceLexer.kt create mode 100644 obfuscation-explorer/src/main/kotlin/formats/util/sigws/SignificantWhitespaceParserUtil.kt create mode 100644 obfuscation-explorer/src/main/kotlin/formats/util/util.kt create mode 100644 obfuscation-explorer/src/main/kotlin/formats/xsrg/XSrgMappingsFormatParser.kt create mode 100644 obfuscation-explorer/src/main/kotlin/formats/xsrg/XSrgMappingsFormatType.kt create mode 100644 obfuscation-explorer/src/main/kotlin/formats/xsrg/lang/XSrgFile.kt create mode 100644 obfuscation-explorer/src/main/kotlin/formats/xsrg/lang/XSrgFileType.kt create mode 100644 obfuscation-explorer/src/main/kotlin/formats/xsrg/lang/XSrgLanguage.kt create mode 100644 obfuscation-explorer/src/main/kotlin/formats/xsrg/lang/XSrgParserDefinition.kt create mode 100644 obfuscation-explorer/src/main/kotlin/lookup/HashLookupIndex.kt create mode 100644 obfuscation-explorer/src/main/kotlin/lookup/HashLookupTable.kt create mode 100644 obfuscation-explorer/src/main/kotlin/lookup/LookupIndex.kt create mode 100644 obfuscation-explorer/src/main/kotlin/lookup/MultiLookupTable.kt create mode 100644 obfuscation-explorer/src/main/kotlin/mappings/MappingElement.kt create mode 100644 obfuscation-explorer/src/main/kotlin/mappings/MappingLocation.kt create mode 100644 obfuscation-explorer/src/main/kotlin/mappings/MappingNamespace.kt create mode 100644 obfuscation-explorer/src/main/kotlin/mappings/MappingSet.kt create mode 100644 obfuscation-explorer/src/main/kotlin/mappings/MappingSetBuilder.kt create mode 100644 obfuscation-explorer/src/main/kotlin/mappings/MappingSetSource.kt create mode 100644 obfuscation-explorer/src/main/kotlin/mappings/MappingsDefinition.kt create mode 100644 obfuscation-explorer/src/main/kotlin/mappings/MappingsFormatParser.kt create mode 100644 obfuscation-explorer/src/main/kotlin/mappings/store/MappingLookupIndex.kt create mode 100644 obfuscation-explorer/src/main/kotlin/mappings/store/MappingLookupTable.kt create mode 100644 obfuscation-explorer/src/main/kotlin/mappings/store/NamespaceLookup.kt create mode 100644 obfuscation-explorer/src/main/kotlin/ref/ref.kt create mode 100644 obfuscation-explorer/src/main/kotlin/ref/types.kt create mode 100644 obfuscation-explorer/src/main/kotlin/util.kt create mode 100644 obfuscation-explorer/src/main/resources/META-INF/plugin.xml create mode 100644 obfuscation-explorer/src/main/resources/META-INF/pluginIcon.svg create mode 100644 obfuscation-explorer/src/main/resources/fileTypes/csrg.svg create mode 100644 obfuscation-explorer/src/main/resources/fileTypes/enigma.svg create mode 100644 obfuscation-explorer/src/main/resources/fileTypes/jam.svg create mode 100644 obfuscation-explorer/src/main/resources/fileTypes/obfex.svg create mode 100644 obfuscation-explorer/src/main/resources/fileTypes/proguard.svg create mode 100644 obfuscation-explorer/src/main/resources/fileTypes/srg.svg create mode 100644 obfuscation-explorer/src/main/resources/fileTypes/tinyv1.svg create mode 100644 obfuscation-explorer/src/main/resources/fileTypes/tinyv2.svg create mode 100644 obfuscation-explorer/src/main/resources/fileTypes/tsrg.svg create mode 100644 obfuscation-explorer/src/main/resources/fileTypes/tsrg2.svg create mode 100644 obfuscation-explorer/src/main/resources/fileTypes/xsrg.svg create mode 100644 obfuscation-explorer/src/test/kotlin/TypeParsingTest.kt create mode 100644 obfuscation-explorer/src/test/kotlin/formats/ParserFixture.kt create mode 100644 obfuscation-explorer/src/test/kotlin/formats/csrg/CSrgMappingsFormatParserTest.kt create mode 100644 obfuscation-explorer/src/test/kotlin/formats/enigma/EnigmaLexerTest.kt create mode 100644 obfuscation-explorer/src/test/kotlin/formats/enigma/EnigmaMappingsFormatParserTest.kt create mode 100644 obfuscation-explorer/src/test/kotlin/formats/jam/JamMappingsFormatParserTest.kt create mode 100644 obfuscation-explorer/src/test/kotlin/formats/proguard/ProGuardMappingsFormatParserTest.kt create mode 100644 obfuscation-explorer/src/test/kotlin/formats/srg/SrgLexerTest.kt create mode 100644 obfuscation-explorer/src/test/kotlin/formats/srg/SrgMappingsFormatParserTest.kt create mode 100644 obfuscation-explorer/src/test/kotlin/formats/tinyv1/TinyV1MappingsFormatParserTest.kt create mode 100644 obfuscation-explorer/src/test/kotlin/formats/tinyv2/TinyV2LexerTest.kt create mode 100644 obfuscation-explorer/src/test/kotlin/formats/tinyv2/TinyV2MappingsFormatParserTest.kt create mode 100644 obfuscation-explorer/src/test/kotlin/formats/tsrg/TSrgMappingsFormatParserTest.kt create mode 100644 obfuscation-explorer/src/test/kotlin/formats/tsrg2/TSrg2LexerTest.kt create mode 100644 obfuscation-explorer/src/test/kotlin/formats/tsrg2/TSrg2MappingsFormatParserTest.kt create mode 100644 obfuscation-explorer/src/test/kotlin/formats/xsrg/XSrgMappingsFormatParserTest.kt create mode 100644 obfuscation-explorer/src/test/resources/io/mcdev/obfex/formats/enigma/lexer/fixtures/test.mapping create mode 100644 obfuscation-explorer/src/test/resources/io/mcdev/obfex/formats/enigma/lexer/fixtures/test.txt create mode 100644 obfuscation-explorer/src/test/resources/io/mcdev/obfex/formats/srg/lexer/fixtures/test.srg create mode 100644 obfuscation-explorer/src/test/resources/io/mcdev/obfex/formats/srg/lexer/fixtures/test.txt create mode 100644 obfuscation-explorer/src/test/resources/io/mcdev/obfex/formats/tinyv2/lexer/fixtures/test.tiny create mode 100644 obfuscation-explorer/src/test/resources/io/mcdev/obfex/formats/tinyv2/lexer/fixtures/test.txt create mode 100644 obfuscation-explorer/src/test/resources/io/mcdev/obfex/formats/tsrg2/lexer/fixtures/test.tsrg2 create mode 100644 obfuscation-explorer/src/test/resources/io/mcdev/obfex/formats/tsrg2/lexer/fixtures/test.txt diff --git a/build.gradle.kts b/build.gradle.kts index 8dda80673..e2df1d8af 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -18,52 +18,37 @@ * along with this program. If not, see . */ -import org.cadixdev.gradle.licenser.header.HeaderStyle -import org.cadixdev.gradle.licenser.tasks.LicenseUpdate import org.gradle.internal.jvm.Jvm import org.jetbrains.changelog.Changelog import org.jetbrains.gradle.ext.settings import org.jetbrains.gradle.ext.taskTriggers import org.jetbrains.intellij.tasks.PrepareSandboxTask -import org.jetbrains.kotlin.gradle.tasks.KotlinCompile -import org.jlleitschuh.gradle.ktlint.tasks.BaseKtLintCheckTask -import org.jlleitschuh.gradle.ktlint.tasks.KtLintFormatTask plugins { - kotlin("jvm") version "1.9.20" java - mcdev groovy idea - id("org.jetbrains.intellij") version "1.17.2" - id("org.cadixdev.licenser") - id("org.jlleitschuh.gradle.ktlint") version "10.3.0" - id("org.jetbrains.changelog") version "2.2.0" + id(libs.plugins.kotlin.get().pluginId) + id(libs.plugins.intellij.get().pluginId) + id(libs.plugins.licenser.get().pluginId) + id(libs.plugins.ktlint.get().pluginId) + id(libs.plugins.changelog.get().pluginId) + `mcdev-core` + `mcdev-parsing` + `mcdev-publishing` } val ideaVersionName: String by project val coreVersion: String by project -val pluginTomlVersion: String by project val gradleToolingExtension: Configuration by configurations.creating val testLibs: Configuration by configurations.creating { isTransitive = false } -group = "com.demonwav.minecraft-dev" +group = "com.demonwav.mcdev" version = "$ideaVersionName-$coreVersion" -java { - toolchain { - languageVersion.set(JavaLanguageVersion.of(17)) - } -} -kotlin { - jvmToolchain { - languageVersion.set(java.toolchain.languageVersion.get()) - } -} - val gradleToolingExtensionSourceSet: SourceSet = sourceSets.create("gradle-tooling-extension") { configurations.named(compileOnlyConfigurationName) { extendsFrom(gradleToolingExtension) @@ -100,45 +85,20 @@ val externalAnnotationsJar = tasks.register("externalAnnotationsJar") { archiveFileName.set("externalAnnotations.jar") } -repositories { - maven("https://repo.denwav.dev/repository/maven-public/") - maven("https://maven.fabricmc.net/") { - content { - includeModule("net.fabricmc", "mapping-io") - includeModule("net.fabricmc", "fabric-loader") - } - } - mavenCentral() - maven("https://repo.spongepowered.org/maven/") -} - dependencies { // Add tools.jar for the JDI API implementation(files(Jvm.current().toolsJar)) + implementation(files(gradleToolingExtensionJar)) + implementation(libs.mixinExtras.expressions) testLibs(libs.mixinExtras.common) - // Kotlin - implementation(kotlin("stdlib-jdk8")) - implementation(kotlin("reflect")) - implementation(libs.bundles.coroutines) - - implementation(files(gradleToolingExtensionJar)) - implementation(libs.mappingIo) implementation(libs.bundles.asm) implementation(libs.bundles.fuel) - jflex(libs.jflex.lib) - jflexSkeleton(libs.jflex.skeleton) { - artifact { - extension = "skeleton" - } - } - grammarKit(libs.grammarKit) - testLibs(libs.test.mockJdk) testLibs(libs.test.mixin) testLibs(libs.test.spongeapi) { @@ -181,7 +141,7 @@ dependencies { to.attribute(filtered, true).attribute(artifactType, "jar") parameters { - ideaVersion.set(providers.gradleProperty("ideaVersion")) + ideaVersion.set(libs.versions.intellij.ide) ideaVersionName.set(providers.gradleProperty("ideaVersionName")) depsFile.set(layout.projectDirectory.file(".gradle/intellij-deps.json")) } @@ -199,8 +159,6 @@ changelog { } intellij { - // IntelliJ IDEA dependency - version.set(providers.gradleProperty("ideaVersion")) // Bundled plugin dependencies plugins.addAll( "java", @@ -208,20 +166,15 @@ intellij { "gradle", "Groovy", "Kotlin", - "org.toml.lang:$pluginTomlVersion", "ByteCodeViewer", "org.intellij.intelliLang", "properties", // needed dependencies for unit tests "junit" ) + plugins.addProvider(libs.versions.pluginToml.map { "org.toml.lang:$it" }) pluginName.set("Minecraft Development") - updateSinceUntilBuild.set(true) - - downloadSources.set(providers.gradleProperty("downloadIdeaSources").map { it.toBoolean() }) - - sandboxDir.set(layout.projectDirectory.dir(".sandbox").toString()) } tasks.patchPluginXml { @@ -229,36 +182,10 @@ tasks.patchPluginXml { changeNotes = changelog.render(Changelog.OutputType.HTML) } -tasks.publishPlugin { - // Build numbers are used for - properties["buildNumber"]?.let { buildNumber -> - project.version = "${project.version}-$buildNumber" - } - properties["mcdev.deploy.token"]?.let { deployToken -> - token.set(deployToken.toString()) - } - channels.add(properties["mcdev.deploy.channel"]?.toString() ?: "Stable") -} - tasks.runPluginVerifier { ideVersions.addAll("IC-$ideaVersionName") } -tasks.withType().configureEach { - options.encoding = "UTF-8" - options.compilerArgs = listOf("-proc:none") - options.release.set(17) -} - -tasks.withType().configureEach { - kotlinOptions { - jvmTarget = JavaVersion.VERSION_17.toString() - // K2 causes the following error: https://youtrack.jetbrains.com/issue/KT-52786 - freeCompilerArgs = listOf(/*"-Xuse-k2", */"-Xjvm-default=all", "-Xjdk-release=17") - kotlinDaemonJvmArguments.add("-Xmx2G") - } -} - // Compile classes to be loaded into the Gradle VM to Java 5 to match Groovy // This is for maximum compatibility, these classes will be loaded into every Gradle import on all // projects (not just Minecraft), so we don't want to break that with an incompatible class version. @@ -294,10 +221,11 @@ tasks.processResources { tasks.test { dependsOn(tasks.jar, testLibs) - useJUnitPlatform() - testLibs.resolvedConfiguration.resolvedArtifacts.forEach { - systemProperty("testLibs.${it.name}", it.file.absolutePath) + doFirst { + testLibs.resolvedConfiguration.resolvedArtifacts.forEach { + systemProperty("testLibs.${it.name}", it.file.absolutePath) + } } systemProperty("NO_FS_ROOTS_ACCESS_CHECK", "true") systemProperty("java.awt.headless", "true") @@ -310,23 +238,10 @@ tasks.test { idea { project.settings.taskTriggers.afterSync("generate") - module { - generatedSourceDirs.add(file("build/gen")) - excludeDirs.add(file(intellij.sandboxDir.get())) - isDownloadJavadoc = true - isDownloadSources = true - } } license { - header.set(resources.text.fromFile(file("copyright.txt"))) - style["flex"] = HeaderStyle.BLOCK_COMMENT.format - style["bnf"] = HeaderStyle.BLOCK_COMMENT.format - - val endings = listOf("java", "kt", "kts", "groovy", "gradle.kts", "xml", "properties", "html", "flex", "bnf") exclude("META-INF/plugin.xml") // https://youtrack.jetbrains.com/issue/IDEA-345026 - include(endings.map { "**/*.$it" }) - exclude("com/demonwav/mcdev/platform/mixin/invalidInjectorMethodSignature/*.java") this.tasks { @@ -355,19 +270,6 @@ license { } } -ktlint { - disabledRules.add("filename") -} -tasks.withType().configureEach { - workerMaxHeapSize.set("512m") -} - -tasks.register("format") { - group = "minecraft" - description = "Formats source code according to project style" - dependsOn(tasks.withType(), tasks.withType()) -} - val generateAtLexer by lexer("AtLexer", "com/demonwav/mcdev/platform/mcp/at/gen") val generateAtParser by parser("AtParser", "com/demonwav/mcdev/platform/mcp/at/gen") @@ -412,12 +314,6 @@ sourceSets.main { java.srcDir(generate) } // Remove gen directory on clean tasks.clean { delete(generate) } -tasks.register("cleanSandbox", Delete::class) { - group = "intellij" - description = "Deletes the sandbox directory." - delete(layout.projectDirectory.dir(".sandbox")) -} - tasks.withType { pluginJar.set(tasks.jar.get().archiveFile) from(externalAnnotationsJar) { diff --git a/buildSrc/build.gradle.kts b/buildSrc/build.gradle.kts index b608a94d9..4e24b640f 100644 --- a/buildSrc/build.gradle.kts +++ b/buildSrc/build.gradle.kts @@ -1,3 +1,25 @@ +/* + * Minecraft Development for IntelliJ + * + * https://mcdev.io/ + * + * Copyright (C) 2024 minecraft-dev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, version 3.0 only. + * + * 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 Lesser General Public License + * along with this program. If not, see . + */ + +import org.jetbrains.kotlin.gradle.tasks.KotlinCompile + /* * Minecraft Development for IntelliJ * @@ -22,12 +44,25 @@ plugins { `kotlin-dsl` } +tasks.withType().configureEach { + options.release.set(8) +} +tasks.withType().configureEach { + kotlinOptions.jvmTarget = "1.8" +} + repositories { mavenCentral() gradlePluginPortal() } dependencies { - implementation("com.google.code.gson:gson:2.9.1") - implementation("org.cadixdev.licenser:org.cadixdev.licenser.gradle.plugin:0.6.1") + // hack for version catalogs + implementation(files(libs.javaClass.superclass.protectionDomain.codeSource.location)) + implementation(libs.gson) + implementation(libs.kotlin.plugin) + implementation(libs.intellij.plugin) + implementation(libs.licenser.plugin) + implementation(libs.ktlint.plugin) + implementation(libs.changelog.plugin) } diff --git a/buildSrc/settings.gradle.kts b/buildSrc/settings.gradle.kts index ab35e99af..4bb019498 100644 --- a/buildSrc/settings.gradle.kts +++ b/buildSrc/settings.gradle.kts @@ -19,3 +19,11 @@ */ rootProject.name = "buildSrc" + +dependencyResolutionManagement { + versionCatalogs { + create("libs") { + from(files("../gradle/libs.versions.toml")) + } + } +} diff --git a/buildSrc/src/main/kotlin/ParserExec.kt b/buildSrc/src/main/kotlin/ParserExec.kt index ad00c9014..870efce0d 100644 --- a/buildSrc/src/main/kotlin/ParserExec.kt +++ b/buildSrc/src/main/kotlin/ParserExec.kt @@ -62,6 +62,7 @@ abstract class ParserExec : JavaExec() { init { mainClass.set("org.intellij.grammar.Main") + @Suppress("LeakingThis") jvmArgs( "--add-opens", "java.base/java.lang=ALL-UNNAMED", "--add-opens", "java.base/java.lang.reflect=ALL-UNNAMED", diff --git a/buildSrc/src/main/kotlin/mcdev.gradle.kts b/buildSrc/src/main/kotlin/mcdev-core.gradle.kts similarity index 56% rename from buildSrc/src/main/kotlin/mcdev.gradle.kts rename to buildSrc/src/main/kotlin/mcdev-core.gradle.kts index 99a58e2eb..88994e8e1 100644 --- a/buildSrc/src/main/kotlin/mcdev.gradle.kts +++ b/buildSrc/src/main/kotlin/mcdev-core.gradle.kts @@ -20,17 +20,129 @@ import com.google.gson.Gson import com.google.gson.GsonBuilder -import com.google.gson.reflect.TypeToken import java.net.HttpURLConnection import java.net.URI -import java.net.URL import java.util.Properties import java.util.zip.ZipFile +import org.cadixdev.gradle.licenser.header.HeaderStyle +import org.gradle.kotlin.dsl.maven +import org.gradle.kotlin.dsl.repositories +import org.gradle.kotlin.dsl.withType +import org.jetbrains.kotlin.gradle.tasks.KotlinCompile +import org.gradle.accessors.dm.LibrariesForLibs +import org.jetbrains.kotlin.gradle.dsl.JvmTarget +import org.jetbrains.kotlin.gradle.dsl.KotlinVersion +import org.jlleitschuh.gradle.ktlint.tasks.KtLintFormatTask -val jflex: Configuration by configurations.creating -val jflexSkeleton: Configuration by configurations.creating -val grammarKit: Configuration by configurations.creating -val compileOnly by configurations +plugins { + java + idea + id("org.jetbrains.kotlin.jvm") + id("org.jetbrains.intellij") + id("org.cadixdev.licenser") + id("org.jlleitschuh.gradle.ktlint") +} + +java { + toolchain { + languageVersion.set(JavaLanguageVersion.of(17)) + } +} + +tasks.withType().configureEach { + options.encoding = "UTF-8" + options.compilerArgs = listOf("-proc:none") + options.release.set(17) +} + +kotlin { + jvmToolchain { + languageVersion.set(JavaLanguageVersion.of(17)) + } +} + +tasks.withType().configureEach { + compilerOptions { + jvmTarget = JvmTarget.JVM_17 + languageVersion = KotlinVersion.KOTLIN_2_0 + freeCompilerArgs = listOf("-Xjvm-default=all", "-Xjdk-release=17") + optIn.add("kotlin.contracts.ExperimentalContracts") + } + kotlinDaemonJvmArguments.add("-Xmx2G") +} + +repositories { + maven("https://repo.denwav.dev/repository/maven-public/") + maven("https://maven.fabricmc.net/") { + content { + includeModule("net.fabricmc", "mapping-io") + includeModule("net.fabricmc", "fabric-loader") + } + } + mavenCentral() + maven("https://repo.spongepowered.org/maven/") +} + +val libs = the() +dependencies { + implementation(libs.kotlin.stdlib) + implementation(libs.kotlin.reflect) + implementation(libs.bundles.coroutines) + + testImplementation(libs.junit.api) + testRuntimeOnly(libs.junit.entine) + testRuntimeOnly(libs.junit.platform.launcher) +} + +intellij { + // IntelliJ IDEA dependency + version.set(libs.versions.intellij.ide) + + updateSinceUntilBuild.set(true) + downloadSources.set(providers.gradleProperty("downloadIdeaSources").map { it.toBoolean() }) + + sandboxDir.set(layout.projectDirectory.dir(".sandbox").toString()) +} + +license { + header.set(resources.text.fromFile(rootProject.layout.projectDirectory.file("copyright.txt"))) + style["flex"] = HeaderStyle.BLOCK_COMMENT.format + style["bnf"] = HeaderStyle.BLOCK_COMMENT.format + + val endings = listOf("java", "kt", "kts", "groovy", "gradle.kts", "xml", "properties", "html", "flex", "bnf") + include(endings.map { "**/*.$it" }) +} + +idea { + module { + excludeDirs.add(file(intellij.sandboxDir.get())) + } +} + +tasks.withType().configureEach { + workerMaxHeapSize.set("1G") +} + +tasks.runIde { + maxHeapSize = "2G" + jvmArgs("--add-exports=java.base/jdk.internal.vm=ALL-UNNAMED") +} + +tasks.register("cleanSandbox", Delete::class) { + group = "intellij" + description = "Deletes the sandbox directory." + delete(layout.projectDirectory.dir(".sandbox")) +} + +tasks.test { + useJUnitPlatform() +} + +tasks.register("format") { + group = "minecraft" + description = "Formats source code according to project style" + dependsOn(tasks.licenseFormat, tasks.ktlintFormat) +} // Analyze dependencies val fileName = ".gradle/intellij-deps.json" @@ -107,6 +219,7 @@ tasks.register("resolveIntellijLibSources") { }.toList() val depList = DepList(ideaVersion, ideaVersionName, deps.sortedWith(compareBy { it.groupId }.thenBy { it.artifactId })) + jsonFile.parentFile.mkdirs() jsonFile.bufferedWriter().use { writer -> GsonBuilder().setPrettyPrinting().create().toJson(depList, writer) } diff --git a/buildSrc/src/main/kotlin/mcdev-parsing.gradle.kts b/buildSrc/src/main/kotlin/mcdev-parsing.gradle.kts new file mode 100644 index 000000000..bdd2234e7 --- /dev/null +++ b/buildSrc/src/main/kotlin/mcdev-parsing.gradle.kts @@ -0,0 +1,46 @@ +/* + * Minecraft Development for IntelliJ + * + * https://mcdev.io/ + * + * Copyright (C) 2024 minecraft-dev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, version 3.0 only. + * + * 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 Lesser General Public License + * along with this program. If not, see . + */ + +import org.gradle.accessors.dm.LibrariesForLibs + +plugins { + idea +} + +val jflex: Configuration by configurations.creating +val jflexSkeleton: Configuration by configurations.creating +val grammarKit: Configuration by configurations.creating + +val libs = the() +dependencies { + jflex(libs.jflex.lib) + jflexSkeleton(libs.jflex.skeleton) { + artifact { + extension = "skeleton" + } + } + grammarKit(libs.grammarKit) +} + +idea { + module { + generatedSourceDirs.add(file("build/gen")) + } +} diff --git a/buildSrc/src/main/kotlin/mcdev-publishing.gradle.kts b/buildSrc/src/main/kotlin/mcdev-publishing.gradle.kts new file mode 100644 index 000000000..33c636ede --- /dev/null +++ b/buildSrc/src/main/kotlin/mcdev-publishing.gradle.kts @@ -0,0 +1,34 @@ +/* + * Minecraft Development for IntelliJ + * + * https://mcdev.io/ + * + * Copyright (C) 2024 minecraft-dev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, version 3.0 only. + * + * 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 Lesser General Public License + * along with this program. If not, see . + */ + +plugins { + id("org.jetbrains.intellij") +} + +tasks.publishPlugin { + // Build numbers are used for nightlies + properties["buildNumber"]?.let { buildNumber -> + project.version = "${project.version}-$buildNumber" + } + properties["mcdev.deploy.token"]?.let { deployToken -> + token.set(deployToken.toString()) + } + channels.add(properties["mcdev.deploy.channel"]?.toString() ?: "Stable") +} diff --git a/buildSrc/src/main/kotlin/util.kt b/buildSrc/src/main/kotlin/util.kt index d36de248f..9236d4a19 100644 --- a/buildSrc/src/main/kotlin/util.kt +++ b/buildSrc/src/main/kotlin/util.kt @@ -22,19 +22,23 @@ import java.io.ByteArrayOutputStream import org.cadixdev.gradle.licenser.LicenseExtension import org.gradle.api.JavaVersion import org.gradle.api.Project +import org.gradle.api.provider.ListProperty +import org.gradle.api.provider.Provider import org.gradle.api.tasks.JavaExec import org.gradle.api.tasks.TaskContainer -import org.gradle.api.tasks.TaskProvider +import org.gradle.api.tasks.util.PatternFilterable import org.gradle.kotlin.dsl.RegisteringDomainObjectDelegateProviderWithTypeAndAction import org.gradle.kotlin.dsl.getValue import org.gradle.kotlin.dsl.provideDelegate import org.gradle.kotlin.dsl.registering import org.gradle.kotlin.dsl.configure +fun ListProperty.addProvider(provider: Provider) = add(provider) + typealias TaskDelegate = RegisteringDomainObjectDelegateProviderWithTypeAndAction fun Project.lexer(flex: String, pack: String): TaskDelegate { - configure { + extensions.configure("license") { exclude(pack.removeSuffix("/") + "/**") } @@ -53,7 +57,7 @@ fun Project.lexer(flex: String, pack: String): TaskDelegate { } fun Project.parser(bnf: String, pack: String): TaskDelegate { - configure { + extensions.configure("license") { exclude(pack.removeSuffix("/") + "/**") } diff --git a/gradle.properties b/gradle.properties index 5d3d680ee..6b35bbedd 100644 --- a/gradle.properties +++ b/gradle.properties @@ -19,9 +19,6 @@ # # suppress inspection "UnusedProperty" for whole file -kotlin.code.style=official - -ideaVersion = 2023.2.2 ideaVersionName = 2023.2.2 coreVersion = 1.8.0 diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 9c1c92308..a2627910d 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,11 +1,35 @@ [versions] -coroutines = "1.6.4" -junit = "5.9.0" -junit-platform = "1.9.0" -asm = "9.3" +kotlin = "2.0.0" +coroutines = "1.8.0-RC2" +junit = "5.10.2" +junit-platform = "1.10.2" +asm = "9.6" fuel = "2.3.1" +licenser = "0.6.1" +ktlint = "10.3.0" +changelog = "2.2.0" +intellij-plugin = "1.17.2" +intellij-ide = "2023.2.2" +pluginToml = "232.8660.88" +psiPlugin = "232.2-SNAPSHOT" + +[plugins] +kotlin = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin" } +intellij = { id = "org.jetbrains.intellij", version.ref = "intellij-plugin" } +licenser = { id = "org.cadixdev.licenser", version.ref = "licenser" } +ktlint = { id = "org.jlleitschuh.gradle.ktlint", version.ref = "ktlint" } +changelog = { id = "org.jetbrains.changelog", version.ref = "changelog" } [libraries] +kotlin-plugin = { module = "org.jetbrains.kotlin.jvm:org.jetbrains.kotlin.jvm.gradle.plugin", version.ref = "kotlin" } +kotlin-stdlib = { module = "org.jetbrains.kotlin:kotlin-stdlib-jdk8", version.ref = "kotlin" } +kotlin-reflect = { module = "org.jetbrains.kotlin:kotlin-reflect", version.ref = "kotlin" } + +intellij-plugin = { module = "org.jetbrains.intellij:org.jetbrains.intellij.gradle.plugin", version.ref = "intellij-plugin" } +licenser-plugin = { module = "org.cadixdev.licenser:org.cadixdev.licenser.gradle.plugin", version.ref = "licenser" } +ktlint-plugin = { module = "org.jlleitschuh.gradle.ktlint:org.jlleitschuh.gradle.ktlint.gradle.plugin", version.ref = "ktlint" } +changelog-plugin = { module = "org.jetbrains.changelog:org.jetbrains.changelog.gradle.plugin", version.ref = "changelog" } + coroutines-core = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-core", version.ref = "coroutines" } coroutines-jdk8 = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-jdk8", version.ref = "coroutines" } coroutines-swing = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-swing", version.ref = "coroutines" } @@ -20,7 +44,7 @@ grammarKit = "org.jetbrains.idea:grammar-kit:1.5.1" # Gradle Tooling gradleToolingExtension = "com.jetbrains.intellij.gradle:gradle-tooling-extension:232-EAP-SNAPSHOT" -annotations = "org.jetbrains:annotations:23.0.0" +annotations = "org.jetbrains:annotations:24.0.0" groovy = "org.codehaus.groovy:groovy-all:2.5.18" asm = { module = "org.ow2.asm:asm", version.ref = "asm" } @@ -28,6 +52,8 @@ asm-tree = { module = "org.ow2.asm:asm-tree", version.ref = "asm" } asm-analysis = { module = "org.ow2.asm:asm-analysis", version.ref = "asm" } asm-util = { module = "org.ow2.asm:asm-util", version.ref = "asm" } +gson = "com.google.code.gson:gson:2.10.1" + fuel = { module = "com.github.kittinunf.fuel:fuel", version.ref = "fuel" } fuel-coroutines = { module = "com.github.kittinunf.fuel:fuel-coroutines", version.ref = "fuel" } diff --git a/obfuscation-explorer/.gitignore b/obfuscation-explorer/.gitignore new file mode 100644 index 000000000..d094c39b9 --- /dev/null +++ b/obfuscation-explorer/.gitignore @@ -0,0 +1,3 @@ +.sandbox/ +build/ +gen/ diff --git a/obfuscation-explorer/build.gradle.kts b/obfuscation-explorer/build.gradle.kts new file mode 100644 index 000000000..1c47bfaf8 --- /dev/null +++ b/obfuscation-explorer/build.gradle.kts @@ -0,0 +1,128 @@ +/* + * Minecraft Development for IntelliJ + * + * https://mcdev.io/ + * + * Copyright (C) 2024 minecraft-dev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, version 3.0 only. + * + * 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 Lesser General Public License + * along with this program. If not, see . + */ + +plugins { + java + groovy + idea + id(libs.plugins.kotlin.get().pluginId) + id(libs.plugins.intellij.get().pluginId) + id(libs.plugins.licenser.get().pluginId) + id(libs.plugins.ktlint.get().pluginId) + `mcdev-core` + `mcdev-parsing` + `mcdev-publishing` +} + +val ideaVersion: String by project +val ideaVersionName: String by project +val coreVersion: String by project +val downloadIdeaSources: String by project + +val jflex by configurations +val jflexSkeleton by configurations +val grammarKit by configurations + +group = "io.mcdev.obfex" +version = "$ideaVersionName-$coreVersion" + +intellij { + plugins.addProvider(libs.versions.psiPlugin.map { "PsiViewer:$it" }) + + pluginName.set("Obfuscation Explorer") +} + +val generateSrgLexer by lexer("SrgLexer", "io/mcdev/obfex/formats/srg/gen") +val generateSrgParser by parser("SrgParser", "io/mcdev/obfex/formats/srg/gen") + +val generateCSrgLexer by lexer("CSrgLexer", "io/mcdev/obfex/formats/csrg/gen") +val generateCSrgParser by parser("CSrgParser", "io/mcdev/obfex/formats/csrg/gen") + +val generateTSrgLexer by lexer("TSrgLexer", "io/mcdev/obfex/formats/tsrg/gen") +val generateTSrgParser by parser("TSrgParser", "io/mcdev/obfex/formats/tsrg/gen") + +val generateTSrg2Lexer by lexer("TSrg2Lexer", "io/mcdev/obfex/formats/tsrg2/gen") +val generateTSrg2Parser by parser("TSrg2Parser", "io/mcdev/obfex/formats/tsrg2/gen") + +val generateJamLexer by lexer("JamLexer", "io/mcdev/obfex/formats/jam/gen") +val generateJamParser by parser("JamParser", "io/mcdev/obfex/formats/jam/gen") + +val generateEnigmaLexer by lexer("EnigmaLexer", "io/mcdev/obfex/formats/enigma/gen") +val generateEnigmaParser by parser("EnigmaParser", "io/mcdev/obfex/formats/enigma/gen") + +val generateTinyV1Lexer by lexer("TinyV1Lexer", "io/mcdev/obfex/formats/tinyv1/gen") +val generateTinyV1Parser by parser("TinyV1Parser", "io/mcdev/obfex/formats/tinyv1/gen") + +val generateTinyV2Lexer by lexer("TinyV2Lexer", "io/mcdev/obfex/formats/tinyv2/gen") +val generateTinyV2Parser by parser("TinyV2Parser", "io/mcdev/obfex/formats/tinyv2/gen") + +val generateProGuardLexer by lexer("ProGuardLexer", "io/mcdev/obfex/formats/proguard/gen") +val generateProGuardParser by parser("ProGuardParser", "io/mcdev/obfex/formats/proguard/gen") + +val generate by tasks.registering { + group = "minecraft" + description = "Generates sources needed to compile the plugin." + outputs.dir(layout.buildDirectory.dir("gen")) + dependsOn( + generateSrgLexer, + generateSrgParser, + generateCSrgLexer, + generateCSrgParser, + generateTSrgLexer, + generateTSrgParser, + generateTSrg2Lexer, + generateTSrg2Parser, + generateJamLexer, + generateJamParser, + generateEnigmaLexer, + generateEnigmaParser, + generateTinyV1Lexer, + generateTinyV1Parser, + generateTinyV2Lexer, + generateTinyV2Parser, + generateProGuardLexer, + generateProGuardParser, + ) +} + +sourceSets.main { java.srcDir(generate) } + +// Remove gen directory on clean +tasks.clean { delete(generate) } + +tasks.buildSearchableOptions { + enabled = false +} + +license { + tasks { + register("gradle") { + files.from( + fileTree(project.projectDir) { + include("*.gradle.kts", "gradle.properties") + exclude("**/buildSrc/**", "**/build/**") + } + ) + } + register("grammars") { + files.from(project.fileTree("src/main/grammars")) + } + } +} diff --git a/obfuscation-explorer/gradle.properties b/obfuscation-explorer/gradle.properties new file mode 100644 index 000000000..ce780324a --- /dev/null +++ b/obfuscation-explorer/gradle.properties @@ -0,0 +1,22 @@ +# +# Minecraft Development for IntelliJ +# +# https://mcdev.io/ +# +# Copyright (C) 2024 minecraft-dev +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published +# by the Free Software Foundation, version 3.0 only. +# +# 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 Lesser General Public License +# along with this program. If not, see . +# + +# suppress inspection "UnusedProperty" for whole file +coreVersion = 0.0.1 diff --git a/obfuscation-explorer/icons/csrg.svg b/obfuscation-explorer/icons/csrg.svg new file mode 100644 index 000000000..ff6729b5a --- /dev/null +++ b/obfuscation-explorer/icons/csrg.svg @@ -0,0 +1,78 @@ + + + + + + + image/svg+xml + + + + + + + + + + + CSG + diff --git a/obfuscation-explorer/icons/enigma.svg b/obfuscation-explorer/icons/enigma.svg new file mode 100644 index 000000000..690bd23ce --- /dev/null +++ b/obfuscation-explorer/icons/enigma.svg @@ -0,0 +1,78 @@ + + + + + + + image/svg+xml + + + + + + + + + + + ENG + diff --git a/obfuscation-explorer/icons/jam.svg b/obfuscation-explorer/icons/jam.svg new file mode 100644 index 000000000..7c3e6e21c --- /dev/null +++ b/obfuscation-explorer/icons/jam.svg @@ -0,0 +1,78 @@ + + + + + + + image/svg+xml + + + + + + + + + + + JAM + diff --git a/obfuscation-explorer/icons/pluginIcon.svg b/obfuscation-explorer/icons/pluginIcon.svg new file mode 100644 index 000000000..ac32bf44e --- /dev/null +++ b/obfuscation-explorer/icons/pluginIcon.svg @@ -0,0 +1,138 @@ + + + Obfuscation Mapper Icon + + + + + + image/svg+xml + + Obfuscation Mapper Icon + + + + Kyle Wood + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/obfuscation-explorer/icons/proguard.svg b/obfuscation-explorer/icons/proguard.svg new file mode 100644 index 000000000..6e0d40573 --- /dev/null +++ b/obfuscation-explorer/icons/proguard.svg @@ -0,0 +1,78 @@ + + + + + + + image/svg+xml + + + + + + + + + + + PRO + diff --git a/obfuscation-explorer/icons/srg.svg b/obfuscation-explorer/icons/srg.svg new file mode 100644 index 000000000..8e0238a87 --- /dev/null +++ b/obfuscation-explorer/icons/srg.svg @@ -0,0 +1,78 @@ + + + + + + + image/svg+xml + + + + + + + + + + + SRG + diff --git a/obfuscation-explorer/icons/tinyv1.svg b/obfuscation-explorer/icons/tinyv1.svg new file mode 100644 index 000000000..814d242ee --- /dev/null +++ b/obfuscation-explorer/icons/tinyv1.svg @@ -0,0 +1,78 @@ + + + + + + + image/svg+xml + + + + + + + + + + + Tv1 + diff --git a/obfuscation-explorer/icons/tinyv2.svg b/obfuscation-explorer/icons/tinyv2.svg new file mode 100644 index 000000000..80af9323c --- /dev/null +++ b/obfuscation-explorer/icons/tinyv2.svg @@ -0,0 +1,78 @@ + + + + + + + image/svg+xml + + + + + + + + + + + Tv2 + diff --git a/obfuscation-explorer/icons/tsrg.svg b/obfuscation-explorer/icons/tsrg.svg new file mode 100644 index 000000000..1c921d25e --- /dev/null +++ b/obfuscation-explorer/icons/tsrg.svg @@ -0,0 +1,78 @@ + + + + + + + image/svg+xml + + + + + + + + + + + TSG + diff --git a/obfuscation-explorer/icons/tsrg2.svg b/obfuscation-explorer/icons/tsrg2.svg new file mode 100644 index 000000000..9dce962f3 --- /dev/null +++ b/obfuscation-explorer/icons/tsrg2.svg @@ -0,0 +1,81 @@ + + + + + + + image/svg+xml + + + + + + + + + + + TG2 + diff --git a/obfuscation-explorer/icons/xsrg.svg b/obfuscation-explorer/icons/xsrg.svg new file mode 100644 index 000000000..d53176a98 --- /dev/null +++ b/obfuscation-explorer/icons/xsrg.svg @@ -0,0 +1,78 @@ + + + + + + + image/svg+xml + + + + + + + + + + + XSG + diff --git a/obfuscation-explorer/src/main/grammars/CSrgLexer.flex b/obfuscation-explorer/src/main/grammars/CSrgLexer.flex new file mode 100644 index 000000000..0ec03ac8d --- /dev/null +++ b/obfuscation-explorer/src/main/grammars/CSrgLexer.flex @@ -0,0 +1,80 @@ +/* + * Minecraft Development for IntelliJ + * + * https://mcdev.io/ + * + * Copyright (C) 2024 minecraft-dev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, version 3.0 only. + * + * 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 Lesser General Public License + * along with this program. If not, see . + */ + +package io.mcdev.obfex.formats.csrg.gen; + +import com.intellij.lexer.*; +import com.intellij.psi.tree.IElementType; +import static io.mcdev.obfex.formats.csrg.gen.psi.CSrgTypes.*; +import static com.intellij.psi.TokenType.*; + +%% + +%{ + public CSrgLexer() { + this((java.io.Reader)null); + } +%} + +%public +%class CSrgLexer +%implements FlexLexer +%function advance +%type IElementType + +%s SIGNATURE + +%unicode + +// Name element is used for all parts of an identifier name +// 1. The parts of the package +// 2. The class name +// 3. The member name +NAME_ELEMENT=[\p{L}_\p{Sc}][\p{L}\p{N}_\p{Sc}]* + +PRIMITIVE=[ZBCSIFDJV] +CLASS_TYPE=(\[+[ZBCSIFDJ]|\[*L[^;\s]+;) + +COMMENT=\s*#.* +WHITE_SPACE=\s +CRLF=\r\n|\n|\r + +%% + + { + "(" { yybegin(SIGNATURE); return OPEN_PAREN; } + "/" { return SLASH; } + {PRIMITIVE} {WHITE_SPACE}? { zzMarkedPos = zzStartRead + 1; return PRIMITIVE; } + {CLASS_TYPE} { return CLASS_TYPE; } + {NAME_ELEMENT} { return NAME_ELEMENT; } +} + + { + ")" { return CLOSE_PAREN; } + {PRIMITIVE} { return PRIMITIVE; } + {CLASS_TYPE} { return CLASS_TYPE; } + {CRLF} { yybegin(YYINITIAL); return CRLF; } + {WHITE_SPACE} { yybegin(YYINITIAL); return WHITE_SPACE; } +} + +{COMMENT} { return COMMENT; } +{CRLF} { yybegin(YYINITIAL); return CRLF; } +{WHITE_SPACE} { return WHITE_SPACE; } +[^] { return BAD_CHARACTER; } diff --git a/obfuscation-explorer/src/main/grammars/CSrgParser.bnf b/obfuscation-explorer/src/main/grammars/CSrgParser.bnf new file mode 100644 index 000000000..56a6f7acd --- /dev/null +++ b/obfuscation-explorer/src/main/grammars/CSrgParser.bnf @@ -0,0 +1,79 @@ +/* + * Minecraft Development for IntelliJ + * + * https://mcdev.io/ + * + * Copyright (C) 2024 minecraft-dev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, version 3.0 only. + * + * 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 Lesser General Public License + * along with this program. If not, see . + */ + +{ + parserClass="io.mcdev.obfex.formats.csrg.gen.CSrgParser" + + extends="com.intellij.extapi.psi.ASTWrapperPsiElement" + + psiClassPrefix="CSrg" + psiImplClassSuffix="Impl" + psiPackage="io.mcdev.obfex.formats.csrg.gen.psi" + psiImplPackage="io.mcdev.obfex.formats.csrg.gen.psi.impl" + + elementTypeHolderClass="io.mcdev.obfex.formats.csrg.gen.psi.CSrgTypes" + elementTypeClass="io.mcdev.obfex.formats.csrg.lang.psi.CSrgElementType" + tokenTypeClass="io.mcdev.obfex.formats.csrg.lang.psi.CSrgTokenType" + + tokens=[ + COMMENT="regexp:\s*#.*" + ] + + // do not record error reporting information in recover rules + consumeTokenMethod(".*_recover")="consumeTokenFast" +} + +csrg_file ::= line* + +private line ::= !<> mapping? line_end {pin=2 recoverWhile=line_recover} +private line_end ::= <> | CRLF +private line_recover ::= !(mapping | line_end) + +mapping ::= method_mapping | field_mapping | class_mapping + +method_mapping ::= mapping_part mapping_part method_signature mapping_part { + methods=[ + obfClassName="/mapping_part[0]" + obfMethodName="/mapping_part[1]" + obfSignature="/method_signature" + deobfMethodName="/mapping_part[2]" + ] +} +field_mapping ::= mapping_part mapping_part mapping_part { + methods=[ + obfClassName="/mapping_part[0]" + obfFieldName="/mapping_part[1]" + deobfName="/mapping_part[2]" + ] +} +class_mapping ::= mapping_part mapping_part { + methods=[ + obfName="/mapping_part[0]" + deobfName="/mapping_part[1]" + ] +} + +mapping_part ::= PRIMITIVE | identifier +type_desc ::= PRIMITIVE | CLASS_TYPE +identifier ::= NAME_ELEMENT (SLASH NAME_ELEMENT)* {pin(".*")=1} + +method_signature ::= OPEN_PAREN param_types CLOSE_PAREN return_type +param_types ::= type_desc* +return_type ::= type_desc diff --git a/obfuscation-explorer/src/main/grammars/EnigmaLexer.flex b/obfuscation-explorer/src/main/grammars/EnigmaLexer.flex new file mode 100644 index 000000000..81f4bfc5a --- /dev/null +++ b/obfuscation-explorer/src/main/grammars/EnigmaLexer.flex @@ -0,0 +1,97 @@ +/* + * Minecraft Development for IntelliJ + * + * https://mcdev.io/ + * + * Copyright (C) 2024 minecraft-dev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, version 3.0 only. + * + * 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 Lesser General Public License + * along with this program. If not, see . + */ + +package io.mcdev.obfex.formats.enigma.gen; + +import com.intellij.lexer.*; +import com.intellij.psi.tree.IElementType; +import static io.mcdev.obfex.formats.enigma.gen.psi.EnigmaTypes.*; +import static com.intellij.psi.TokenType.*; + +%% + +%{ + public EnigmaLexer() { + this((java.io.Reader)null); + } +%} + +%public +%class EnigmaLexer +%implements FlexLexer +%function advance +%type IElementType + +%s DOC_LINE +%s SIGNATURE + +%unicode + +// Name element is used for all parts of an identifier name +// 1. The parts of the package +// 2. The class name +// 3. The member name +NAME_ELEMENT=[\p{L}_\p{Sc}][\p{L}\p{N}_\p{Sc}]* + +PRIMITIVE=[ZBCSIFDJV] +CLASS_TYPE=(\[+[ZBCSIFDJ]|\[*L[^;\s]+;) +INDEX=\d+ + +COMMENT=\s*#.* +DOC_TEXT=[^\r\n]+ + +TAB=\t +WHITE_SPACE=\s +CRLF=\r\n|\n|\r + +%% + + { + "CLASS" { return CLASS_KEY; } + "FIELD" { return FIELD_KEY; } + "METHOD" { return METHOD_KEY; } + "ARG" { return ARG_KEY; } + "COMMENT" { yybegin(DOC_LINE); return COMMENT_KEY; } + "(" { yybegin(SIGNATURE); return OPEN_PAREN; } + "/" { return SLASH; } + "" { return CONSTRUCTOR; } + {INDEX} { return INDEX; } + {PRIMITIVE} {WHITE_SPACE}? { zzMarkedPos = zzStartRead + 1; return PRIMITIVE; } + {CLASS_TYPE} { return CLASS_TYPE; } + {NAME_ELEMENT} { return NAME_ELEMENT; } + {TAB} { return TAB; } +} + + { + {DOC_TEXT} { return DOC_TEXT; } +} + + { + ")" { return CLOSE_PAREN; } + {PRIMITIVE} { return PRIMITIVE; } + {CLASS_TYPE} { return CLASS_TYPE; } + {CRLF} { yybegin(YYINITIAL); return CRLF; } + {WHITE_SPACE} { yybegin(YYINITIAL); return WHITE_SPACE; } +} + +{COMMENT} { return COMMENT; } +{CRLF} { yybegin(YYINITIAL); return CRLF; } +{WHITE_SPACE} { return WHITE_SPACE; } +[^] { return BAD_CHARACTER; } diff --git a/obfuscation-explorer/src/main/grammars/EnigmaParser.bnf b/obfuscation-explorer/src/main/grammars/EnigmaParser.bnf new file mode 100644 index 000000000..5491fe668 --- /dev/null +++ b/obfuscation-explorer/src/main/grammars/EnigmaParser.bnf @@ -0,0 +1,78 @@ +/* + * Minecraft Development for IntelliJ + * + * https://mcdev.io/ + * + * Copyright (C) 2024 minecraft-dev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, version 3.0 only. + * + * 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 Lesser General Public License + * along with this program. If not, see . + */ + +{ + parserClass="io.mcdev.obfex.formats.enigma.gen.EnigmaParser" + + extends="com.intellij.extapi.psi.ASTWrapperPsiElement" + parserUtilClass='io.mcdev.obfex.formats.enigma.lang.psi.EnigmaParserUtil' + + psiClassPrefix="Enigma" + psiImplClassSuffix="Impl" + psiPackage="io.mcdev.obfex.formats.enigma.gen.psi" + psiImplPackage="io.mcdev.obfex.formats.enigma.gen.psi.impl" + + elementTypeHolderClass="io.mcdev.obfex.formats.enigma.gen.psi.EnigmaTypes" + elementTypeClass="io.mcdev.obfex.formats.enigma.lang.psi.EnigmaElementType" + tokenTypeClass="io.mcdev.obfex.formats.enigma.lang.psi.EnigmaTokenType" + + tokens=[ + COMMENT="regexp:\s*#.*" + TAB="regexp:\t" + ] + + consumeTokenMethod="consumeTokenFast" +} + +enigma_file ::= entry* + +private entry ::= !<> class_mapping* +private line_end ::= <> | CRLF+ + +external indent ::= indent0 VIRTUAL_OPEN VIRTUAL_CLOSE <> + +class_mapping ::= CLASS_KEY class_mapping_part+ line_end <>? {pin=1} +class_mapping_part ::= mapping_part | INDEX + +private class_children ::= doc_line* member_mapping* + +member_mapping ::= field_mapping | method_mapping | class_mapping {recoverWhile=member_recover} +private member_recover ::= !(FIELD_KEY | METHOD_KEY | (VIRTUAL_CLOSE* CLASS_KEY)) + +doc_line ::= COMMENT_KEY DOC_TEXT? line_end {pin=1} + +field_mapping ::= FIELD_KEY (<> mapping_part)+ type_desc line_end nested_docs? {pin=1} + +method_mapping ::= METHOD_KEY mapping_part+ method_signature line_end <>? {pin=1} + +private method_children ::= doc_line* arg_mapping* + +arg_mapping ::= ARG_KEY arg_index mapping_part line_end nested_docs? {pin=1} +arg_index ::= INDEX + +private nested_docs ::= <> + +mapping_part ::= PRIMITIVE | CONSTRUCTOR | identifier +type_desc ::= PRIMITIVE | CLASS_TYPE +identifier ::= NAME_ELEMENT (SLASH NAME_ELEMENT)* {pin(".*")=1} + +method_signature ::= OPEN_PAREN param_types CLOSE_PAREN return_type +param_types ::= type_desc* +return_type ::= type_desc diff --git a/obfuscation-explorer/src/main/grammars/JamLexer.flex b/obfuscation-explorer/src/main/grammars/JamLexer.flex new file mode 100644 index 000000000..9877d2273 --- /dev/null +++ b/obfuscation-explorer/src/main/grammars/JamLexer.flex @@ -0,0 +1,90 @@ +/* + * Minecraft Development for IntelliJ + * + * https://mcdev.io/ + * + * Copyright (C) 2024 minecraft-dev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, version 3.0 only. + * + * 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 Lesser General Public License + * along with this program. If not, see . + */ + +package io.mcdev.obfex.formats.jam.gen; + +import com.intellij.lexer.*; +import com.intellij.psi.tree.IElementType; +import static io.mcdev.obfex.formats.jam.gen.psi.JamTypes.*; +import static com.intellij.psi.TokenType.*; + +%% + +%{ + public JamLexer() { + this((java.io.Reader)null); + } +%} + +%public +%class JamLexer +%implements FlexLexer +%function advance +%type IElementType + +%s ENTRY +%s SIGNATURE + +%unicode + +// Name element is used for all parts of an identifier name +// 1. The parts of the package +// 2. The class name +// 3. The member name +NAME_ELEMENT=[\p{L}_\p{Sc}][\p{L}\p{N}_\p{Sc}]* + +PRIMITIVE=[ZBCSIFDJV] +CLASS_TYPE=(\[+[ZBCSIFDJ]|\[*L[^;\s]+;) +INDEX=\d + +COMMENT=\s*#.* +WHITE_SPACE=\s+ +CRLF=\r\n|\n|\r + +%% + + { + "CL" { yybegin(ENTRY); return CLASS_KEY; } + "FD" { yybegin(ENTRY); return FIELD_KEY; } + "MD" { yybegin(ENTRY); return METHOD_KEY; } + "MP" { yybegin(ENTRY); return PARAM_KEY; } +} + + { + "(" { yybegin(SIGNATURE); return OPEN_PAREN; } + "/" { return SLASH; } + {INDEX} {WHITE_SPACE}? { zzMarkedPos = zzStartRead + 1; return INDEX; } + {PRIMITIVE} {WHITE_SPACE}? { zzMarkedPos = zzStartRead + 1; return PRIMITIVE; } + {CLASS_TYPE} { return CLASS_TYPE; } + {NAME_ELEMENT} { return NAME_ELEMENT; } +} + + { + ")" { return CLOSE_PAREN; } + {PRIMITIVE} { return PRIMITIVE; } + {CLASS_TYPE} { return CLASS_TYPE; } + {CRLF} { yybegin(YYINITIAL); return CRLF; } + {WHITE_SPACE} { yybegin(ENTRY); return WHITE_SPACE; } +} + +{COMMENT} { return COMMENT; } +{CRLF} { yybegin(YYINITIAL); return CRLF; } +{WHITE_SPACE} { return WHITE_SPACE; } +[^] { return BAD_CHARACTER; } diff --git a/obfuscation-explorer/src/main/grammars/JamParser.bnf b/obfuscation-explorer/src/main/grammars/JamParser.bnf new file mode 100644 index 000000000..7d5d330fd --- /dev/null +++ b/obfuscation-explorer/src/main/grammars/JamParser.bnf @@ -0,0 +1,93 @@ +/* + * Minecraft Development for IntelliJ + * + * https://mcdev.io/ + * + * Copyright (C) 2024 minecraft-dev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, version 3.0 only. + * + * 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 Lesser General Public License + * along with this program. If not, see . + */ + +{ + parserClass="io.mcdev.obfex.formats.jam.gen.JamParser" + + extends="com.intellij.extapi.psi.ASTWrapperPsiElement" + + psiClassPrefix="Jam" + psiImplClassSuffix="Impl" + psiPackage="io.mcdev.obfex.formats.jam.gen.psi" + psiImplPackage="io.mcdev.obfex.formats.jam.gen.psi.impl" + + elementTypeHolderClass="io.mcdev.obfex.formats.jam.gen.psi.JamTypes" + elementTypeClass="io.mcdev.obfex.formats.jam.lang.psi.JamElementType" + tokenTypeClass="io.mcdev.obfex.formats.jam.lang.psi.JamTokenType" + + tokens=[ + COMMENT="regexp:\s*#.*" + ] + + // do not record error reporting information in recover rules + consumeTokenMethod(".*_recover")="consumeTokenFast" +} + +srg_file ::= line* + +private line ::= !<> mapping? line_end {pin=2 recoverWhile=line_recover} +private line_end ::= <> | CRLF +private line_recover ::= !(mapping | line_end) + +mapping ::= class_mapping | field_mapping | method_mapping | param_mapping + +class_mapping ::= CLASS_KEY mapping_part mapping_part { + pin=1 + methods=[ + obfName="/mapping_part[0]" + deobfName="/mapping_part[1]" + ] +} +field_mapping ::= FIELD_KEY mapping_part mapping_part type_desc mapping_part { + pin=1 + methods=[ + obfClassName="/mapping_part[0]" + obfFieldName="/mapping_part[1]" + obfType="/type_desc" + deobfName="/mapping_part[2]" + ] +} +method_mapping ::= METHOD_KEY mapping_part mapping_part method_signature mapping_part { + pin=1 + methods=[ + obfClassName="/mapping_part[0]" + obfMethodName="/mapping_part[1]" + obfSignature="/method_signature" + deobfName="/mapping_part[2]" + ] +} +param_mapping ::= PARAM_KEY mapping_part mapping_part method_signature mapping_part mapping_part { + pin=1 + methods=[ + obfClassName="/mapping_part[0]" + obfMethodName="/mapping_part[1]" + obfSignature="/method_signature" + parameterIndex="/mapping_part[2]" + deobfName="/mapping_part[3]" + ] +} + +mapping_part ::= PRIMITIVE | identifier | INDEX +type_desc ::= PRIMITIVE | CLASS_TYPE +identifier ::= NAME_ELEMENT (SLASH NAME_ELEMENT)* {pin(".*")=1} + +method_signature ::= OPEN_PAREN param_types CLOSE_PAREN return_type {pin=1} +param_types ::= type_desc* +return_type ::= type_desc diff --git a/obfuscation-explorer/src/main/grammars/ProGuardLexer.flex b/obfuscation-explorer/src/main/grammars/ProGuardLexer.flex new file mode 100644 index 000000000..4881947ac --- /dev/null +++ b/obfuscation-explorer/src/main/grammars/ProGuardLexer.flex @@ -0,0 +1,78 @@ +/* + * Minecraft Development for IntelliJ + * + * https://mcdev.io/ + * + * Copyright (C) 2024 minecraft-dev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, version 3.0 only. + * + * 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 Lesser General Public License + * along with this program. If not, see . + */ + +package io.mcdev.obfex.formats.proguard.gen; + +import com.intellij.lexer.*; +import com.intellij.psi.tree.IElementType; +import static io.mcdev.obfex.formats.proguard.gen.psi.ProGuardTypes.*; +import static com.intellij.psi.TokenType.*; + +%% + +%{ + public ProGuardLexer() { + this((java.io.Reader)null); + } +%} + +%public +%class ProGuardLexer +%implements FlexLexer +%function advance +%type IElementType + +%s CORE + +%unicode + +NAME_ELEMENT=[\p{L}_\p{Sc}][\p{L}\p{N}_\p{Sc}]* + +PRIMITIVE=void|boolean|char|byte|short|int|long|float|double + +NUMBER=\d +POINTER=\s*->\s* + +COMMENT=\s*#.* + +WHITE_SPACE=\s +CRLF=\r\n|\n|\r + +%% + + { + ":" { return COLON; } + "." { return DOT; } + "," { return COMMA; } + "(" { return OPEN_PAREN; } + ")" { return CLOSE_PAREN; } + "" { return INIT; } + "" { return INIT; } + "[]" { return ARRAY_BRACKETS; } + "package-info" { return PACKAGE_INFO; } + {NUMBER}+ { return NUMBER; } + {POINTER} { return POINTER; } + {PRIMITIVE} { return PRIMITIVE; } + {NAME_ELEMENT} { return NAME_ELEMENT; } +} + +{COMMENT} { return COMMENT; } +{WHITE_SPACE} { return WHITE_SPACE; } +[^] { return BAD_CHARACTER; } diff --git a/obfuscation-explorer/src/main/grammars/ProGuardParser.bnf b/obfuscation-explorer/src/main/grammars/ProGuardParser.bnf new file mode 100644 index 000000000..503c5cd0a --- /dev/null +++ b/obfuscation-explorer/src/main/grammars/ProGuardParser.bnf @@ -0,0 +1,69 @@ +/* + * Minecraft Development for IntelliJ + * + * https://mcdev.io/ + * + * Copyright (C) 2024 minecraft-dev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, version 3.0 only. + * + * 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 Lesser General Public License + * along with this program. If not, see . + */ + +{ + parserClass="io.mcdev.obfex.formats.proguard.gen.ProGuardParser" + + extends="com.intellij.extapi.psi.ASTWrapperPsiElement" + + psiClassPrefix="ProGuard" + psiImplClassSuffix="Impl" + psiPackage="io.mcdev.obfex.formats.proguard.gen.psi" + psiImplPackage="io.mcdev.obfex.formats.proguard.gen.psi.impl" + + elementTypeHolderClass="io.mcdev.obfex.formats.proguard.gen.psi.ProGuardTypes" + elementTypeClass="io.mcdev.obfex.formats.proguard.lang.psi.ProGuardElementType" + tokenTypeClass="io.mcdev.obfex.formats.proguard.lang.psi.ProGuardTokenType" + + tokens=[ + COMMENT="regexp:\s*#.*" + ] +} + +proguard_file ::= entry* + +private entry ::= !<> class_mapping? + +class_mapping ::= identifier POINTER identifier COLON member_mapping* { + methods=[ + fromName="/class_name[0]" + toName="/class_name[1]" + ] +} + +member_mapping ::= method_mapping | field_mapping + +field_mapping ::= type_desc field_name POINTER field_name { + methods=[ + fromName="/field_name[0]" + toName="/field_name[1]" + ] +} +field_name ::= identifier | NUMBER + +method_mapping ::= (NUMBER COLON NUMBER COLON)? return_type method_name method_signature POINTER method_name +return_type ::= type_desc +method_name ::= identifier | INIT | CLINIT + +method_signature ::= OPEN_PAREN (param_type (COMMA param_type)*)? CLOSE_PAREN {pin=1} +param_type ::= type_desc + +identifier ::= NAME_ELEMENT (DOT (NAME_ELEMENT | PACKAGE_INFO))* {pin(".*")=1} +type_desc ::= (identifier | PRIMITIVE) ARRAY_BRACKETS* diff --git a/obfuscation-explorer/src/main/grammars/SrgLexer.flex b/obfuscation-explorer/src/main/grammars/SrgLexer.flex new file mode 100644 index 000000000..8c4018e0b --- /dev/null +++ b/obfuscation-explorer/src/main/grammars/SrgLexer.flex @@ -0,0 +1,96 @@ +/* + * Minecraft Development for IntelliJ + * + * https://mcdev.io/ + * + * Copyright (C) 2024 minecraft-dev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, version 3.0 only. + * + * 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 Lesser General Public License + * along with this program. If not, see . + */ + +package io.mcdev.obfex.formats.srg.gen; + +import com.intellij.lexer.*; +import com.intellij.psi.tree.IElementType; +import static io.mcdev.obfex.formats.srg.gen.psi.SrgTypes.*; +import static com.intellij.psi.TokenType.*; + +%% + +%{ + public SrgLexer() { + this((java.io.Reader)null); + } +%} + +%public +%class SrgLexer +%implements FlexLexer +%function advance +%type IElementType + +%s PACKAGE_ENTRY +%s ENTRY +%s SIGNATURE + +%unicode + +// Package name entries just match until whitespace is hit +PACKAGE_NAME=[^\s\r\n]+ + +// Name element is used for all parts of an identifier name +// 1. The parts of the package +// 2. The class name +// 3. The member name +NAME_ELEMENT=[\p{L}_\p{Sc}][\p{L}\p{N}_\p{Sc}]* + +PRIMITIVE=[ZBCSIFDJV] +CLASS_TYPE=(\[+[ZBCSIFDJ]|\[*L[^;\s]+;) + +COMMENT=\s*#.* +WHITE_SPACE=\s+ +CRLF=\r\n|\n|\r + +%% + + { + "PK:" { yybegin(PACKAGE_ENTRY); return PACKAGE_KEY; } + "CL:" { yybegin(ENTRY); return CLASS_KEY; } + "FD:" { yybegin(ENTRY); return FIELD_KEY; } + "MD:" { yybegin(ENTRY); return METHOD_KEY; } +} + + { + {PACKAGE_NAME} { return PACKAGE_NAME; } +} + + { + "(" { yybegin(SIGNATURE); return OPEN_PAREN; } + "/" { return SLASH; } + {PRIMITIVE} {WHITE_SPACE}? { zzMarkedPos = zzStartRead + 1; return PRIMITIVE; } + {CLASS_TYPE} { return CLASS_TYPE; } + {NAME_ELEMENT} { return NAME_ELEMENT; } +} + + { + ")" { return CLOSE_PAREN; } + {PRIMITIVE} { return PRIMITIVE; } + {CLASS_TYPE} { return CLASS_TYPE; } + {CRLF} { yybegin(YYINITIAL); return CRLF; } + {WHITE_SPACE} { yybegin(ENTRY); return WHITE_SPACE; } +} + +{COMMENT} { return COMMENT; } +{CRLF} { yybegin(YYINITIAL); return CRLF; } +{WHITE_SPACE} { return WHITE_SPACE; } +[^] { return BAD_CHARACTER; } diff --git a/obfuscation-explorer/src/main/grammars/SrgParser.bnf b/obfuscation-explorer/src/main/grammars/SrgParser.bnf new file mode 100644 index 000000000..cffbc519d --- /dev/null +++ b/obfuscation-explorer/src/main/grammars/SrgParser.bnf @@ -0,0 +1,111 @@ +/* + * Minecraft Development for IntelliJ + * + * https://mcdev.io/ + * + * Copyright (C) 2024 minecraft-dev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, version 3.0 only. + * + * 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 Lesser General Public License + * along with this program. If not, see . + */ + +{ + parserClass="io.mcdev.obfex.formats.srg.gen.SrgParser" + + extends="com.intellij.extapi.psi.ASTWrapperPsiElement" + + psiClassPrefix="Srg" + psiImplClassSuffix="Impl" + psiPackage="io.mcdev.obfex.formats.srg.gen.psi" + psiImplPackage="io.mcdev.obfex.formats.srg.gen.psi.impl" + + elementTypeHolderClass="io.mcdev.obfex.formats.srg.gen.psi.SrgTypes" + elementTypeClass="io.mcdev.obfex.formats.srg.lang.psi.SrgElementType" + tokenTypeClass="io.mcdev.obfex.formats.srg.lang.psi.SrgTokenType" + + tokens=[ + COMMENT="regexp:\s*#.*" + ] + + // do not record error reporting information in recover rules + consumeTokenMethod(".*_recover")="consumeTokenFast" +} + +srg_file ::= line* + +private line ::= !<> mapping? line_end {pin=2 recoverWhile=line_recover} +private line_end ::= <> | CRLF +private line_recover ::= !(mapping | line_end) + +mapping ::= package_mapping | class_mapping | field_mapping | method_mapping + +package_mapping ::= PACKAGE_KEY package_mapping_part package_mapping_part { + pin=1 + methods=[ + obfName="/package_mapping_part[0]" + deobfName="/package_mapping_part[1]" + ] +} +class_mapping ::= CLASS_KEY mapping_part mapping_part { + pin=1 + methods=[ + obfName="/mapping_part[0]" + deobfName="/mapping_part[1]" + ] +} +field_mapping ::= FIELD_KEY (extended_field_mapping | standard_field_mapping) { + pin=1 + methods=[ + obfName + obfSignature + deobfName + deobfSignature + ] + mixin="io.mcdev.obfex.formats.srg.lang.psi.mixins.impl.SrgFieldMappingImplMixin" + implements="io.mcdev.obfex.formats.srg.lang.psi.mixins.SrgFieldMappingMixin" +} +method_mapping ::= METHOD_KEY mapping_part method_signature mapping_part method_signature { + pin=1 + methods=[ + obfName="/mapping_part[0]" + obfSignature="/method_signature[0]" + deobfName="/mapping_part[1]" + deobfSignature="/method_signature[1]" + ] +} + +package_mapping_part ::= PACKAGE_NAME + +standard_field_mapping ::= mapping_part mapping_part { + methods=[ + obfName="/mapping_part[0]" + deobfName="/mapping_part[1]" + ] +} +extended_field_mapping ::= mapping_part type_desc mapping_part type_desc { + pin=3 + methods=[ + obfName="/mapping_part[0]" + obfType="/type_desc[0]" + deobfName="/mapping_part[1]" + deobfType="/type_desc[1]" + ] +} + +mapping_part ::= PRIMITIVE | identifier +type_desc ::= PRIMITIVE | CLASS_TYPE +identifier ::= name_part (SLASH name_part)* {pin(".*")=1} +name_part ::= NAME_ELEMENT + +method_signature ::= OPEN_PAREN param_types CLOSE_PAREN return_type {pin=1} +param_types ::= type_desc* +return_type ::= type_desc diff --git a/obfuscation-explorer/src/main/grammars/TSrg2Lexer.flex b/obfuscation-explorer/src/main/grammars/TSrg2Lexer.flex new file mode 100644 index 000000000..59a44a855 --- /dev/null +++ b/obfuscation-explorer/src/main/grammars/TSrg2Lexer.flex @@ -0,0 +1,101 @@ +/* + * Minecraft Development for IntelliJ + * + * https://mcdev.io/ + * + * Copyright (C) 2024 minecraft-dev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, version 3.0 only. + * + * 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 Lesser General Public License + * along with this program. If not, see . + */ + +package io.mcdev.obfex.formats.tsrg2.gen; + +import com.intellij.lexer.*; +import com.intellij.psi.tree.IElementType; +import static io.mcdev.obfex.formats.tsrg2.gen.psi.TSrg2Types.*; +import static com.intellij.psi.TokenType.*; + +%% + +%{ + public TSrg2Lexer() { + this((java.io.Reader)null); + } +%} + +%public +%class TSrg2Lexer +%implements FlexLexer +%function advance +%type IElementType + +%s HEADER +%s CORE +%s SIGNATURE + +%unicode + +// Name element is used for all parts of an identifier name +// 1. The parts of the package +// 2. The class name +// 3. The member name +NAME_ELEMENT=[\p{L}_\p{Sc}][\p{L}\p{N}_\p{Sc}]* + +PRIMITIVE=[ZBCSIFDJV] +CLASS_TYPE=(\[+[ZBCSIFDJ]|\[*L[^;\s]+;) + +COMMENT=\s*#.* + +TAB=\t +WHITE_SPACE=\s +CRLF=\r\n|\n|\r +NAMESPACE=\w +DIGIT=\d + +%% + + { + "tsrg2" { yybegin(HEADER); return TSRG2_KEY; } +} + +
{ + {NAMESPACE}+ { return NAMESPACE_KEY; } + {CRLF} { yybegin(CORE); return CRLF; } +} + + { + "(" { yybegin(SIGNATURE); return OPEN_PAREN; } + "/" { return SLASH; } + {PRIMITIVE} {WHITE_SPACE}? { zzMarkedPos = zzStartRead + 1; return PRIMITIVE; } + "" { return INIT; } + "" { return CLINIT; } + "static" { return STATIC; } + "package-info" { return NAME_ELEMENT; } + {DIGIT}+ { return DIGIT; } + {CLASS_TYPE} { return CLASS_TYPE; } + {NAME_ELEMENT} { return NAME_ELEMENT; } + {TAB} { return TAB; } +} + + { + ")" { return CLOSE_PAREN; } + {PRIMITIVE} { return PRIMITIVE; } + {CLASS_TYPE} { return CLASS_TYPE; } + {CRLF} { yybegin(CORE); return CRLF; } + {WHITE_SPACE} { yybegin(CORE); return WHITE_SPACE; } +} + +{COMMENT} { return COMMENT; } +{CRLF} { yybegin(CORE); return CRLF; } +{WHITE_SPACE} { return WHITE_SPACE; } +[^] { return BAD_CHARACTER; } diff --git a/obfuscation-explorer/src/main/grammars/TSrg2Parser.bnf b/obfuscation-explorer/src/main/grammars/TSrg2Parser.bnf new file mode 100644 index 000000000..0653b6d20 --- /dev/null +++ b/obfuscation-explorer/src/main/grammars/TSrg2Parser.bnf @@ -0,0 +1,81 @@ +/* + * Minecraft Development for IntelliJ + * + * https://mcdev.io/ + * + * Copyright (C) 2024 minecraft-dev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, version 3.0 only. + * + * 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 Lesser General Public License + * along with this program. If not, see . + */ + +{ + parserClass="io.mcdev.obfex.formats.tsrg2.gen.TSrg2Parser" + + extends="com.intellij.extapi.psi.ASTWrapperPsiElement" + parserUtilClass='io.mcdev.obfex.formats.util.sigws.SignificantWhitespaceParserUtil' + + psiClassPrefix="TSrg2" + psiImplClassSuffix="Impl" + psiPackage="io.mcdev.obfex.formats.tsrg2.gen.psi" + psiImplPackage="io.mcdev.obfex.formats.tsrg2.gen.psi.impl" + + elementTypeHolderClass="io.mcdev.obfex.formats.tsrg2.gen.psi.TSrg2Types" + elementTypeClass="io.mcdev.obfex.formats.tsrg2.lang.psi.TSrg2ElementType" + tokenTypeClass="io.mcdev.obfex.formats.tsrg2.lang.psi.TSrg2TokenType" + + tokens=[ + COMMENT="regexp:\s*#.*" + TAB="regexp:\t" + ] + + consumeTokenMethod="consumeTokenFast" +} + +tsrg_file ::= file_element? +file_element ::= header entry* + +private line_end ::= <> | CRLF+ + +header ::= TSRG2_KEY namespace* line_end {pin=1} + +namespace ::= NAMESPACE_KEY + +private entry ::= !<> class_mapping? + +external indent ::= indent0 VIRTUAL_OPEN VIRTUAL_CLOSE <> + +class_mapping ::= mapping_part+ line_end <>? {pin=1 recoverWhile=mapping_recover} +private mapping_recover ::= !(mapping_part | VIRTUAL_CLOSE) + +private class_children ::= member_mapping* +member_mapping ::= method_mapping | field_mapping {recoverWhile=mapping_recover} + +method_mapping ::= mapping_part method_signature mapping_part+ line_end <>? {pin=2} +private method_members ::= static_decl? param_mapping* + +field_mapping ::= mapping_part+ line_end <>? {pin=1} + +private static_decl ::= STATIC line_end {pin=1 recoverWhile=static_recover} +private static_recover ::= !(VIRTUAL_CLOSE | DIGIT) + +mapping_part ::= PRIMITIVE | INIT | CLINIT | DIGIT | identifier +type_desc ::= PRIMITIVE | CLASS_TYPE +identifier ::= NAME_ELEMENT (SLASH NAME_ELEMENT)* {pin(".*")=1} + +method_signature ::= OPEN_PAREN param_types CLOSE_PAREN return_type {pin=1} +param_types ::= type_desc* +return_type ::= type_desc + +param_mapping ::= param_index mapping_part* line_end {pin=1 recoverWhile=param_recover} +param_index ::= DIGIT +private param_recover ::= !(VIRTUAL_CLOSE | DIGIT) diff --git a/obfuscation-explorer/src/main/grammars/TSrgLexer.flex b/obfuscation-explorer/src/main/grammars/TSrgLexer.flex new file mode 100644 index 000000000..778ea33a7 --- /dev/null +++ b/obfuscation-explorer/src/main/grammars/TSrgLexer.flex @@ -0,0 +1,83 @@ +/* + * Minecraft Development for IntelliJ + * + * https://mcdev.io/ + * + * Copyright (C) 2024 minecraft-dev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, version 3.0 only. + * + * 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 Lesser General Public License + * along with this program. If not, see . + */ + +package io.mcdev.obfex.formats.tsrg.gen; + +import com.intellij.lexer.*; +import com.intellij.psi.tree.IElementType; +import static io.mcdev.obfex.formats.tsrg.gen.psi.TSrgTypes.*; +import static com.intellij.psi.TokenType.*; + +%% + +%{ + public TSrgLexer() { + this((java.io.Reader)null); + } +%} + +%public +%class TSrgLexer +%implements FlexLexer +%function advance +%type IElementType + +%s SIGNATURE + +%unicode + +// Name element is used for all parts of an identifier name +// 1. The parts of the package +// 2. The class name +// 3. The member name +NAME_ELEMENT=[\p{L}_\p{Sc}][\p{L}\p{N}_\p{Sc}]* + +PRIMITIVE=[ZBCSIFDJV] +CLASS_TYPE=(\[+[ZBCSIFDJ]|\[*L[^;\s]+;) + +COMMENT=\s*#.* + +TAB=\t +WHITE_SPACE=\s +CRLF=\r\n|\n|\r + +%% + + { + "(" { yybegin(SIGNATURE); return OPEN_PAREN; } + "/" { return SLASH; } + {TAB} { return TAB; } + {PRIMITIVE} {WHITE_SPACE}? { zzMarkedPos = zzStartRead + 1; return PRIMITIVE; } + {CLASS_TYPE} { return CLASS_TYPE; } + {NAME_ELEMENT} { return NAME_ELEMENT; } +} + + { + ")" { return CLOSE_PAREN; } + {PRIMITIVE} { return PRIMITIVE; } + {CLASS_TYPE} { return CLASS_TYPE; } + {CRLF} { yybegin(YYINITIAL); return CRLF; } + {WHITE_SPACE} { yybegin(YYINITIAL); return WHITE_SPACE; } +} + +{COMMENT} { return COMMENT; } +{CRLF} { yybegin(YYINITIAL); return CRLF; } +{WHITE_SPACE} { return WHITE_SPACE; } +[^] { return BAD_CHARACTER; } diff --git a/obfuscation-explorer/src/main/grammars/TSrgParser.bnf b/obfuscation-explorer/src/main/grammars/TSrgParser.bnf new file mode 100644 index 000000000..ff4eab6c7 --- /dev/null +++ b/obfuscation-explorer/src/main/grammars/TSrgParser.bnf @@ -0,0 +1,70 @@ +/* + * Minecraft Development for IntelliJ + * + * https://mcdev.io/ + * + * Copyright (C) 2024 minecraft-dev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, version 3.0 only. + * + * 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 Lesser General Public License + * along with this program. If not, see . + */ + +{ + parserClass="io.mcdev.obfex.formats.tsrg.gen.TSrgParser" + + extends="com.intellij.extapi.psi.ASTWrapperPsiElement" + + psiClassPrefix="TSrg" + psiImplClassSuffix="Impl" + psiPackage="io.mcdev.obfex.formats.tsrg.gen.psi" + psiImplPackage="io.mcdev.obfex.formats.tsrg.gen.psi.impl" + + elementTypeHolderClass="io.mcdev.obfex.formats.tsrg.gen.psi.TSrgTypes" + elementTypeClass="io.mcdev.obfex.formats.tsrg.lang.psi.TSrgElementType" + tokenTypeClass="io.mcdev.obfex.formats.tsrg.lang.psi.TSrgTokenType" + + tokens=[ + COMMENT="regexp:\s*#.*" + ] + + // do not record error reporting information in recover rules + consumeTokenMethod(".*_recover")="consumeTokenFast" +} + +tsrg_file ::= entry* + +private entry ::= !<> mapping? line_end {recoverWhile=line_recover} +private line_end ::= <> | CRLF +private line_recover ::= !(mapping | line_end) + +mapping ::= class_mapping (line_end member_mapping)* + +class_mapping ::= obf_class_mapping_part deobf_class_mapping_part +obf_class_mapping_part ::= mapping_part +deobf_class_mapping_part ::= mapping_part + +member_mapping ::= TAB (method_mapping | field_mapping) +method_mapping ::= obf_method_mapping_part method_signature deobf_method_mapping_part +obf_method_mapping_part ::= mapping_part +deobf_method_mapping_part ::= mapping_part + +field_mapping ::= obf_field_mapping_part deobf_field_mapping_part +obf_field_mapping_part ::= mapping_part +deobf_field_mapping_part ::= mapping_part + +mapping_part ::= PRIMITIVE | identifier +type_desc ::= PRIMITIVE | CLASS_TYPE +identifier ::= NAME_ELEMENT (SLASH NAME_ELEMENT)* {pin(".*")=1} + +method_signature ::= OPEN_PAREN param_types CLOSE_PAREN return_type +param_types ::= type_desc* +return_type ::= type_desc diff --git a/obfuscation-explorer/src/main/grammars/TinyV1Lexer.flex b/obfuscation-explorer/src/main/grammars/TinyV1Lexer.flex new file mode 100644 index 000000000..0f839f463 --- /dev/null +++ b/obfuscation-explorer/src/main/grammars/TinyV1Lexer.flex @@ -0,0 +1,93 @@ +/* + * Minecraft Development for IntelliJ + * + * https://mcdev.io/ + * + * Copyright (C) 2024 minecraft-dev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, version 3.0 only. + * + * 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 Lesser General Public License + * along with this program. If not, see . + */ + +package io.mcdev.obfex.formats.tinyv1.gen; + +import com.intellij.lexer.*; +import com.intellij.psi.tree.IElementType; +import static io.mcdev.obfex.formats.tinyv1.gen.psi.TinyV1Types.*; +import static com.intellij.psi.TokenType.*; + +%% + +%{ + public TinyV1Lexer() { + this((java.io.Reader)null); + } +%} + +%public +%class TinyV1Lexer +%implements FlexLexer +%function advance +%type IElementType + +%s NAMESPACES +%s CORE +%s ENTRY +%s SIGNATURE + +%unicode + +NAMESPACE=[^\s] + +NAME_ELEMENT=[\p{L}_\p{Sc}][\p{L}\p{N}_\p{Sc}]* +PRIMITIVE=[ZBCSIFDJV] +CLASS_TYPE=(\[+[ZBCSIFDJ]|\[*L[^;\s]+;) + +COMMENT=\s*#.* +WHITE_SPACE=\s+ +CRLF=\r\n|\n|\r + +%% + + { + "v1" { yybegin(NAMESPACES); return V1_KEY; } +} + + { + {NAMESPACE}+ { return NAMESPACE_KEY; } +} + + { + "CLASS" { yybegin(ENTRY); return CLASS_KEY; } + "FIELD" { yybegin(ENTRY); return FIELD_KEY; } + "METHOD" { yybegin(ENTRY); return METHOD_KEY; } +} + + { + "(" { yybegin(SIGNATURE); return OPEN_PAREN; } + "/" { return SLASH; } + {PRIMITIVE} {WHITE_SPACE}? { zzMarkedPos = zzStartRead + 1; return PRIMITIVE; } + {CLASS_TYPE} { return CLASS_TYPE; } + {NAME_ELEMENT} { return NAME_ELEMENT; } +} + + { + ")" { return CLOSE_PAREN; } + {PRIMITIVE} { return PRIMITIVE; } + {CLASS_TYPE} { return CLASS_TYPE; } + {WHITE_SPACE} { yybegin(ENTRY); return WHITE_SPACE; } +} + +{COMMENT} { return COMMENT; } +{CRLF} { yybegin(CORE); return CRLF; } +{WHITE_SPACE} { return WHITE_SPACE; } +[^] { return BAD_CHARACTER; } diff --git a/obfuscation-explorer/src/main/grammars/TinyV1Parser.bnf b/obfuscation-explorer/src/main/grammars/TinyV1Parser.bnf new file mode 100644 index 000000000..ae61cc24a --- /dev/null +++ b/obfuscation-explorer/src/main/grammars/TinyV1Parser.bnf @@ -0,0 +1,67 @@ +/* + * Minecraft Development for IntelliJ + * + * https://mcdev.io/ + * + * Copyright (C) 2024 minecraft-dev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, version 3.0 only. + * + * 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 Lesser General Public License + * along with this program. If not, see . + */ + +{ + parserClass="io.mcdev.obfex.formats.tinyv1.gen.TinyV1Parser" + + extends="com.intellij.extapi.psi.ASTWrapperPsiElement" + + psiClassPrefix="TinyV1" + psiImplClassSuffix="Impl" + psiPackage="io.mcdev.obfex.formats.tinyv1.gen.psi" + psiImplPackage="io.mcdev.obfex.formats.tinyv1.gen.psi.impl" + + elementTypeHolderClass="io.mcdev.obfex.formats.tinyv1.gen.psi.TinyV1Types" + elementTypeClass="io.mcdev.obfex.formats.tinyv1.lang.psi.TinyV1ElementType" + tokenTypeClass="io.mcdev.obfex.formats.tinyv1.lang.psi.TinyV1TokenType" + + tokens=[ + COMMENT="regexp:\s*#.*" + ] + + // do not record error reporting information in recover rules + consumeTokenMethod(".*_recover")="consumeTokenFast" +} + +tinyv1_file ::= file_element? +file_element ::= header line_end line* + +header ::= V1_KEY namespace* {pin=1} +namespace ::= NAMESPACE_KEY + +private line_end ::= <> | CRLF +private line_recover ::= !(mapping | line_end) + +private line ::= !<> mapping? line_end {pin=2 recoverWhile=line_recover} + +mapping ::= class_mapping | field_mapping | method_mapping + +class_mapping ::= CLASS_KEY mapping_part* {pin=1} +field_mapping ::= FIELD_KEY mapping_part type_desc mapping_part* {pin=1} +method_mapping ::= METHOD_KEY mapping_part method_signature mapping_part* {pin=1} + +mapping_part ::= PRIMITIVE | identifier +type_desc ::= PRIMITIVE | CLASS_TYPE +identifier ::= name_part (SLASH name_part)* {pin(".*")=1} +name_part ::= NAME_ELEMENT + +method_signature ::= OPEN_PAREN param_types CLOSE_PAREN return_type {pin=1} +param_types ::= type_desc* +return_type ::= type_desc diff --git a/obfuscation-explorer/src/main/grammars/TinyV2Lexer.flex b/obfuscation-explorer/src/main/grammars/TinyV2Lexer.flex new file mode 100644 index 000000000..08fe089be --- /dev/null +++ b/obfuscation-explorer/src/main/grammars/TinyV2Lexer.flex @@ -0,0 +1,154 @@ +/* + * Minecraft Development for IntelliJ + * + * https://mcdev.io/ + * + * Copyright (C) 2024 minecraft-dev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, version 3.0 only. + * + * 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 Lesser General Public License + * along with this program. If not, see . + */ + +package io.mcdev.obfex.formats.tinyv2.gen; + +import com.intellij.lexer.*; +import com.intellij.psi.tree.IElementType; +import static io.mcdev.obfex.formats.tinyv2.gen.psi.TinyV2Types.*; +import static com.intellij.psi.TokenType.*; + +%% + +%{ + private int versionNumCounter = 0; + + public TinyV2Lexer() { + this((java.io.Reader)null); + } +%} + +%public +%class TinyV2Lexer +%implements FlexLexer +%function advance +%type IElementType + +%s HEADER +%s MAYBE_PROPERTIES +%s PROPERTIES +%s PROP_VALUE +%s DOC_LINE +%s CORE +%s CORE2 +%s MAPPING +%s PARAM_MAPPING +%s LOCAL_VAR_MAPPING +%s SIGNATURE + +%unicode + +SAFE_STRING=[^\\\n\r\t\0\s] +PROP_VALUE=[^\s] + +DOC_TEXT=[^\r\n]+ + +NAME_ELEMENT=[\p{L}_\p{Sc}][\p{L}\p{N}_\p{Sc}]* + +PRIMITIVE=[ZBCSIFDJV] +CLASS_TYPE=(\[+[ZBCSIFDJ]|\[*L[^;\s]+;) + +DIGIT=\d + +COMMENT=\s*#.* + +WHITE_SPACE=\s +CRLF=\r\n|\n|\r + +%% + + { + "tiny" { yybegin(HEADER); return TINY_KEY; } +} + +
{ + {DIGIT}+ { if (versionNumCounter++ <= 2) return VERSION_NUM; else return NAMESPACE_KEY; } + {SAFE_STRING}+ { return NAMESPACE_KEY; } + {CRLF} { yybegin(MAYBE_PROPERTIES); return CRLF; } +} + + { + "c" { yybegin(MAPPING); return CLASS_KEY; } + {CRLF} { yybegin(CORE); return CRLF; } + {WHITE_SPACE} { yybegin(PROPERTIES); return WHITE_SPACE; } +} + + { + {SAFE_STRING}+ { return PROPERTY_KEY; } + {CRLF} { yybegin(MAYBE_PROPERTIES); return CRLF; } + {WHITE_SPACE} { yybegin(PROP_VALUE); return WHITE_SPACE; } +} + + { + {CRLF} { yybegin(MAYBE_PROPERTIES); return CRLF; } + {PROP_VALUE}+ { return PROPERTY_VALUE; } +} + + { + "c" { yybegin(MAPPING); return CLASS_KEY; } + {CRLF} { yybegin(CORE); return CRLF; } + {WHITE_SPACE} { yybegin(CORE2); return WHITE_SPACE; } +} + + { + "m" { yybegin(MAPPING); return METHOD_KEY; } + "f" { yybegin(MAPPING); return FIELD_KEY; } + "p" { yybegin(PARAM_MAPPING); return PARAM_KEY; } + "v" { yybegin(LOCAL_VAR_MAPPING); return VAR_KEY; } + "c" { yybegin(DOC_LINE); return COMMENT_KEY; } +} + + { + {DOC_TEXT} { return DOC_TEXT; } + {CRLF} { yybegin(CORE); return CRLF; } +} + + { + "(" { yybegin(SIGNATURE); return OPEN_PAREN; } + "/" { return SLASH; } + {PRIMITIVE} {WHITE_SPACE}? { zzMarkedPos = zzStartRead + 1; return PRIMITIVE; } + "" { return INIT; } + "" { return CLINIT; } + "package-info" { return NAME_ELEMENT; } + {CLASS_TYPE} { return CLASS_TYPE; } + {NAME_ELEMENT} { return NAME_ELEMENT; } +} + + { + {DIGIT}+ { yybegin(MAPPING); return DIGIT; } +} + + { + {DIGIT}+ { return DIGIT; } + {NAME_ELEMENT} { return NAME_ELEMENT; } +} + + { + ")" { return CLOSE_PAREN; } + {PRIMITIVE} { return PRIMITIVE; } + {CLASS_TYPE} { return CLASS_TYPE; } + {CRLF} { yybegin(CORE); return CRLF; } + {WHITE_SPACE} { yybegin(MAPPING); return WHITE_SPACE; } +} + +{COMMENT} { return COMMENT; } +{CRLF} { yybegin(CORE); return CRLF; } +{WHITE_SPACE} { return WHITE_SPACE; } +[^] { return BAD_CHARACTER; } diff --git a/obfuscation-explorer/src/main/grammars/TinyV2Parser.bnf b/obfuscation-explorer/src/main/grammars/TinyV2Parser.bnf new file mode 100644 index 000000000..5f9f7ef0f --- /dev/null +++ b/obfuscation-explorer/src/main/grammars/TinyV2Parser.bnf @@ -0,0 +1,94 @@ +/* + * Minecraft Development for IntelliJ + * + * https://mcdev.io/ + * + * Copyright (C) 2024 minecraft-dev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, version 3.0 only. + * + * 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 Lesser General Public License + * along with this program. If not, see . + */ + +{ + parserClass="io.mcdev.obfex.formats.tinyv2.gen.TinyV2Parser" + + extends="com.intellij.extapi.psi.ASTWrapperPsiElement" + + psiClassPrefix="TinyV2" + psiImplClassSuffix="Impl" + psiPackage="io.mcdev.obfex.formats.tinyv2.gen.psi" + psiImplPackage="io.mcdev.obfex.formats.tinyv2.gen.psi.impl" + + elementTypeHolderClass="io.mcdev.obfex.formats.tinyv2.gen.psi.TinyV2Types" + elementTypeClass="io.mcdev.obfex.formats.tinyv2.lang.psi.TinyV2ElementType" + tokenTypeClass="io.mcdev.obfex.formats.tinyv2.lang.psi.TinyV2TokenType" + + tokens=[ + COMMENT="regexp:\s*#.*" + ] + +// consumeTokenMethod="consumeTokenFast" +} + +tinyv2_file ::= file_element? +file_element ::= header (line_end property*)? class_mapping* + +header ::= TINY_KEY VERSION_NUM VERSION_NUM namespace* {pin=1 recoverWhile=header_recover} { + methods=[ + majorVersionNum="/VERSION_NUM[0]" + minorVersionNum="/VERSION_NUM[1]" + ] +} +private header_recover ::= !line_end + +namespace ::= NAMESPACE_KEY + +property ::= PROPERTY_KEY PROPERTY_VALUE? line_end { + methods=[ + propertyKey="/PROPERTY_KEY" + propergyValue="/PROPERTY_VALUE" + ] +} + +private line_end ::= <> | CRLF+ + +class_mapping ::= CLASS_KEY mapping_part* line_end class_member* {pin=1 recoverWhile=class_recover} +private class_recover ::= !CLASS_KEY + +doc_line ::= COMMENT_KEY DOC_TEXT? line_end {pin=1} + +class_member ::= doc_line | method_mapping | field_mapping {recoverWhile=member_recover} +private member_recover ::= !(METHOD_KEY | FIELD_KEY | CLASS_KEY) +method_mapping ::= METHOD_KEY method_signature mapping_part* line_end method_member* {pin=1} + +method_member ::= doc_line | param_mapping | local_var_mapping + +param_mapping ::= PARAM_KEY param_index mapping_part* line_end doc_line* {pin=1 recoverWhile=param_recover} +private param_recover ::= !(PARAM_KEY | VAR_KEY | METHOD_KEY | FIELD_KEY | CLASS_KEY) +param_index ::= DIGIT + +local_var_mapping ::= VAR_KEY local_var_index local_var_start lvt_index? mapping_part* line_end doc_line* {pin=1 recoverWhile=local_var_recover} +private local_var_recover ::= !(VAR_KEY | PARAM_KEY | METHOD_KEY | FIELD_KEY | CLASS_KEY) +local_var_index ::= DIGIT +local_var_start ::= DIGIT +lvt_index ::= DIGIT + +field_mapping ::= FIELD_KEY type_desc mapping_part* line_end doc_line* {pin=1} + +mapping_part ::= PRIMITIVE | INIT | CLINIT | identifier + +type_desc ::= PRIMITIVE | CLASS_TYPE +identifier ::= NAME_ELEMENT (SLASH NAME_ELEMENT)* {pin(".*")=1} + +method_signature ::= OPEN_PAREN param_types CLOSE_PAREN return_type {pin=1} +param_types ::= type_desc* +return_type ::= type_desc diff --git a/obfuscation-explorer/src/main/kotlin/ObfIcons.kt b/obfuscation-explorer/src/main/kotlin/ObfIcons.kt new file mode 100644 index 000000000..e115d251b --- /dev/null +++ b/obfuscation-explorer/src/main/kotlin/ObfIcons.kt @@ -0,0 +1,44 @@ +/* + * Minecraft Development for IntelliJ + * + * https://mcdev.io/ + * + * Copyright (C) 2024 minecraft-dev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, version 3.0 only. + * + * 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 Lesser General Public License + * along with this program. If not, see . + */ + +package io.mcdev.obfex + +import com.intellij.ui.IconManager +import javax.swing.Icon + +object ObfIcons { + + val OBF_EX_ICON = load("/fileTypes/obfex.svg") + + val SRG_ICON = load("/fileTypes/srg.svg") + val TSRG_ICON = load("/fileTypes/tsrg.svg") + val TSRG2_ICON = load("/fileTypes/tsrg2.svg") + val CSRG_ICON = load("/fileTypes/csrg.svg") + val XSRG_ICON = load("/fileTypes/xsrg.svg") + val JAM_ICON = load("/fileTypes/jam.svg") + val ENIGMA_ICON = load("/fileTypes/enigma.svg") + val TINY_V1_ICON = load("/fileTypes/tinyv1.svg") + val TINY_V2_ICON = load("/fileTypes/tinyv2.svg") + val PROGUARD_ICONS = load("/fileTypes/proguard.svg") + + private fun load(path: String): Icon { + return IconManager.getInstance().getIcon(path, ObfIcons::class.java.classLoader) + } +} diff --git a/obfuscation-explorer/src/main/kotlin/facet/ObfExFacet.kt b/obfuscation-explorer/src/main/kotlin/facet/ObfExFacet.kt new file mode 100644 index 000000000..68b8e6981 --- /dev/null +++ b/obfuscation-explorer/src/main/kotlin/facet/ObfExFacet.kt @@ -0,0 +1,44 @@ +/* + * Minecraft Development for IntelliJ + * + * https://mcdev.io/ + * + * Copyright (C) 2024 minecraft-dev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, version 3.0 only. + * + * 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 Lesser General Public License + * along with this program. If not, see . + */ + +package io.mcdev.obfex.facet + +import com.intellij.facet.Facet +import com.intellij.facet.FacetTypeId +import com.intellij.facet.FacetTypeRegistry +import com.intellij.openapi.module.Module +import io.mcdev.obfex.facet.ObfExFacetType.Companion.TYPE_ID + +class ObfExFacet( + module: Module, + name: String, + config: ObfExFacetConfiguration, + underlyingFacet: Facet<*>? +) : Facet(facetType, module, name, config, underlyingFacet) { + + companion object { + val ID = FacetTypeId(TYPE_ID) + + val facetType: ObfExFacetType + get() = facetTypeOrNull as ObfExFacetType + val facetTypeOrNull: ObfExFacetType? + get() = FacetTypeRegistry.getInstance().findFacetType(TYPE_ID) as? ObfExFacetType + } +} diff --git a/obfuscation-explorer/src/main/kotlin/facet/ObfExFacetConfiguration.kt b/obfuscation-explorer/src/main/kotlin/facet/ObfExFacetConfiguration.kt new file mode 100644 index 000000000..eacda08d9 --- /dev/null +++ b/obfuscation-explorer/src/main/kotlin/facet/ObfExFacetConfiguration.kt @@ -0,0 +1,66 @@ +/* + * Minecraft Development for IntelliJ + * + * https://mcdev.io/ + * + * Copyright (C) 2024 minecraft-dev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, version 3.0 only. + * + * 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 Lesser General Public License + * along with this program. If not, see . + */ + +package io.mcdev.obfex.facet + +import com.intellij.facet.FacetConfiguration +import com.intellij.facet.ui.FacetEditorContext +import com.intellij.facet.ui.FacetEditorTab +import com.intellij.facet.ui.FacetValidatorsManager +import com.intellij.openapi.components.PersistentStateComponent +import com.intellij.openapi.components.State +import com.intellij.util.xmlb.annotations.Attribute +import com.intellij.util.xmlb.annotations.Tag +import com.intellij.util.xmlb.annotations.XCollection +import io.mcdev.obfex.facet.config.ObfExFacetEditorTab +import io.mcdev.obfex.formats.MappingsFormatType + +@State(name = "ObfExFacet") +class ObfExFacetConfiguration : FacetConfiguration, PersistentStateComponent { + + private var state = ObfExFacetConfigurationData() + + override fun createEditorTabs( + editorContext: FacetEditorContext?, + validatorsManager: FacetValidatorsManager? + ): Array { + return arrayOf(ObfExFacetEditorTab(this)) + } + + override fun getState() = state + override fun loadState(state: ObfExFacetConfigurationData) { + this.state = state + } +} + +data class ObfExFacetConfigurationData( + @Tag("mappingTargets") + @XCollection(elementName = "mappingTarget", style = XCollection.Style.v2) + var mappingTargets: List = listOf(), +) + +data class MappingTargetConfig( + + @Attribute("type", converter = MappingsFormatType.Converter::class) + var type: MappingsFormatType? = null, + + @Attribute("uri") + var uri: String? = null, +) diff --git a/obfuscation-explorer/src/main/kotlin/facet/ObfExFacetType.kt b/obfuscation-explorer/src/main/kotlin/facet/ObfExFacetType.kt new file mode 100644 index 000000000..02d2dda8b --- /dev/null +++ b/obfuscation-explorer/src/main/kotlin/facet/ObfExFacetType.kt @@ -0,0 +1,47 @@ +/* + * Minecraft Development for IntelliJ + * + * https://mcdev.io/ + * + * Copyright (C) 2024 minecraft-dev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, version 3.0 only. + * + * 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 Lesser General Public License + * along with this program. If not, see . + */ + +package io.mcdev.obfex.facet + +import com.intellij.facet.Facet +import com.intellij.facet.FacetType +import com.intellij.openapi.module.Module +import com.intellij.openapi.module.ModuleType +import io.mcdev.obfex.ObfIcons + +class ObfExFacetType : + FacetType(ObfExFacet.ID, TYPE_ID, "Obfuscation Explorer") { + + override fun createFacet( + module: Module, + name: String, + configuration: ObfExFacetConfiguration, + underlyingFacet: Facet<*>? + ) = ObfExFacet(module, name, configuration, underlyingFacet) + + override fun createDefaultConfiguration() = ObfExFacetConfiguration() + override fun isSuitableModuleType(moduleType: ModuleType<*>?) = true + + override fun getIcon() = ObfIcons.OBF_EX_ICON + + companion object { + const val TYPE_ID = "obfex" + } +} diff --git a/obfuscation-explorer/src/main/kotlin/facet/config/MappingsTable.kt b/obfuscation-explorer/src/main/kotlin/facet/config/MappingsTable.kt new file mode 100644 index 000000000..0bcee35d7 --- /dev/null +++ b/obfuscation-explorer/src/main/kotlin/facet/config/MappingsTable.kt @@ -0,0 +1,313 @@ +/* + * Minecraft Development for IntelliJ + * + * https://mcdev.io/ + * + * Copyright (C) 2024 minecraft-dev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, version 3.0 only. + * + * 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 Lesser General Public License + * along with this program. If not, see . + */ + +package io.mcdev.obfex.facet.config + +import com.intellij.openapi.actionSystem.AnAction +import com.intellij.openapi.actionSystem.AnActionEvent +import com.intellij.openapi.diagnostic.logger +import com.intellij.openapi.fileChooser.FileChooser +import com.intellij.openapi.fileChooser.FileChooserDescriptor +import com.intellij.openapi.vfs.VirtualFile +import com.intellij.openapi.wm.IdeFocusManager +import com.intellij.ui.ToolbarDecorator +import com.intellij.ui.components.JBTextField +import com.intellij.ui.components.textFieldWithBrowseButton +import com.intellij.ui.table.JBTable +import com.intellij.util.IconUtil +import com.intellij.util.ui.AbstractTableCellEditor +import io.mcdev.obfex.facet.MappingTargetConfig +import io.mcdev.obfex.facet.ObfExFacetConfiguration +import io.mcdev.obfex.formats.MappingsFormatType +import io.mcdev.obfex.mappings.MappingsFile +import io.mcdev.obfex.mappings.MappingsFormatParser +import java.awt.Component +import java.net.URI +import java.net.URISyntaxException +import java.nio.file.Paths +import javax.swing.JComponent +import javax.swing.JTable +import javax.swing.ListSelectionModel +import javax.swing.table.AbstractTableModel +import javax.swing.table.DefaultTableCellRenderer + +class MappingsTable(private val config: ObfExFacetConfiguration) { + + private val state: MutableList = configToState(config.state.mappingTargets).toMutableList() + + private val model: MappingsFileTableModel = MappingsFileTableModel(state) + private val table: JBTable = createTable(model) + val panel: JComponent = createPanel(table, model) + + fun stateToConfig(): List { + if (!table.isEditing) { + return state.map { MappingTargetConfig(it.type, it.uri?.toString() ?: "") } + } + + val value = table.cellEditor.cellEditorValue as? URI + val currentRow = table.editingRow + + return state.mapIndexed { i, row -> + if (i == currentRow) { + // TODO file type + MappingTargetConfig(null, value?.toString() ?: "") + } else { + MappingTargetConfig(row.type, row.uri?.toString() ?: "") + } + } + } + + private fun configToState(list: List): List { + return list.map { value -> + val (type, uri) = value + if (type == null && uri.isNullOrBlank()) { + MappingsTableRow.NULL + } else { + MappingsTableRow(type, if (uri.isNullOrBlank()) null else URI.create(uri)) + } + } + } + + fun reset() { + state.clear() + state.addAll(configToState(config.state.mappingTargets)) + + table.clearSelection() + model.fireTableDataChanged() + } + + private class MappingsFileTableModel(val state: MutableList) : AbstractTableModel() { + override fun getRowCount(): Int = state.size + override fun getColumnCount(): Int = 1 + override fun getColumnClass(columnIndex: Int): Class<*> = URI::class.java + override fun getColumnName(column: Int): String = "Mappings Files" + + override fun isCellEditable(rowIndex: Int, columnIndex: Int): Boolean = true + + override fun getValueAt(rowIndex: Int, columnIndex: Int): Any { + return state[rowIndex] + } + override fun setValueAt(aValue: Any?, rowIndex: Int, columnIndex: Int) { + val uri = when (aValue) { + is URI -> aValue + is String -> aValue.toUriFromUserInput() + else -> return + } + val type: MappingsFormatType? = uri?.let { + val path = uri.path + val matchedParsers = MappingsFormatParser.EP_NAME.extensions.filter { parser -> + parser.expectedFileExtensions.any { ext -> path.endsWith(ext) } + } + if (matchedParsers.isEmpty()) { + null + } else if (matchedParsers.size == 1) { + // TODO Provide a separate optional API method for getting file type, for parsers which don't + // need to do a full parse to determine type + // TODO Make a util for this since it's repeated here + (matchedParsers.single().parse(toVirtualFile(uri))?.source as? MappingsFile)?.type + } else { + val file = toVirtualFile(uri) + val allMatching = matchedParsers.filterNot { parser -> + parser.isSupportedFile(file) + } + if (allMatching.isEmpty()) { + null + } else if (allMatching.size == 1) { + (allMatching.single().parse(toVirtualFile(uri))?.source as? MappingsFile)?.type + } else { + logger().warn( + "Multiple support mappings parsers found for URI: " + + "$uri; choosing first registered" + ) + (allMatching.first().parse(toVirtualFile(uri))?.source as? MappingsFile)?.type + } + } + } + state[rowIndex] = MappingsTableRow(type, uri) + } + + private fun toVirtualFile(uri: URI): VirtualFile { + // This needs to handle http uris as well - probably by downloading to the .idea directory or something maybe + // need to have an external file manager for handling that, keep track of etags, redownload on change, etc + TODO() + } + } + + private class Editor( + private val model: MappingsFileTableModel, + private val state: MutableList = model.state, + ) : AbstractTableCellEditor() { + private val textField = textFieldWithBrowseButton(null, "Mappings Files", fileDesc, null) + private var row: Int = -1 + + override fun getCellEditorValue(): Any? { + return textField.text.toUriFromUserInput() + } + + override fun getTableCellEditorComponent( + table: JTable?, + value: Any?, + isSelected: Boolean, + row: Int, + column: Int + ): Component { + this.row = row + textField.text = when (value) { + is URI -> value.toPresentableText() + else -> "" + } + return textField + } + + override fun stopCellEditing(): Boolean { + if (row != -1 && state.size > row) { + val uri = textField.text.toUriFromUserInput() + state[row] = MappingsTableRow(null, uri) + model.fireTableCellUpdated(row, 0) + } + textField.text = "" + return super.stopCellEditing() + } + } + + private object Renderer : DefaultTableCellRenderer() { + private fun readResolve(): Any = Renderer + + override fun getTableCellRendererComponent( + table: JTable?, + value: Any?, + isSelected: Boolean, + hasFocus: Boolean, + row: Int, + column: Int + ): Component { + val field = JBTextField() + field.text = when (value) { + is URI -> value.toPresentableText() + is String -> value + else -> "" + } + field.isEditable = false + + return field + } + } + + companion object { + private fun createTable(model: MappingsFileTableModel): JBTable { + val table = JBTable(model) + + table.setShowGrid(true) + table.setEnableAntialiasing(true) + + table.emptyText.text = "No mappings files" + + table.visibleRowCount = 8 + + table.setDefaultRenderer(URI::class.java, Renderer) + table.setDefaultEditor(URI::class.java, Editor(model)) + + table.selectionModel.selectionMode = ListSelectionModel.SINGLE_SELECTION + + return table + } + + private fun createPanel(table: JBTable, model: MappingsFileTableModel): JComponent { + val state = model.state + + return ToolbarDecorator.createDecorator(table) + .disableUpDownActions() + .setAddAction { + val initial = state.size + + val chosenFiles = FileChooser.chooseFiles(fileDesc, null, null) + for (chosenFile in chosenFiles) { + state.add(MappingsTableRow(null, chosenFile.toNioPath().toUri())) + } + model.fireTableRowsInserted(initial, state.lastIndex) + } + .setAddIcon(IconUtil.addPackageIcon) + .setAddActionName("Add File") + .addExtraAction(object : AnAction( + "Add Text", + "Create blank entry for manual input", + IconUtil.addBlankLineIcon + ) { + override fun actionPerformed(e: AnActionEvent) { + state.add(MappingsTableRow.NULL) + if (table.isEditing) { + table.cellEditor.stopCellEditing() + } + model.fireTableRowsInserted(state.lastIndex - 1, state.lastIndex) + + IdeFocusManager.getGlobalInstance().doWhenFocusSettlesDown { + IdeFocusManager.getGlobalInstance().requestFocus(table, true) + + table.changeSelection(state.lastIndex, 0, false, false) + table.editCellAt(state.lastIndex, 0) + } + } + }) + .setRemoveAction { + val selected = table.selectedRow + if (selected == -1) { + return@setRemoveAction + } + if (table.isEditing) { + table.cellEditor?.stopCellEditing() + } + + state.removeAt(selected) + model.fireTableRowsDeleted(selected, selected) + + IdeFocusManager.getGlobalInstance().doWhenFocusSettlesDown { + IdeFocusManager.getGlobalInstance().requestFocus(table, true) + } + } + .setButtonComparator("Add File", "Add Text", "Remove") + .createPanel() + } + + private fun URI.toPresentableText(): String { + if (scheme == "file") { + return Paths.get(this).toAbsolutePath().toString() + } + return toString() + } + + private fun String.toUriFromUserInput(): URI? { + if (isBlank()) { + return null + } + val uri = try { + URI(this) + } catch (_: URISyntaxException) { + return null + } + if (uri.scheme == null) { + // plain file path + return Paths.get(this).toUri() + } + return uri + } + + private val fileDesc = FileChooserDescriptor(true, false, false, false, false, true) + } +} diff --git a/obfuscation-explorer/src/main/kotlin/facet/config/MappingsTableCell.kt b/obfuscation-explorer/src/main/kotlin/facet/config/MappingsTableCell.kt new file mode 100644 index 000000000..3ea2c4133 --- /dev/null +++ b/obfuscation-explorer/src/main/kotlin/facet/config/MappingsTableCell.kt @@ -0,0 +1,39 @@ +/* + * Minecraft Development for IntelliJ + * + * https://mcdev.io/ + * + * Copyright (C) 2024 minecraft-dev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, version 3.0 only. + * + * 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 Lesser General Public License + * along with this program. If not, see . + */ + +package io.mcdev.obfex.facet.config + +import com.intellij.ui.dsl.builder.panel +import io.mcdev.obfex.mappings.MappingsDefinition +import io.mcdev.obfex.mappings.MappingsFile +import java.awt.Component + +object MappingsTableCell { + + fun createComponent(def: MappingsDefinition): Component = panel { + row { + when (val s = def.source) { + is MappingsFile -> { + icon(s.type.icon) + } + } + } + } +} diff --git a/obfuscation-explorer/src/main/kotlin/facet/config/MappingsTableRow.kt b/obfuscation-explorer/src/main/kotlin/facet/config/MappingsTableRow.kt new file mode 100644 index 000000000..abfe6a661 --- /dev/null +++ b/obfuscation-explorer/src/main/kotlin/facet/config/MappingsTableRow.kt @@ -0,0 +1,33 @@ +/* + * Minecraft Development for IntelliJ + * + * https://mcdev.io/ + * + * Copyright (C) 2024 minecraft-dev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, version 3.0 only. + * + * 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 Lesser General Public License + * along with this program. If not, see . + */ + +package io.mcdev.obfex.facet.config + +import io.mcdev.obfex.formats.MappingsFormatType +import java.net.URI + +data class MappingsTableRow( + val type: MappingsFormatType? = null, + val uri: URI? = null, +) { + companion object { + val NULL = MappingsTableRow() + } +} diff --git a/obfuscation-explorer/src/main/kotlin/facet/config/ObfExFacetEditorTab.kt b/obfuscation-explorer/src/main/kotlin/facet/config/ObfExFacetEditorTab.kt new file mode 100644 index 000000000..4c54f1d83 --- /dev/null +++ b/obfuscation-explorer/src/main/kotlin/facet/config/ObfExFacetEditorTab.kt @@ -0,0 +1,55 @@ +/* + * Minecraft Development for IntelliJ + * + * https://mcdev.io/ + * + * Copyright (C) 2024 minecraft-dev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, version 3.0 only. + * + * 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 Lesser General Public License + * along with this program. If not, see . + */ + +package io.mcdev.obfex.facet.config + +import com.intellij.facet.ui.FacetEditorTab +import com.intellij.ui.dsl.builder.AlignX +import com.intellij.ui.dsl.builder.panel +import io.mcdev.obfex.facet.ObfExFacetConfiguration +import javax.swing.JComponent + +class ObfExFacetEditorTab(private val config: ObfExFacetConfiguration) : FacetEditorTab() { + + private val table = MappingsTable(config) + + override fun createComponent(): JComponent { + return panel { + row { + cell(table.panel) + .align(AlignX.FILL) + } + } + } + + override fun isModified(): Boolean { + return config.state.mappingTargets != table.stateToConfig() + } + + override fun getDisplayName(): String = "Obfuscation Explorer Module Settings" + + override fun reset() { + table.reset() + } + + override fun apply() { + config.state.mappingTargets = table.stateToConfig() + } +} diff --git a/obfuscation-explorer/src/main/kotlin/formats/MappingsFormatType.kt b/obfuscation-explorer/src/main/kotlin/formats/MappingsFormatType.kt new file mode 100644 index 000000000..7281318d7 --- /dev/null +++ b/obfuscation-explorer/src/main/kotlin/formats/MappingsFormatType.kt @@ -0,0 +1,41 @@ +/* + * Minecraft Development for IntelliJ + * + * https://mcdev.io/ + * + * Copyright (C) 2024 minecraft-dev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, version 3.0 only. + * + * 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 Lesser General Public License + * along with this program. If not, see . + */ + +package io.mcdev.obfex.formats + +import com.intellij.util.xmlb.Converter as IConverter +import javax.swing.Icon +import org.jetbrains.annotations.NonNls + +abstract class MappingsFormatType(@param:NonNls val id: String) { + + abstract val icon: Icon + abstract val name: String + + class Converter : IConverter() { + override fun toString(value: MappingsFormatType): String? { + return value.id + } + + override fun fromString(value: String): MappingsFormatType? { + return MappingsFormatTypeManager.get().registeredTypes.firstOrNull { it.id == value } + } + } +} diff --git a/obfuscation-explorer/src/main/kotlin/formats/MappingsFormatTypeManager.kt b/obfuscation-explorer/src/main/kotlin/formats/MappingsFormatTypeManager.kt new file mode 100644 index 000000000..a757981ea --- /dev/null +++ b/obfuscation-explorer/src/main/kotlin/formats/MappingsFormatTypeManager.kt @@ -0,0 +1,43 @@ +/* + * Minecraft Development for IntelliJ + * + * https://mcdev.io/ + * + * Copyright (C) 2024 minecraft-dev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, version 3.0 only. + * + * 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 Lesser General Public License + * along with this program. If not, see . + */ + +package io.mcdev.obfex.formats + +import java.util.Collections + +class MappingsFormatTypeManager { + + private val types: HashMap = hashMapOf() + + fun registerType(type: MappingsFormatType) { + if (types.containsKey(type.id)) { + throw IllegalArgumentException("Cannot register mappings format type: ${type.id}; Already registered") + } + types[type.id] = type + } + + val registeredTypes: Collection + get() = Collections.unmodifiableCollection(types.values) + + companion object { + private val instance = MappingsFormatTypeManager() + fun get() = instance + } +} diff --git a/obfuscation-explorer/src/main/kotlin/formats/csrg/CSrgMappingsFormatParser.kt b/obfuscation-explorer/src/main/kotlin/formats/csrg/CSrgMappingsFormatParser.kt new file mode 100644 index 000000000..9612623c9 --- /dev/null +++ b/obfuscation-explorer/src/main/kotlin/formats/csrg/CSrgMappingsFormatParser.kt @@ -0,0 +1,109 @@ +/* + * Minecraft Development for IntelliJ + * + * https://mcdev.io/ + * + * Copyright (C) 2024 minecraft-dev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, version 3.0 only. + * + * 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 Lesser General Public License + * along with this program. If not, see . + */ + +package io.mcdev.obfex.formats.csrg + +import com.intellij.openapi.vfs.VirtualFile +import io.mcdev.obfex.MappingPart +import io.mcdev.obfex.forLines +import io.mcdev.obfex.mappings.FileCoords +import io.mcdev.obfex.mappings.MappingsDefinition +import io.mcdev.obfex.mappings.MappingsDefinitionBuilder +import io.mcdev.obfex.mappings.UnnamedMappingsFormatParser +import io.mcdev.obfex.mappings.clazz +import io.mcdev.obfex.mappings.field +import io.mcdev.obfex.mappings.method +import io.mcdev.obfex.ref.MethodDescriptor +import io.mcdev.obfex.ref.asClass +import io.mcdev.obfex.ref.asField +import io.mcdev.obfex.ref.asMethod +import io.mcdev.obfex.ref.highPriority +import io.mcdev.obfex.ref.lowPriority + +class CSrgMappingsFormatParser : UnnamedMappingsFormatParser { + + override val expectedFileExtensions: Array = arrayOf("csrg") + + override fun parse(file: VirtualFile): MappingsDefinition { + val builder = MappingsDefinitionBuilder(CSrgMappingsFormatType, file) + + file.forLines { lineNum, parts, line -> + when (parts.size) { + 2 -> parseClassLine(builder, lineNum, parts[0], parts[1]) + 3 -> parseFieldLine(builder, lineNum, parts[0], parts[1], parts[2]) + 4 -> parseMethodLine(builder, lineNum, parts[0], parts[1], parts[2], parts[3]) + else -> { + builder.error("Unrecognized line: $line", FileCoords(lineNum)) + } + } + + true + } + + return builder.build() + } + + private fun parseClassLine( + builder: MappingsDefinitionBuilder, + lineNum: Int, + leftClass: MappingPart, + rightClass: MappingPart, + ) { + builder.clazz(lineNum.highPriority) { + with(leftClass.asClass().from) + with(rightClass.asClass().to) + } + } + + private fun parseFieldLine( + builder: MappingsDefinitionBuilder, + lineNum: Int, + className: MappingPart, + leftField: MappingPart, + rightField: MappingPart, + ) { + builder.clazz(className.asClass().from, lineNum.lowPriority).field(lineNum.highPriority) { + with(leftField.asField().from) + with(rightField.asField().to) + } + } + + private fun parseMethodLine( + builder: MappingsDefinitionBuilder, + lineNum: Int, + className: MappingPart, + leftMethod: MappingPart, + methodDesc: MappingPart, + rightMethod: MappingPart, + ) { + val descParsed = MethodDescriptor.parse(methodDesc) + if (descParsed == null) { + builder.error("Invalid method descriptor: ${methodDesc.value}", FileCoords(lineNum, methodDesc)) + return + } + + builder.clazz(className.asClass().from, lineNum.lowPriority).method(lineNum.highPriority) { + desc = descParsed + + with(leftMethod.asMethod().from) + with(rightMethod.asMethod().to) + } + } +} diff --git a/obfuscation-explorer/src/main/kotlin/formats/csrg/CSrgMappingsFormatType.kt b/obfuscation-explorer/src/main/kotlin/formats/csrg/CSrgMappingsFormatType.kt new file mode 100644 index 000000000..60ff641b5 --- /dev/null +++ b/obfuscation-explorer/src/main/kotlin/formats/csrg/CSrgMappingsFormatType.kt @@ -0,0 +1,31 @@ +/* + * Minecraft Development for IntelliJ + * + * https://mcdev.io/ + * + * Copyright (C) 2024 minecraft-dev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, version 3.0 only. + * + * 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 Lesser General Public License + * along with this program. If not, see . + */ + +package io.mcdev.obfex.formats.csrg + +import io.mcdev.obfex.ObfIcons +import io.mcdev.obfex.formats.MappingsFormatType +import javax.swing.Icon + +object CSrgMappingsFormatType : MappingsFormatType("CSRG") { + + override val icon: Icon = ObfIcons.CSRG_ICON + override val name: String = "CSRG" +} diff --git a/obfuscation-explorer/src/main/kotlin/formats/csrg/lang/CSrgFile.kt b/obfuscation-explorer/src/main/kotlin/formats/csrg/lang/CSrgFile.kt new file mode 100644 index 000000000..3210479dd --- /dev/null +++ b/obfuscation-explorer/src/main/kotlin/formats/csrg/lang/CSrgFile.kt @@ -0,0 +1,30 @@ +/* + * Minecraft Development for IntelliJ + * + * https://mcdev.io/ + * + * Copyright (C) 2024 minecraft-dev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, version 3.0 only. + * + * 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 Lesser General Public License + * along with this program. If not, see . + */ + +package io.mcdev.obfex.formats.csrg.lang + +import com.intellij.extapi.psi.PsiFileBase +import com.intellij.openapi.fileTypes.FileType +import com.intellij.psi.FileViewProvider + +class CSrgFile(viewProvider: FileViewProvider) : PsiFileBase(viewProvider, CSrgLanguage) { + override fun getFileType(): FileType = CSrgFileType + override fun toString(): String = CSrgFileType.description +} diff --git a/obfuscation-explorer/src/main/kotlin/formats/csrg/lang/CSrgFileType.kt b/obfuscation-explorer/src/main/kotlin/formats/csrg/lang/CSrgFileType.kt new file mode 100644 index 000000000..28547b243 --- /dev/null +++ b/obfuscation-explorer/src/main/kotlin/formats/csrg/lang/CSrgFileType.kt @@ -0,0 +1,33 @@ +/* + * Minecraft Development for IntelliJ + * + * https://mcdev.io/ + * + * Copyright (C) 2024 minecraft-dev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, version 3.0 only. + * + * 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 Lesser General Public License + * along with this program. If not, see . + */ + +package io.mcdev.obfex.formats.csrg.lang + +import com.intellij.openapi.fileTypes.LanguageFileType +import io.mcdev.obfex.ObfIcons +import javax.swing.Icon + +object CSrgFileType : LanguageFileType(CSrgLanguage) { + + override fun getName(): String = "CSRG" + override fun getDescription(): String = "CSRG obfuscation mapping file" + override fun getDefaultExtension(): String = "csrg" + override fun getIcon(): Icon = ObfIcons.CSRG_ICON +} diff --git a/obfuscation-explorer/src/main/kotlin/formats/csrg/lang/CSrgLanguage.kt b/obfuscation-explorer/src/main/kotlin/formats/csrg/lang/CSrgLanguage.kt new file mode 100644 index 000000000..20aa671a8 --- /dev/null +++ b/obfuscation-explorer/src/main/kotlin/formats/csrg/lang/CSrgLanguage.kt @@ -0,0 +1,25 @@ +/* + * Minecraft Development for IntelliJ + * + * https://mcdev.io/ + * + * Copyright (C) 2024 minecraft-dev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, version 3.0 only. + * + * 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 Lesser General Public License + * along with this program. If not, see . + */ + +package io.mcdev.obfex.formats.csrg.lang + +import com.intellij.lang.Language + +object CSrgLanguage : Language("CSRG") diff --git a/obfuscation-explorer/src/main/kotlin/formats/csrg/lang/CSrgParserDefinition.kt b/obfuscation-explorer/src/main/kotlin/formats/csrg/lang/CSrgParserDefinition.kt new file mode 100644 index 000000000..a3f420f6c --- /dev/null +++ b/obfuscation-explorer/src/main/kotlin/formats/csrg/lang/CSrgParserDefinition.kt @@ -0,0 +1,50 @@ +/* + * Minecraft Development for IntelliJ + * + * https://mcdev.io/ + * + * Copyright (C) 2024 minecraft-dev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, version 3.0 only. + * + * 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 Lesser General Public License + * along with this program. If not, see . + */ + +package io.mcdev.obfex.formats.csrg.lang + +import com.intellij.lang.ASTNode +import com.intellij.lang.Language +import com.intellij.lang.ParserDefinition +import com.intellij.lang.PsiParser +import com.intellij.lexer.Lexer +import com.intellij.openapi.project.Project +import com.intellij.psi.FileViewProvider +import com.intellij.psi.PsiElement +import com.intellij.psi.PsiFile +import com.intellij.psi.tree.IFileElementType +import com.intellij.psi.tree.TokenSet +import io.mcdev.obfex.formats.csrg.gen.CSrgParser +import io.mcdev.obfex.formats.csrg.gen.psi.CSrgTypes +import io.mcdev.obfex.formats.csrg.lang.psi.CSrgLexerAdapter + +class CSrgParserDefinition : ParserDefinition { + + override fun createLexer(project: Project): Lexer = CSrgLexerAdapter() + override fun getCommentTokens(): TokenSet = COMMENTS + override fun getStringLiteralElements(): TokenSet = TokenSet.EMPTY + override fun createParser(project: Project): PsiParser = CSrgParser() + override fun getFileNodeType(): IFileElementType = FILE + override fun createFile(viewProvider: FileViewProvider): PsiFile = CSrgFile(viewProvider) + override fun createElement(node: ASTNode): PsiElement = CSrgTypes.Factory.createElement(node) +} + +private val COMMENTS = TokenSet.create(CSrgTypes.COMMENT) +private val FILE = IFileElementType(Language.findInstance(CSrgLanguage::class.java)) diff --git a/obfuscation-explorer/src/main/kotlin/formats/csrg/lang/colors/CSrgAnnotator.kt b/obfuscation-explorer/src/main/kotlin/formats/csrg/lang/colors/CSrgAnnotator.kt new file mode 100644 index 000000000..7bd29d121 --- /dev/null +++ b/obfuscation-explorer/src/main/kotlin/formats/csrg/lang/colors/CSrgAnnotator.kt @@ -0,0 +1,50 @@ +/* + * Minecraft Development for IntelliJ + * + * https://mcdev.io/ + * + * Copyright (C) 2024 minecraft-dev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, version 3.0 only. + * + * 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 Lesser General Public License + * along with this program. If not, see . + */ + +package io.mcdev.obfex.formats.csrg.lang.colors + +import com.intellij.lang.annotation.AnnotationHolder +import com.intellij.lang.annotation.Annotator +import com.intellij.psi.PsiElement +import io.mcdev.obfex.formats.csrg.gen.psi.CSrgClassMapping +import io.mcdev.obfex.formats.csrg.gen.psi.CSrgFieldMapping +import io.mcdev.obfex.formats.csrg.gen.psi.CSrgMethodMapping +import io.mcdev.obfex.formats.util.registerHighlight + +class CSrgAnnotator : Annotator { + override fun annotate(element: PsiElement, holder: AnnotationHolder) { + when (element) { + is CSrgClassMapping -> { + registerHighlight(element.obfName, CSrgSyntaxHighlighter.CLASS_NAME, holder) + registerHighlight(element.deobfName, CSrgSyntaxHighlighter.CLASS_NAME, holder, true) + } + is CSrgMethodMapping -> { + registerHighlight(element.obfClassName, CSrgSyntaxHighlighter.CLASS_NAME, holder) + registerHighlight(element.obfMethodName, CSrgSyntaxHighlighter.METHOD, holder) + registerHighlight(element.deobfMethodName, CSrgSyntaxHighlighter.METHOD, holder, true) + } + is CSrgFieldMapping -> { + registerHighlight(element.obfClassName, CSrgSyntaxHighlighter.CLASS_NAME, holder) + registerHighlight(element.obfFieldName, CSrgSyntaxHighlighter.FIELD, holder) + registerHighlight(element.deobfName, CSrgSyntaxHighlighter.FIELD, holder, true) + } + } + } +} diff --git a/obfuscation-explorer/src/main/kotlin/formats/csrg/lang/colors/CSrgSyntaxHighlighter.kt b/obfuscation-explorer/src/main/kotlin/formats/csrg/lang/colors/CSrgSyntaxHighlighter.kt new file mode 100644 index 000000000..0b026674f --- /dev/null +++ b/obfuscation-explorer/src/main/kotlin/formats/csrg/lang/colors/CSrgSyntaxHighlighter.kt @@ -0,0 +1,71 @@ +/* + * Minecraft Development for IntelliJ + * + * https://mcdev.io/ + * + * Copyright (C) 2024 minecraft-dev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, version 3.0 only. + * + * 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 Lesser General Public License + * along with this program. If not, see . + */ + +package io.mcdev.obfex.formats.csrg.lang.colors + +import com.intellij.openapi.editor.DefaultLanguageHighlighterColors +import com.intellij.openapi.editor.colors.TextAttributesKey +import com.intellij.openapi.fileTypes.SyntaxHighlighterBase +import com.intellij.psi.tree.IElementType +import io.mcdev.obfex.formats.csrg.gen.psi.CSrgTypes +import io.mcdev.obfex.formats.csrg.lang.psi.CSrgLexerAdapter + +class CSrgSyntaxHighlighter : SyntaxHighlighterBase() { + override fun getHighlightingLexer() = CSrgLexerAdapter() + + override fun getTokenHighlights(tokenType: IElementType?): Array = + when (tokenType) { + CSrgTypes.CLASS_TYPE -> CLASS_TYPE_KEYS + CSrgTypes.PRIMITIVE -> PRIMITIVE_KEYS + CSrgTypes.COMMENT -> COMMENT_KEYS + else -> TextAttributesKey.EMPTY_ARRAY + } + + companion object { + val CLASS_NAME = TextAttributesKey.createTextAttributesKey( + "CSRG_CLASS_NAME", + DefaultLanguageHighlighterColors.STRING + ) + val METHOD = TextAttributesKey.createTextAttributesKey( + "CSRG_METHOD", + DefaultLanguageHighlighterColors.INSTANCE_METHOD + ) + val FIELD = TextAttributesKey.createTextAttributesKey( + "CSRG_FIELD", + DefaultLanguageHighlighterColors.INSTANCE_FIELD + ) + val PRIMITIVE = TextAttributesKey.createTextAttributesKey( + "CSRG_PRIMITIVE", + DefaultLanguageHighlighterColors.NUMBER + ) + val CLASS_TYPE = TextAttributesKey.createTextAttributesKey( + "CSRG_CLASS_TYPE", + DefaultLanguageHighlighterColors.CLASS_REFERENCE + ) + val COMMENT = TextAttributesKey.createTextAttributesKey( + "CSRG_COMMENT", + DefaultLanguageHighlighterColors.LINE_COMMENT + ) + + private val PRIMITIVE_KEYS = arrayOf(PRIMITIVE) + private val CLASS_TYPE_KEYS = arrayOf(CLASS_TYPE) + private val COMMENT_KEYS = arrayOf(COMMENT) + } +} diff --git a/obfuscation-explorer/src/main/kotlin/formats/csrg/lang/colors/CSrgSyntaxHighlighterFactory.kt b/obfuscation-explorer/src/main/kotlin/formats/csrg/lang/colors/CSrgSyntaxHighlighterFactory.kt new file mode 100644 index 000000000..3bf0c2ecd --- /dev/null +++ b/obfuscation-explorer/src/main/kotlin/formats/csrg/lang/colors/CSrgSyntaxHighlighterFactory.kt @@ -0,0 +1,29 @@ +/* + * Minecraft Development for IntelliJ + * + * https://mcdev.io/ + * + * Copyright (C) 2024 minecraft-dev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, version 3.0 only. + * + * 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 Lesser General Public License + * along with this program. If not, see . + */ + +package io.mcdev.obfex.formats.csrg.lang.colors + +import com.intellij.openapi.fileTypes.SyntaxHighlighterFactory +import com.intellij.openapi.project.Project +import com.intellij.openapi.vfs.VirtualFile + +class CSrgSyntaxHighlighterFactory : SyntaxHighlighterFactory() { + override fun getSyntaxHighlighter(project: Project?, virtualFile: VirtualFile?) = CSrgSyntaxHighlighter() +} diff --git a/obfuscation-explorer/src/main/kotlin/formats/csrg/lang/psi/CSrgElementType.kt b/obfuscation-explorer/src/main/kotlin/formats/csrg/lang/psi/CSrgElementType.kt new file mode 100644 index 000000000..3692d908d --- /dev/null +++ b/obfuscation-explorer/src/main/kotlin/formats/csrg/lang/psi/CSrgElementType.kt @@ -0,0 +1,27 @@ +/* + * Minecraft Development for IntelliJ + * + * https://mcdev.io/ + * + * Copyright (C) 2024 minecraft-dev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, version 3.0 only. + * + * 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 Lesser General Public License + * along with this program. If not, see . + */ + +package io.mcdev.obfex.formats.csrg.lang.psi + +import com.intellij.psi.tree.IElementType +import io.mcdev.obfex.formats.csrg.lang.CSrgLanguage +import org.jetbrains.annotations.NonNls + +class CSrgElementType(@NonNls debugName: String) : IElementType(debugName, CSrgLanguage) diff --git a/obfuscation-explorer/src/main/kotlin/formats/csrg/lang/psi/CSrgLexerAdapter.kt b/obfuscation-explorer/src/main/kotlin/formats/csrg/lang/psi/CSrgLexerAdapter.kt new file mode 100644 index 000000000..360bddc6c --- /dev/null +++ b/obfuscation-explorer/src/main/kotlin/formats/csrg/lang/psi/CSrgLexerAdapter.kt @@ -0,0 +1,26 @@ +/* + * Minecraft Development for IntelliJ + * + * https://mcdev.io/ + * + * Copyright (C) 2024 minecraft-dev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, version 3.0 only. + * + * 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 Lesser General Public License + * along with this program. If not, see . + */ + +package io.mcdev.obfex.formats.csrg.lang.psi + +import com.intellij.lexer.FlexAdapter +import io.mcdev.obfex.formats.csrg.gen.CSrgLexer + +class CSrgLexerAdapter : FlexAdapter(CSrgLexer()) diff --git a/obfuscation-explorer/src/main/kotlin/formats/csrg/lang/psi/CSrgTokenType.kt b/obfuscation-explorer/src/main/kotlin/formats/csrg/lang/psi/CSrgTokenType.kt new file mode 100644 index 000000000..d5edfbe72 --- /dev/null +++ b/obfuscation-explorer/src/main/kotlin/formats/csrg/lang/psi/CSrgTokenType.kt @@ -0,0 +1,27 @@ +/* + * Minecraft Development for IntelliJ + * + * https://mcdev.io/ + * + * Copyright (C) 2024 minecraft-dev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, version 3.0 only. + * + * 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 Lesser General Public License + * along with this program. If not, see . + */ + +package io.mcdev.obfex.formats.csrg.lang.psi + +import com.intellij.psi.tree.IElementType +import io.mcdev.obfex.formats.csrg.lang.CSrgLanguage +import org.jetbrains.annotations.NonNls + +class CSrgTokenType(@NonNls debugName: String) : IElementType(debugName, CSrgLanguage) diff --git a/obfuscation-explorer/src/main/kotlin/formats/enigma/EnigmaLanguageCodeStyleSettingsProvider.kt b/obfuscation-explorer/src/main/kotlin/formats/enigma/EnigmaLanguageCodeStyleSettingsProvider.kt new file mode 100644 index 000000000..57dfaf832 --- /dev/null +++ b/obfuscation-explorer/src/main/kotlin/formats/enigma/EnigmaLanguageCodeStyleSettingsProvider.kt @@ -0,0 +1,46 @@ +/* + * Minecraft Development for IntelliJ + * + * https://mcdev.io/ + * + * Copyright (C) 2024 minecraft-dev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, version 3.0 only. + * + * 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 Lesser General Public License + * along with this program. If not, see . + */ + +package io.mcdev.obfex.formats.enigma + +import io.mcdev.obfex.formats.enigma.lang.EnigmaLanguage +import io.mcdev.obfex.formats.util.TabCodeStyleSettingsProvider +import org.intellij.lang.annotations.Language + +class EnigmaLanguageCodeStyleSettingsProvider : TabCodeStyleSettingsProvider() { + override fun getLanguage() = EnigmaLanguage + override fun getCodeSample(settingsType: SettingsType) = SAMPLE +} + +@Language("Enigma") +private const val SAMPLE = """ +CLASS net/minecraft/class_6489 net/minecraft/GameVersion + COMMENT The game version interface used by Minecraft, replacing the javabridge + COMMENT one's occurrences in Minecraft code. + METHOD method_37912 getSaveVersion ()Lnet/minecraft/class_6595; + COMMENT {@return the save version information for this game version} + METHOD method_48017 getResourceVersion (Lnet/minecraft/class_3264;)I + ARG 1 type + METHOD method_48018 getId ()Ljava/lang/String; + METHOD method_48019 getName ()Ljava/lang/String; + METHOD method_48020 getProtocolVersion ()I + METHOD method_48021 getBuildTime ()Ljava/util/Date; + METHOD method_48022 isStable ()Z +""" diff --git a/obfuscation-explorer/src/main/kotlin/formats/enigma/EnigmaMappingsFormatParser.kt b/obfuscation-explorer/src/main/kotlin/formats/enigma/EnigmaMappingsFormatParser.kt new file mode 100644 index 000000000..9f0aec3a0 --- /dev/null +++ b/obfuscation-explorer/src/main/kotlin/formats/enigma/EnigmaMappingsFormatParser.kt @@ -0,0 +1,218 @@ +/* + * Minecraft Development for IntelliJ + * + * https://mcdev.io/ + * + * Copyright (C) 2024 minecraft-dev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, version 3.0 only. + * + * 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 Lesser General Public License + * along with this program. If not, see . + */ + +package io.mcdev.obfex.formats.enigma + +import com.intellij.openapi.vfs.VirtualFile +import io.mcdev.obfex.MappingPart +import io.mcdev.obfex.forLinesIndent +import io.mcdev.obfex.formats.util.indicesFrom +import io.mcdev.obfex.mappings.ClassMappingBuilder +import io.mcdev.obfex.mappings.FileCoords +import io.mcdev.obfex.mappings.MappingIssuesRegistry +import io.mcdev.obfex.mappings.MappingSetBuilderCore +import io.mcdev.obfex.mappings.MappingsDefinition +import io.mcdev.obfex.mappings.MappingsDefinitionBuilder +import io.mcdev.obfex.mappings.MethodMappingBuilder +import io.mcdev.obfex.mappings.UnnamedMappingsFormatParser +import io.mcdev.obfex.mappings.clazz +import io.mcdev.obfex.mappings.field +import io.mcdev.obfex.mappings.method +import io.mcdev.obfex.mappings.ns +import io.mcdev.obfex.mappings.param +import io.mcdev.obfex.ref.asClass +import io.mcdev.obfex.ref.asField +import io.mcdev.obfex.ref.asMethod +import io.mcdev.obfex.ref.asMethodDesc +import io.mcdev.obfex.ref.asParam +import io.mcdev.obfex.ref.asParamIndex +import io.mcdev.obfex.ref.asTypeDef +import io.mcdev.obfex.ref.highPriority + +class EnigmaMappingsFormatParser : UnnamedMappingsFormatParser { + + override val expectedFileExtensions: Array = arrayOf("mapping", "mappings") + + private data class State( + val builder: MappingsDefinitionBuilder, + var baseIndent: Int = 0, + var classNamesStack: ArrayDeque = ArrayDeque(), + var classMapping: ClassMappingBuilder? = null, + var methodMapping: MethodMappingBuilder? = null, + ) : MappingSetBuilderCore by builder, MappingIssuesRegistry by builder + + override fun parse(file: VirtualFile): MappingsDefinition { + val builder = MappingsDefinitionBuilder(EnigmaMappingsFormatType, file) + + val state = State(builder) + file.forLinesIndent { indent, lineNum, parts, _ -> + when { + indent == state.baseIndent -> parseClass(state, lineNum, parts) + indent == state.baseIndent + 1 -> parseMember(state, lineNum, parts) + indent == state.baseIndent + 2 -> parseSubMember(state, lineNum, parts) + indent < state.baseIndent -> { + // new class starting + state.baseIndent = indent + while (state.classNamesStack.size > state.baseIndent) { + state.classNamesStack.removeLastOrNull() + } + parseClass(state, lineNum, parts) + } + } + + return@forLinesIndent true + } + + return builder.build() + } + + private fun parseClass(state: State, lineNum: Int, parts: Array) { + state.classMapping = null + state.methodMapping = null + + if (parts.isEmpty()) { + return + } + + when (parts[0].value) { + "CLASS" -> { + if (parts.size != 3) { + state.error("Unexpected number of entries in class mapping line", FileCoords(lineNum)) + return + } + + val mapping = state.clazz(lineNum.highPriority, parts[0].col) { + for (i in parts.indicesFrom(1)) { + val className = fullClassName(state, i - 1, parts[i].value) + if (className == null) { + state.error( + "All component names for class mapping could not be built as" + + "not all names in the hierarchy are present", + FileCoords(lineNum) + ) + } else { + with(className.asClass().ns(i - 1)) + } + } + } + + state.classMapping = mapping + state.classNamesStack.addLast(mapping) + } + "COMMENT" -> return + else -> state.error("Unexpected mapping file entry", FileCoords(lineNum)) + } + } + + private fun fullClassName(state: State, index: Int, name: String): String? { + if (state.classNamesStack.isEmpty()) { + return name + } + + val last = state.classNamesStack.lastOrNull()?.names?.get(index) ?: return null + @Suppress("ConvertToStringTemplate") + return last + '$' + name + } + + private fun parseMember(state: State, lineNum: Int, parts: Array) { + state.methodMapping = null + + if (parts.isEmpty()) { + return + } + + when (parts[0].value) { + "FIELD" -> parseField(state, lineNum, parts) + "METHOD" -> parseMethod(state, lineNum, parts) + "CLASS" -> { + state.baseIndent++ + parseClass(state, lineNum, parts) + } + "COMMENT" -> return + else -> state.error("Unexpected mapping file entry", FileCoords(lineNum)) + } + } + + private fun parseField(state: State, lineNum: Int, parts: Array) { + val builder = state.classMapping ?: return + + val fieldType = when (parts.size) { + 3 -> null + 4 -> parts[3].value.asTypeDef() + ?: return builder.error("Invalid field type", FileCoords(lineNum, parts[3])) + else -> return state.error("Unexpected number of entries in field mapping line", FileCoords(lineNum)) + } + + builder.field(lineNum.highPriority, parts[0].col) { + type = fieldType + + with(parts[1].asField().from) + with(parts[2].asField().to) + } + } + + private fun parseMethod(state: State, lineNum: Int, parts: Array) { + val builder = state.classMapping ?: return + + val (leftName, rightName, methodDesc) = when (parts.size) { + 3 -> Triple(parts[1], parts[1], parts[2]) + 4 -> Triple(parts[1], parts[2], parts[3]) + else -> return state.error("Unexpected number of entries in method mapping line", FileCoords(lineNum)) + } + + val parsed = methodDesc.value.asMethodDesc() + ?: return state.error("Invalid method descriptor", FileCoords(lineNum, methodDesc)) + + state.methodMapping = builder.method(lineNum.highPriority, parts[0].col) { + desc = parsed + + with(leftName.asMethod().from) + with(rightName.asMethod().to) + } + } + + private fun parseSubMember(state: State, lineNum: Int, parts: Array) { + val builder = state.methodMapping ?: return + + if (parts.isEmpty()) { + return + } + + if (parts[0].value == "COMMENT") { + return + } + if (parts[0].value != "ARG") { + return state.error("Unexpected mapping file entry", FileCoords(lineNum)) + } + + if (parts.size != 3) { + return state.error("Unexpected number of entries in method parameter mapping line", FileCoords(lineNum)) + } + + val index = parts[1].value.toIntOrNull() + ?: return state.error("Method parameter index is not a valid integer", FileCoords(lineNum, parts[1])) + + // TODO enigma actually uses lvt indices, not param. Enigma doesn't include metadata on if the method is static + // or not so we can't reliably work it out here + builder.param(index.asParamIndex(), lineNum.highPriority) { + with(parts[2].asParam().to) + } + } +} diff --git a/obfuscation-explorer/src/main/kotlin/formats/enigma/EnigmaMappingsFormatType.kt b/obfuscation-explorer/src/main/kotlin/formats/enigma/EnigmaMappingsFormatType.kt new file mode 100644 index 000000000..4348fa228 --- /dev/null +++ b/obfuscation-explorer/src/main/kotlin/formats/enigma/EnigmaMappingsFormatType.kt @@ -0,0 +1,31 @@ +/* + * Minecraft Development for IntelliJ + * + * https://mcdev.io/ + * + * Copyright (C) 2024 minecraft-dev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, version 3.0 only. + * + * 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 Lesser General Public License + * along with this program. If not, see . + */ + +package io.mcdev.obfex.formats.enigma + +import io.mcdev.obfex.ObfIcons +import io.mcdev.obfex.formats.MappingsFormatType +import javax.swing.Icon + +object EnigmaMappingsFormatType : MappingsFormatType("Enigma") { + + override val icon: Icon = ObfIcons.ENIGMA_ICON + override val name: String = "Enigma" +} diff --git a/obfuscation-explorer/src/main/kotlin/formats/enigma/lang/EnigmaFile.kt b/obfuscation-explorer/src/main/kotlin/formats/enigma/lang/EnigmaFile.kt new file mode 100644 index 000000000..eeec3b18e --- /dev/null +++ b/obfuscation-explorer/src/main/kotlin/formats/enigma/lang/EnigmaFile.kt @@ -0,0 +1,31 @@ +/* + * Minecraft Development for IntelliJ + * + * https://mcdev.io/ + * + * Copyright (C) 2024 minecraft-dev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, version 3.0 only. + * + * 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 Lesser General Public License + * along with this program. If not, see . + */ + +package io.mcdev.obfex.formats.enigma.lang + +import com.intellij.extapi.psi.PsiFileBase +import com.intellij.openapi.fileTypes.FileType +import com.intellij.psi.FileViewProvider + +class EnigmaFile(viewProvider: FileViewProvider) : PsiFileBase(viewProvider, EnigmaLanguage) { + + override fun getFileType(): FileType = EnigmaFileType + override fun toString(): String = EnigmaFileType.description +} diff --git a/obfuscation-explorer/src/main/kotlin/formats/enigma/lang/EnigmaFileType.kt b/obfuscation-explorer/src/main/kotlin/formats/enigma/lang/EnigmaFileType.kt new file mode 100644 index 000000000..4747853b6 --- /dev/null +++ b/obfuscation-explorer/src/main/kotlin/formats/enigma/lang/EnigmaFileType.kt @@ -0,0 +1,33 @@ +/* + * Minecraft Development for IntelliJ + * + * https://mcdev.io/ + * + * Copyright (C) 2024 minecraft-dev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, version 3.0 only. + * + * 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 Lesser General Public License + * along with this program. If not, see . + */ + +package io.mcdev.obfex.formats.enigma.lang + +import com.intellij.openapi.fileTypes.LanguageFileType +import io.mcdev.obfex.ObfIcons +import javax.swing.Icon + +object EnigmaFileType : LanguageFileType(EnigmaLanguage) { + + override fun getName(): String = "Enigma" + override fun getDescription(): String = "Enigma obfuscation mapping file" + override fun getDefaultExtension(): String = "mapping" + override fun getIcon(): Icon = ObfIcons.ENIGMA_ICON +} diff --git a/obfuscation-explorer/src/main/kotlin/formats/enigma/lang/EnigmaLanguage.kt b/obfuscation-explorer/src/main/kotlin/formats/enigma/lang/EnigmaLanguage.kt new file mode 100644 index 000000000..db7f12b86 --- /dev/null +++ b/obfuscation-explorer/src/main/kotlin/formats/enigma/lang/EnigmaLanguage.kt @@ -0,0 +1,25 @@ +/* + * Minecraft Development for IntelliJ + * + * https://mcdev.io/ + * + * Copyright (C) 2024 minecraft-dev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, version 3.0 only. + * + * 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 Lesser General Public License + * along with this program. If not, see . + */ + +package io.mcdev.obfex.formats.enigma.lang + +import com.intellij.lang.Language + +object EnigmaLanguage : Language("Enigma") diff --git a/obfuscation-explorer/src/main/kotlin/formats/enigma/lang/EnigmaLayoutLexer.kt b/obfuscation-explorer/src/main/kotlin/formats/enigma/lang/EnigmaLayoutLexer.kt new file mode 100644 index 000000000..ca192f43d --- /dev/null +++ b/obfuscation-explorer/src/main/kotlin/formats/enigma/lang/EnigmaLayoutLexer.kt @@ -0,0 +1,35 @@ +/* + * Minecraft Development for IntelliJ + * + * https://mcdev.io/ + * + * Copyright (C) 2024 minecraft-dev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, version 3.0 only. + * + * 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 Lesser General Public License + * along with this program. If not, see . + */ + +package io.mcdev.obfex.formats.enigma.lang + +import com.intellij.psi.tree.IElementType +import com.intellij.psi.tree.TokenSet +import io.mcdev.obfex.formats.enigma.gen.psi.EnigmaTypes +import io.mcdev.obfex.formats.enigma.lang.psi.EnigmaLexerAdapter +import io.mcdev.obfex.formats.util.sigws.SignificantWhitespaceLexer + +class EnigmaLayoutLexer : SignificantWhitespaceLexer(EnigmaLexerAdapter()) { + + override val newlineTokens: TokenSet = TokenSet.create(EnigmaTypes.CRLF) + override val tabTokens: TokenSet = TokenSet.create(EnigmaTypes.TAB) + override val virtualOpenToken: IElementType = EnigmaTypes.VIRTUAL_OPEN + override val virtualCloseToken: IElementType = EnigmaTypes.VIRTUAL_CLOSE +} diff --git a/obfuscation-explorer/src/main/kotlin/formats/enigma/lang/EnigmaParserDefinition.kt b/obfuscation-explorer/src/main/kotlin/formats/enigma/lang/EnigmaParserDefinition.kt new file mode 100644 index 000000000..cb0d103e1 --- /dev/null +++ b/obfuscation-explorer/src/main/kotlin/formats/enigma/lang/EnigmaParserDefinition.kt @@ -0,0 +1,49 @@ +/* + * Minecraft Development for IntelliJ + * + * https://mcdev.io/ + * + * Copyright (C) 2024 minecraft-dev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, version 3.0 only. + * + * 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 Lesser General Public License + * along with this program. If not, see . + */ + +package io.mcdev.obfex.formats.enigma.lang + +import com.intellij.lang.ASTNode +import com.intellij.lang.ParserDefinition +import com.intellij.lang.PsiParser +import com.intellij.lexer.Lexer +import com.intellij.openapi.project.Project +import com.intellij.psi.FileViewProvider +import com.intellij.psi.PsiElement +import com.intellij.psi.PsiFile +import com.intellij.psi.tree.IFileElementType +import com.intellij.psi.tree.TokenSet +import io.mcdev.obfex.formats.enigma.gen.EnigmaParser +import io.mcdev.obfex.formats.enigma.gen.psi.EnigmaTypes + +class EnigmaParserDefinition : ParserDefinition { + override fun createLexer(project: Project): Lexer = EnigmaLayoutLexer() + override fun getCommentTokens(): TokenSet = COMMENTS + override fun getStringLiteralElements(): TokenSet = TokenSet.EMPTY + override fun createParser(project: Project): PsiParser = EnigmaParser() + override fun getFileNodeType(): IFileElementType = FILE + override fun createFile(viewProvider: FileViewProvider): PsiFile = EnigmaFile(viewProvider) + override fun createElement(node: ASTNode): PsiElement = EnigmaTypes.Factory.createElement(node) + override fun getWhitespaceTokens(): TokenSet = WHITESPACE +} + +private val WHITESPACE = TokenSet.orSet(TokenSet.WHITE_SPACE, TokenSet.create(EnigmaTypes.TAB)) +private val COMMENTS = TokenSet.create(EnigmaTypes.COMMENT) +private val FILE = IFileElementType(EnigmaLanguage) diff --git a/obfuscation-explorer/src/main/kotlin/formats/enigma/lang/colors/EnigmaAnnotator.kt b/obfuscation-explorer/src/main/kotlin/formats/enigma/lang/colors/EnigmaAnnotator.kt new file mode 100644 index 000000000..41e8f8dc8 --- /dev/null +++ b/obfuscation-explorer/src/main/kotlin/formats/enigma/lang/colors/EnigmaAnnotator.kt @@ -0,0 +1,50 @@ +/* + * Minecraft Development for IntelliJ + * + * https://mcdev.io/ + * + * Copyright (C) 2024 minecraft-dev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, version 3.0 only. + * + * 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 Lesser General Public License + * along with this program. If not, see . + */ + +package io.mcdev.obfex.formats.enigma.lang.colors + +import com.intellij.lang.annotation.AnnotationHolder +import com.intellij.lang.annotation.Annotator +import com.intellij.psi.PsiElement +import io.mcdev.obfex.formats.enigma.gen.psi.EnigmaArgIndex +import io.mcdev.obfex.formats.enigma.gen.psi.EnigmaArgMapping +import io.mcdev.obfex.formats.enigma.gen.psi.EnigmaClassMappingPart +import io.mcdev.obfex.formats.enigma.gen.psi.EnigmaFieldMapping +import io.mcdev.obfex.formats.enigma.gen.psi.EnigmaMappingPart +import io.mcdev.obfex.formats.enigma.gen.psi.EnigmaMethodMapping +import io.mcdev.obfex.formats.util.registerHighlight + +class EnigmaAnnotator : Annotator { + override fun annotate(element: PsiElement, holder: AnnotationHolder) { + val key = when (element) { + is EnigmaClassMappingPart -> EnigmaSyntaxHighlighter.CLASS_NAME + is EnigmaArgIndex -> EnigmaSyntaxHighlighter.PARAM_INDEX + is EnigmaMappingPart -> when (element.parent) { + is EnigmaMethodMapping -> EnigmaSyntaxHighlighter.METHOD + is EnigmaFieldMapping -> EnigmaSyntaxHighlighter.FIELD + is EnigmaArgMapping -> EnigmaSyntaxHighlighter.PARAM + else -> return + } + else -> return + } + + registerHighlight(element, key, holder) + } +} diff --git a/obfuscation-explorer/src/main/kotlin/formats/enigma/lang/colors/EnigmaSyntaxHighlighter.kt b/obfuscation-explorer/src/main/kotlin/formats/enigma/lang/colors/EnigmaSyntaxHighlighter.kt new file mode 100644 index 000000000..b5d4cea4e --- /dev/null +++ b/obfuscation-explorer/src/main/kotlin/formats/enigma/lang/colors/EnigmaSyntaxHighlighter.kt @@ -0,0 +1,95 @@ +/* + * Minecraft Development for IntelliJ + * + * https://mcdev.io/ + * + * Copyright (C) 2024 minecraft-dev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, version 3.0 only. + * + * 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 Lesser General Public License + * along with this program. If not, see . + */ + +package io.mcdev.obfex.formats.enigma.lang.colors + +import com.intellij.openapi.editor.DefaultLanguageHighlighterColors +import com.intellij.openapi.editor.colors.TextAttributesKey +import com.intellij.openapi.fileTypes.SyntaxHighlighterBase +import com.intellij.psi.tree.IElementType +import io.mcdev.obfex.formats.enigma.gen.psi.EnigmaTypes +import io.mcdev.obfex.formats.enigma.lang.psi.EnigmaLexerAdapter + +class EnigmaSyntaxHighlighter : SyntaxHighlighterBase() { + + override fun getHighlightingLexer() = EnigmaLexerAdapter() + + override fun getTokenHighlights(tokenType: IElementType?): Array = + when (tokenType) { + EnigmaTypes.CLASS_KEY, EnigmaTypes.FIELD_KEY, EnigmaTypes.METHOD_KEY, + EnigmaTypes.ARG_KEY, EnigmaTypes.COMMENT_KEY -> KEYWORD_KEYS + EnigmaTypes.CLASS_TYPE -> CLASS_TYPE_KEYS + EnigmaTypes.PRIMITIVE -> PRIMITIVE_KEYS + EnigmaTypes.COMMENT -> COMMENT_KEYS + EnigmaTypes.DOC_TEXT -> DOC_KEYS + else -> TextAttributesKey.EMPTY_ARRAY + } + + @Suppress("MemberVisibilityCanBePrivate") + companion object { + val KEYWORD = TextAttributesKey.createTextAttributesKey( + "ENIGMA_KEYWORD", + DefaultLanguageHighlighterColors.KEYWORD + ) + val CLASS_NAME = TextAttributesKey.createTextAttributesKey( + "ENIGMA_CLASS_NAME", + DefaultLanguageHighlighterColors.STRING + ) + val METHOD = TextAttributesKey.createTextAttributesKey( + "ENIGMA_METHOD", + DefaultLanguageHighlighterColors.INSTANCE_METHOD + ) + val FIELD = TextAttributesKey.createTextAttributesKey( + "ENIGMA_FIELD", + DefaultLanguageHighlighterColors.INSTANCE_FIELD + ) + val PARAM = TextAttributesKey.createTextAttributesKey( + "ENIGMA_PARAM", + DefaultLanguageHighlighterColors.PARAMETER + ) + val PARAM_INDEX = TextAttributesKey.createTextAttributesKey( + "ENIGMA_PARAM_INDEX", + DefaultLanguageHighlighterColors.INLAY_DEFAULT + ) + val PRIMITIVE = TextAttributesKey.createTextAttributesKey( + "ENIGMA_PRIMITIVE", + DefaultLanguageHighlighterColors.NUMBER + ) + val CLASS_TYPE = TextAttributesKey.createTextAttributesKey( + "ENIGMA_CLASS_TYPE", + DefaultLanguageHighlighterColors.CLASS_REFERENCE + ) + val COMMENT = TextAttributesKey.createTextAttributesKey( + "ENIGMA_COMMENT", + DefaultLanguageHighlighterColors.LINE_COMMENT + ) + val DOC = TextAttributesKey.createTextAttributesKey( + "ENIGMA_JAVADOC", + DefaultLanguageHighlighterColors.DOC_COMMENT + ) + + private val KEYWORD_KEYS = arrayOf(KEYWORD) + private val PRIMITIVE_KEYS = arrayOf(PRIMITIVE) + private val CLASS_TYPE_KEYS = arrayOf(CLASS_TYPE) + private val COMMENT_KEYS = arrayOf(COMMENT) + private val DOC_KEYS = arrayOf(DOC) + private val PARAM_INDEX_KEYS = arrayOf(PARAM_INDEX) + } +} diff --git a/obfuscation-explorer/src/main/kotlin/formats/enigma/lang/colors/EnigmaSyntaxHighlighterFactory.kt b/obfuscation-explorer/src/main/kotlin/formats/enigma/lang/colors/EnigmaSyntaxHighlighterFactory.kt new file mode 100644 index 000000000..2ffe35199 --- /dev/null +++ b/obfuscation-explorer/src/main/kotlin/formats/enigma/lang/colors/EnigmaSyntaxHighlighterFactory.kt @@ -0,0 +1,29 @@ +/* + * Minecraft Development for IntelliJ + * + * https://mcdev.io/ + * + * Copyright (C) 2024 minecraft-dev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, version 3.0 only. + * + * 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 Lesser General Public License + * along with this program. If not, see . + */ + +package io.mcdev.obfex.formats.enigma.lang.colors + +import com.intellij.openapi.fileTypes.SyntaxHighlighterFactory +import com.intellij.openapi.project.Project +import com.intellij.openapi.vfs.VirtualFile + +class EnigmaSyntaxHighlighterFactory : SyntaxHighlighterFactory() { + override fun getSyntaxHighlighter(project: Project?, virtualFile: VirtualFile?) = EnigmaSyntaxHighlighter() +} diff --git a/obfuscation-explorer/src/main/kotlin/formats/enigma/lang/psi/EnigmaElementType.kt b/obfuscation-explorer/src/main/kotlin/formats/enigma/lang/psi/EnigmaElementType.kt new file mode 100644 index 000000000..3eaa68d93 --- /dev/null +++ b/obfuscation-explorer/src/main/kotlin/formats/enigma/lang/psi/EnigmaElementType.kt @@ -0,0 +1,27 @@ +/* + * Minecraft Development for IntelliJ + * + * https://mcdev.io/ + * + * Copyright (C) 2024 minecraft-dev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, version 3.0 only. + * + * 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 Lesser General Public License + * along with this program. If not, see . + */ + +package io.mcdev.obfex.formats.enigma.lang.psi + +import com.intellij.psi.tree.IElementType +import io.mcdev.obfex.formats.enigma.lang.EnigmaLanguage +import org.jetbrains.annotations.NonNls + +class EnigmaElementType(@NonNls debugName: String) : IElementType(debugName, EnigmaLanguage) diff --git a/obfuscation-explorer/src/main/kotlin/formats/enigma/lang/psi/EnigmaLexerAdapter.kt b/obfuscation-explorer/src/main/kotlin/formats/enigma/lang/psi/EnigmaLexerAdapter.kt new file mode 100644 index 000000000..043b75846 --- /dev/null +++ b/obfuscation-explorer/src/main/kotlin/formats/enigma/lang/psi/EnigmaLexerAdapter.kt @@ -0,0 +1,26 @@ +/* + * Minecraft Development for IntelliJ + * + * https://mcdev.io/ + * + * Copyright (C) 2024 minecraft-dev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, version 3.0 only. + * + * 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 Lesser General Public License + * along with this program. If not, see . + */ + +package io.mcdev.obfex.formats.enigma.lang.psi + +import com.intellij.lexer.FlexAdapter +import io.mcdev.obfex.formats.enigma.gen.EnigmaLexer + +class EnigmaLexerAdapter : FlexAdapter(EnigmaLexer()) diff --git a/obfuscation-explorer/src/main/kotlin/formats/enigma/lang/psi/EnigmaParserUtil.kt b/obfuscation-explorer/src/main/kotlin/formats/enigma/lang/psi/EnigmaParserUtil.kt new file mode 100644 index 000000000..9297125f7 --- /dev/null +++ b/obfuscation-explorer/src/main/kotlin/formats/enigma/lang/psi/EnigmaParserUtil.kt @@ -0,0 +1,42 @@ +/* + * Minecraft Development for IntelliJ + * + * https://mcdev.io/ + * + * Copyright (C) 2024 minecraft-dev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, version 3.0 only. + * + * 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 Lesser General Public License + * along with this program. If not, see . + */ + +package io.mcdev.obfex.formats.enigma.lang.psi + +import com.intellij.lang.PsiBuilder +import io.mcdev.obfex.formats.enigma.gen.psi.EnigmaTypes +import io.mcdev.obfex.formats.util.sigws.SignificantWhitespaceParserUtil + +@Suppress("UNUSED_PARAMETER") +object EnigmaParserUtil : SignificantWhitespaceParserUtil() { + @JvmStatic + fun fieldMappingPart( + builder: PsiBuilder, + level: Int, + ): Boolean { + if (builder.tokenType !== EnigmaTypes.PRIMITIVE) { + return true + } + val next = builder.lookAhead(1) + return next != null && // eof + next !== EnigmaTypes.CRLF && // end of line + next !== EnigmaTypes.VIRTUAL_CLOSE // automatically inserted at eof + } +} diff --git a/obfuscation-explorer/src/main/kotlin/formats/enigma/lang/psi/EnigmaTokenType.kt b/obfuscation-explorer/src/main/kotlin/formats/enigma/lang/psi/EnigmaTokenType.kt new file mode 100644 index 000000000..2ada8946f --- /dev/null +++ b/obfuscation-explorer/src/main/kotlin/formats/enigma/lang/psi/EnigmaTokenType.kt @@ -0,0 +1,27 @@ +/* + * Minecraft Development for IntelliJ + * + * https://mcdev.io/ + * + * Copyright (C) 2024 minecraft-dev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, version 3.0 only. + * + * 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 Lesser General Public License + * along with this program. If not, see . + */ + +package io.mcdev.obfex.formats.enigma.lang.psi + +import com.intellij.psi.tree.IElementType +import io.mcdev.obfex.formats.enigma.lang.EnigmaLanguage +import org.jetbrains.annotations.NonNls + +class EnigmaTokenType(@NonNls debugName: String) : IElementType(debugName, EnigmaLanguage) diff --git a/obfuscation-explorer/src/main/kotlin/formats/jam/JamMappingsFormatParser.kt b/obfuscation-explorer/src/main/kotlin/formats/jam/JamMappingsFormatParser.kt new file mode 100644 index 000000000..b2116efad --- /dev/null +++ b/obfuscation-explorer/src/main/kotlin/formats/jam/JamMappingsFormatParser.kt @@ -0,0 +1,146 @@ +/* + * Minecraft Development for IntelliJ + * + * https://mcdev.io/ + * + * Copyright (C) 2024 minecraft-dev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, version 3.0 only. + * + * 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 Lesser General Public License + * along with this program. If not, see . + */ + +package io.mcdev.obfex.formats.jam + +import com.intellij.openapi.vfs.VirtualFile +import io.mcdev.obfex.MappingPart +import io.mcdev.obfex.forLines +import io.mcdev.obfex.mappings.FileCoords +import io.mcdev.obfex.mappings.MappingsDefinition +import io.mcdev.obfex.mappings.MappingsDefinitionBuilder +import io.mcdev.obfex.mappings.UnnamedMappingsFormatParser +import io.mcdev.obfex.mappings.clazz +import io.mcdev.obfex.mappings.field +import io.mcdev.obfex.mappings.method +import io.mcdev.obfex.mappings.param +import io.mcdev.obfex.ref.asClass +import io.mcdev.obfex.ref.asField +import io.mcdev.obfex.ref.asMethod +import io.mcdev.obfex.ref.asMethodDesc +import io.mcdev.obfex.ref.asParam +import io.mcdev.obfex.ref.asParamIndex +import io.mcdev.obfex.ref.asRef +import io.mcdev.obfex.ref.asTypeDef +import io.mcdev.obfex.ref.highPriority +import io.mcdev.obfex.ref.lowPriority + +@Suppress("DuplicatedCode") +class JamMappingsFormatParser : UnnamedMappingsFormatParser { + + override val expectedFileExtensions: Array = arrayOf("jam") + + override fun parse(file: VirtualFile): MappingsDefinition { + val builder = MappingsDefinitionBuilder(JamMappingsFormatType, file) + + file.forLines { lineNum, parts, _ -> + if (parts.isEmpty()) { + return@forLines true + } + + val key = parts.first() + + when (key.value) { + "CL" -> parseClassLine(builder, lineNum, parts) + "FD" -> parseFieldLine(builder, lineNum, parts) + "MD" -> parseMethodLine(builder, lineNum, parts) + "MP" -> parseMethodParamLine(builder, lineNum, parts) + else -> builder.error("Unrecognized key: ${key.value.substring(0, 2)}", FileCoords(lineNum)) + } + + return@forLines true + } + + return builder.build() + } + + private fun parseClassLine(builder: MappingsDefinitionBuilder, lineNum: Int, parts: Array) { + if (parts.size != 3) { + builder.error("Class line is invalid", FileCoords(lineNum)) + return + } + + val (_, leftClass, rightClass) = parts + builder.clazz(lineNum.highPriority) { + with(leftClass.asClass().from) + with(rightClass.asClass().to) + } + } + + private fun parseFieldLine(builder: MappingsDefinitionBuilder, lineNum: Int, parts: Array) { + if (parts.size != 5) { + builder.error("Field line is invalid", FileCoords(lineNum)) + return + } + + val (_, leftClass, leftName, leftDesc, rightName) = parts + + val leftType = leftDesc.value.asTypeDef() + ?: return builder.error("Field descriptor is invalid", FileCoords(lineNum, leftDesc)) + + builder.clazz(leftClass.asClass().from, lineNum.lowPriority).field(lineNum.highPriority) { + type = leftType + + with(leftName.asField().from) + with(rightName.asField().to) + } + } + + private fun parseMethodLine(builder: MappingsDefinitionBuilder, lineNum: Int, parts: Array) { + if (parts.size != 5) { + builder.error("Method line is invalid", FileCoords(lineNum)) + return + } + + val (_, leftClass, leftMethod, leftDesc, rightName) = parts + + val leftType = leftDesc.value.asMethodDesc() + ?: return builder.error("Method descriptor is invalid", FileCoords(lineNum, leftDesc)) + + builder.clazz(leftClass.asClass().from, lineNum.lowPriority).method(lineNum.highPriority) { + desc = leftType + + with(leftMethod.asMethod().from) + with(rightName.asMethod().to) + } + } + + private fun parseMethodParamLine(builder: MappingsDefinitionBuilder, lineNum: Int, parts: Array) { + if (parts.size != 6) { + builder.error("Method parameter line is invalid", FileCoords(lineNum)) + return + } + + val (_, leftClass, leftMethod, leftDesc, index) = parts + val rightName = parts[5] + + val leftType = leftDesc.value.asMethodDesc() + ?: return builder.error("Method descriptor is invalid", FileCoords(lineNum, leftDesc)) + + val indexNum = index.value.toIntOrNull() + ?: return builder.error("Method param index is invalid", FileCoords(lineNum, index)) + + builder.clazz(leftClass.asClass().from, lineNum.lowPriority) + .method(leftMethod.asMethod().asRef(leftType).from, lineNum.lowPriority) + .param(indexNum.asParamIndex(), lineNum.highPriority) { + with(rightName.asParam().to) + } + } +} diff --git a/obfuscation-explorer/src/main/kotlin/formats/jam/JamMappingsFormatType.kt b/obfuscation-explorer/src/main/kotlin/formats/jam/JamMappingsFormatType.kt new file mode 100644 index 000000000..27dee8e19 --- /dev/null +++ b/obfuscation-explorer/src/main/kotlin/formats/jam/JamMappingsFormatType.kt @@ -0,0 +1,31 @@ +/* + * Minecraft Development for IntelliJ + * + * https://mcdev.io/ + * + * Copyright (C) 2024 minecraft-dev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, version 3.0 only. + * + * 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 Lesser General Public License + * along with this program. If not, see . + */ + +package io.mcdev.obfex.formats.jam + +import io.mcdev.obfex.ObfIcons +import io.mcdev.obfex.formats.MappingsFormatType +import javax.swing.Icon + +object JamMappingsFormatType : MappingsFormatType("JAM") { + + override val icon: Icon = ObfIcons.JAM_ICON + override val name: String = "JAM" +} diff --git a/obfuscation-explorer/src/main/kotlin/formats/jam/lang/JamFile.kt b/obfuscation-explorer/src/main/kotlin/formats/jam/lang/JamFile.kt new file mode 100644 index 000000000..36d1279ea --- /dev/null +++ b/obfuscation-explorer/src/main/kotlin/formats/jam/lang/JamFile.kt @@ -0,0 +1,30 @@ +/* + * Minecraft Development for IntelliJ + * + * https://mcdev.io/ + * + * Copyright (C) 2024 minecraft-dev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, version 3.0 only. + * + * 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 Lesser General Public License + * along with this program. If not, see . + */ + +package io.mcdev.obfex.formats.jam.lang + +import com.intellij.extapi.psi.PsiFileBase +import com.intellij.openapi.fileTypes.FileType +import com.intellij.psi.FileViewProvider + +class JamFile(viewProvider: FileViewProvider) : PsiFileBase(viewProvider, JamLanguage) { + override fun getFileType(): FileType = JamFileType + override fun toString(): String = JamFileType.description +} diff --git a/obfuscation-explorer/src/main/kotlin/formats/jam/lang/JamFileType.kt b/obfuscation-explorer/src/main/kotlin/formats/jam/lang/JamFileType.kt new file mode 100644 index 000000000..bcb4d1148 --- /dev/null +++ b/obfuscation-explorer/src/main/kotlin/formats/jam/lang/JamFileType.kt @@ -0,0 +1,33 @@ +/* + * Minecraft Development for IntelliJ + * + * https://mcdev.io/ + * + * Copyright (C) 2024 minecraft-dev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, version 3.0 only. + * + * 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 Lesser General Public License + * along with this program. If not, see . + */ + +package io.mcdev.obfex.formats.jam.lang + +import com.intellij.openapi.fileTypes.LanguageFileType +import io.mcdev.obfex.ObfIcons +import javax.swing.Icon + +object JamFileType : LanguageFileType(JamLanguage) { + + override fun getName(): String = "JAM" + override fun getDescription(): String = "JAM obfuscation mapping file" + override fun getDefaultExtension(): String = "jam" + override fun getIcon(): Icon = ObfIcons.JAM_ICON +} diff --git a/obfuscation-explorer/src/main/kotlin/formats/jam/lang/JamLanguage.kt b/obfuscation-explorer/src/main/kotlin/formats/jam/lang/JamLanguage.kt new file mode 100644 index 000000000..97c02c42f --- /dev/null +++ b/obfuscation-explorer/src/main/kotlin/formats/jam/lang/JamLanguage.kt @@ -0,0 +1,25 @@ +/* + * Minecraft Development for IntelliJ + * + * https://mcdev.io/ + * + * Copyright (C) 2024 minecraft-dev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, version 3.0 only. + * + * 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 Lesser General Public License + * along with this program. If not, see . + */ + +package io.mcdev.obfex.formats.jam.lang + +import com.intellij.lang.Language + +object JamLanguage : Language("JAM") diff --git a/obfuscation-explorer/src/main/kotlin/formats/jam/lang/JamParserDefinition.kt b/obfuscation-explorer/src/main/kotlin/formats/jam/lang/JamParserDefinition.kt new file mode 100644 index 000000000..3671c028e --- /dev/null +++ b/obfuscation-explorer/src/main/kotlin/formats/jam/lang/JamParserDefinition.kt @@ -0,0 +1,50 @@ +/* + * Minecraft Development for IntelliJ + * + * https://mcdev.io/ + * + * Copyright (C) 2024 minecraft-dev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, version 3.0 only. + * + * 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 Lesser General Public License + * along with this program. If not, see . + */ + +package io.mcdev.obfex.formats.jam.lang + +import com.intellij.lang.ASTNode +import com.intellij.lang.Language +import com.intellij.lang.ParserDefinition +import com.intellij.lang.PsiParser +import com.intellij.lexer.Lexer +import com.intellij.openapi.project.Project +import com.intellij.psi.FileViewProvider +import com.intellij.psi.PsiElement +import com.intellij.psi.PsiFile +import com.intellij.psi.tree.IFileElementType +import com.intellij.psi.tree.TokenSet +import io.mcdev.obfex.formats.jam.gen.JamParser +import io.mcdev.obfex.formats.jam.gen.psi.JamTypes +import io.mcdev.obfex.formats.jam.lang.psi.JamLexerAdapter + +class JamParserDefinition : ParserDefinition { + + override fun createLexer(project: Project): Lexer = JamLexerAdapter() + override fun getCommentTokens(): TokenSet = COMMENTS + override fun getStringLiteralElements(): TokenSet = TokenSet.EMPTY + override fun createParser(project: Project): PsiParser = JamParser() + override fun getFileNodeType(): IFileElementType = FILE + override fun createFile(viewProvider: FileViewProvider): PsiFile = JamFile(viewProvider) + override fun createElement(node: ASTNode): PsiElement = JamTypes.Factory.createElement(node) +} + +private val COMMENTS = TokenSet.create(JamTypes.COMMENT) +private val FILE = IFileElementType(Language.findInstance(JamLanguage::class.java)) diff --git a/obfuscation-explorer/src/main/kotlin/formats/jam/lang/colors/JamAnnotator.kt b/obfuscation-explorer/src/main/kotlin/formats/jam/lang/colors/JamAnnotator.kt new file mode 100644 index 000000000..1d8b67ed5 --- /dev/null +++ b/obfuscation-explorer/src/main/kotlin/formats/jam/lang/colors/JamAnnotator.kt @@ -0,0 +1,61 @@ +/* + * Minecraft Development for IntelliJ + * + * https://mcdev.io/ + * + * Copyright (C) 2024 minecraft-dev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, version 3.0 only. + * + * 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 Lesser General Public License + * along with this program. If not, see . + */ + +package io.mcdev.obfex.formats.jam.lang.colors + +import com.intellij.lang.annotation.AnnotationHolder +import com.intellij.lang.annotation.Annotator +import com.intellij.psi.PsiElement +import io.mcdev.obfex.formats.jam.gen.psi.JamClassMapping +import io.mcdev.obfex.formats.jam.gen.psi.JamFieldMapping +import io.mcdev.obfex.formats.jam.gen.psi.JamMethodMapping +import io.mcdev.obfex.formats.jam.gen.psi.JamParamMapping +import io.mcdev.obfex.formats.util.registerHighlight + +class JamAnnotator : Annotator { + + override fun annotate(element: PsiElement, holder: AnnotationHolder) { + when (element) { + is JamClassMapping -> { + registerHighlight(element.obfName, JamSyntaxHighlighter.CLASS_NAME, holder) + registerHighlight(element.deobfName, JamSyntaxHighlighter.CLASS_NAME, holder, true) + } + + is JamMethodMapping -> { + registerHighlight(element.obfClassName, JamSyntaxHighlighter.CLASS_NAME, holder) + registerHighlight(element.obfMethodName, JamSyntaxHighlighter.METHOD, holder) + registerHighlight(element.deobfName, JamSyntaxHighlighter.METHOD, holder, true) + } + + is JamFieldMapping -> { + registerHighlight(element.obfClassName, JamSyntaxHighlighter.CLASS_NAME, holder) + registerHighlight(element.obfFieldName, JamSyntaxHighlighter.FIELD, holder) + registerHighlight(element.deobfName, JamSyntaxHighlighter.FIELD, holder, true) + } + + is JamParamMapping -> { + registerHighlight(element.obfClassName, JamSyntaxHighlighter.CLASS_NAME, holder) + registerHighlight(element.obfMethodName, JamSyntaxHighlighter.METHOD, holder) + registerHighlight(element.parameterIndex, JamSyntaxHighlighter.PARAM_INDEX, holder) + registerHighlight(element.deobfName, JamSyntaxHighlighter.PARAM, holder, true) + } + } + } +} diff --git a/obfuscation-explorer/src/main/kotlin/formats/jam/lang/colors/JamSyntaxHighlighter.kt b/obfuscation-explorer/src/main/kotlin/formats/jam/lang/colors/JamSyntaxHighlighter.kt new file mode 100644 index 000000000..565a54c44 --- /dev/null +++ b/obfuscation-explorer/src/main/kotlin/formats/jam/lang/colors/JamSyntaxHighlighter.kt @@ -0,0 +1,86 @@ +/* + * Minecraft Development for IntelliJ + * + * https://mcdev.io/ + * + * Copyright (C) 2024 minecraft-dev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, version 3.0 only. + * + * 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 Lesser General Public License + * along with this program. If not, see . + */ + +package io.mcdev.obfex.formats.jam.lang.colors + +import com.intellij.lexer.Lexer +import com.intellij.openapi.editor.DefaultLanguageHighlighterColors +import com.intellij.openapi.editor.colors.TextAttributesKey +import com.intellij.openapi.fileTypes.SyntaxHighlighterBase +import com.intellij.psi.tree.IElementType +import io.mcdev.obfex.formats.jam.gen.psi.JamTypes +import io.mcdev.obfex.formats.jam.lang.psi.JamLexerAdapter +import io.mcdev.obfex.formats.srg.lang.colors.SrgSyntaxHighlighter + +class JamSyntaxHighlighter : SyntaxHighlighterBase() { + + override fun getHighlightingLexer(): Lexer = JamLexerAdapter() + + override fun getTokenHighlights(tokenType: IElementType?): Array { + return when (tokenType) { + JamTypes.CLASS_KEY, JamTypes.FIELD_KEY, JamTypes.METHOD_KEY, JamTypes.PARAM_KEY -> KEYWORD_KEYS + JamTypes.CLASS_TYPE -> CLASS_TYPE_KEYS + JamTypes.PRIMITIVE -> PRIMITIVE_KEYS + JamTypes.COMMENT -> COMMENT_KEYS + else -> TextAttributesKey.EMPTY_ARRAY + } + } + + companion object { + val KEYWORD = TextAttributesKey.createTextAttributesKey("JAM_KEYWORD", DefaultLanguageHighlighterColors.KEYWORD) + val CLASS_NAME = TextAttributesKey.createTextAttributesKey( + "JAM_CLASS_NAME", + DefaultLanguageHighlighterColors.STRING + ) + val METHOD = TextAttributesKey.createTextAttributesKey( + "JAM_METHOD", + DefaultLanguageHighlighterColors.INSTANCE_METHOD + ) + val FIELD = TextAttributesKey.createTextAttributesKey( + "JAM_FIELD", + DefaultLanguageHighlighterColors.INSTANCE_FIELD + ) + val PARAM = TextAttributesKey.createTextAttributesKey( + "JAM_PARAM", + DefaultLanguageHighlighterColors.PARAMETER + ) + val PARAM_INDEX = TextAttributesKey.createTextAttributesKey( + "JAM_PARAM_INDEX", + DefaultLanguageHighlighterColors.INLAY_DEFAULT + ) + val PRIMITIVE = TextAttributesKey.createTextAttributesKey( + "JAM_PRIMITIVE", + DefaultLanguageHighlighterColors.NUMBER + ) + val CLASS_TYPE = TextAttributesKey.createTextAttributesKey( + "JAM_CLASS_TYPE", + DefaultLanguageHighlighterColors.CLASS_REFERENCE + ) + val COMMENT = TextAttributesKey.createTextAttributesKey( + "JAM_COMMENT", + DefaultLanguageHighlighterColors.LINE_COMMENT + ) + + private val KEYWORD_KEYS = arrayOf(SrgSyntaxHighlighter.KEYWORD) + private val PRIMITIVE_KEYS = arrayOf(SrgSyntaxHighlighter.PRIMITIVE) + private val CLASS_TYPE_KEYS = arrayOf(SrgSyntaxHighlighter.CLASS_TYPE) + private val COMMENT_KEYS = arrayOf(SrgSyntaxHighlighter.COMMENT) + } +} diff --git a/obfuscation-explorer/src/main/kotlin/formats/jam/lang/colors/JamSyntaxHighlighterFactory.kt b/obfuscation-explorer/src/main/kotlin/formats/jam/lang/colors/JamSyntaxHighlighterFactory.kt new file mode 100644 index 000000000..52896b4d7 --- /dev/null +++ b/obfuscation-explorer/src/main/kotlin/formats/jam/lang/colors/JamSyntaxHighlighterFactory.kt @@ -0,0 +1,31 @@ +/* + * Minecraft Development for IntelliJ + * + * https://mcdev.io/ + * + * Copyright (C) 2024 minecraft-dev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, version 3.0 only. + * + * 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 Lesser General Public License + * along with this program. If not, see . + */ + +package io.mcdev.obfex.formats.jam.lang.colors + +import com.intellij.openapi.fileTypes.SyntaxHighlighter +import com.intellij.openapi.fileTypes.SyntaxHighlighterFactory +import com.intellij.openapi.project.Project +import com.intellij.openapi.vfs.VirtualFile + +class JamSyntaxHighlighterFactory : SyntaxHighlighterFactory() { + override fun getSyntaxHighlighter(project: Project?, virtualFile: VirtualFile?): SyntaxHighlighter = + JamSyntaxHighlighter() +} diff --git a/obfuscation-explorer/src/main/kotlin/formats/jam/lang/psi/JamElementType.kt b/obfuscation-explorer/src/main/kotlin/formats/jam/lang/psi/JamElementType.kt new file mode 100644 index 000000000..385c99650 --- /dev/null +++ b/obfuscation-explorer/src/main/kotlin/formats/jam/lang/psi/JamElementType.kt @@ -0,0 +1,27 @@ +/* + * Minecraft Development for IntelliJ + * + * https://mcdev.io/ + * + * Copyright (C) 2024 minecraft-dev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, version 3.0 only. + * + * 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 Lesser General Public License + * along with this program. If not, see . + */ + +package io.mcdev.obfex.formats.jam.lang.psi + +import com.intellij.psi.tree.IElementType +import io.mcdev.obfex.formats.jam.lang.JamLanguage +import org.jetbrains.annotations.NonNls + +class JamElementType(@NonNls debugName: String) : IElementType(debugName, JamLanguage) diff --git a/obfuscation-explorer/src/main/kotlin/formats/jam/lang/psi/JamLexerAdapter.kt b/obfuscation-explorer/src/main/kotlin/formats/jam/lang/psi/JamLexerAdapter.kt new file mode 100644 index 000000000..80a8a15e5 --- /dev/null +++ b/obfuscation-explorer/src/main/kotlin/formats/jam/lang/psi/JamLexerAdapter.kt @@ -0,0 +1,26 @@ +/* + * Minecraft Development for IntelliJ + * + * https://mcdev.io/ + * + * Copyright (C) 2024 minecraft-dev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, version 3.0 only. + * + * 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 Lesser General Public License + * along with this program. If not, see . + */ + +package io.mcdev.obfex.formats.jam.lang.psi + +import com.intellij.lexer.FlexAdapter +import io.mcdev.obfex.formats.jam.gen.JamLexer + +class JamLexerAdapter : FlexAdapter(JamLexer()) diff --git a/obfuscation-explorer/src/main/kotlin/formats/jam/lang/psi/JamTokenType.kt b/obfuscation-explorer/src/main/kotlin/formats/jam/lang/psi/JamTokenType.kt new file mode 100644 index 000000000..aa7e73788 --- /dev/null +++ b/obfuscation-explorer/src/main/kotlin/formats/jam/lang/psi/JamTokenType.kt @@ -0,0 +1,27 @@ +/* + * Minecraft Development for IntelliJ + * + * https://mcdev.io/ + * + * Copyright (C) 2024 minecraft-dev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, version 3.0 only. + * + * 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 Lesser General Public License + * along with this program. If not, see . + */ + +package io.mcdev.obfex.formats.jam.lang.psi + +import com.intellij.psi.tree.IElementType +import io.mcdev.obfex.formats.jam.lang.JamLanguage +import org.jetbrains.annotations.NonNls + +class JamTokenType(@NonNls debugName: String) : IElementType(debugName, JamLanguage) diff --git a/obfuscation-explorer/src/main/kotlin/formats/proguard/ProGuardMappingsFormatParser.kt b/obfuscation-explorer/src/main/kotlin/formats/proguard/ProGuardMappingsFormatParser.kt new file mode 100644 index 000000000..82b8457ea --- /dev/null +++ b/obfuscation-explorer/src/main/kotlin/formats/proguard/ProGuardMappingsFormatParser.kt @@ -0,0 +1,217 @@ +/* + * Minecraft Development for IntelliJ + * + * https://mcdev.io/ + * + * Copyright (C) 2024 minecraft-dev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, version 3.0 only. + * + * 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 Lesser General Public License + * along with this program. If not, see . + */ + +package io.mcdev.obfex.formats.proguard + +import com.intellij.openapi.vfs.VirtualFile +import io.mcdev.obfex.MappingPart +import io.mcdev.obfex.forLinesIndent +import io.mcdev.obfex.mappings.ClassMappingBuilder +import io.mcdev.obfex.mappings.FileCoords +import io.mcdev.obfex.mappings.MappingsDefinition +import io.mcdev.obfex.mappings.MappingsDefinitionBuilder +import io.mcdev.obfex.mappings.UnnamedMappingsFormatParser +import io.mcdev.obfex.mappings.clazz +import io.mcdev.obfex.mappings.field +import io.mcdev.obfex.mappings.method +import io.mcdev.obfex.ref.ArrayTypeDef +import io.mcdev.obfex.ref.ClassTypeDef +import io.mcdev.obfex.ref.MethodDescriptor +import io.mcdev.obfex.ref.PrimitiveTypeDef +import io.mcdev.obfex.ref.ReturnTypeDef +import io.mcdev.obfex.ref.TypeDef +import io.mcdev.obfex.ref.VoidTypeDef +import io.mcdev.obfex.ref.asClass +import io.mcdev.obfex.ref.asField +import io.mcdev.obfex.ref.asMethod +import io.mcdev.obfex.ref.highPriority + +@Suppress("DuplicatedCode") +class ProGuardMappingsFormatParser : UnnamedMappingsFormatParser { + + override val expectedFileExtensions: Array = arrayOf("map") + + override fun parse(file: VirtualFile): MappingsDefinition { + val builder = MappingsDefinitionBuilder(ProGuardMappingsFormatType, file) + + var classMapping: ClassMappingBuilder? = null + file.forLinesIndent { indent, lineNum, parts, _ -> + if (indent > 0) { + val c = classMapping + if (c == null) { + builder.error("Member mapping line with no associated class", FileCoords(lineNum)) + } else { + parseMember(c, lineNum, parts) + } + } else { + classMapping = parseClass(builder, lineNum, parts) + } + + return@forLinesIndent true + } + + return builder.build() + } + + private fun parseClass( + builder: MappingsDefinitionBuilder, + lineNum: Int, + parts: Array, + ): ClassMappingBuilder? { + if (parts.size != 3) { + builder.error("Invalid class mapping line", FileCoords(lineNum)) + return null + } + + if (parts[1].value != "->") { + builder.error("Unrecognized class mapping separator", FileCoords(lineNum)) + return null + } + + val leftClass = parts[0] + val rightClass = parts[2].value.trimEnd(':') + + return builder.clazz(lineNum.highPriority) { + with(leftClass.asClass().from) + with(rightClass.asClass().to) + } + } + + private val methodLinesRegex = Regex("\\d+:\\d+:") + + private fun parseMember(builder: ClassMappingBuilder, lineNum: Int, parts: Array) { + if (parts.size != 4) { + builder.error("Unrecognized member mapping line", FileCoords(lineNum)) + return + } + + if (parts[2].value != "->") { + builder.error("Unrecognized member mapping separator", FileCoords(lineNum)) + return + } + + val memberDesc = parts[0] + val leftName = parts[1] + val rightName = parts[3] + + val match = methodLinesRegex.matchAt(memberDesc.value, 0) + if (match != null) { + // method + val returnTypeText = memberDesc.value.substring(match.range.last + 1) + + val paramsIndex = leftName.value.indexOf('(') + if (paramsIndex == -1) { + builder.error("Invalid method descriptor", FileCoords(lineNum, leftName)) + return + } + + val methodName = leftName.value.substring(0, paramsIndex) + val returnType = parseType(returnTypeText) + ?: return builder.error("Invalid return type", FileCoords(lineNum, memberDesc)) + + val methodParams = leftName.value.substring(paramsIndex) + + val params = parseMethodParams(methodParams) + ?: return builder.error("Invalid method descriptor", FileCoords(lineNum, leftName)) + + val methodDesc = MethodDescriptor(params, returnType) + + builder.method(lineNum.highPriority) { + desc = methodDesc + + with(methodName.asMethod().from) + with(rightName.asMethod().to) + } + } else { + val fieldType = parseType(memberDesc.value) + if (fieldType !is TypeDef) { + builder.error("Invalid field type", FileCoords(lineNum, memberDesc)) + return + } + + builder.field(lineNum.highPriority) { + type = fieldType + + with(leftName.asField().from) + with(rightName.asField().to) + } + } + } + + // Proguard doesn't use standard descriptor syntax + private fun parseMethodParams(params: String): List? { + val res = mutableListOf() + + // first index is ( + var index = 1 + while (index < params.length) { + val nextIndex = params.indexOfAny(charArrayOf(',', ')'), index) + if (nextIndex == -1) { + return null + } + + val type = parseType(params.substring(index, nextIndex)) + if (type !is TypeDef) { + return null + } + res += type + + index = nextIndex + 1 + } + + return res + } + + private fun parseType(text: String): ReturnTypeDef? { + return when (text) { + "boolean" -> PrimitiveTypeDef.BOOLEAN + "char" -> PrimitiveTypeDef.CHAR + "byte" -> PrimitiveTypeDef.BYTE + "short" -> PrimitiveTypeDef.SHORT + "int" -> PrimitiveTypeDef.INT + "long" -> PrimitiveTypeDef.LONG + "float" -> PrimitiveTypeDef.FLOAT + "double" -> PrimitiveTypeDef.DOUBLE + "void" -> VoidTypeDef + else -> { + var arrayCount = 0 + while (true) { + if (text.lastIndexOf("[]", text.lastIndex - (arrayCount * 2)) > 0) { + arrayCount++ + continue + } else { + break + } + } + + if (arrayCount > 0) { + val baseType = parseType(text.substring(0, text.lastIndex - arrayCount * 2)) + if (baseType !is TypeDef) { + return null + } + + ArrayTypeDef(baseType, arrayCount) + } else { + ClassTypeDef(text.asClass()) + } + } + } + } +} diff --git a/obfuscation-explorer/src/main/kotlin/formats/proguard/ProGuardMappingsFormatType.kt b/obfuscation-explorer/src/main/kotlin/formats/proguard/ProGuardMappingsFormatType.kt new file mode 100644 index 000000000..ffd2d9d15 --- /dev/null +++ b/obfuscation-explorer/src/main/kotlin/formats/proguard/ProGuardMappingsFormatType.kt @@ -0,0 +1,31 @@ +/* + * Minecraft Development for IntelliJ + * + * https://mcdev.io/ + * + * Copyright (C) 2024 minecraft-dev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, version 3.0 only. + * + * 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 Lesser General Public License + * along with this program. If not, see . + */ + +package io.mcdev.obfex.formats.proguard + +import io.mcdev.obfex.ObfIcons +import io.mcdev.obfex.formats.MappingsFormatType +import javax.swing.Icon + +object ProGuardMappingsFormatType : MappingsFormatType("ProGuard") { + + override val icon: Icon = ObfIcons.PROGUARD_ICONS + override val name: String = "ProGuard" +} diff --git a/obfuscation-explorer/src/main/kotlin/formats/proguard/lang/ProGuardFile.kt b/obfuscation-explorer/src/main/kotlin/formats/proguard/lang/ProGuardFile.kt new file mode 100644 index 000000000..c4b642f41 --- /dev/null +++ b/obfuscation-explorer/src/main/kotlin/formats/proguard/lang/ProGuardFile.kt @@ -0,0 +1,30 @@ +/* + * Minecraft Development for IntelliJ + * + * https://mcdev.io/ + * + * Copyright (C) 2024 minecraft-dev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, version 3.0 only. + * + * 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 Lesser General Public License + * along with this program. If not, see . + */ + +package io.mcdev.obfex.formats.proguard.lang + +import com.intellij.extapi.psi.PsiFileBase +import com.intellij.openapi.fileTypes.FileType +import com.intellij.psi.FileViewProvider + +class ProGuardFile(viewProGuardFile: FileViewProvider) : PsiFileBase(viewProGuardFile, ProGuardLanguage) { + override fun getFileType(): FileType = ProGuardFileType + override fun toString(): String = ProGuardFileType.description +} diff --git a/obfuscation-explorer/src/main/kotlin/formats/proguard/lang/ProGuardFileType.kt b/obfuscation-explorer/src/main/kotlin/formats/proguard/lang/ProGuardFileType.kt new file mode 100644 index 000000000..d7a1c60af --- /dev/null +++ b/obfuscation-explorer/src/main/kotlin/formats/proguard/lang/ProGuardFileType.kt @@ -0,0 +1,33 @@ +/* + * Minecraft Development for IntelliJ + * + * https://mcdev.io/ + * + * Copyright (C) 2024 minecraft-dev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, version 3.0 only. + * + * 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 Lesser General Public License + * along with this program. If not, see . + */ + +package io.mcdev.obfex.formats.proguard.lang + +import com.intellij.openapi.fileTypes.LanguageFileType +import io.mcdev.obfex.ObfIcons +import javax.swing.Icon + +object ProGuardFileType : LanguageFileType(ProGuardLanguage) { + + override fun getName(): String = "ProGuard" + override fun getDescription(): String = "ProGuard obfuscation mapping file" + override fun getDefaultExtension(): String = "map" + override fun getIcon(): Icon = ObfIcons.PROGUARD_ICONS +} diff --git a/obfuscation-explorer/src/main/kotlin/formats/proguard/lang/ProGuardFileTypeDetector.kt b/obfuscation-explorer/src/main/kotlin/formats/proguard/lang/ProGuardFileTypeDetector.kt new file mode 100644 index 000000000..3e98b82d4 --- /dev/null +++ b/obfuscation-explorer/src/main/kotlin/formats/proguard/lang/ProGuardFileTypeDetector.kt @@ -0,0 +1,73 @@ +/* + * Minecraft Development for IntelliJ + * + * https://mcdev.io/ + * + * Copyright (C) 2024 minecraft-dev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, version 3.0 only. + * + * 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 Lesser General Public License + * along with this program. If not, see . + */ + +package io.mcdev.obfex.formats.proguard.lang + +import com.intellij.openapi.fileTypes.FileType +import com.intellij.openapi.fileTypes.FileTypeRegistry.FileTypeDetector +import com.intellij.openapi.util.io.ByteSequence +import com.intellij.openapi.vfs.VirtualFile +import org.intellij.lang.annotations.Language + +class ProGuardFileTypeDetector : FileTypeDetector { + + override fun detect(file: VirtualFile, firstBytes: ByteSequence, firstCharsIfText: CharSequence?): FileType? { + if (firstCharsIfText == null) { + return null + } + + val lines = firstCharsIfText.lineSequence() + .filter { !LINE_COMMENT.matches(it) } + .toList() + + if (lines.isEmpty()) { + return null + } + + // first line must be a class mapping line + val firstLine = lines.first() + if (firstLine.matches(CLASS_MAPPING_LINE)) { + return ProGuardFileType + } + + return null + } + + private companion object { + private val LINE_COMMENT = Regex("^\\s*#.*$") + @Language("RegExp") + private const val JAVA_IDENTIFIER_REGEX = + // Initial name (either class or first package identifier) + "\\p{javaJavaIdentifierStart}\\p{javaUnicodeIdentifierPart}*" + + // additional name parts (package and class name) + "(\\.\\p{javaJavaIdentifierStart}\\p{javaUnicodeIdentifierPart}*)*" + + private val CLASS_MAPPING_LINE = Regex( + // starts at beginning of the line + "^" + + JAVA_IDENTIFIER_REGEX + + // Separator + " -> " + + JAVA_IDENTIFIER_REGEX + + // ending can include a trailing comment + ":\\s*(#.*)?$" + ) + } +} diff --git a/obfuscation-explorer/src/main/kotlin/formats/proguard/lang/ProGuardLanguage.kt b/obfuscation-explorer/src/main/kotlin/formats/proguard/lang/ProGuardLanguage.kt new file mode 100644 index 000000000..90018c1ea --- /dev/null +++ b/obfuscation-explorer/src/main/kotlin/formats/proguard/lang/ProGuardLanguage.kt @@ -0,0 +1,25 @@ +/* + * Minecraft Development for IntelliJ + * + * https://mcdev.io/ + * + * Copyright (C) 2024 minecraft-dev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, version 3.0 only. + * + * 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 Lesser General Public License + * along with this program. If not, see . + */ + +package io.mcdev.obfex.formats.proguard.lang + +import com.intellij.lang.Language + +object ProGuardLanguage : Language("ProGuard") diff --git a/obfuscation-explorer/src/main/kotlin/formats/proguard/lang/ProGuardParserDefinition.kt b/obfuscation-explorer/src/main/kotlin/formats/proguard/lang/ProGuardParserDefinition.kt new file mode 100644 index 000000000..28da969eb --- /dev/null +++ b/obfuscation-explorer/src/main/kotlin/formats/proguard/lang/ProGuardParserDefinition.kt @@ -0,0 +1,48 @@ +/* + * Minecraft Development for IntelliJ + * + * https://mcdev.io/ + * + * Copyright (C) 2024 minecraft-dev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, version 3.0 only. + * + * 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 Lesser General Public License + * along with this program. If not, see . + */ + +package io.mcdev.obfex.formats.proguard.lang + +import com.intellij.lang.ASTNode +import com.intellij.lang.ParserDefinition +import com.intellij.lang.PsiParser +import com.intellij.lexer.Lexer +import com.intellij.openapi.project.Project +import com.intellij.psi.FileViewProvider +import com.intellij.psi.PsiElement +import com.intellij.psi.PsiFile +import com.intellij.psi.tree.IFileElementType +import com.intellij.psi.tree.TokenSet +import io.mcdev.obfex.formats.proguard.gen.ProGuardParser +import io.mcdev.obfex.formats.proguard.gen.psi.ProGuardTypes +import io.mcdev.obfex.formats.proguard.lang.psi.ProGuardLexerAdapter + +class ProGuardParserDefinition : ParserDefinition { + override fun createLexer(project: Project?): Lexer = ProGuardLexerAdapter() + override fun getCommentTokens(): TokenSet = COMMENTS + override fun getStringLiteralElements(): TokenSet = TokenSet.EMPTY + override fun createParser(project: Project?): PsiParser = ProGuardParser() + override fun getFileNodeType(): IFileElementType = FILE + override fun createFile(viewProvider: FileViewProvider): PsiFile = ProGuardFile(viewProvider) + override fun createElement(node: ASTNode?): PsiElement = ProGuardTypes.Factory.createElement(node) +} + +private val COMMENTS = TokenSet.create(ProGuardTypes.COMMENT) +private val FILE = IFileElementType(ProGuardLanguage) diff --git a/obfuscation-explorer/src/main/kotlin/formats/proguard/lang/colors/ProGuardAnnotator.kt b/obfuscation-explorer/src/main/kotlin/formats/proguard/lang/colors/ProGuardAnnotator.kt new file mode 100644 index 000000000..01c62d467 --- /dev/null +++ b/obfuscation-explorer/src/main/kotlin/formats/proguard/lang/colors/ProGuardAnnotator.kt @@ -0,0 +1,54 @@ +/* + * Minecraft Development for IntelliJ + * + * https://mcdev.io/ + * + * Copyright (C) 2024 minecraft-dev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, version 3.0 only. + * + * 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 Lesser General Public License + * along with this program. If not, see . + */ + +package io.mcdev.obfex.formats.proguard.lang.colors + +import com.intellij.lang.annotation.AnnotationHolder +import com.intellij.lang.annotation.Annotator +import com.intellij.psi.PsiElement +import io.mcdev.obfex.formats.proguard.gen.psi.ProGuardClassMapping +import io.mcdev.obfex.formats.proguard.gen.psi.ProGuardFieldName +import io.mcdev.obfex.formats.proguard.gen.psi.ProGuardMethodName +import io.mcdev.obfex.formats.proguard.gen.psi.ProGuardTypeDesc +import io.mcdev.obfex.formats.util.registerHighlight + +class ProGuardAnnotator : Annotator { + override fun annotate(element: PsiElement, holder: AnnotationHolder) { + if (element is ProGuardClassMapping) { + for (identifier in element.identifierList) { + registerHighlight(identifier, ProGuardSyntaxHighlighter.CLASS_NAME, holder) + } + return + } + if (element is ProGuardTypeDesc) { + element.identifier?.let { ident -> + registerHighlight(ident, ProGuardSyntaxHighlighter.CLASS_NAME, holder) + } + } + + val key = when (element) { + is ProGuardMethodName -> ProGuardSyntaxHighlighter.METHOD + is ProGuardFieldName -> ProGuardSyntaxHighlighter.FIELD + else -> return + } + + registerHighlight(element, key, holder) + } +} diff --git a/obfuscation-explorer/src/main/kotlin/formats/proguard/lang/colors/ProGuardSyntaxHighlighter.kt b/obfuscation-explorer/src/main/kotlin/formats/proguard/lang/colors/ProGuardSyntaxHighlighter.kt new file mode 100644 index 000000000..9a3891f26 --- /dev/null +++ b/obfuscation-explorer/src/main/kotlin/formats/proguard/lang/colors/ProGuardSyntaxHighlighter.kt @@ -0,0 +1,98 @@ +/* + * Minecraft Development for IntelliJ + * + * https://mcdev.io/ + * + * Copyright (C) 2024 minecraft-dev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, version 3.0 only. + * + * 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 Lesser General Public License + * along with this program. If not, see . + */ + +package io.mcdev.obfex.formats.proguard.lang.colors + +import com.intellij.lexer.Lexer +import com.intellij.openapi.editor.DefaultLanguageHighlighterColors +import com.intellij.openapi.editor.colors.TextAttributesKey +import com.intellij.openapi.fileTypes.SyntaxHighlighterBase +import com.intellij.psi.tree.IElementType +import io.mcdev.obfex.formats.proguard.gen.psi.ProGuardTypes +import io.mcdev.obfex.formats.proguard.lang.psi.ProGuardLexerAdapter + +class ProGuardSyntaxHighlighter : SyntaxHighlighterBase() { + + override fun getHighlightingLexer(): Lexer = ProGuardLexerAdapter() + + override fun getTokenHighlights(tokenType: IElementType?): Array = + when (tokenType) { + ProGuardTypes.PRIMITIVE -> PRIMITIVE_KEYS + ProGuardTypes.NUMBER -> NUMBER_KEYS + ProGuardTypes.DOT -> DOT_KEYS + ProGuardTypes.COMMA -> COMMA_KEYS + ProGuardTypes.COMMENT -> COMMENT_KEYS + ProGuardTypes.COLON -> COLON_KEYS + ProGuardTypes.POINTER -> POINTER_KEYS + else -> TextAttributesKey.EMPTY_ARRAY + } + + @Suppress("MemberVisibilityCanBePrivate") + companion object { + val CLASS_NAME = TextAttributesKey.createTextAttributesKey( + "PROGUARD_CLASS_NAME", + DefaultLanguageHighlighterColors.STRING + ) + val METHOD = TextAttributesKey.createTextAttributesKey( + "PROGUARD_METHOD", + DefaultLanguageHighlighterColors.INSTANCE_METHOD + ) + val FIELD = TextAttributesKey.createTextAttributesKey( + "PROGUARD_FIELD", + DefaultLanguageHighlighterColors.INSTANCE_FIELD + ) + val PRIMITIVE = TextAttributesKey.createTextAttributesKey( + "PROGUARD_PRIMITIVE", + DefaultLanguageHighlighterColors.KEYWORD + ) + val NUMBER = TextAttributesKey.createTextAttributesKey( + "PROGUARD_NUMBER", + DefaultLanguageHighlighterColors.NUMBER + ) + val COMMENT = TextAttributesKey.createTextAttributesKey( + "PROGUARD_COMMENT", + DefaultLanguageHighlighterColors.LINE_COMMENT + ) + val COMMA = TextAttributesKey.createTextAttributesKey( + "PROGUARD_COMMA", + DefaultLanguageHighlighterColors.DOT + ) + val DOT = TextAttributesKey.createTextAttributesKey( + "PROGUARD_DOT", + COMMA + ) + val COLON = TextAttributesKey.createTextAttributesKey( + "PROGUARD_COLON", + DOT + ) + val POINTER = TextAttributesKey.createTextAttributesKey( + "PROGUARD_POINTER", + DefaultLanguageHighlighterColors.INLAY_TEXT_WITHOUT_BACKGROUND + ) + + private val PRIMITIVE_KEYS = arrayOf(PRIMITIVE) + private val NUMBER_KEYS = arrayOf(NUMBER) + private val COMMENT_KEYS = arrayOf(COMMENT) + private val COMMA_KEYS = arrayOf(COMMA) + private val DOT_KEYS = arrayOf(DOT) + private val COLON_KEYS = arrayOf(COLON) + private val POINTER_KEYS = arrayOf(POINTER) + } +} diff --git a/obfuscation-explorer/src/main/kotlin/formats/proguard/lang/colors/ProGuardSyntaxHighlighterFactory.kt b/obfuscation-explorer/src/main/kotlin/formats/proguard/lang/colors/ProGuardSyntaxHighlighterFactory.kt new file mode 100644 index 000000000..18ab0b539 --- /dev/null +++ b/obfuscation-explorer/src/main/kotlin/formats/proguard/lang/colors/ProGuardSyntaxHighlighterFactory.kt @@ -0,0 +1,31 @@ +/* + * Minecraft Development for IntelliJ + * + * https://mcdev.io/ + * + * Copyright (C) 2024 minecraft-dev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, version 3.0 only. + * + * 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 Lesser General Public License + * along with this program. If not, see . + */ + +package io.mcdev.obfex.formats.proguard.lang.colors + +import com.intellij.openapi.fileTypes.SyntaxHighlighter +import com.intellij.openapi.fileTypes.SyntaxHighlighterFactory +import com.intellij.openapi.project.Project +import com.intellij.openapi.vfs.VirtualFile + +class ProGuardSyntaxHighlighterFactory : SyntaxHighlighterFactory() { + override fun getSyntaxHighlighter(project: Project?, virtualFile: VirtualFile?): SyntaxHighlighter = + ProGuardSyntaxHighlighter() +} diff --git a/obfuscation-explorer/src/main/kotlin/formats/proguard/lang/psi/ProGuardElementType.kt b/obfuscation-explorer/src/main/kotlin/formats/proguard/lang/psi/ProGuardElementType.kt new file mode 100644 index 000000000..cb64d0ed7 --- /dev/null +++ b/obfuscation-explorer/src/main/kotlin/formats/proguard/lang/psi/ProGuardElementType.kt @@ -0,0 +1,27 @@ +/* + * Minecraft Development for IntelliJ + * + * https://mcdev.io/ + * + * Copyright (C) 2024 minecraft-dev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, version 3.0 only. + * + * 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 Lesser General Public License + * along with this program. If not, see . + */ + +package io.mcdev.obfex.formats.proguard.lang.psi + +import com.intellij.psi.tree.IElementType +import io.mcdev.obfex.formats.proguard.lang.ProGuardLanguage +import org.jetbrains.annotations.NonNls + +class ProGuardElementType(@NonNls debugName: String) : IElementType(debugName, ProGuardLanguage) diff --git a/obfuscation-explorer/src/main/kotlin/formats/proguard/lang/psi/ProGuardLexerAdapter.kt b/obfuscation-explorer/src/main/kotlin/formats/proguard/lang/psi/ProGuardLexerAdapter.kt new file mode 100644 index 000000000..9c99f8348 --- /dev/null +++ b/obfuscation-explorer/src/main/kotlin/formats/proguard/lang/psi/ProGuardLexerAdapter.kt @@ -0,0 +1,26 @@ +/* + * Minecraft Development for IntelliJ + * + * https://mcdev.io/ + * + * Copyright (C) 2024 minecraft-dev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, version 3.0 only. + * + * 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 Lesser General Public License + * along with this program. If not, see . + */ + +package io.mcdev.obfex.formats.proguard.lang.psi + +import com.intellij.lexer.FlexAdapter +import io.mcdev.obfex.formats.proguard.gen.ProGuardLexer + +class ProGuardLexerAdapter : FlexAdapter(ProGuardLexer()) diff --git a/obfuscation-explorer/src/main/kotlin/formats/proguard/lang/psi/ProGuardTokenType.kt b/obfuscation-explorer/src/main/kotlin/formats/proguard/lang/psi/ProGuardTokenType.kt new file mode 100644 index 000000000..23df1a981 --- /dev/null +++ b/obfuscation-explorer/src/main/kotlin/formats/proguard/lang/psi/ProGuardTokenType.kt @@ -0,0 +1,27 @@ +/* + * Minecraft Development for IntelliJ + * + * https://mcdev.io/ + * + * Copyright (C) 2024 minecraft-dev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, version 3.0 only. + * + * 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 Lesser General Public License + * along with this program. If not, see . + */ + +package io.mcdev.obfex.formats.proguard.lang.psi + +import com.intellij.psi.tree.IElementType +import io.mcdev.obfex.formats.proguard.lang.ProGuardLanguage +import org.jetbrains.annotations.NonNls + +class ProGuardTokenType(@NonNls debugName: String) : IElementType(debugName, ProGuardLanguage) diff --git a/obfuscation-explorer/src/main/kotlin/formats/srg/SrgMappingsFormatParser.kt b/obfuscation-explorer/src/main/kotlin/formats/srg/SrgMappingsFormatParser.kt new file mode 100644 index 000000000..113b170a6 --- /dev/null +++ b/obfuscation-explorer/src/main/kotlin/formats/srg/SrgMappingsFormatParser.kt @@ -0,0 +1,180 @@ +/* + * Minecraft Development for IntelliJ + * + * https://mcdev.io/ + * + * Copyright (C) 2024 minecraft-dev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, version 3.0 only. + * + * 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 Lesser General Public License + * along with this program. If not, see . + */ + +package io.mcdev.obfex.formats.srg + +import com.intellij.openapi.vfs.VirtualFile +import io.mcdev.obfex.MappingPart +import io.mcdev.obfex.forLines +import io.mcdev.obfex.mappings.FileCoords +import io.mcdev.obfex.mappings.MappingsDefinition +import io.mcdev.obfex.mappings.MappingsDefinitionBuilder +import io.mcdev.obfex.mappings.UnnamedMappingsFormatParser +import io.mcdev.obfex.mappings.clazz +import io.mcdev.obfex.mappings.field +import io.mcdev.obfex.mappings.method +import io.mcdev.obfex.mappings.pack +import io.mcdev.obfex.ref.asClass +import io.mcdev.obfex.ref.asField +import io.mcdev.obfex.ref.asMethod +import io.mcdev.obfex.ref.asMethodDesc +import io.mcdev.obfex.ref.asPackage +import io.mcdev.obfex.ref.highPriority +import io.mcdev.obfex.ref.lowPriority +import io.mcdev.obfex.splitOnLast + +@Suppress("DuplicatedCode") +class SrgMappingsFormatParser : UnnamedMappingsFormatParser { + + override val expectedFileExtensions: Array = arrayOf("srg") + + override fun parse(file: VirtualFile): MappingsDefinition { + val builder = MappingsDefinitionBuilder(SrgMappingsFormatType, file) + + file.forLines { lineNum, parts, _ -> + if (parts.isEmpty()) { + return@forLines true + } + + val key = parts.first() + + when (key.value) { + "PK:" -> parsePackageLine(builder, lineNum, parts) + "CL:" -> parseClassLine(builder, lineNum, parts) + "MD:" -> parseMethodLine(builder, lineNum, parts) + "FD:" -> parseFieldLine(builder, lineNum, parts) + else -> builder.error("Unrecognized key: ${key.value.substring(0, 3)}", FileCoords(lineNum)) + } + + return@forLines true + } + + return builder.build() + } + + private fun parsePackageLine(builder: MappingsDefinitionBuilder, lineNum: Int, parts: Array) { + if (parts.size != 3) { + builder.error("Package line is invalid", FileCoords(lineNum)) + return + } + + val (_, leftPack, rightPack) = parts + builder.pack(lineNum.highPriority) { + with(leftPack.asPackage().from) + with(rightPack.asPackage().to) + } + } + + private fun parseClassLine(builder: MappingsDefinitionBuilder, lineNum: Int, parts: Array) { + if (parts.size != 3) { + builder.error("Class line is invalid", FileCoords(lineNum)) + return + } + + val (_, leftClass, rightClass) = parts + builder.clazz(lineNum.highPriority) { + with(leftClass.asClass().from) + with(rightClass.asClass().to) + } + } + + private fun parseFieldLine(builder: MappingsDefinitionBuilder, lineNum: Int, parts: Array) { + if (parts.size != 3) { + builder.error("Field line is invalid", FileCoords(lineNum)) + return + } + + val (leftClass, leftField) = parts[1].splitOnLast('/') + if (leftField == null) { + builder.error( + "Field mapping is invalid, no field is specified after class", + FileCoords(lineNum, parts[1]) + ) + return + } + + val (rightClass, rightField) = parts[2].splitOnLast('/') + if (rightField == null) { + builder.error( + "Field mapping is invalid, no field is specified after class", + FileCoords(lineNum, parts[2]) + ) + return + } + + builder.clazz(leftClass.asClass().from, lineNum.lowPriority) { + unlessExists(rightClass.asClass().to) + + field(lineNum.highPriority) { + with(leftField.asField().from) + with(rightField.asField().to) + } + } + } + + private fun parseMethodLine(builder: MappingsDefinitionBuilder, lineNum: Int, parts: Array) { + if (parts.size != 5) { + builder.error("Method line is invalid", FileCoords(lineNum)) + return + } + + val (leftClass, leftMethod) = parts[1].splitOnLast('/') + if (leftMethod == null) { + builder.error( + "Method mapping is invalid, no method is specified after class", + FileCoords(lineNum, parts[1]) + ) + return + } + + val (rightClass, rightMethod) = parts[3].splitOnLast('/') + if (rightMethod == null) { + builder.error( + "Method mapping is invalid, no method is specified after class", + FileCoords(lineNum, parts[3]) + ) + return + } + + val leftDesc = parts[2].value.asMethodDesc() + if (leftDesc == null) { + builder.error("Method descriptor is invalid", FileCoords(lineNum, parts[2])) + return + } + + // we don't actually store this descriptor, but we verify it for valid syntax + val rightDesc = parts[4].value.asMethodDesc() + if (rightDesc == null) { + builder.error("Method descriptor is invalid", FileCoords(lineNum, parts[4])) + return + } + + builder.clazz(leftClass.asClass().from, lineNum.lowPriority) { + unlessExists(rightClass.asClass().to) + + method(lineNum.highPriority) { + desc = leftDesc + + with(leftMethod.asMethod().from) + with(rightMethod.asMethod().to) + } + } + } +} diff --git a/obfuscation-explorer/src/main/kotlin/formats/srg/SrgMappingsFormatType.kt b/obfuscation-explorer/src/main/kotlin/formats/srg/SrgMappingsFormatType.kt new file mode 100644 index 000000000..0b2ee94ba --- /dev/null +++ b/obfuscation-explorer/src/main/kotlin/formats/srg/SrgMappingsFormatType.kt @@ -0,0 +1,31 @@ +/* + * Minecraft Development for IntelliJ + * + * https://mcdev.io/ + * + * Copyright (C) 2024 minecraft-dev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, version 3.0 only. + * + * 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 Lesser General Public License + * along with this program. If not, see . + */ + +package io.mcdev.obfex.formats.srg + +import io.mcdev.obfex.ObfIcons +import io.mcdev.obfex.formats.MappingsFormatType +import javax.swing.Icon + +object SrgMappingsFormatType : MappingsFormatType("SRG") { + + override val icon: Icon = ObfIcons.SRG_ICON + override val name: String = "SRG" +} diff --git a/obfuscation-explorer/src/main/kotlin/formats/srg/lang/SrgFile.kt b/obfuscation-explorer/src/main/kotlin/formats/srg/lang/SrgFile.kt new file mode 100644 index 000000000..dae743ad7 --- /dev/null +++ b/obfuscation-explorer/src/main/kotlin/formats/srg/lang/SrgFile.kt @@ -0,0 +1,30 @@ +/* + * Minecraft Development for IntelliJ + * + * https://mcdev.io/ + * + * Copyright (C) 2024 minecraft-dev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, version 3.0 only. + * + * 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 Lesser General Public License + * along with this program. If not, see . + */ + +package io.mcdev.obfex.formats.srg.lang + +import com.intellij.extapi.psi.PsiFileBase +import com.intellij.openapi.fileTypes.FileType +import com.intellij.psi.FileViewProvider + +class SrgFile(viewProvider: FileViewProvider) : PsiFileBase(viewProvider, SrgLanguage) { + override fun getFileType(): FileType = SrgFileType + override fun toString(): String = SrgFileType.description +} diff --git a/obfuscation-explorer/src/main/kotlin/formats/srg/lang/SrgFileType.kt b/obfuscation-explorer/src/main/kotlin/formats/srg/lang/SrgFileType.kt new file mode 100644 index 000000000..5bf893600 --- /dev/null +++ b/obfuscation-explorer/src/main/kotlin/formats/srg/lang/SrgFileType.kt @@ -0,0 +1,33 @@ +/* + * Minecraft Development for IntelliJ + * + * https://mcdev.io/ + * + * Copyright (C) 2024 minecraft-dev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, version 3.0 only. + * + * 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 Lesser General Public License + * along with this program. If not, see . + */ + +package io.mcdev.obfex.formats.srg.lang + +import com.intellij.openapi.fileTypes.LanguageFileType +import io.mcdev.obfex.ObfIcons +import javax.swing.Icon + +object SrgFileType : LanguageFileType(SrgLanguage) { + + override fun getName(): String = "SRG" + override fun getDescription(): String = "SRG obfuscation mapping file" + override fun getDefaultExtension(): String = "srg" + override fun getIcon(): Icon = ObfIcons.SRG_ICON +} diff --git a/obfuscation-explorer/src/main/kotlin/formats/srg/lang/SrgLanguage.kt b/obfuscation-explorer/src/main/kotlin/formats/srg/lang/SrgLanguage.kt new file mode 100644 index 000000000..8dfe82567 --- /dev/null +++ b/obfuscation-explorer/src/main/kotlin/formats/srg/lang/SrgLanguage.kt @@ -0,0 +1,25 @@ +/* + * Minecraft Development for IntelliJ + * + * https://mcdev.io/ + * + * Copyright (C) 2024 minecraft-dev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, version 3.0 only. + * + * 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 Lesser General Public License + * along with this program. If not, see . + */ + +package io.mcdev.obfex.formats.srg.lang + +import com.intellij.lang.Language + +object SrgLanguage : Language("SRG") diff --git a/obfuscation-explorer/src/main/kotlin/formats/srg/lang/SrgParserDefinition.kt b/obfuscation-explorer/src/main/kotlin/formats/srg/lang/SrgParserDefinition.kt new file mode 100644 index 000000000..01523cea4 --- /dev/null +++ b/obfuscation-explorer/src/main/kotlin/formats/srg/lang/SrgParserDefinition.kt @@ -0,0 +1,50 @@ +/* + * Minecraft Development for IntelliJ + * + * https://mcdev.io/ + * + * Copyright (C) 2024 minecraft-dev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, version 3.0 only. + * + * 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 Lesser General Public License + * along with this program. If not, see . + */ + +package io.mcdev.obfex.formats.srg.lang + +import com.intellij.lang.ASTNode +import com.intellij.lang.Language +import com.intellij.lang.ParserDefinition +import com.intellij.lang.PsiParser +import com.intellij.lexer.Lexer +import com.intellij.openapi.project.Project +import com.intellij.psi.FileViewProvider +import com.intellij.psi.PsiElement +import com.intellij.psi.PsiFile +import com.intellij.psi.tree.IFileElementType +import com.intellij.psi.tree.TokenSet +import io.mcdev.obfex.formats.srg.gen.SrgParser +import io.mcdev.obfex.formats.srg.gen.psi.SrgTypes +import io.mcdev.obfex.formats.srg.lang.psi.SrgLexerAdapter + +class SrgParserDefinition : ParserDefinition { + + override fun createLexer(project: Project): Lexer = SrgLexerAdapter() + override fun getCommentTokens(): TokenSet = COMMENTS + override fun getStringLiteralElements(): TokenSet = TokenSet.EMPTY + override fun createParser(project: Project): PsiParser = SrgParser() + override fun getFileNodeType(): IFileElementType = FILE + override fun createFile(viewProvider: FileViewProvider): PsiFile = SrgFile(viewProvider) + override fun createElement(node: ASTNode): PsiElement = SrgTypes.Factory.createElement(node) +} + +private val COMMENTS = TokenSet.create(SrgTypes.COMMENT) +private val FILE = IFileElementType(Language.findInstance(SrgLanguage::class.java)) diff --git a/obfuscation-explorer/src/main/kotlin/formats/srg/lang/colors/SrgAnnotator.kt b/obfuscation-explorer/src/main/kotlin/formats/srg/lang/colors/SrgAnnotator.kt new file mode 100644 index 000000000..1dc744e46 --- /dev/null +++ b/obfuscation-explorer/src/main/kotlin/formats/srg/lang/colors/SrgAnnotator.kt @@ -0,0 +1,72 @@ +/* + * Minecraft Development for IntelliJ + * + * https://mcdev.io/ + * + * Copyright (C) 2024 minecraft-dev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, version 3.0 only. + * + * 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 Lesser General Public License + * along with this program. If not, see . + */ + +package io.mcdev.obfex.formats.srg.lang.colors + +import com.intellij.lang.annotation.AnnotationHolder +import com.intellij.lang.annotation.Annotator +import com.intellij.lang.annotation.HighlightSeverity +import com.intellij.psi.PsiElement +import io.mcdev.obfex.formats.srg.gen.psi.SrgClassMapping +import io.mcdev.obfex.formats.srg.gen.psi.SrgFieldMapping +import io.mcdev.obfex.formats.srg.gen.psi.SrgMethodMapping +import io.mcdev.obfex.formats.srg.lang.SrgLanguage +import io.mcdev.obfex.formats.util.registerHighlight + +class SrgAnnotator : Annotator { + + override fun annotate(element: PsiElement, holder: AnnotationHolder) { + when (element) { + is SrgClassMapping -> { + registerHighlight(element.obfName, SrgSyntaxHighlighter.CLASS_NAME, holder) + registerHighlight(element.deobfName, SrgSyntaxHighlighter.CLASS_NAME, holder, true) + } + + is SrgMethodMapping -> { + registerHighlight(element.obfName, SrgSyntaxHighlighter.CLASS_NAME, holder) + element.obfName?.identifier?.namePartList?.last()?.let { name -> + registerHighlight(name, SrgSyntaxHighlighter.METHOD, holder) + } + registerHighlight(element.deobfName, SrgSyntaxHighlighter.CLASS_NAME, holder, true) + element.deobfName?.identifier?.namePartList?.last()?.let { name -> + registerHighlight(name, SrgSyntaxHighlighter.METHOD, holder, true) + } + } + + is SrgFieldMapping -> { + registerHighlight(element.obfName, SrgSyntaxHighlighter.CLASS_NAME, holder) + element.obfName?.identifier?.namePartList?.last()?.let { name -> + registerHighlight(name, SrgSyntaxHighlighter.FIELD, holder) + } + registerHighlight(element.deobfName, SrgSyntaxHighlighter.CLASS_NAME, holder, true) + element.deobfName?.identifier?.namePartList?.last()?.let { name -> + registerHighlight(name, SrgSyntaxHighlighter.FIELD, holder, true) + } + + if (element.getExtendedFieldMapping() != null && element.containingFile.language == SrgLanguage) { + val msg = "Extended field mappings are not allowed in standard SRG mapping files" + holder.newAnnotation(HighlightSeverity.ERROR, msg) + .range(element) + .create() + } + } + } + } +} diff --git a/obfuscation-explorer/src/main/kotlin/formats/srg/lang/colors/SrgSyntaxHighlighter.kt b/obfuscation-explorer/src/main/kotlin/formats/srg/lang/colors/SrgSyntaxHighlighter.kt new file mode 100644 index 000000000..ad60e9c3c --- /dev/null +++ b/obfuscation-explorer/src/main/kotlin/formats/srg/lang/colors/SrgSyntaxHighlighter.kt @@ -0,0 +1,78 @@ +/* + * Minecraft Development for IntelliJ + * + * https://mcdev.io/ + * + * Copyright (C) 2024 minecraft-dev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, version 3.0 only. + * + * 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 Lesser General Public License + * along with this program. If not, see . + */ + +package io.mcdev.obfex.formats.srg.lang.colors + +import com.intellij.lexer.Lexer +import com.intellij.openapi.editor.DefaultLanguageHighlighterColors +import com.intellij.openapi.editor.colors.TextAttributesKey +import com.intellij.openapi.fileTypes.SyntaxHighlighterBase +import com.intellij.psi.tree.IElementType +import io.mcdev.obfex.formats.srg.gen.psi.SrgTypes +import io.mcdev.obfex.formats.srg.lang.psi.SrgLexerAdapter + +class SrgSyntaxHighlighter : SyntaxHighlighterBase() { + + override fun getHighlightingLexer(): Lexer = SrgLexerAdapter() + + override fun getTokenHighlights(tokenType: IElementType?): Array { + return when (tokenType) { + SrgTypes.PACKAGE_KEY, SrgTypes.CLASS_KEY, SrgTypes.FIELD_KEY, SrgTypes.METHOD_KEY -> KEYWORD_KEYS + SrgTypes.CLASS_TYPE -> CLASS_TYPE_KEYS + SrgTypes.PRIMITIVE -> PRIMITIVE_KEYS + SrgTypes.COMMENT -> COMMENT_KEYS + else -> TextAttributesKey.EMPTY_ARRAY + } + } + + @Suppress("MemberVisibilityCanBePrivate") + companion object { + val KEYWORD = TextAttributesKey.createTextAttributesKey("SRG_KEYWORD", DefaultLanguageHighlighterColors.KEYWORD) + val CLASS_NAME = TextAttributesKey.createTextAttributesKey( + "SRG_CLASS_NAME", + DefaultLanguageHighlighterColors.STRING + ) + val METHOD = TextAttributesKey.createTextAttributesKey( + "SRG_METHOD", + DefaultLanguageHighlighterColors.INSTANCE_METHOD + ) + val FIELD = TextAttributesKey.createTextAttributesKey( + "SRG_FIELD", + DefaultLanguageHighlighterColors.INSTANCE_FIELD + ) + val PRIMITIVE = TextAttributesKey.createTextAttributesKey( + "SRG_PRIMITIVE", + DefaultLanguageHighlighterColors.NUMBER + ) + val CLASS_TYPE = TextAttributesKey.createTextAttributesKey( + "SRG_CLASS_TYPE", + DefaultLanguageHighlighterColors.CLASS_REFERENCE + ) + val COMMENT = TextAttributesKey.createTextAttributesKey( + "SRG_COMMENT", + DefaultLanguageHighlighterColors.LINE_COMMENT + ) + + private val KEYWORD_KEYS = arrayOf(KEYWORD) + private val PRIMITIVE_KEYS = arrayOf(PRIMITIVE) + private val CLASS_TYPE_KEYS = arrayOf(CLASS_TYPE) + private val COMMENT_KEYS = arrayOf(COMMENT) + } +} diff --git a/obfuscation-explorer/src/main/kotlin/formats/srg/lang/colors/SrgSyntaxHighlighterFactory.kt b/obfuscation-explorer/src/main/kotlin/formats/srg/lang/colors/SrgSyntaxHighlighterFactory.kt new file mode 100644 index 000000000..a0a5f4074 --- /dev/null +++ b/obfuscation-explorer/src/main/kotlin/formats/srg/lang/colors/SrgSyntaxHighlighterFactory.kt @@ -0,0 +1,31 @@ +/* + * Minecraft Development for IntelliJ + * + * https://mcdev.io/ + * + * Copyright (C) 2024 minecraft-dev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, version 3.0 only. + * + * 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 Lesser General Public License + * along with this program. If not, see . + */ + +package io.mcdev.obfex.formats.srg.lang.colors + +import com.intellij.openapi.fileTypes.SyntaxHighlighter +import com.intellij.openapi.fileTypes.SyntaxHighlighterFactory +import com.intellij.openapi.project.Project +import com.intellij.openapi.vfs.VirtualFile + +class SrgSyntaxHighlighterFactory : SyntaxHighlighterFactory() { + override fun getSyntaxHighlighter(project: Project?, virtualFile: VirtualFile?): SyntaxHighlighter = + SrgSyntaxHighlighter() +} diff --git a/obfuscation-explorer/src/main/kotlin/formats/srg/lang/psi/SrgElementType.kt b/obfuscation-explorer/src/main/kotlin/formats/srg/lang/psi/SrgElementType.kt new file mode 100644 index 000000000..e6e20f8f5 --- /dev/null +++ b/obfuscation-explorer/src/main/kotlin/formats/srg/lang/psi/SrgElementType.kt @@ -0,0 +1,27 @@ +/* + * Minecraft Development for IntelliJ + * + * https://mcdev.io/ + * + * Copyright (C) 2024 minecraft-dev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, version 3.0 only. + * + * 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 Lesser General Public License + * along with this program. If not, see . + */ + +package io.mcdev.obfex.formats.srg.lang.psi + +import com.intellij.psi.tree.IElementType +import io.mcdev.obfex.formats.srg.lang.SrgLanguage +import org.jetbrains.annotations.NonNls + +class SrgElementType(@NonNls debugName: String) : IElementType(debugName, SrgLanguage) diff --git a/obfuscation-explorer/src/main/kotlin/formats/srg/lang/psi/SrgLexerAdapter.kt b/obfuscation-explorer/src/main/kotlin/formats/srg/lang/psi/SrgLexerAdapter.kt new file mode 100644 index 000000000..8e4c08a3e --- /dev/null +++ b/obfuscation-explorer/src/main/kotlin/formats/srg/lang/psi/SrgLexerAdapter.kt @@ -0,0 +1,26 @@ +/* + * Minecraft Development for IntelliJ + * + * https://mcdev.io/ + * + * Copyright (C) 2024 minecraft-dev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, version 3.0 only. + * + * 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 Lesser General Public License + * along with this program. If not, see . + */ + +package io.mcdev.obfex.formats.srg.lang.psi + +import com.intellij.lexer.FlexAdapter +import io.mcdev.obfex.formats.srg.gen.SrgLexer + +class SrgLexerAdapter : FlexAdapter(SrgLexer()) diff --git a/obfuscation-explorer/src/main/kotlin/formats/srg/lang/psi/SrgTokenType.kt b/obfuscation-explorer/src/main/kotlin/formats/srg/lang/psi/SrgTokenType.kt new file mode 100644 index 000000000..e4fa4a3db --- /dev/null +++ b/obfuscation-explorer/src/main/kotlin/formats/srg/lang/psi/SrgTokenType.kt @@ -0,0 +1,27 @@ +/* + * Minecraft Development for IntelliJ + * + * https://mcdev.io/ + * + * Copyright (C) 2024 minecraft-dev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, version 3.0 only. + * + * 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 Lesser General Public License + * along with this program. If not, see . + */ + +package io.mcdev.obfex.formats.srg.lang.psi + +import com.intellij.psi.tree.IElementType +import io.mcdev.obfex.formats.srg.lang.SrgLanguage +import org.jetbrains.annotations.NonNls + +class SrgTokenType(@NonNls debugName: String) : IElementType(debugName, SrgLanguage) diff --git a/obfuscation-explorer/src/main/kotlin/formats/srg/lang/psi/mixins/SrgFieldMappingMixin.kt b/obfuscation-explorer/src/main/kotlin/formats/srg/lang/psi/mixins/SrgFieldMappingMixin.kt new file mode 100644 index 000000000..fb6f8c41f --- /dev/null +++ b/obfuscation-explorer/src/main/kotlin/formats/srg/lang/psi/mixins/SrgFieldMappingMixin.kt @@ -0,0 +1,36 @@ +/* + * Minecraft Development for IntelliJ + * + * https://mcdev.io/ + * + * Copyright (C) 2024 minecraft-dev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, version 3.0 only. + * + * 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 Lesser General Public License + * along with this program. If not, see . + */ + +package io.mcdev.obfex.formats.srg.lang.psi.mixins + +import com.intellij.psi.PsiElement +import io.mcdev.obfex.formats.srg.gen.psi.SrgExtendedFieldMapping +import io.mcdev.obfex.formats.srg.gen.psi.SrgMappingPart +import io.mcdev.obfex.formats.srg.gen.psi.SrgStandardFieldMapping + +interface SrgFieldMappingMixin : PsiElement { + fun getExtendedFieldMapping(): SrgExtendedFieldMapping? + + fun getStandardFieldMapping(): SrgStandardFieldMapping? + + val mappingPartList: List + val obfName: SrgMappingPart? + val deobfName: SrgMappingPart? +} diff --git a/obfuscation-explorer/src/main/kotlin/formats/srg/lang/psi/mixins/impl/SrgFieldMappingImplMixin.kt b/obfuscation-explorer/src/main/kotlin/formats/srg/lang/psi/mixins/impl/SrgFieldMappingImplMixin.kt new file mode 100644 index 000000000..e4917bda5 --- /dev/null +++ b/obfuscation-explorer/src/main/kotlin/formats/srg/lang/psi/mixins/impl/SrgFieldMappingImplMixin.kt @@ -0,0 +1,38 @@ +/* + * Minecraft Development for IntelliJ + * + * https://mcdev.io/ + * + * Copyright (C) 2024 minecraft-dev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, version 3.0 only. + * + * 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 Lesser General Public License + * along with this program. If not, see . + */ + +package io.mcdev.obfex.formats.srg.lang.psi.mixins.impl + +import com.intellij.extapi.psi.ASTWrapperPsiElement +import com.intellij.lang.ASTNode +import io.mcdev.obfex.formats.srg.gen.psi.SrgMappingPart +import io.mcdev.obfex.formats.srg.lang.psi.mixins.SrgFieldMappingMixin + +abstract class SrgFieldMappingImplMixin(node: ASTNode) : ASTWrapperPsiElement(node), SrgFieldMappingMixin { + + override val mappingPartList: List + get() = getStandardFieldMapping()?.mappingPartList ?: getStandardFieldMapping()?.mappingPartList ?: emptyList() + + override val obfName: SrgMappingPart? + get() = getStandardFieldMapping()?.obfName ?: getExtendedFieldMapping()?.obfName + + override val deobfName: SrgMappingPart? + get() = getStandardFieldMapping()?.deobfName ?: getExtendedFieldMapping()?.deobfName +} diff --git a/obfuscation-explorer/src/main/kotlin/formats/tinyv1/TinyV1MappingsFormatParser.kt b/obfuscation-explorer/src/main/kotlin/formats/tinyv1/TinyV1MappingsFormatParser.kt new file mode 100644 index 000000000..222dd6255 --- /dev/null +++ b/obfuscation-explorer/src/main/kotlin/formats/tinyv1/TinyV1MappingsFormatParser.kt @@ -0,0 +1,193 @@ +/* + * Minecraft Development for IntelliJ + * + * https://mcdev.io/ + * + * Copyright (C) 2024 minecraft-dev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, version 3.0 only. + * + * 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 Lesser General Public License + * along with this program. If not, see . + */ + +package io.mcdev.obfex.formats.tinyv1 + +import com.intellij.openapi.vfs.VirtualFile +import io.mcdev.obfex.MappingPart +import io.mcdev.obfex.forLines +import io.mcdev.obfex.formats.util.indicesFrom +import io.mcdev.obfex.mappings.FileCoords +import io.mcdev.obfex.mappings.MappingsDefinition +import io.mcdev.obfex.mappings.MappingsDefinitionBuilder +import io.mcdev.obfex.mappings.MappingsFormatParser +import io.mcdev.obfex.mappings.clazz +import io.mcdev.obfex.mappings.field +import io.mcdev.obfex.mappings.method +import io.mcdev.obfex.mappings.ns +import io.mcdev.obfex.ref.asClass +import io.mcdev.obfex.ref.asField +import io.mcdev.obfex.ref.asMethod +import io.mcdev.obfex.ref.asMethodDesc +import io.mcdev.obfex.ref.asTypeDef +import io.mcdev.obfex.ref.highPriority +import io.mcdev.obfex.ref.lowPriority +import io.mcdev.obfex.splitMappingLine + +class TinyV1MappingsFormatParser : MappingsFormatParser { + + override val expectedFileExtensions: Array = arrayOf("tiny") + + override fun isSupportedFile(file: VirtualFile): Boolean { + val firstLine = file.inputStream.bufferedReader(file.charset).use { reader -> + reader.readLine() + } + return getNamespaces(firstLine) != null + } + + override fun parse(file: VirtualFile): MappingsDefinition? { + val firstLine = file.inputStream.bufferedReader(file.charset).use { reader -> + reader.readLine() + } + + val namespaces = getNamespaces(firstLine) ?: return null + + val builder = MappingsDefinitionBuilder(TinyV1MappingsFormatType, file, *namespaces) + + file.forLines(skipLines = 1, preserveBlank = true) { lineNum, parts, _ -> + if (parts.isEmpty()) { + return@forLines true + } + + val key = parts[0].value + when (key) { + "CLASS" -> parseClass(builder, namespaces.size, lineNum, parts) + "FIELD" -> parseField(builder, namespaces.size, lineNum, parts) + "METHOD" -> parseMethod(builder, namespaces.size, lineNum, parts) + } + + return@forLines true + } + + return builder.build() + } + + private fun getNamespaces(line: String?): Array? { + if (line == null) { + return null + } + + val header = line.splitMappingLine() + if (header.size < 3) { + return null + } + + if (header[0].value != "v1") { + return null + } + + val namespaces = arrayOfNulls(header.size - 1) + for (index in 1 until header.size) { + namespaces[index - 1] = header[index].value + } + + @Suppress("UNCHECKED_CAST") + return namespaces as Array + } + + private fun parseClass( + builder: MappingsDefinitionBuilder, + namespaceCount: Int, + lineNum: Int, + parts: Array + ) { + if (parts.size - 1 > namespaceCount) { + builder.error("Unexpected number of names present", FileCoords(lineNum)) + return + } + + if (parts.size == 1) { + builder.warning("Class mapping line has no names", FileCoords(lineNum)) + return + } + + builder.clazz(lineNum.highPriority) { + for (i in parts.indicesFrom(1)) { + with(parts[i].asClass().ns(i - 1)) + } + } + } + + private fun parseField( + builder: MappingsDefinitionBuilder, + namespaceCount: Int, + lineNum: Int, + parts: Array + ) { + if (parts.size - 3 > namespaceCount) { + builder.error("Unexpected number of names present", FileCoords(lineNum)) + return + } + + if (parts.size < 3) { + builder.error("Field mapping line does not have all necessary components", FileCoords(lineNum)) + return + } + + if (parts.size == 3) { + builder.warning("Field mapping line has no names", FileCoords(lineNum)) + } + + val owner = parts[1] + val fieldType = parts[2].value.asTypeDef() + ?: return builder.error("Field mapping has an invalid type", FileCoords(lineNum, parts[2])) + + builder.clazz(owner.asClass().ns(0), lineNum.lowPriority).field { + type = fieldType + + for (i in parts.indicesFrom(3)) { + with(parts[i].asField().ns(i - 3)) + } + } + } + + private fun parseMethod( + builder: MappingsDefinitionBuilder, + namespaceCount: Int, + lineNum: Int, + parts: Array + ) { + if (parts.size - 3 > namespaceCount) { + builder.error("Unexpected number of names present", FileCoords(lineNum)) + return + } + + if (parts.size < 3) { + builder.error("Method mapping line does not have all necessary components", FileCoords(lineNum)) + return + } + + if (parts.size == 3) { + builder.warning("Method mapping line has no names", FileCoords(lineNum)) + } + + val owner = parts[1] + val methodDesc = parts[2].value.asMethodDesc() + ?: return builder.error("Method mapping has an invalid descriptor", FileCoords(lineNum, parts[2])) + + builder.clazz(owner.asClass().ns(0)).method { + desc = methodDesc + + for (i in parts.indicesFrom(3)) { + with(parts[i].asMethod().ns(i - 3)) + } + } + } +} diff --git a/obfuscation-explorer/src/main/kotlin/formats/tinyv1/TinyV1MappingsFormatType.kt b/obfuscation-explorer/src/main/kotlin/formats/tinyv1/TinyV1MappingsFormatType.kt new file mode 100644 index 000000000..97fa275e0 --- /dev/null +++ b/obfuscation-explorer/src/main/kotlin/formats/tinyv1/TinyV1MappingsFormatType.kt @@ -0,0 +1,31 @@ +/* + * Minecraft Development for IntelliJ + * + * https://mcdev.io/ + * + * Copyright (C) 2024 minecraft-dev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, version 3.0 only. + * + * 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 Lesser General Public License + * along with this program. If not, see . + */ + +package io.mcdev.obfex.formats.tinyv1 + +import io.mcdev.obfex.ObfIcons +import io.mcdev.obfex.formats.MappingsFormatType +import javax.swing.Icon + +object TinyV1MappingsFormatType : MappingsFormatType("tiny-v1") { + + override val icon: Icon = ObfIcons.TINY_V1_ICON + override val name: String = "Tiny v2" +} diff --git a/obfuscation-explorer/src/main/kotlin/formats/tinyv1/lang/TinyFileTypeDetector.kt b/obfuscation-explorer/src/main/kotlin/formats/tinyv1/lang/TinyFileTypeDetector.kt new file mode 100644 index 000000000..1335d5f16 --- /dev/null +++ b/obfuscation-explorer/src/main/kotlin/formats/tinyv1/lang/TinyFileTypeDetector.kt @@ -0,0 +1,48 @@ +/* + * Minecraft Development for IntelliJ + * + * https://mcdev.io/ + * + * Copyright (C) 2024 minecraft-dev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, version 3.0 only. + * + * 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 Lesser General Public License + * along with this program. If not, see . + */ + +package io.mcdev.obfex.formats.tinyv1.lang + +import com.intellij.openapi.fileTypes.FileType +import com.intellij.openapi.fileTypes.FileTypeRegistry +import com.intellij.openapi.util.io.ByteSequence +import com.intellij.openapi.vfs.VirtualFile +import io.mcdev.obfex.formats.tinyv2.lang.TinyV2FileType + +class TinyFileTypeDetector : FileTypeRegistry.FileTypeDetector { + + override fun detect(file: VirtualFile, firstBytes: ByteSequence, firstCharsIfText: CharSequence?): FileType? { + if (firstCharsIfText == null) { + return null + } + + if (!file.name.endsWith(".tiny")) { + return null + } + + if (firstCharsIfText.startsWith("v1")) { + return TinyV1FileType + } else if (firstCharsIfText.startsWith("tiny\t2\t0")) { + return TinyV2FileType + } + + return null + } +} diff --git a/obfuscation-explorer/src/main/kotlin/formats/tinyv1/lang/TinyV1File.kt b/obfuscation-explorer/src/main/kotlin/formats/tinyv1/lang/TinyV1File.kt new file mode 100644 index 000000000..8ce70d216 --- /dev/null +++ b/obfuscation-explorer/src/main/kotlin/formats/tinyv1/lang/TinyV1File.kt @@ -0,0 +1,30 @@ +/* + * Minecraft Development for IntelliJ + * + * https://mcdev.io/ + * + * Copyright (C) 2024 minecraft-dev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, version 3.0 only. + * + * 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 Lesser General Public License + * along with this program. If not, see . + */ + +package io.mcdev.obfex.formats.tinyv1.lang + +import com.intellij.extapi.psi.PsiFileBase +import com.intellij.openapi.fileTypes.FileType +import com.intellij.psi.FileViewProvider + +class TinyV1File(viewProvider: FileViewProvider) : PsiFileBase(viewProvider, TinyV1Language) { + override fun getFileType(): FileType = TinyV1FileType + override fun toString(): String = TinyV1FileType.description +} diff --git a/obfuscation-explorer/src/main/kotlin/formats/tinyv1/lang/TinyV1FileType.kt b/obfuscation-explorer/src/main/kotlin/formats/tinyv1/lang/TinyV1FileType.kt new file mode 100644 index 000000000..81a6e88ca --- /dev/null +++ b/obfuscation-explorer/src/main/kotlin/formats/tinyv1/lang/TinyV1FileType.kt @@ -0,0 +1,33 @@ +/* + * Minecraft Development for IntelliJ + * + * https://mcdev.io/ + * + * Copyright (C) 2024 minecraft-dev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, version 3.0 only. + * + * 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 Lesser General Public License + * along with this program. If not, see . + */ + +package io.mcdev.obfex.formats.tinyv1.lang + +import com.intellij.openapi.fileTypes.LanguageFileType +import io.mcdev.obfex.ObfIcons +import javax.swing.Icon + +object TinyV1FileType : LanguageFileType(TinyV1Language) { + + override fun getName(): String = "Tiny v1" + override fun getDescription(): String = "Tiny v1 obfuscation mapping file" + override fun getDefaultExtension(): String = "tiny" + override fun getIcon(): Icon = ObfIcons.TINY_V1_ICON +} diff --git a/obfuscation-explorer/src/main/kotlin/formats/tinyv1/lang/TinyV1Language.kt b/obfuscation-explorer/src/main/kotlin/formats/tinyv1/lang/TinyV1Language.kt new file mode 100644 index 000000000..96e41f60e --- /dev/null +++ b/obfuscation-explorer/src/main/kotlin/formats/tinyv1/lang/TinyV1Language.kt @@ -0,0 +1,25 @@ +/* + * Minecraft Development for IntelliJ + * + * https://mcdev.io/ + * + * Copyright (C) 2024 minecraft-dev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, version 3.0 only. + * + * 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 Lesser General Public License + * along with this program. If not, see . + */ + +package io.mcdev.obfex.formats.tinyv1.lang + +import com.intellij.lang.Language + +object TinyV1Language : Language("TinyV1") diff --git a/obfuscation-explorer/src/main/kotlin/formats/tinyv1/lang/TinyV1ParserDefinition.kt b/obfuscation-explorer/src/main/kotlin/formats/tinyv1/lang/TinyV1ParserDefinition.kt new file mode 100644 index 000000000..ef41f962d --- /dev/null +++ b/obfuscation-explorer/src/main/kotlin/formats/tinyv1/lang/TinyV1ParserDefinition.kt @@ -0,0 +1,50 @@ +/* + * Minecraft Development for IntelliJ + * + * https://mcdev.io/ + * + * Copyright (C) 2024 minecraft-dev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, version 3.0 only. + * + * 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 Lesser General Public License + * along with this program. If not, see . + */ + +package io.mcdev.obfex.formats.tinyv1.lang + +import com.intellij.lang.ASTNode +import com.intellij.lang.Language +import com.intellij.lang.ParserDefinition +import com.intellij.lang.PsiParser +import com.intellij.lexer.Lexer +import com.intellij.openapi.project.Project +import com.intellij.psi.FileViewProvider +import com.intellij.psi.PsiElement +import com.intellij.psi.PsiFile +import com.intellij.psi.tree.IFileElementType +import com.intellij.psi.tree.TokenSet +import io.mcdev.obfex.formats.tinyv1.gen.TinyV1Parser +import io.mcdev.obfex.formats.tinyv1.gen.psi.TinyV1Types +import io.mcdev.obfex.formats.tinyv1.lang.psi.TinyV1LexerAdapter + +class TinyV1ParserDefinition : ParserDefinition { + + override fun createLexer(project: Project?): Lexer = TinyV1LexerAdapter() + override fun getCommentTokens(): TokenSet = COMMENTS + override fun getStringLiteralElements(): TokenSet = TokenSet.EMPTY + override fun createParser(project: Project?): PsiParser = TinyV1Parser() + override fun getFileNodeType(): IFileElementType = FILE + override fun createFile(viewProvider: FileViewProvider): PsiFile = TinyV1File(viewProvider) + override fun createElement(node: ASTNode): PsiElement = TinyV1Types.Factory.createElement(node) +} + +private val COMMENTS = TokenSet.create(TinyV1Types.COMMENT) +private val FILE = IFileElementType(Language.findInstance(TinyV1Language::class.java)) diff --git a/obfuscation-explorer/src/main/kotlin/formats/tinyv1/lang/colors/TinyV1Annotator.kt b/obfuscation-explorer/src/main/kotlin/formats/tinyv1/lang/colors/TinyV1Annotator.kt new file mode 100644 index 000000000..0ad56fbc5 --- /dev/null +++ b/obfuscation-explorer/src/main/kotlin/formats/tinyv1/lang/colors/TinyV1Annotator.kt @@ -0,0 +1,51 @@ +/* + * Minecraft Development for IntelliJ + * + * https://mcdev.io/ + * + * Copyright (C) 2024 minecraft-dev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, version 3.0 only. + * + * 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 Lesser General Public License + * along with this program. If not, see . + */ + +package io.mcdev.obfex.formats.tinyv1.lang.colors + +import com.intellij.lang.annotation.AnnotationHolder +import com.intellij.lang.annotation.Annotator +import com.intellij.psi.PsiElement +import com.intellij.psi.util.childrenOfType +import io.mcdev.obfex.formats.tinyv1.gen.psi.TinyV1ClassMapping +import io.mcdev.obfex.formats.tinyv1.gen.psi.TinyV1FieldMapping +import io.mcdev.obfex.formats.tinyv1.gen.psi.TinyV1MappingPart +import io.mcdev.obfex.formats.tinyv1.gen.psi.TinyV1MethodMapping +import io.mcdev.obfex.formats.util.registerHighlight + +class TinyV1Annotator : Annotator { + + override fun annotate(element: PsiElement, holder: AnnotationHolder) { + val key = when (element) { + is TinyV1ClassMapping -> TinyV1SyntaxHighlighter.CLASS_NAME + is TinyV1MethodMapping -> TinyV1SyntaxHighlighter.METHOD + is TinyV1FieldMapping -> TinyV1SyntaxHighlighter.FIELD + else -> return + } + + for ((i, part) in element.childrenOfType().withIndex()) { + if (i == 0) { + registerHighlight(part, TinyV1SyntaxHighlighter.CLASS_NAME, holder) + } else { + registerHighlight(part, key, holder) + } + } + } +} diff --git a/obfuscation-explorer/src/main/kotlin/formats/tinyv1/lang/colors/TinyV1RainbowVisitor.kt b/obfuscation-explorer/src/main/kotlin/formats/tinyv1/lang/colors/TinyV1RainbowVisitor.kt new file mode 100644 index 000000000..3c92da078 --- /dev/null +++ b/obfuscation-explorer/src/main/kotlin/formats/tinyv1/lang/colors/TinyV1RainbowVisitor.kt @@ -0,0 +1,94 @@ +/* + * Minecraft Development for IntelliJ + * + * https://mcdev.io/ + * + * Copyright (C) 2024 minecraft-dev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, version 3.0 only. + * + * 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 Lesser General Public License + * along with this program. If not, see . + */ + +package io.mcdev.obfex.formats.tinyv1.lang.colors + +import com.intellij.codeInsight.daemon.impl.HighlightVisitor +import com.intellij.psi.PsiElement +import com.intellij.psi.util.findParentOfType +import io.mcdev.obfex.formats.tinyv1.gen.psi.TinyV1ClassMapping +import io.mcdev.obfex.formats.tinyv1.gen.psi.TinyV1FieldMapping +import io.mcdev.obfex.formats.tinyv1.gen.psi.TinyV1FileElement +import io.mcdev.obfex.formats.tinyv1.gen.psi.TinyV1MappingPart +import io.mcdev.obfex.formats.tinyv1.gen.psi.TinyV1MethodMapping +import io.mcdev.obfex.formats.tinyv1.gen.psi.TinyV1MethodSignature +import io.mcdev.obfex.formats.tinyv1.gen.psi.TinyV1Namespace +import io.mcdev.obfex.formats.tinyv1.gen.psi.TinyV1TypeDesc +import io.mcdev.obfex.formats.tinyv1.lang.TinyV1Language +import io.mcdev.obfex.formats.util.NamespaceRainbowVisitor + +class TinyV1RainbowVisitor : NamespaceRainbowVisitor(TinyV1Language) { + + override fun visit(element: PsiElement) { + if (element is TinyV1Namespace) { + highlightNamespaceDecl(element) + return + } + + val file = element.findParentOfType() ?: return + val namespaces = file.header.namespaceList + + if (element is TinyV1MappingPart) { + handleMappingPart(element, namespaces) + } else if (element is TinyV1TypeDesc && element.parent is TinyV1FieldMapping) { + handleTypeSignature(element, namespaces) + } else if (element is TinyV1MethodSignature) { + handleTypeSignature(element, namespaces) + } + } + + private fun handleMappingPart(element: TinyV1MappingPart, namespaces: List) { + var index = indexOf(element) + + if (element.parent is TinyV1ClassMapping) { + if (namespaces.size > index) { + highlightElement(element, index, HighlightType.CLASS) + } + return + } + + // fields and methods + // first part is always class name + if (index == 0 && namespaces.isNotEmpty()) { + highlightElement(element, index, HighlightType.CLASS) + return + } + + index-- + if (namespaces.size <= index) { + return + } + + when (element.parent) { + is TinyV1MethodMapping -> highlightElement(element, index, HighlightType.METHOD) + is TinyV1FieldMapping -> highlightElement(element, index, HighlightType.FIELD) + } + } + + private fun handleTypeSignature(element: PsiElement, namespaces: List) { + if (namespaces.isEmpty()) { + return + } + + highlightTypeSignature(element) + } + + override fun clone(): HighlightVisitor = TinyV1RainbowVisitor() +} diff --git a/obfuscation-explorer/src/main/kotlin/formats/tinyv1/lang/colors/TinyV1SyntaxHighlighter.kt b/obfuscation-explorer/src/main/kotlin/formats/tinyv1/lang/colors/TinyV1SyntaxHighlighter.kt new file mode 100644 index 000000000..53efb8b09 --- /dev/null +++ b/obfuscation-explorer/src/main/kotlin/formats/tinyv1/lang/colors/TinyV1SyntaxHighlighter.kt @@ -0,0 +1,87 @@ +/* + * Minecraft Development for IntelliJ + * + * https://mcdev.io/ + * + * Copyright (C) 2024 minecraft-dev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, version 3.0 only. + * + * 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 Lesser General Public License + * along with this program. If not, see . + */ + +package io.mcdev.obfex.formats.tinyv1.lang.colors + +import com.intellij.lexer.Lexer +import com.intellij.openapi.editor.DefaultLanguageHighlighterColors +import com.intellij.openapi.editor.colors.TextAttributesKey +import com.intellij.openapi.fileTypes.SyntaxHighlighterBase +import com.intellij.psi.tree.IElementType +import io.mcdev.obfex.formats.tinyv1.gen.psi.TinyV1Types +import io.mcdev.obfex.formats.tinyv1.lang.psi.TinyV1LexerAdapter + +class TinyV1SyntaxHighlighter : SyntaxHighlighterBase() { + + override fun getHighlightingLexer(): Lexer = TinyV1LexerAdapter() + + override fun getTokenHighlights(tokenType: IElementType?): Array { + return when (tokenType) { + TinyV1Types.V1_KEY, TinyV1Types.CLASS_KEY, TinyV1Types.FIELD_KEY, TinyV1Types.METHOD_KEY -> KEYWORD_KEYS + TinyV1Types.NAMESPACE_KEY -> NAMESPACE_KEYS + TinyV1Types.CLASS_TYPE -> CLASS_TYPE_KEYS + TinyV1Types.PRIMITIVE -> PRIMITIVE_KEYS + TinyV1Types.COMMENT -> COMMENT_KEYS + else -> TextAttributesKey.EMPTY_ARRAY + } + } + + @Suppress("MemberVisibilityCanBePrivate") + companion object { + val KEYWORD = TextAttributesKey.createTextAttributesKey( + "TINYV1_KEYWORD", + DefaultLanguageHighlighterColors.KEYWORD + ) + val NAMESPACE = TextAttributesKey.createTextAttributesKey( + "TINYV1_NAMESPACE", + DefaultLanguageHighlighterColors.METADATA + ) + val CLASS_NAME = TextAttributesKey.createTextAttributesKey( + "TINYV1_CLASS_NAME", + DefaultLanguageHighlighterColors.STRING + ) + val METHOD = TextAttributesKey.createTextAttributesKey( + "TINYV1_METHOD", + DefaultLanguageHighlighterColors.INSTANCE_METHOD + ) + val FIELD = TextAttributesKey.createTextAttributesKey( + "TINYV1_FIELD", + DefaultLanguageHighlighterColors.INSTANCE_FIELD + ) + val PRIMITIVE = TextAttributesKey.createTextAttributesKey( + "TINYV1_PRIMITIVE", + DefaultLanguageHighlighterColors.NUMBER + ) + val CLASS_TYPE = TextAttributesKey.createTextAttributesKey( + "TINYV1_CLASS_TYPE", + DefaultLanguageHighlighterColors.CLASS_REFERENCE + ) + val COMMENT = TextAttributesKey.createTextAttributesKey( + "TINYV1_COMMENT", + DefaultLanguageHighlighterColors.LINE_COMMENT + ) + + private val KEYWORD_KEYS = arrayOf(KEYWORD) + private val NAMESPACE_KEYS = arrayOf(NAMESPACE) + private val PRIMITIVE_KEYS = arrayOf(PRIMITIVE) + private val CLASS_TYPE_KEYS = arrayOf(CLASS_TYPE) + private val COMMENT_KEYS = arrayOf(COMMENT) + } +} diff --git a/obfuscation-explorer/src/main/kotlin/formats/tinyv1/lang/colors/TinyV1SyntaxHighlighterFactory.kt b/obfuscation-explorer/src/main/kotlin/formats/tinyv1/lang/colors/TinyV1SyntaxHighlighterFactory.kt new file mode 100644 index 000000000..b60e6c0e6 --- /dev/null +++ b/obfuscation-explorer/src/main/kotlin/formats/tinyv1/lang/colors/TinyV1SyntaxHighlighterFactory.kt @@ -0,0 +1,31 @@ +/* + * Minecraft Development for IntelliJ + * + * https://mcdev.io/ + * + * Copyright (C) 2024 minecraft-dev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, version 3.0 only. + * + * 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 Lesser General Public License + * along with this program. If not, see . + */ + +package io.mcdev.obfex.formats.tinyv1.lang.colors + +import com.intellij.openapi.fileTypes.SyntaxHighlighter +import com.intellij.openapi.fileTypes.SyntaxHighlighterFactory +import com.intellij.openapi.project.Project +import com.intellij.openapi.vfs.VirtualFile + +class TinyV1SyntaxHighlighterFactory : SyntaxHighlighterFactory() { + override fun getSyntaxHighlighter(project: Project?, virtualFile: VirtualFile?): SyntaxHighlighter = + TinyV1SyntaxHighlighter() +} diff --git a/obfuscation-explorer/src/main/kotlin/formats/tinyv1/lang/psi/TinyV1ElementType.kt b/obfuscation-explorer/src/main/kotlin/formats/tinyv1/lang/psi/TinyV1ElementType.kt new file mode 100644 index 000000000..b8feef501 --- /dev/null +++ b/obfuscation-explorer/src/main/kotlin/formats/tinyv1/lang/psi/TinyV1ElementType.kt @@ -0,0 +1,27 @@ +/* + * Minecraft Development for IntelliJ + * + * https://mcdev.io/ + * + * Copyright (C) 2024 minecraft-dev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, version 3.0 only. + * + * 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 Lesser General Public License + * along with this program. If not, see . + */ + +package io.mcdev.obfex.formats.tinyv1.lang.psi + +import com.intellij.psi.tree.IElementType +import io.mcdev.obfex.formats.tinyv1.lang.TinyV1Language +import org.jetbrains.annotations.NonNls + +class TinyV1ElementType(@NonNls debugName: String) : IElementType(debugName, TinyV1Language) diff --git a/obfuscation-explorer/src/main/kotlin/formats/tinyv1/lang/psi/TinyV1LexerAdapter.kt b/obfuscation-explorer/src/main/kotlin/formats/tinyv1/lang/psi/TinyV1LexerAdapter.kt new file mode 100644 index 000000000..7fa56b00a --- /dev/null +++ b/obfuscation-explorer/src/main/kotlin/formats/tinyv1/lang/psi/TinyV1LexerAdapter.kt @@ -0,0 +1,26 @@ +/* + * Minecraft Development for IntelliJ + * + * https://mcdev.io/ + * + * Copyright (C) 2024 minecraft-dev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, version 3.0 only. + * + * 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 Lesser General Public License + * along with this program. If not, see . + */ + +package io.mcdev.obfex.formats.tinyv1.lang.psi + +import com.intellij.lexer.FlexAdapter +import io.mcdev.obfex.formats.tinyv1.gen.TinyV1Lexer + +class TinyV1LexerAdapter : FlexAdapter(TinyV1Lexer()) diff --git a/obfuscation-explorer/src/main/kotlin/formats/tinyv1/lang/psi/TinyV1TokenType.kt b/obfuscation-explorer/src/main/kotlin/formats/tinyv1/lang/psi/TinyV1TokenType.kt new file mode 100644 index 000000000..3cc0cbc8e --- /dev/null +++ b/obfuscation-explorer/src/main/kotlin/formats/tinyv1/lang/psi/TinyV1TokenType.kt @@ -0,0 +1,27 @@ +/* + * Minecraft Development for IntelliJ + * + * https://mcdev.io/ + * + * Copyright (C) 2024 minecraft-dev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, version 3.0 only. + * + * 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 Lesser General Public License + * along with this program. If not, see . + */ + +package io.mcdev.obfex.formats.tinyv1.lang.psi + +import com.intellij.psi.tree.IElementType +import io.mcdev.obfex.formats.tinyv1.lang.TinyV1Language +import org.jetbrains.annotations.NonNls + +class TinyV1TokenType(@NonNls debugName: String) : IElementType(debugName, TinyV1Language) diff --git a/obfuscation-explorer/src/main/kotlin/formats/tinyv2/TinyV2LanguageCodeStyleSettingsProvider.kt b/obfuscation-explorer/src/main/kotlin/formats/tinyv2/TinyV2LanguageCodeStyleSettingsProvider.kt new file mode 100644 index 000000000..80747f030 --- /dev/null +++ b/obfuscation-explorer/src/main/kotlin/formats/tinyv2/TinyV2LanguageCodeStyleSettingsProvider.kt @@ -0,0 +1,43 @@ +/* + * Minecraft Development for IntelliJ + * + * https://mcdev.io/ + * + * Copyright (C) 2024 minecraft-dev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, version 3.0 only. + * + * 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 Lesser General Public License + * along with this program. If not, see . + */ + +package io.mcdev.obfex.formats.tinyv2 + +import io.mcdev.obfex.formats.tinyv2.lang.TinyV2Language +import io.mcdev.obfex.formats.util.TabCodeStyleSettingsProvider +import org.intellij.lang.annotations.Language + +class TinyV2LanguageCodeStyleSettingsProvider : TabCodeStyleSettingsProvider() { + override fun getLanguage() = TinyV2Language + override fun getCodeSample(settingsType: SettingsType) = SAMPLE +} + +@Language("TinyV2") +private const val SAMPLE = """ +tiny 2 0 source target target2 +c class_1 class1Ns0Rename class1Ns1Rename + f I field_1 field1Ns0Rename field1Ns1Rename + m ()I method_1 method1Ns0Rename method1Ns1Rename + p 1 param_1 param1Ns0Rename param1Ns1Rename + v 2 2 2 var_1 var1Ns0Rename var1Ns1Rename +c class_1${'$'}class_2 class1Ns0Rename${'$'}class2Ns0Rename class1Ns1Rename${'$'}class2Ns1Rename + f I field_2 field2Ns0Rename field2Ns1Rename +c class_3 class3Ns0Rename class3Ns1Rename +""" diff --git a/obfuscation-explorer/src/main/kotlin/formats/tinyv2/TinyV2MappingsFormatParser.kt b/obfuscation-explorer/src/main/kotlin/formats/tinyv2/TinyV2MappingsFormatParser.kt new file mode 100644 index 000000000..deeb8c98e --- /dev/null +++ b/obfuscation-explorer/src/main/kotlin/formats/tinyv2/TinyV2MappingsFormatParser.kt @@ -0,0 +1,325 @@ +/* + * Minecraft Development for IntelliJ + * + * https://mcdev.io/ + * + * Copyright (C) 2024 minecraft-dev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, version 3.0 only. + * + * 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 Lesser General Public License + * along with this program. If not, see . + */ + +package io.mcdev.obfex.formats.tinyv2 + +import com.intellij.openapi.vfs.VirtualFile +import io.mcdev.obfex.MappingPart +import io.mcdev.obfex.forLinesIndent +import io.mcdev.obfex.formats.util.indicesFrom +import io.mcdev.obfex.mappings.ClassMappingBuilder +import io.mcdev.obfex.mappings.FileCoords +import io.mcdev.obfex.mappings.MappingsDefinition +import io.mcdev.obfex.mappings.MappingsDefinitionBuilder +import io.mcdev.obfex.mappings.MappingsFormatParser +import io.mcdev.obfex.mappings.MethodMappingBuilder +import io.mcdev.obfex.mappings.clazz +import io.mcdev.obfex.mappings.field +import io.mcdev.obfex.mappings.localVar +import io.mcdev.obfex.mappings.method +import io.mcdev.obfex.mappings.ns +import io.mcdev.obfex.mappings.param +import io.mcdev.obfex.ref.LvtIndex +import io.mcdev.obfex.ref.asClass +import io.mcdev.obfex.ref.asField +import io.mcdev.obfex.ref.asLocal +import io.mcdev.obfex.ref.asLocalVar +import io.mcdev.obfex.ref.asLvtIndex +import io.mcdev.obfex.ref.asMethod +import io.mcdev.obfex.ref.asMethodDesc +import io.mcdev.obfex.ref.asParam +import io.mcdev.obfex.ref.asParamIndex +import io.mcdev.obfex.ref.asTypeDef +import io.mcdev.obfex.ref.highPriority +import io.mcdev.obfex.splitMappingLine + +class TinyV2MappingsFormatParser : MappingsFormatParser { + + override val expectedFileExtensions: Array = arrayOf("tiny") + + override fun isSupportedFile(file: VirtualFile): Boolean { + val firstLine = file.inputStream.bufferedReader(file.charset).use { reader -> + reader.readLine() + } + return getNamespaces(firstLine) != null + } + + override fun parse(file: VirtualFile): MappingsDefinition? { + val firstLine = file.inputStream.bufferedReader(file.charset).use { reader -> + reader.readLine() + } + + val namespaces = getNamespaces(firstLine) ?: return null + + val builder = MappingsDefinitionBuilder(TinyV2MappingsFormatType, file, *namespaces) + + var classMapping: ClassMappingBuilder? = null + var methodMapping: MethodMappingBuilder? = null + file.forLinesIndent(skipLines = 1, preserveBlank = true) { indent, lineNum, parts, _ -> + if (parts.isEmpty()) { + return@forLinesIndent true + } + + val key = parts[0].value + when (key) { + "c" -> { + methodMapping = null + classMapping = parseClass(builder, indent, namespaces.size, lineNum, parts) + } + "f" -> { + methodMapping = null + parseField(classMapping, indent, namespaces.size, lineNum, parts) + } + "m" -> methodMapping = parseMethod(classMapping, indent, namespaces.size, lineNum, parts) + "p" -> parseParam(methodMapping, indent, namespaces.size, lineNum, parts) + "v" -> parseLocalVar(methodMapping, indent, namespaces.size, lineNum, parts) + } + + return@forLinesIndent true + } + + return builder.build() + } + + private fun getNamespaces(line: String?): Array? { + if (line == null) { + return null + } + + val header = line.splitMappingLine() + if (header.size < 5) { + return null + } + + if (header[0].value != "tiny" || header[1].value != "2" || header[2].value != "0") { + return null + } + + val namespaces = arrayOfNulls(header.size - 3) + for (index in header.indicesFrom(3)) { + namespaces[index - 3] = header[index].value + } + + @Suppress("UNCHECKED_CAST") + return namespaces as Array + } + + private fun parseClass( + builder: MappingsDefinitionBuilder, + indent: Int, + namespaceCount: Int, + lineNum: Int, + parts: Array + ): ClassMappingBuilder? { + if (indent != 0) { + builder.error("Unexpected indent of class mapping line", FileCoords(lineNum)) + return null + } + + if (parts.size - 1 > namespaceCount) { + builder.error("Unexpected number of names present", FileCoords(lineNum)) + return null + } + + if (parts.size == 1) { + builder.warning("Class mapping has no names", FileCoords(lineNum)) + } + + return builder.clazz(lineNum.highPriority) { + for (i in parts.indicesFrom(1)) { + with(parts[i].asClass().ns(i - 1)) + } + } + } + + private fun parseField( + builder: ClassMappingBuilder?, + indent: Int, + namespaceCount: Int, + lineNum: Int, + parts: Array + ) { + if (builder == null) { + return + } + + if (indent != 1) { + builder.error("Unexpected indent of field mapping line", FileCoords(lineNum)) + return + } + + if (parts.size - 2 > namespaceCount) { + builder.error("Unexpected number of names present", FileCoords(lineNum)) + return + } + + if (parts.size < 2) { + builder.error("Field mapping line does not have all necessary components", FileCoords(lineNum)) + return + } + + if (parts.size == 2) { + builder.warning("Field mapping line has no names", FileCoords(lineNum)) + } + + val fieldType = parts[1].value.asTypeDef() + ?: return builder.error("Field descriptor is invalid", FileCoords(lineNum, parts[1])) + + builder.field(lineNum.highPriority) { + type = fieldType + + for (i in parts.indicesFrom(2)) { + with(parts[i].asField().ns(i - 2)) + } + } + } + + private fun parseMethod( + builder: ClassMappingBuilder?, + indent: Int, + namespaceCount: Int, + lineNum: Int, + parts: Array + ): MethodMappingBuilder? { + if (builder == null) { + return null + } + + if (indent != 1) { + builder.error("Unexpected indent of method mapping line", FileCoords(lineNum)) + return null + } + + if (parts.size - 2 > namespaceCount) { + builder.error("Unexpected number of names present", FileCoords(lineNum)) + return null + } + + if (parts.size < 2) { + builder.error("Method mapping line does not have all necessary components", FileCoords(lineNum)) + return null + } + + if (parts.size == 2) { + builder.warning("Method mapping line has no names", FileCoords(lineNum)) + } + + val methodDesc = parts[1].value.asMethodDesc() + ?: run { + builder.error("Method descriptor is invalid", FileCoords(lineNum, parts[1])) + return null + } + + return builder.method(lineNum.highPriority) { + desc = methodDesc + + for (i in parts.indicesFrom(2)) { + with(parts[i].asMethod().ns(i - 2)) + } + } + } + + private fun parseParam( + builder: MethodMappingBuilder?, + indent: Int, + namespaceCount: Int, + lineNum: Int, + parts: Array + ) { + if (builder == null) { + return + } + + if (indent != 2) { + builder.error("Unexpected indent of method parameter mapping line", FileCoords(lineNum)) + return + } + + if (parts.size - 2 > namespaceCount) { + builder.error("Unexpected number of names present", FileCoords(lineNum)) + return + } + + if (parts.size < 2) { + builder.error("Method parameter mapping line does not have all necessary components", FileCoords(lineNum)) + return + } + + if (parts.size == 2) { + builder.warning("Method parameter mapping line has no names", FileCoords(lineNum)) + } + + val index = parts[1].value.toIntOrNull() + ?: return builder.error("Method parameter index is not an integer", FileCoords(lineNum, parts[1])) + + builder.param(index.asParamIndex(), lineNum.highPriority) { + for (i in parts.indicesFrom(2)) { + with(parts[i].asParam().ns(i - 2)) + } + } + } + + private fun parseLocalVar( + builder: MethodMappingBuilder?, + indent: Int, + namespaceCount: Int, + lineNum: Int, + parts: Array + ) { + if (builder == null) { + return + } + + if (indent != 2) { + builder.error("Unexpected indent of method local var mapping line", FileCoords(lineNum)) + return + } + + if (parts.size < 3) { + builder.error("Method local var mapping line does not have all necessary components", FileCoords(lineNum)) + return + } + + val localIndex = parts[1].value.toIntOrNull()?.asLocal() + ?: return builder.error("Method local var index is not an integer", FileCoords(lineNum, parts[1])) + val localStart = parts[2].value.toIntOrNull()?.asLocal() + ?: return builder.error("Method local var start offset is not an integer", FileCoords(lineNum, parts[2])) + + val lvtIndex = if (parts.size >= 4) { + // lvtIndex is optional + parts[3].value.toIntOrNull()?.asLvtIndex() + } else { + null + } ?: LvtIndex.UNKNOWN + + val nameStartIndex = if (lvtIndex.isKnown) 4 else 3 + + if (parts.size - nameStartIndex > namespaceCount) { + builder.error("Unexpected number of names present", FileCoords(lineNum)) + return + } + + builder.localVar(localIndex.asLocalVar(startIndex = localStart), lvtIndex, lineNum.highPriority) { + for (i in parts.indicesFrom(nameStartIndex)) { + with(parts[i].asLocal().ns(i - nameStartIndex)) + } + } + } +} diff --git a/obfuscation-explorer/src/main/kotlin/formats/tinyv2/TinyV2MappingsFormatType.kt b/obfuscation-explorer/src/main/kotlin/formats/tinyv2/TinyV2MappingsFormatType.kt new file mode 100644 index 000000000..49c93abb8 --- /dev/null +++ b/obfuscation-explorer/src/main/kotlin/formats/tinyv2/TinyV2MappingsFormatType.kt @@ -0,0 +1,31 @@ +/* + * Minecraft Development for IntelliJ + * + * https://mcdev.io/ + * + * Copyright (C) 2024 minecraft-dev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, version 3.0 only. + * + * 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 Lesser General Public License + * along with this program. If not, see . + */ + +package io.mcdev.obfex.formats.tinyv2 + +import io.mcdev.obfex.ObfIcons +import io.mcdev.obfex.formats.MappingsFormatType +import javax.swing.Icon + +object TinyV2MappingsFormatType : MappingsFormatType("tiny-v2") { + + override val icon: Icon = ObfIcons.TINY_V2_ICON + override val name: String = "Tiny v2" +} diff --git a/obfuscation-explorer/src/main/kotlin/formats/tinyv2/lang/TinyV2File.kt b/obfuscation-explorer/src/main/kotlin/formats/tinyv2/lang/TinyV2File.kt new file mode 100644 index 000000000..fde59952f --- /dev/null +++ b/obfuscation-explorer/src/main/kotlin/formats/tinyv2/lang/TinyV2File.kt @@ -0,0 +1,30 @@ +/* + * Minecraft Development for IntelliJ + * + * https://mcdev.io/ + * + * Copyright (C) 2024 minecraft-dev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, version 3.0 only. + * + * 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 Lesser General Public License + * along with this program. If not, see . + */ + +package io.mcdev.obfex.formats.tinyv2.lang + +import com.intellij.extapi.psi.PsiFileBase +import com.intellij.openapi.fileTypes.FileType +import com.intellij.psi.FileViewProvider + +class TinyV2File(viewProvider: FileViewProvider) : PsiFileBase(viewProvider, TinyV2Language) { + override fun getFileType(): FileType = TinyV2FileType + override fun toString(): String = TinyV2FileType.description +} diff --git a/obfuscation-explorer/src/main/kotlin/formats/tinyv2/lang/TinyV2FileType.kt b/obfuscation-explorer/src/main/kotlin/formats/tinyv2/lang/TinyV2FileType.kt new file mode 100644 index 000000000..ad78d1aeb --- /dev/null +++ b/obfuscation-explorer/src/main/kotlin/formats/tinyv2/lang/TinyV2FileType.kt @@ -0,0 +1,33 @@ +/* + * Minecraft Development for IntelliJ + * + * https://mcdev.io/ + * + * Copyright (C) 2024 minecraft-dev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, version 3.0 only. + * + * 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 Lesser General Public License + * along with this program. If not, see . + */ + +package io.mcdev.obfex.formats.tinyv2.lang + +import com.intellij.openapi.fileTypes.LanguageFileType +import io.mcdev.obfex.ObfIcons +import javax.swing.Icon + +object TinyV2FileType : LanguageFileType(TinyV2Language) { + + override fun getName(): String = "Tiny v2" + override fun getDescription(): String = "Tiny v2 obfuscation mapping file" + override fun getDefaultExtension(): String = "tiny" + override fun getIcon(): Icon = ObfIcons.TINY_V2_ICON +} diff --git a/obfuscation-explorer/src/main/kotlin/formats/tinyv2/lang/TinyV2Language.kt b/obfuscation-explorer/src/main/kotlin/formats/tinyv2/lang/TinyV2Language.kt new file mode 100644 index 000000000..b13323c81 --- /dev/null +++ b/obfuscation-explorer/src/main/kotlin/formats/tinyv2/lang/TinyV2Language.kt @@ -0,0 +1,25 @@ +/* + * Minecraft Development for IntelliJ + * + * https://mcdev.io/ + * + * Copyright (C) 2024 minecraft-dev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, version 3.0 only. + * + * 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 Lesser General Public License + * along with this program. If not, see . + */ + +package io.mcdev.obfex.formats.tinyv2.lang + +import com.intellij.lang.Language + +object TinyV2Language : Language("TinyV2") diff --git a/obfuscation-explorer/src/main/kotlin/formats/tinyv2/lang/TinyV2ParserDefinition.kt b/obfuscation-explorer/src/main/kotlin/formats/tinyv2/lang/TinyV2ParserDefinition.kt new file mode 100644 index 000000000..9793d1107 --- /dev/null +++ b/obfuscation-explorer/src/main/kotlin/formats/tinyv2/lang/TinyV2ParserDefinition.kt @@ -0,0 +1,50 @@ +/* + * Minecraft Development for IntelliJ + * + * https://mcdev.io/ + * + * Copyright (C) 2024 minecraft-dev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, version 3.0 only. + * + * 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 Lesser General Public License + * along with this program. If not, see . + */ + +package io.mcdev.obfex.formats.tinyv2.lang + +import com.intellij.lang.ASTNode +import com.intellij.lang.Language +import com.intellij.lang.ParserDefinition +import com.intellij.lang.PsiParser +import com.intellij.lexer.Lexer +import com.intellij.openapi.project.Project +import com.intellij.psi.FileViewProvider +import com.intellij.psi.PsiElement +import com.intellij.psi.PsiFile +import com.intellij.psi.tree.IFileElementType +import com.intellij.psi.tree.TokenSet +import io.mcdev.obfex.formats.tinyv2.gen.TinyV2Parser +import io.mcdev.obfex.formats.tinyv2.gen.psi.TinyV2Types +import io.mcdev.obfex.formats.tinyv2.lang.psi.TinyV2LexerAdapter + +class TinyV2ParserDefinition : ParserDefinition { + + override fun createLexer(project: Project?): Lexer = TinyV2LexerAdapter() + override fun getCommentTokens(): TokenSet = COMMENTS + override fun getStringLiteralElements(): TokenSet = TokenSet.EMPTY + override fun createParser(project: Project?): PsiParser = TinyV2Parser() + override fun getFileNodeType(): IFileElementType = FILE + override fun createFile(viewProvider: FileViewProvider): PsiFile = TinyV2File(viewProvider) + override fun createElement(node: ASTNode?): PsiElement = TinyV2Types.Factory.createElement(node) +} + +private val COMMENTS = TokenSet.create(TinyV2Types.COMMENT) +private val FILE = IFileElementType(Language.findInstance(TinyV2Language::class.java)) diff --git a/obfuscation-explorer/src/main/kotlin/formats/tinyv2/lang/colors/TinyV2Annotator.kt b/obfuscation-explorer/src/main/kotlin/formats/tinyv2/lang/colors/TinyV2Annotator.kt new file mode 100644 index 000000000..47a79629e --- /dev/null +++ b/obfuscation-explorer/src/main/kotlin/formats/tinyv2/lang/colors/TinyV2Annotator.kt @@ -0,0 +1,66 @@ +/* + * Minecraft Development for IntelliJ + * + * https://mcdev.io/ + * + * Copyright (C) 2024 minecraft-dev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, version 3.0 only. + * + * 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 Lesser General Public License + * along with this program. If not, see . + */ + +package io.mcdev.obfex.formats.tinyv2.lang.colors + +import com.intellij.lang.annotation.AnnotationHolder +import com.intellij.lang.annotation.Annotator +import com.intellij.psi.PsiElement +import com.intellij.psi.util.childrenOfType +import io.mcdev.obfex.formats.tinyv2.gen.psi.TinyV2ClassMapping +import io.mcdev.obfex.formats.tinyv2.gen.psi.TinyV2FieldMapping +import io.mcdev.obfex.formats.tinyv2.gen.psi.TinyV2LocalVarIndex +import io.mcdev.obfex.formats.tinyv2.gen.psi.TinyV2LocalVarMapping +import io.mcdev.obfex.formats.tinyv2.gen.psi.TinyV2LocalVarStart +import io.mcdev.obfex.formats.tinyv2.gen.psi.TinyV2LvtIndex +import io.mcdev.obfex.formats.tinyv2.gen.psi.TinyV2MappingPart +import io.mcdev.obfex.formats.tinyv2.gen.psi.TinyV2MethodMapping +import io.mcdev.obfex.formats.tinyv2.gen.psi.TinyV2ParamIndex +import io.mcdev.obfex.formats.tinyv2.gen.psi.TinyV2ParamMapping +import io.mcdev.obfex.formats.util.registerHighlight + +class TinyV2Annotator : Annotator { + + override fun annotate(element: PsiElement, holder: AnnotationHolder) { + when (element) { + is TinyV2ParamIndex -> { + registerHighlight(element, TinyV2SyntaxHighlighter.PARAM_INDEX, holder) + return + } + is TinyV2LocalVarIndex, is TinyV2LocalVarStart, is TinyV2LvtIndex -> { + registerHighlight(element, TinyV2SyntaxHighlighter.LOCAL_VAR_INDEX, holder) + return + } + } + + val key = when (element) { + is TinyV2ClassMapping -> TinyV2SyntaxHighlighter.CLASS_NAME + is TinyV2MethodMapping -> TinyV2SyntaxHighlighter.METHOD + is TinyV2FieldMapping -> TinyV2SyntaxHighlighter.FIELD + is TinyV2ParamMapping -> TinyV2SyntaxHighlighter.PARAM + is TinyV2LocalVarMapping -> TinyV2SyntaxHighlighter.LOCAL_VAR + else -> return + } + + for (part in element.childrenOfType()) { + registerHighlight(part, key, holder) + } + } +} diff --git a/obfuscation-explorer/src/main/kotlin/formats/tinyv2/lang/colors/TinyV2RainbowVisitor.kt b/obfuscation-explorer/src/main/kotlin/formats/tinyv2/lang/colors/TinyV2RainbowVisitor.kt new file mode 100644 index 000000000..0739002fe --- /dev/null +++ b/obfuscation-explorer/src/main/kotlin/formats/tinyv2/lang/colors/TinyV2RainbowVisitor.kt @@ -0,0 +1,75 @@ +/* + * Minecraft Development for IntelliJ + * + * https://mcdev.io/ + * + * Copyright (C) 2024 minecraft-dev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, version 3.0 only. + * + * 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 Lesser General Public License + * along with this program. If not, see . + */ + +package io.mcdev.obfex.formats.tinyv2.lang.colors + +import com.intellij.codeInsight.daemon.impl.HighlightVisitor +import com.intellij.psi.PsiElement +import com.intellij.psi.util.findParentOfType +import io.mcdev.obfex.formats.tinyv2.gen.psi.TinyV2ClassMapping +import io.mcdev.obfex.formats.tinyv2.gen.psi.TinyV2FieldMapping +import io.mcdev.obfex.formats.tinyv2.gen.psi.TinyV2FileElement +import io.mcdev.obfex.formats.tinyv2.gen.psi.TinyV2LocalVarMapping +import io.mcdev.obfex.formats.tinyv2.gen.psi.TinyV2MappingPart +import io.mcdev.obfex.formats.tinyv2.gen.psi.TinyV2MethodMapping +import io.mcdev.obfex.formats.tinyv2.gen.psi.TinyV2MethodSignature +import io.mcdev.obfex.formats.tinyv2.gen.psi.TinyV2Namespace +import io.mcdev.obfex.formats.tinyv2.gen.psi.TinyV2ParamMapping +import io.mcdev.obfex.formats.tinyv2.gen.psi.TinyV2TypeDesc +import io.mcdev.obfex.formats.tinyv2.lang.TinyV2Language +import io.mcdev.obfex.formats.util.NamespaceRainbowVisitor + +class TinyV2RainbowVisitor : NamespaceRainbowVisitor(TinyV2Language) { + + override fun visit(element: PsiElement) { + if (element is TinyV2Namespace) { + highlightNamespaceDecl(element) + return + } + + val file = element.findParentOfType() ?: return + val namespaces = file.header.namespaceList + + if (element is TinyV2MappingPart) { + handleMappingPart(element, namespaces) + } else if (element is TinyV2TypeDesc && element.parent is TinyV2FieldMapping) { + highlightTypeSignature(element) + } else if (element is TinyV2MethodSignature) { + highlightTypeSignature(element) + } + } + + private fun handleMappingPart(element: TinyV2MappingPart, namespaces: List) { + val index = indexOf(element.parent) + if (namespaces.size <= index) { + return + } + + when (element.parent.parent) { + is TinyV2ClassMapping -> highlightElement(element, index, HighlightType.CLASS) + is TinyV2FieldMapping -> highlightElement(element, index, HighlightType.FIELD) + is TinyV2MethodMapping -> highlightElement(element, index, HighlightType.METHOD) + is TinyV2ParamMapping -> highlightElement(element, index, HighlightType.PARAM) + is TinyV2LocalVarMapping -> highlightElement(element, index, HighlightType.LOCAL_VAR) + } + } + + override fun clone(): HighlightVisitor = TinyV2RainbowVisitor() +} diff --git a/obfuscation-explorer/src/main/kotlin/formats/tinyv2/lang/colors/TinyV2SyntaxHighlighter.kt b/obfuscation-explorer/src/main/kotlin/formats/tinyv2/lang/colors/TinyV2SyntaxHighlighter.kt new file mode 100644 index 000000000..97aa4e1ea --- /dev/null +++ b/obfuscation-explorer/src/main/kotlin/formats/tinyv2/lang/colors/TinyV2SyntaxHighlighter.kt @@ -0,0 +1,127 @@ +/* + * Minecraft Development for IntelliJ + * + * https://mcdev.io/ + * + * Copyright (C) 2024 minecraft-dev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, version 3.0 only. + * + * 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 Lesser General Public License + * along with this program. If not, see . + */ + +package io.mcdev.obfex.formats.tinyv2.lang.colors + +import com.intellij.ide.highlighter.custom.CustomHighlighterColors +import com.intellij.lexer.Lexer +import com.intellij.openapi.editor.DefaultLanguageHighlighterColors +import com.intellij.openapi.editor.colors.TextAttributesKey +import com.intellij.openapi.fileTypes.SyntaxHighlighterBase +import com.intellij.psi.tree.IElementType +import io.mcdev.obfex.formats.tinyv2.gen.psi.TinyV2Types +import io.mcdev.obfex.formats.tinyv2.lang.psi.TinyV2LexerAdapter + +class TinyV2SyntaxHighlighter : SyntaxHighlighterBase() { + + override fun getHighlightingLexer(): Lexer = TinyV2LexerAdapter() + + override fun getTokenHighlights(tokenType: IElementType?): Array { + return when (tokenType) { + TinyV2Types.TINY_KEY, TinyV2Types.VERSION_NUM -> KEYWORD_KEYS + + TinyV2Types.CLASS_KEY, TinyV2Types.FIELD_KEY, TinyV2Types.METHOD_KEY, TinyV2Types.PARAM_KEY, + TinyV2Types.VAR_KEY -> KEYWORD_KEYS + + TinyV2Types.COMMENT_KEY -> KEYWORD_KEYS + TinyV2Types.DOC_TEXT -> DOC_COMMENT_KEYS + TinyV2Types.NAMESPACE_KEY -> NAMESPACE_KEYS + TinyV2Types.PROPERTY_KEY -> PROPERTY_KEY_KEYS + TinyV2Types.PROPERTY_VALUE -> PROPERTY_VALUE_KEYS + TinyV2Types.CLASS_TYPE -> CLASS_TYPE_KEYS + TinyV2Types.PRIMITIVE -> PRIMITIVE_KEYS + TinyV2Types.COMMENT -> COMMENT_KEYS + else -> TextAttributesKey.EMPTY_ARRAY + } + } + + @Suppress("MemberVisibilityCanBePrivate") + companion object { + val KEYWORD = TextAttributesKey.createTextAttributesKey( + "TINYV2_KEYWORD", + DefaultLanguageHighlighterColors.KEYWORD + ) + val NAMESPACE = TextAttributesKey.createTextAttributesKey( + "TINYV2_NAMESPACE", + DefaultLanguageHighlighterColors.METADATA + ) + val PROPERTY_KEY = TextAttributesKey.createTextAttributesKey( + "TINYV2_PROPERTY_KEY", + CustomHighlighterColors.CUSTOM_KEYWORD2_ATTRIBUTES + ) + val PROPERTY_VALUE = TextAttributesKey.createTextAttributesKey( + "TINYV2_PROPERTY_VALUE", + CustomHighlighterColors.CUSTOM_KEYWORD3_ATTRIBUTES + ) + val DOC_COMMENT = TextAttributesKey.createTextAttributesKey( + "TINYV2_DOC_COMMENT", + DefaultLanguageHighlighterColors.DOC_COMMENT + ) + val CLASS_NAME = TextAttributesKey.createTextAttributesKey( + "TINYV2_CLASS_NAME", + DefaultLanguageHighlighterColors.STRING + ) + val METHOD = TextAttributesKey.createTextAttributesKey( + "TINYV2_METHOD", + DefaultLanguageHighlighterColors.INSTANCE_METHOD + ) + val FIELD = TextAttributesKey.createTextAttributesKey( + "TINYV2_FIELD", + DefaultLanguageHighlighterColors.INSTANCE_FIELD + ) + val PARAM_INDEX = TextAttributesKey.createTextAttributesKey( + "TINYV2_PARAM_INDEX", + DefaultLanguageHighlighterColors.INLAY_DEFAULT + ) + val PARAM = TextAttributesKey.createTextAttributesKey( + "TINYV2_PARAM", + DefaultLanguageHighlighterColors.PARAMETER + ) + val LOCAL_VAR_INDEX = TextAttributesKey.createTextAttributesKey( + "TINYV2_LOCAL_VAR_INDEX", + DefaultLanguageHighlighterColors.INLAY_DEFAULT + ) + val LOCAL_VAR = TextAttributesKey.createTextAttributesKey( + "TINYV2_LOCAL_VAR", + DefaultLanguageHighlighterColors.PARAMETER + ) + val PRIMITIVE = TextAttributesKey.createTextAttributesKey( + "TINYV2_PRIMITIVE", + DefaultLanguageHighlighterColors.NUMBER + ) + val CLASS_TYPE = TextAttributesKey.createTextAttributesKey( + "TINYV2_CLASS_TYPE", + DefaultLanguageHighlighterColors.CLASS_REFERENCE + ) + val COMMENT = TextAttributesKey.createTextAttributesKey( + "TINYV2_COMMENT", + DefaultLanguageHighlighterColors.LINE_COMMENT + ) + + private val KEYWORD_KEYS = arrayOf(KEYWORD) + private val DOC_COMMENT_KEYS = arrayOf(DOC_COMMENT) + private val NAMESPACE_KEYS = arrayOf(NAMESPACE) + private val PROPERTY_KEY_KEYS = arrayOf(PROPERTY_KEY) + private val PROPERTY_VALUE_KEYS = arrayOf(PROPERTY_VALUE) + private val PRIMITIVE_KEYS = arrayOf(PRIMITIVE) + private val CLASS_TYPE_KEYS = arrayOf(CLASS_TYPE) + private val COMMENT_KEYS = arrayOf(COMMENT) + } +} diff --git a/obfuscation-explorer/src/main/kotlin/formats/tinyv2/lang/colors/TinyV2SyntaxHighlighterFactory.kt b/obfuscation-explorer/src/main/kotlin/formats/tinyv2/lang/colors/TinyV2SyntaxHighlighterFactory.kt new file mode 100644 index 000000000..0b394db55 --- /dev/null +++ b/obfuscation-explorer/src/main/kotlin/formats/tinyv2/lang/colors/TinyV2SyntaxHighlighterFactory.kt @@ -0,0 +1,31 @@ +/* + * Minecraft Development for IntelliJ + * + * https://mcdev.io/ + * + * Copyright (C) 2024 minecraft-dev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, version 3.0 only. + * + * 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 Lesser General Public License + * along with this program. If not, see . + */ + +package io.mcdev.obfex.formats.tinyv2.lang.colors + +import com.intellij.openapi.fileTypes.SyntaxHighlighter +import com.intellij.openapi.fileTypes.SyntaxHighlighterFactory +import com.intellij.openapi.project.Project +import com.intellij.openapi.vfs.VirtualFile + +class TinyV2SyntaxHighlighterFactory : SyntaxHighlighterFactory() { + override fun getSyntaxHighlighter(project: Project?, virtualFile: VirtualFile?): SyntaxHighlighter = + TinyV2SyntaxHighlighter() +} diff --git a/obfuscation-explorer/src/main/kotlin/formats/tinyv2/lang/psi/TinyV2ElementType.kt b/obfuscation-explorer/src/main/kotlin/formats/tinyv2/lang/psi/TinyV2ElementType.kt new file mode 100644 index 000000000..948aab3f6 --- /dev/null +++ b/obfuscation-explorer/src/main/kotlin/formats/tinyv2/lang/psi/TinyV2ElementType.kt @@ -0,0 +1,27 @@ +/* + * Minecraft Development for IntelliJ + * + * https://mcdev.io/ + * + * Copyright (C) 2024 minecraft-dev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, version 3.0 only. + * + * 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 Lesser General Public License + * along with this program. If not, see . + */ + +package io.mcdev.obfex.formats.tinyv2.lang.psi + +import com.intellij.psi.tree.IElementType +import io.mcdev.obfex.formats.tinyv2.lang.TinyV2Language +import org.jetbrains.annotations.NonNls + +class TinyV2ElementType(@NonNls debugName: String) : IElementType(debugName, TinyV2Language) diff --git a/obfuscation-explorer/src/main/kotlin/formats/tinyv2/lang/psi/TinyV2LexerAdapter.kt b/obfuscation-explorer/src/main/kotlin/formats/tinyv2/lang/psi/TinyV2LexerAdapter.kt new file mode 100644 index 000000000..ebad18636 --- /dev/null +++ b/obfuscation-explorer/src/main/kotlin/formats/tinyv2/lang/psi/TinyV2LexerAdapter.kt @@ -0,0 +1,26 @@ +/* + * Minecraft Development for IntelliJ + * + * https://mcdev.io/ + * + * Copyright (C) 2024 minecraft-dev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, version 3.0 only. + * + * 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 Lesser General Public License + * along with this program. If not, see . + */ + +package io.mcdev.obfex.formats.tinyv2.lang.psi + +import com.intellij.lexer.FlexAdapter +import io.mcdev.obfex.formats.tinyv2.gen.TinyV2Lexer + +class TinyV2LexerAdapter : FlexAdapter(TinyV2Lexer()) diff --git a/obfuscation-explorer/src/main/kotlin/formats/tinyv2/lang/psi/TinyV2TokenType.kt b/obfuscation-explorer/src/main/kotlin/formats/tinyv2/lang/psi/TinyV2TokenType.kt new file mode 100644 index 000000000..94a30ab94 --- /dev/null +++ b/obfuscation-explorer/src/main/kotlin/formats/tinyv2/lang/psi/TinyV2TokenType.kt @@ -0,0 +1,27 @@ +/* + * Minecraft Development for IntelliJ + * + * https://mcdev.io/ + * + * Copyright (C) 2024 minecraft-dev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, version 3.0 only. + * + * 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 Lesser General Public License + * along with this program. If not, see . + */ + +package io.mcdev.obfex.formats.tinyv2.lang.psi + +import com.intellij.psi.tree.IElementType +import io.mcdev.obfex.formats.tinyv2.lang.TinyV2Language +import org.jetbrains.annotations.NonNls + +class TinyV2TokenType(@NonNls debugName: String) : IElementType(debugName, TinyV2Language) diff --git a/obfuscation-explorer/src/main/kotlin/formats/tsrg/TSrgLanguageCodeStyleSettingsProvider.kt b/obfuscation-explorer/src/main/kotlin/formats/tsrg/TSrgLanguageCodeStyleSettingsProvider.kt new file mode 100644 index 000000000..353d1df0f --- /dev/null +++ b/obfuscation-explorer/src/main/kotlin/formats/tsrg/TSrgLanguageCodeStyleSettingsProvider.kt @@ -0,0 +1,40 @@ +/* + * Minecraft Development for IntelliJ + * + * https://mcdev.io/ + * + * Copyright (C) 2024 minecraft-dev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, version 3.0 only. + * + * 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 Lesser General Public License + * along with this program. If not, see . + */ + +package io.mcdev.obfex.formats.tsrg + +import io.mcdev.obfex.formats.tsrg.lang.TSrgLanguage +import io.mcdev.obfex.formats.util.TabCodeStyleSettingsProvider +import org.intellij.lang.annotations.Language + +class TSrgLanguageCodeStyleSettingsProvider : TabCodeStyleSettingsProvider() { + override fun getLanguage() = TSrgLanguage + override fun getCodeSample(settingsType: SettingsType) = SAMPLE +} + +@Language("TSRG") +private const val SAMPLE = """ +class_1 class1Ns0Rename + field_1 field1Ns0Rename + method_1 ()I method1Ns0Rename +class_1${'$'}class_2 class1Ns0Rename${'$'}class2Ns0Rename + field_2 field2Ns0Rename +class_3 class3Ns0Rename +""" diff --git a/obfuscation-explorer/src/main/kotlin/formats/tsrg/TSrgMappingsFormatParser.kt b/obfuscation-explorer/src/main/kotlin/formats/tsrg/TSrgMappingsFormatParser.kt new file mode 100644 index 000000000..eca112c29 --- /dev/null +++ b/obfuscation-explorer/src/main/kotlin/formats/tsrg/TSrgMappingsFormatParser.kt @@ -0,0 +1,120 @@ +/* + * Minecraft Development for IntelliJ + * + * https://mcdev.io/ + * + * Copyright (C) 2024 minecraft-dev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, version 3.0 only. + * + * 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 Lesser General Public License + * along with this program. If not, see . + */ + +package io.mcdev.obfex.formats.tsrg + +import com.intellij.openapi.vfs.VirtualFile +import io.mcdev.obfex.MappingPart +import io.mcdev.obfex.forLinesIndent +import io.mcdev.obfex.mappings.ClassMappingBuilder +import io.mcdev.obfex.mappings.FileCoords +import io.mcdev.obfex.mappings.MappingsDefinition +import io.mcdev.obfex.mappings.MappingsDefinitionBuilder +import io.mcdev.obfex.mappings.UnnamedMappingsFormatParser +import io.mcdev.obfex.mappings.clazz +import io.mcdev.obfex.mappings.field +import io.mcdev.obfex.mappings.method +import io.mcdev.obfex.ref.asClass +import io.mcdev.obfex.ref.asField +import io.mcdev.obfex.ref.asMethod +import io.mcdev.obfex.ref.asMethodDesc +import io.mcdev.obfex.ref.highPriority + +@Suppress("DuplicatedCode") +class TSrgMappingsFormatParser : UnnamedMappingsFormatParser { + + override val expectedFileExtensions: Array = arrayOf("tsrg") + + override fun parse(file: VirtualFile): MappingsDefinition { + val builder = MappingsDefinitionBuilder(TSrgMappingsFormatType, file) + + var classMapping: ClassMappingBuilder? = null + file.forLinesIndent { indent, lineNum, parts, _ -> + if (indent > 0) { + val c = classMapping + if (c == null) { + builder.error("Member mapping line with no associated class", FileCoords(lineNum)) + } else { + parseMember(c, lineNum, parts) + } + } else { + classMapping = parseClass(builder, lineNum, parts) + } + + return@forLinesIndent true + } + + return builder.build() + } + + private fun parseClass( + builder: MappingsDefinitionBuilder, + lineNum: Int, + parts: Array, + ): ClassMappingBuilder? { + if (parts.size != 2) { + builder.error("Invalid class line", FileCoords(lineNum)) + return null + } + + val (leftClass, rightClass) = parts + + return builder.clazz(lineNum.highPriority) { + with(leftClass.asClass().from) + with(rightClass.asClass().to) + } + } + + private fun parseMember(builder: ClassMappingBuilder, lineNum: Int, parts: Array) { + when (parts.size) { + 2 -> parseField(builder, lineNum, parts[0], parts[1]) + 3 -> parseMethod(builder, lineNum, parts[0], parts[1], parts[2]) + else -> builder.error("Invalid member line", FileCoords(lineNum)) + } + } + + private fun parseField(builder: ClassMappingBuilder, lineNum: Int, leftName: MappingPart, rightName: MappingPart) { + builder.field(lineNum.highPriority) { + with(leftName.asField().from) + with(rightName.asField().to) + } + } + + private fun parseMethod( + builder: ClassMappingBuilder, + lineNum: Int, + leftName: MappingPart, + leftDesc: MappingPart, + rightName: MappingPart, + ) { + val methodDesc = leftDesc.value.asMethodDesc() + if (methodDesc == null) { + builder.error("Invalid method descriptor", FileCoords(lineNum, leftDesc)) + return + } + + builder.method(lineNum.highPriority) { + desc = methodDesc + + with(leftName.asMethod().from) + with(rightName.asMethod().to) + } + } +} diff --git a/obfuscation-explorer/src/main/kotlin/formats/tsrg/TSrgMappingsFormatType.kt b/obfuscation-explorer/src/main/kotlin/formats/tsrg/TSrgMappingsFormatType.kt new file mode 100644 index 000000000..91e44374e --- /dev/null +++ b/obfuscation-explorer/src/main/kotlin/formats/tsrg/TSrgMappingsFormatType.kt @@ -0,0 +1,31 @@ +/* + * Minecraft Development for IntelliJ + * + * https://mcdev.io/ + * + * Copyright (C) 2024 minecraft-dev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, version 3.0 only. + * + * 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 Lesser General Public License + * along with this program. If not, see . + */ + +package io.mcdev.obfex.formats.tsrg + +import io.mcdev.obfex.ObfIcons +import io.mcdev.obfex.formats.MappingsFormatType +import javax.swing.Icon + +object TSrgMappingsFormatType : MappingsFormatType("TSRG") { + + override val icon: Icon = ObfIcons.TSRG_ICON + override val name: String = "TSRG" +} diff --git a/obfuscation-explorer/src/main/kotlin/formats/tsrg/lang/TSrgFile.kt b/obfuscation-explorer/src/main/kotlin/formats/tsrg/lang/TSrgFile.kt new file mode 100644 index 000000000..715d56f6d --- /dev/null +++ b/obfuscation-explorer/src/main/kotlin/formats/tsrg/lang/TSrgFile.kt @@ -0,0 +1,30 @@ +/* + * Minecraft Development for IntelliJ + * + * https://mcdev.io/ + * + * Copyright (C) 2024 minecraft-dev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, version 3.0 only. + * + * 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 Lesser General Public License + * along with this program. If not, see . + */ + +package io.mcdev.obfex.formats.tsrg.lang + +import com.intellij.extapi.psi.PsiFileBase +import com.intellij.openapi.fileTypes.FileType +import com.intellij.psi.FileViewProvider + +class TSrgFile(viewProvider: FileViewProvider) : PsiFileBase(viewProvider, TSrgLanguage) { + override fun getFileType(): FileType = TSrgFileType + override fun toString(): String = TSrgFileType.description +} diff --git a/obfuscation-explorer/src/main/kotlin/formats/tsrg/lang/TSrgFileType.kt b/obfuscation-explorer/src/main/kotlin/formats/tsrg/lang/TSrgFileType.kt new file mode 100644 index 000000000..1c98cb5fb --- /dev/null +++ b/obfuscation-explorer/src/main/kotlin/formats/tsrg/lang/TSrgFileType.kt @@ -0,0 +1,33 @@ +/* + * Minecraft Development for IntelliJ + * + * https://mcdev.io/ + * + * Copyright (C) 2024 minecraft-dev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, version 3.0 only. + * + * 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 Lesser General Public License + * along with this program. If not, see . + */ + +package io.mcdev.obfex.formats.tsrg.lang + +import com.intellij.openapi.fileTypes.LanguageFileType +import io.mcdev.obfex.ObfIcons +import javax.swing.Icon + +object TSrgFileType : LanguageFileType(TSrgLanguage) { + + override fun getName(): String = "TSRG" + override fun getDescription(): String = "TSRG obfuscation mapping file" + override fun getDefaultExtension(): String = "tsrg" + override fun getIcon(): Icon = ObfIcons.TSRG_ICON +} diff --git a/obfuscation-explorer/src/main/kotlin/formats/tsrg/lang/TSrgFileTypeDetector.kt b/obfuscation-explorer/src/main/kotlin/formats/tsrg/lang/TSrgFileTypeDetector.kt new file mode 100644 index 000000000..833f1bd13 --- /dev/null +++ b/obfuscation-explorer/src/main/kotlin/formats/tsrg/lang/TSrgFileTypeDetector.kt @@ -0,0 +1,45 @@ +/* + * Minecraft Development for IntelliJ + * + * https://mcdev.io/ + * + * Copyright (C) 2024 minecraft-dev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, version 3.0 only. + * + * 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 Lesser General Public License + * along with this program. If not, see . + */ + +package io.mcdev.obfex.formats.tsrg.lang + +import com.intellij.openapi.fileTypes.FileType +import com.intellij.openapi.fileTypes.FileTypeRegistry +import com.intellij.openapi.util.io.ByteSequence +import com.intellij.openapi.vfs.VirtualFile +import io.mcdev.obfex.formats.tsrg2.lang.TSrg2FileType + +class TSrgFileTypeDetector : FileTypeRegistry.FileTypeDetector { + + override fun detect(file: VirtualFile, firstBytes: ByteSequence, firstCharsIfText: CharSequence?): FileType? { + if (firstCharsIfText == null) { + return null + } + if (!file.name.endsWith(".tsrg")) { + return null + } + + return if (firstCharsIfText.startsWith("tsrg2")) { + TSrg2FileType + } else { + TSrgFileType + } + } +} diff --git a/obfuscation-explorer/src/main/kotlin/formats/tsrg/lang/TSrgLanguage.kt b/obfuscation-explorer/src/main/kotlin/formats/tsrg/lang/TSrgLanguage.kt new file mode 100644 index 000000000..cdcc43184 --- /dev/null +++ b/obfuscation-explorer/src/main/kotlin/formats/tsrg/lang/TSrgLanguage.kt @@ -0,0 +1,25 @@ +/* + * Minecraft Development for IntelliJ + * + * https://mcdev.io/ + * + * Copyright (C) 2024 minecraft-dev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, version 3.0 only. + * + * 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 Lesser General Public License + * along with this program. If not, see . + */ + +package io.mcdev.obfex.formats.tsrg.lang + +import com.intellij.lang.Language + +object TSrgLanguage : Language("TSRG") diff --git a/obfuscation-explorer/src/main/kotlin/formats/tsrg/lang/TSrgParserDefinition.kt b/obfuscation-explorer/src/main/kotlin/formats/tsrg/lang/TSrgParserDefinition.kt new file mode 100644 index 000000000..ed27b2239 --- /dev/null +++ b/obfuscation-explorer/src/main/kotlin/formats/tsrg/lang/TSrgParserDefinition.kt @@ -0,0 +1,50 @@ +/* + * Minecraft Development for IntelliJ + * + * https://mcdev.io/ + * + * Copyright (C) 2024 minecraft-dev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, version 3.0 only. + * + * 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 Lesser General Public License + * along with this program. If not, see . + */ + +package io.mcdev.obfex.formats.tsrg.lang + +import com.intellij.lang.ASTNode +import com.intellij.lang.Language +import com.intellij.lang.ParserDefinition +import com.intellij.lang.PsiParser +import com.intellij.lexer.Lexer +import com.intellij.openapi.project.Project +import com.intellij.psi.FileViewProvider +import com.intellij.psi.PsiElement +import com.intellij.psi.PsiFile +import com.intellij.psi.tree.IFileElementType +import com.intellij.psi.tree.TokenSet +import io.mcdev.obfex.formats.tsrg.gen.TSrgParser +import io.mcdev.obfex.formats.tsrg.gen.psi.TSrgTypes +import io.mcdev.obfex.formats.tsrg.lang.psi.TSrgLexerAdapter + +class TSrgParserDefinition : ParserDefinition { + + override fun createLexer(project: Project): Lexer = TSrgLexerAdapter() + override fun getCommentTokens(): TokenSet = COMMENTS + override fun getStringLiteralElements(): TokenSet = TokenSet.EMPTY + override fun createParser(project: Project): PsiParser = TSrgParser() + override fun getFileNodeType(): IFileElementType = FILE + override fun createFile(viewProvider: FileViewProvider): PsiFile = TSrgFile(viewProvider) + override fun createElement(node: ASTNode): PsiElement = TSrgTypes.Factory.createElement(node) +} + +private val COMMENTS = TokenSet.create(TSrgTypes.COMMENT) +private val FILE = IFileElementType(Language.findInstance(TSrgLanguage::class.java)) diff --git a/obfuscation-explorer/src/main/kotlin/formats/tsrg/lang/colors/TSrgAnnotator.kt b/obfuscation-explorer/src/main/kotlin/formats/tsrg/lang/colors/TSrgAnnotator.kt new file mode 100644 index 000000000..efca82b58 --- /dev/null +++ b/obfuscation-explorer/src/main/kotlin/formats/tsrg/lang/colors/TSrgAnnotator.kt @@ -0,0 +1,50 @@ +/* + * Minecraft Development for IntelliJ + * + * https://mcdev.io/ + * + * Copyright (C) 2024 minecraft-dev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, version 3.0 only. + * + * 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 Lesser General Public License + * along with this program. If not, see . + */ + +package io.mcdev.obfex.formats.tsrg.lang.colors + +import com.intellij.lang.annotation.AnnotationHolder +import com.intellij.lang.annotation.Annotator +import com.intellij.psi.PsiElement +import io.mcdev.obfex.formats.tsrg.gen.psi.TSrgDeobfClassMappingPart +import io.mcdev.obfex.formats.tsrg.gen.psi.TSrgDeobfFieldMappingPart +import io.mcdev.obfex.formats.tsrg.gen.psi.TSrgDeobfMethodMappingPart +import io.mcdev.obfex.formats.tsrg.gen.psi.TSrgObfClassMappingPart +import io.mcdev.obfex.formats.tsrg.gen.psi.TSrgObfFieldMappingPart +import io.mcdev.obfex.formats.tsrg.gen.psi.TSrgObfMethodMappingPart +import io.mcdev.obfex.formats.util.registerHighlight + +class TSrgAnnotator : Annotator { + override fun annotate(element: PsiElement, holder: AnnotationHolder) { + val key = when (element) { + is TSrgObfClassMappingPart, is TSrgDeobfClassMappingPart -> TSrgSyntaxHighlighter.CLASS_NAME + is TSrgObfMethodMappingPart, is TSrgDeobfMethodMappingPart -> TSrgSyntaxHighlighter.METHOD + is TSrgObfFieldMappingPart, is TSrgDeobfFieldMappingPart -> TSrgSyntaxHighlighter.FIELD + else -> return + } + + val highlight = when (element) { + is TSrgDeobfClassMappingPart, is TSrgDeobfMethodMappingPart, is TSrgDeobfFieldMappingPart -> true + else -> false + } + + registerHighlight(element, key, holder, highlight) + } +} diff --git a/obfuscation-explorer/src/main/kotlin/formats/tsrg/lang/colors/TSrgSyntaxHighlighter.kt b/obfuscation-explorer/src/main/kotlin/formats/tsrg/lang/colors/TSrgSyntaxHighlighter.kt new file mode 100644 index 000000000..ffd0a711a --- /dev/null +++ b/obfuscation-explorer/src/main/kotlin/formats/tsrg/lang/colors/TSrgSyntaxHighlighter.kt @@ -0,0 +1,71 @@ +/* + * Minecraft Development for IntelliJ + * + * https://mcdev.io/ + * + * Copyright (C) 2024 minecraft-dev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, version 3.0 only. + * + * 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 Lesser General Public License + * along with this program. If not, see . + */ + +package io.mcdev.obfex.formats.tsrg.lang.colors + +import com.intellij.openapi.editor.DefaultLanguageHighlighterColors +import com.intellij.openapi.editor.colors.TextAttributesKey +import com.intellij.openapi.fileTypes.SyntaxHighlighterBase +import com.intellij.psi.tree.IElementType +import io.mcdev.obfex.formats.tsrg.gen.psi.TSrgTypes +import io.mcdev.obfex.formats.tsrg.lang.psi.TSrgLexerAdapter + +class TSrgSyntaxHighlighter : SyntaxHighlighterBase() { + override fun getHighlightingLexer() = TSrgLexerAdapter() + + override fun getTokenHighlights(tokenType: IElementType?): Array = + when (tokenType) { + TSrgTypes.CLASS_TYPE -> CLASS_TYPE_KEYS + TSrgTypes.PRIMITIVE -> PRIMITIVE_KEYS + TSrgTypes.COMMENT -> COMMENT_KEYS + else -> TextAttributesKey.EMPTY_ARRAY + } + + companion object { + val CLASS_NAME = TextAttributesKey.createTextAttributesKey( + "TSRG_CLASS_NAME", + DefaultLanguageHighlighterColors.STRING + ) + val METHOD = TextAttributesKey.createTextAttributesKey( + "TSRG_METHOD", + DefaultLanguageHighlighterColors.INSTANCE_METHOD + ) + val FIELD = TextAttributesKey.createTextAttributesKey( + "TSRG_FIELD", + DefaultLanguageHighlighterColors.INSTANCE_FIELD + ) + val PRIMITIVE = TextAttributesKey.createTextAttributesKey( + "TSRG_PRIMITIVE", + DefaultLanguageHighlighterColors.NUMBER + ) + val CLASS_TYPE = TextAttributesKey.createTextAttributesKey( + "TSRG_CLASS_TYPE", + DefaultLanguageHighlighterColors.CLASS_REFERENCE + ) + val COMMENT = TextAttributesKey.createTextAttributesKey( + "TSRG_COMMENT", + DefaultLanguageHighlighterColors.LINE_COMMENT + ) + + private val PRIMITIVE_KEYS = arrayOf(PRIMITIVE) + private val CLASS_TYPE_KEYS = arrayOf(CLASS_TYPE) + private val COMMENT_KEYS = arrayOf(COMMENT) + } +} diff --git a/obfuscation-explorer/src/main/kotlin/formats/tsrg/lang/colors/TSrgSyntaxHighlighterFactory.kt b/obfuscation-explorer/src/main/kotlin/formats/tsrg/lang/colors/TSrgSyntaxHighlighterFactory.kt new file mode 100644 index 000000000..ca4be21ac --- /dev/null +++ b/obfuscation-explorer/src/main/kotlin/formats/tsrg/lang/colors/TSrgSyntaxHighlighterFactory.kt @@ -0,0 +1,31 @@ +/* + * Minecraft Development for IntelliJ + * + * https://mcdev.io/ + * + * Copyright (C) 2024 minecraft-dev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, version 3.0 only. + * + * 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 Lesser General Public License + * along with this program. If not, see . + */ + +package io.mcdev.obfex.formats.tsrg.lang.colors + +import com.intellij.openapi.fileTypes.SyntaxHighlighter +import com.intellij.openapi.fileTypes.SyntaxHighlighterFactory +import com.intellij.openapi.project.Project +import com.intellij.openapi.vfs.VirtualFile + +class TSrgSyntaxHighlighterFactory : SyntaxHighlighterFactory() { + override fun getSyntaxHighlighter(project: Project?, virtualFile: VirtualFile?): SyntaxHighlighter = + TSrgSyntaxHighlighter() +} diff --git a/obfuscation-explorer/src/main/kotlin/formats/tsrg/lang/psi/TSrgElementType.kt b/obfuscation-explorer/src/main/kotlin/formats/tsrg/lang/psi/TSrgElementType.kt new file mode 100644 index 000000000..d30f81466 --- /dev/null +++ b/obfuscation-explorer/src/main/kotlin/formats/tsrg/lang/psi/TSrgElementType.kt @@ -0,0 +1,27 @@ +/* + * Minecraft Development for IntelliJ + * + * https://mcdev.io/ + * + * Copyright (C) 2024 minecraft-dev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, version 3.0 only. + * + * 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 Lesser General Public License + * along with this program. If not, see . + */ + +package io.mcdev.obfex.formats.tsrg.lang.psi + +import com.intellij.psi.tree.IElementType +import io.mcdev.obfex.formats.tsrg.lang.TSrgLanguage +import org.jetbrains.annotations.NonNls + +class TSrgElementType(@NonNls debugName: String) : IElementType(debugName, TSrgLanguage) diff --git a/obfuscation-explorer/src/main/kotlin/formats/tsrg/lang/psi/TSrgLexerAdapter.kt b/obfuscation-explorer/src/main/kotlin/formats/tsrg/lang/psi/TSrgLexerAdapter.kt new file mode 100644 index 000000000..9cbff8905 --- /dev/null +++ b/obfuscation-explorer/src/main/kotlin/formats/tsrg/lang/psi/TSrgLexerAdapter.kt @@ -0,0 +1,26 @@ +/* + * Minecraft Development for IntelliJ + * + * https://mcdev.io/ + * + * Copyright (C) 2024 minecraft-dev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, version 3.0 only. + * + * 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 Lesser General Public License + * along with this program. If not, see . + */ + +package io.mcdev.obfex.formats.tsrg.lang.psi + +import com.intellij.lexer.FlexAdapter +import io.mcdev.obfex.formats.tsrg.gen.TSrgLexer + +class TSrgLexerAdapter : FlexAdapter(TSrgLexer()) diff --git a/obfuscation-explorer/src/main/kotlin/formats/tsrg/lang/psi/TSrgTokenType.kt b/obfuscation-explorer/src/main/kotlin/formats/tsrg/lang/psi/TSrgTokenType.kt new file mode 100644 index 000000000..3a88a017a --- /dev/null +++ b/obfuscation-explorer/src/main/kotlin/formats/tsrg/lang/psi/TSrgTokenType.kt @@ -0,0 +1,27 @@ +/* + * Minecraft Development for IntelliJ + * + * https://mcdev.io/ + * + * Copyright (C) 2024 minecraft-dev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, version 3.0 only. + * + * 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 Lesser General Public License + * along with this program. If not, see . + */ + +package io.mcdev.obfex.formats.tsrg.lang.psi + +import com.intellij.psi.tree.IElementType +import io.mcdev.obfex.formats.tsrg.lang.TSrgLanguage +import org.jetbrains.annotations.NonNls + +class TSrgTokenType(@NonNls debugName: String) : IElementType(debugName, TSrgLanguage) diff --git a/obfuscation-explorer/src/main/kotlin/formats/tsrg2/TSrg2LanguageCodeStyleSettingsProvider.kt b/obfuscation-explorer/src/main/kotlin/formats/tsrg2/TSrg2LanguageCodeStyleSettingsProvider.kt new file mode 100644 index 000000000..6ef69e11a --- /dev/null +++ b/obfuscation-explorer/src/main/kotlin/formats/tsrg2/TSrg2LanguageCodeStyleSettingsProvider.kt @@ -0,0 +1,44 @@ +/* + * Minecraft Development for IntelliJ + * + * https://mcdev.io/ + * + * Copyright (C) 2024 minecraft-dev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, version 3.0 only. + * + * 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 Lesser General Public License + * along with this program. If not, see . + */ + +package io.mcdev.obfex.formats.tsrg2 + +import io.mcdev.obfex.formats.tsrg2.lang.TSrg2Language +import io.mcdev.obfex.formats.util.TabCodeStyleSettingsProvider +import org.intellij.lang.annotations.Language + +class TSrg2LanguageCodeStyleSettingsProvider : TabCodeStyleSettingsProvider() { + override fun getLanguage() = TSrg2Language + override fun getCodeSample(settingsType: SettingsType): String? { + TODO("Not yet implemented") + } +} + +@Language("TSRGv2") +private const val SAMPLE = """ +tsrg2 source target target2 +class_1 class1Ns0Rename class1Ns1Rename + field_1 I field1Ns0Rename field1Ns1Rename + method_1 ()I method1Ns0Rename method1Ns1Rename + 1 param_1 param1Ns0Rename param1Ns1Rename +class_1${'$'}class_2 class1Ns0Rename$${'$'}class2Ns0Rename class1Ns1Rename$${'$'}class2Ns1Rename + field_2 I field2Ns0Rename field2Ns1Rename +class_3 class3Ns0Rename class3Ns1Rename +""" diff --git a/obfuscation-explorer/src/main/kotlin/formats/tsrg2/TSrg2MappingsFormatParser.kt b/obfuscation-explorer/src/main/kotlin/formats/tsrg2/TSrg2MappingsFormatParser.kt new file mode 100644 index 000000000..9e38da4cd --- /dev/null +++ b/obfuscation-explorer/src/main/kotlin/formats/tsrg2/TSrg2MappingsFormatParser.kt @@ -0,0 +1,231 @@ +/* + * Minecraft Development for IntelliJ + * + * https://mcdev.io/ + * + * Copyright (C) 2024 minecraft-dev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, version 3.0 only. + * + * 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 Lesser General Public License + * along with this program. If not, see . + */ + +package io.mcdev.obfex.formats.tsrg2 + +import com.intellij.openapi.vfs.VirtualFile +import io.mcdev.obfex.MappingPart +import io.mcdev.obfex.Tristate +import io.mcdev.obfex.forLinesIndent +import io.mcdev.obfex.formats.util.indicesFrom +import io.mcdev.obfex.mappings.ClassMappingBuilder +import io.mcdev.obfex.mappings.FileCoords +import io.mcdev.obfex.mappings.MappingsDefinition +import io.mcdev.obfex.mappings.MappingsDefinitionBuilder +import io.mcdev.obfex.mappings.MappingsFormatParser +import io.mcdev.obfex.mappings.MethodMappingBuilder +import io.mcdev.obfex.mappings.clazz +import io.mcdev.obfex.mappings.field +import io.mcdev.obfex.mappings.method +import io.mcdev.obfex.mappings.ns +import io.mcdev.obfex.mappings.param +import io.mcdev.obfex.ref.MethodDescriptor +import io.mcdev.obfex.ref.asClass +import io.mcdev.obfex.ref.asField +import io.mcdev.obfex.ref.asMethod +import io.mcdev.obfex.ref.asMethodDesc +import io.mcdev.obfex.ref.asParam +import io.mcdev.obfex.ref.asParamIndex +import io.mcdev.obfex.ref.highPriority +import io.mcdev.obfex.splitMappingLine + +class TSrg2MappingsFormatParser : MappingsFormatParser { + + override val expectedFileExtensions: Array = arrayOf("tsrg") + + override fun isSupportedFile(file: VirtualFile): Boolean { + val firstLine = file.inputStream.bufferedReader(file.charset).use { reader -> + reader.readLine() + } + return getNamespaces(firstLine) != null + } + + override fun parse(file: VirtualFile): MappingsDefinition? { + val firstLine = file.inputStream.bufferedReader(file.charset).use { reader -> + reader.readLine() + } + val namespaces = getNamespaces(firstLine) ?: return null + val namespaceCount = namespaces.size + + val builder = MappingsDefinitionBuilder(TSrg2MappingsFormatType, file, *namespaces) + + var classMapping: ClassMappingBuilder? = null + var methodMapping: MethodMappingBuilder? = null + file.forLinesIndent(skipLines = 1, preserveBlank = true) { indent, lineNum, parts, _ -> + when (indent) { + 0 -> { + methodMapping = null + classMapping = parseClass(builder, namespaceCount, lineNum, parts) + } + 1 -> { + methodMapping = parseClassMember(classMapping, namespaceCount, lineNum, parts) + } + 2 -> parseMethodMember(methodMapping, namespaceCount, lineNum, parts) + } + + return@forLinesIndent true + } + + return builder.build() + } + + private fun parseClass( + builder: MappingsDefinitionBuilder, + namespaceCount: Int, + lineNum: Int, + parts: Array + ): ClassMappingBuilder? { + if (parts.size > namespaceCount) { + builder.error("Unexpected number of names present", FileCoords(lineNum)) + return null + } + + return builder.clazz(lineNum.highPriority) { + for (i in parts.indices) { + with(parts[i].asClass().ns(i)) + } + } + } + + private fun parseClassMember( + builder: ClassMappingBuilder?, + namespaceCount: Int, + lineNum: Int, + parts: Array + ): MethodMappingBuilder? { + val desc = parts.getOrNull(1)?.value?.asMethodDesc() + if (desc != null) { + return parseMethod(builder, namespaceCount, lineNum, parts, desc) + } else { + parseField(builder, namespaceCount, lineNum, parts) + return null + } + } + + private fun parseField( + builder: ClassMappingBuilder?, + namespaceCount: Int, + lineNum: Int, + parts: Array + ) { + if (builder == null) { + return + } + + if (parts.size > namespaceCount) { + builder.error("Unexpected number of names present", FileCoords(lineNum)) + return + } + + builder.field(lineNum.highPriority) { + for (i in parts.indices) { + with(parts[i].asField().ns(i)) + } + } + } + + private fun parseMethod( + builder: ClassMappingBuilder?, + namespaceCount: Int, + lineNum: Int, + parts: Array, + methodDesc: MethodDescriptor, + ): MethodMappingBuilder? { + if (builder == null) { + return null + } + + if (parts.size - 1 > namespaceCount) { + builder.error("Unexpected number of names present", FileCoords(lineNum)) + return null + } + + return builder.method(lineNum.highPriority) { + desc = methodDesc + // We default static to false, it will be set to true later if we see the static keyword + meta = meta.copy(isStatic = Tristate.FALSE) + + with(parts[0].asMethod().ns(0)) + + for (i in parts.indicesFrom(2)) { + with(parts[i].asMethod().ns(i - 1)) + } + } + } + + private fun parseMethodMember( + builder: MethodMappingBuilder?, + namespaceCount: Int, + lineNum: Int, + parts: Array + ) { + if (builder == null) { + return + } + + if (parts.size == 1 && parts[0].value == "static") { + builder.meta = builder.meta.copy(isStatic = Tristate.TRUE) + return + } + + if (parts.size - 1 > namespaceCount) { + builder.error("Unexpected number of names present", FileCoords(lineNum)) + return + } + + if (parts.isEmpty()) { + return + } + val index = parts[0].value.toIntOrNull() + if (index == null) { + builder.error("Method parameter index is not an integer", FileCoords(lineNum, parts[0])) + return + } + + builder.param(index.asParamIndex(), lineNum.highPriority) { + for (i in parts.indicesFrom(1)) { + with(parts[i].asParam().ns(i - 1)) + } + } + } + + private fun getNamespaces(line: String?): Array? { + if (line == null) { + return null + } + + val header = line.splitMappingLine() + if (header.size < 3) { + return null + } + + if (header[0].value != "tsrg2") { + return null + } + + val namespaces = arrayOfNulls(header.size - 1) + for (index in header.indicesFrom(1)) { + namespaces[index - 1] = header[index].value + } + + @Suppress("UNCHECKED_CAST") + return namespaces as Array + } +} diff --git a/obfuscation-explorer/src/main/kotlin/formats/tsrg2/TSrg2MappingsFormatType.kt b/obfuscation-explorer/src/main/kotlin/formats/tsrg2/TSrg2MappingsFormatType.kt new file mode 100644 index 000000000..3b86ed5db --- /dev/null +++ b/obfuscation-explorer/src/main/kotlin/formats/tsrg2/TSrg2MappingsFormatType.kt @@ -0,0 +1,31 @@ +/* + * Minecraft Development for IntelliJ + * + * https://mcdev.io/ + * + * Copyright (C) 2024 minecraft-dev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, version 3.0 only. + * + * 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 Lesser General Public License + * along with this program. If not, see . + */ + +package io.mcdev.obfex.formats.tsrg2 + +import io.mcdev.obfex.ObfIcons +import io.mcdev.obfex.formats.MappingsFormatType +import javax.swing.Icon + +object TSrg2MappingsFormatType : MappingsFormatType("TSRG2") { + + override val icon: Icon = ObfIcons.TSRG2_ICON + override val name: String = "TSRG v2" +} diff --git a/obfuscation-explorer/src/main/kotlin/formats/tsrg2/lang/TSrg2File.kt b/obfuscation-explorer/src/main/kotlin/formats/tsrg2/lang/TSrg2File.kt new file mode 100644 index 000000000..fb4cf7d3c --- /dev/null +++ b/obfuscation-explorer/src/main/kotlin/formats/tsrg2/lang/TSrg2File.kt @@ -0,0 +1,30 @@ +/* + * Minecraft Development for IntelliJ + * + * https://mcdev.io/ + * + * Copyright (C) 2024 minecraft-dev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, version 3.0 only. + * + * 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 Lesser General Public License + * along with this program. If not, see . + */ + +package io.mcdev.obfex.formats.tsrg2.lang + +import com.intellij.extapi.psi.PsiFileBase +import com.intellij.openapi.fileTypes.FileType +import com.intellij.psi.FileViewProvider + +class TSrg2File(viewProvider: FileViewProvider) : PsiFileBase(viewProvider, TSrg2Language) { + override fun getFileType(): FileType = TSrg2FileType + override fun toString(): String = TSrg2FileType.description +} diff --git a/obfuscation-explorer/src/main/kotlin/formats/tsrg2/lang/TSrg2FileType.kt b/obfuscation-explorer/src/main/kotlin/formats/tsrg2/lang/TSrg2FileType.kt new file mode 100644 index 000000000..116dd6920 --- /dev/null +++ b/obfuscation-explorer/src/main/kotlin/formats/tsrg2/lang/TSrg2FileType.kt @@ -0,0 +1,33 @@ +/* + * Minecraft Development for IntelliJ + * + * https://mcdev.io/ + * + * Copyright (C) 2024 minecraft-dev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, version 3.0 only. + * + * 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 Lesser General Public License + * along with this program. If not, see . + */ + +package io.mcdev.obfex.formats.tsrg2.lang + +import com.intellij.openapi.fileTypes.LanguageFileType +import io.mcdev.obfex.ObfIcons +import javax.swing.Icon + +object TSrg2FileType : LanguageFileType(TSrg2Language) { + + override fun getName(): String = "TSRGv2" + override fun getDescription(): String = "TSRGv2 obfuscation mapping file" + override fun getDefaultExtension(): String = "tsrg" + override fun getIcon(): Icon = ObfIcons.TSRG2_ICON +} diff --git a/obfuscation-explorer/src/main/kotlin/formats/tsrg2/lang/TSrg2Language.kt b/obfuscation-explorer/src/main/kotlin/formats/tsrg2/lang/TSrg2Language.kt new file mode 100644 index 000000000..2f97555d5 --- /dev/null +++ b/obfuscation-explorer/src/main/kotlin/formats/tsrg2/lang/TSrg2Language.kt @@ -0,0 +1,25 @@ +/* + * Minecraft Development for IntelliJ + * + * https://mcdev.io/ + * + * Copyright (C) 2024 minecraft-dev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, version 3.0 only. + * + * 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 Lesser General Public License + * along with this program. If not, see . + */ + +package io.mcdev.obfex.formats.tsrg2.lang + +import com.intellij.lang.Language + +object TSrg2Language : Language("TSRGv2") diff --git a/obfuscation-explorer/src/main/kotlin/formats/tsrg2/lang/TSrg2LayoutLexer.kt b/obfuscation-explorer/src/main/kotlin/formats/tsrg2/lang/TSrg2LayoutLexer.kt new file mode 100644 index 000000000..f053f17b9 --- /dev/null +++ b/obfuscation-explorer/src/main/kotlin/formats/tsrg2/lang/TSrg2LayoutLexer.kt @@ -0,0 +1,35 @@ +/* + * Minecraft Development for IntelliJ + * + * https://mcdev.io/ + * + * Copyright (C) 2024 minecraft-dev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, version 3.0 only. + * + * 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 Lesser General Public License + * along with this program. If not, see . + */ + +package io.mcdev.obfex.formats.tsrg2.lang + +import com.intellij.psi.tree.IElementType +import com.intellij.psi.tree.TokenSet +import io.mcdev.obfex.formats.tsrg2.gen.psi.TSrg2Types +import io.mcdev.obfex.formats.tsrg2.lang.psi.TSrg2LexerAdapter +import io.mcdev.obfex.formats.util.sigws.SignificantWhitespaceLexer + +class TSrg2LayoutLexer : SignificantWhitespaceLexer(TSrg2LexerAdapter()) { + + override val newlineTokens: TokenSet = TokenSet.create(TSrg2Types.CRLF) + override val tabTokens: TokenSet = TokenSet.create(TSrg2Types.TAB) + override val virtualOpenToken: IElementType = TSrg2Types.VIRTUAL_OPEN + override val virtualCloseToken: IElementType = TSrg2Types.VIRTUAL_CLOSE +} diff --git a/obfuscation-explorer/src/main/kotlin/formats/tsrg2/lang/TSrg2ParserDefinition.kt b/obfuscation-explorer/src/main/kotlin/formats/tsrg2/lang/TSrg2ParserDefinition.kt new file mode 100644 index 000000000..2a8814535 --- /dev/null +++ b/obfuscation-explorer/src/main/kotlin/formats/tsrg2/lang/TSrg2ParserDefinition.kt @@ -0,0 +1,51 @@ +/* + * Minecraft Development for IntelliJ + * + * https://mcdev.io/ + * + * Copyright (C) 2024 minecraft-dev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, version 3.0 only. + * + * 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 Lesser General Public License + * along with this program. If not, see . + */ + +package io.mcdev.obfex.formats.tsrg2.lang + +import com.intellij.lang.ASTNode +import com.intellij.lang.Language +import com.intellij.lang.ParserDefinition +import com.intellij.lang.PsiParser +import com.intellij.lexer.Lexer +import com.intellij.openapi.project.Project +import com.intellij.psi.FileViewProvider +import com.intellij.psi.PsiElement +import com.intellij.psi.PsiFile +import com.intellij.psi.tree.IFileElementType +import com.intellij.psi.tree.TokenSet +import io.mcdev.obfex.formats.tsrg2.gen.TSrg2Parser +import io.mcdev.obfex.formats.tsrg2.gen.psi.TSrg2Types + +class TSrg2ParserDefinition : ParserDefinition { + + override fun createLexer(project: Project): Lexer = TSrg2LayoutLexer() + override fun getCommentTokens(): TokenSet = COMMENTS + override fun getStringLiteralElements(): TokenSet = TokenSet.EMPTY + override fun createParser(project: Project): PsiParser = TSrg2Parser() + override fun getFileNodeType(): IFileElementType = FILE + override fun createFile(viewProvider: FileViewProvider): PsiFile = TSrg2File(viewProvider) + override fun createElement(node: ASTNode): PsiElement = TSrg2Types.Factory.createElement(node) + override fun getWhitespaceTokens(): TokenSet = WHITESPACE +} + +private val WHITESPACE = TokenSet.orSet(TokenSet.WHITE_SPACE, TokenSet.create(TSrg2Types.TAB)) +private val COMMENTS = TokenSet.create(TSrg2Types.COMMENT) +private val FILE = IFileElementType(Language.findInstance(TSrg2Language::class.java)) diff --git a/obfuscation-explorer/src/main/kotlin/formats/tsrg2/lang/colors/TSrg2Annotator.kt b/obfuscation-explorer/src/main/kotlin/formats/tsrg2/lang/colors/TSrg2Annotator.kt new file mode 100644 index 000000000..04144b29b --- /dev/null +++ b/obfuscation-explorer/src/main/kotlin/formats/tsrg2/lang/colors/TSrg2Annotator.kt @@ -0,0 +1,55 @@ +/* + * Minecraft Development for IntelliJ + * + * https://mcdev.io/ + * + * Copyright (C) 2024 minecraft-dev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, version 3.0 only. + * + * 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 Lesser General Public License + * along with this program. If not, see . + */ + +package io.mcdev.obfex.formats.tsrg2.lang.colors + +import com.intellij.lang.annotation.AnnotationHolder +import com.intellij.lang.annotation.Annotator +import com.intellij.psi.PsiElement +import io.mcdev.obfex.formats.tsrg2.gen.psi.TSrg2ClassMapping +import io.mcdev.obfex.formats.tsrg2.gen.psi.TSrg2FieldMapping +import io.mcdev.obfex.formats.tsrg2.gen.psi.TSrg2MappingPart +import io.mcdev.obfex.formats.tsrg2.gen.psi.TSrg2MethodMapping +import io.mcdev.obfex.formats.tsrg2.gen.psi.TSrg2ParamIndex +import io.mcdev.obfex.formats.tsrg2.gen.psi.TSrg2ParamMapping +import io.mcdev.obfex.formats.util.registerHighlight + +class TSrg2Annotator : Annotator { + override fun annotate(element: PsiElement, holder: AnnotationHolder) { + if (element is TSrg2ParamIndex) { + registerHighlight(element, TSrg2SyntaxHighlighter.PARAM_INDEX, holder) + return + } + + if (element !is TSrg2MappingPart) { + return + } + + val key = when (element.parent) { + is TSrg2ClassMapping -> TSrg2SyntaxHighlighter.CLASS_NAME + is TSrg2MethodMapping -> TSrg2SyntaxHighlighter.METHOD + is TSrg2FieldMapping -> TSrg2SyntaxHighlighter.FIELD + is TSrg2ParamMapping -> TSrg2SyntaxHighlighter.PARAM + else -> return + } + + registerHighlight(element, key, holder) + } +} diff --git a/obfuscation-explorer/src/main/kotlin/formats/tsrg2/lang/colors/TSrg2RainbowVisitor.kt b/obfuscation-explorer/src/main/kotlin/formats/tsrg2/lang/colors/TSrg2RainbowVisitor.kt new file mode 100644 index 000000000..9952c2e6d --- /dev/null +++ b/obfuscation-explorer/src/main/kotlin/formats/tsrg2/lang/colors/TSrg2RainbowVisitor.kt @@ -0,0 +1,78 @@ +/* + * Minecraft Development for IntelliJ + * + * https://mcdev.io/ + * + * Copyright (C) 2024 minecraft-dev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, version 3.0 only. + * + * 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 Lesser General Public License + * along with this program. If not, see . + */ + +package io.mcdev.obfex.formats.tsrg2.lang.colors + +import com.intellij.codeInsight.daemon.impl.HighlightVisitor +import com.intellij.psi.PsiElement +import com.intellij.psi.util.findParentOfType +import io.mcdev.obfex.formats.tsrg2.gen.psi.TSrg2ClassMapping +import io.mcdev.obfex.formats.tsrg2.gen.psi.TSrg2FieldMapping +import io.mcdev.obfex.formats.tsrg2.gen.psi.TSrg2FileElement +import io.mcdev.obfex.formats.tsrg2.gen.psi.TSrg2MappingPart +import io.mcdev.obfex.formats.tsrg2.gen.psi.TSrg2MethodMapping +import io.mcdev.obfex.formats.tsrg2.gen.psi.TSrg2MethodSignature +import io.mcdev.obfex.formats.tsrg2.gen.psi.TSrg2Namespace +import io.mcdev.obfex.formats.tsrg2.gen.psi.TSrg2ParamMapping +import io.mcdev.obfex.formats.tsrg2.lang.TSrg2Language +import io.mcdev.obfex.formats.util.NamespaceRainbowVisitor + +class TSrg2RainbowVisitor : NamespaceRainbowVisitor(TSrg2Language) { + + override fun visit(element: PsiElement) { + if (element is TSrg2Namespace) { + highlightNamespaceDecl(element) + return + } + + val file = element.findParentOfType() ?: return + val namespaces = file.header.namespaceList + + if (element is TSrg2MappingPart) { + handleMappingPart(element, namespaces) + } else if (element is TSrg2MethodSignature) { + handleMethodSignature(element, namespaces) + } + } + + private fun handleMappingPart(element: TSrg2MappingPart, namespaces: List) { + val index = indexOf(element) + if (namespaces.size <= index) { + return + } + + when (element.parent) { + is TSrg2ClassMapping -> highlightElement(element, index, HighlightType.CLASS) + is TSrg2MethodMapping -> highlightElement(element, index, HighlightType.METHOD) + is TSrg2FieldMapping -> highlightElement(element, index, HighlightType.FIELD) + is TSrg2ParamMapping -> highlightElement(element, index, HighlightType.PARAM) + } + } + + private fun handleMethodSignature(element: TSrg2MethodSignature, namespaces: List) { + if (namespaces.isEmpty()) { + return + } + + highlightTypeSignature(element) + } + + override fun clone(): HighlightVisitor = TSrg2RainbowVisitor() +} diff --git a/obfuscation-explorer/src/main/kotlin/formats/tsrg2/lang/colors/TSrg2SyntaxHighlighter.kt b/obfuscation-explorer/src/main/kotlin/formats/tsrg2/lang/colors/TSrg2SyntaxHighlighter.kt new file mode 100644 index 000000000..d26b9b5d1 --- /dev/null +++ b/obfuscation-explorer/src/main/kotlin/formats/tsrg2/lang/colors/TSrg2SyntaxHighlighter.kt @@ -0,0 +1,95 @@ +/* + * Minecraft Development for IntelliJ + * + * https://mcdev.io/ + * + * Copyright (C) 2024 minecraft-dev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, version 3.0 only. + * + * 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 Lesser General Public License + * along with this program. If not, see . + */ + +package io.mcdev.obfex.formats.tsrg2.lang.colors + +import com.intellij.lexer.Lexer +import com.intellij.openapi.editor.DefaultLanguageHighlighterColors +import com.intellij.openapi.editor.colors.TextAttributesKey +import com.intellij.openapi.fileTypes.SyntaxHighlighterBase +import com.intellij.psi.tree.IElementType +import io.mcdev.obfex.formats.tsrg.lang.colors.TSrgSyntaxHighlighter +import io.mcdev.obfex.formats.tsrg2.gen.psi.TSrg2Types +import io.mcdev.obfex.formats.tsrg2.lang.psi.TSrg2LexerAdapter + +class TSrg2SyntaxHighlighter : SyntaxHighlighterBase() { + + override fun getHighlightingLexer(): Lexer = TSrg2LexerAdapter() + + override fun getTokenHighlights(tokenType: IElementType?): Array = + when (tokenType) { + TSrg2Types.TSRG2_KEY, TSrg2Types.STATIC -> KEYWORD_KEYS + TSrg2Types.NAMESPACE_KEY -> NAMESPACE_KEYS + TSrg2Types.PRIMITIVE -> PRIMITIVE_KEYS + TSrg2Types.COMMENT -> COMMENT_KEYS + TSrg2Types.CLASS_TYPE -> CLASS_TYPE_KEYS + else -> TextAttributesKey.EMPTY_ARRAY + } + + @Suppress("MemberVisibilityCanBePrivate") + companion object { + val KEYWORD = TextAttributesKey.createTextAttributesKey( + "TSRG2_KEYWORD", + DefaultLanguageHighlighterColors.KEYWORD + ) + val NAMESPACE = TextAttributesKey.createTextAttributesKey( + "TSRG2_NAMESPACE", + DefaultLanguageHighlighterColors.METADATA + ) + val CLASS_NAME = TextAttributesKey.createTextAttributesKey( + "TSRG2_CLASS_NAME", + TSrgSyntaxHighlighter.CLASS_NAME + ) + val METHOD = TextAttributesKey.createTextAttributesKey( + "TSRG2_METHOD", + TSrgSyntaxHighlighter.METHOD + ) + val FIELD = TextAttributesKey.createTextAttributesKey( + "TSRG2_FIELD", + TSrgSyntaxHighlighter.FIELD + ) + val PRIMITIVE = TextAttributesKey.createTextAttributesKey( + "TSRG2_PRIMITIVE", + TSrgSyntaxHighlighter.PRIMITIVE + ) + val CLASS_TYPE = TextAttributesKey.createTextAttributesKey( + "TSRG2_CLASS_TYPE", + TSrgSyntaxHighlighter.CLASS_TYPE + ) + val COMMENT = TextAttributesKey.createTextAttributesKey( + "TSRG2_COMMENT", + TSrgSyntaxHighlighter.COMMENT + ) + val PARAM_INDEX = TextAttributesKey.createTextAttributesKey( + "TSRG2_PARAM_INDEX", + DefaultLanguageHighlighterColors.INLAY_DEFAULT + ) + val PARAM = TextAttributesKey.createTextAttributesKey( + "TSRG2_PARAM", + DefaultLanguageHighlighterColors.PARAMETER + ) + + private val KEYWORD_KEYS = arrayOf(KEYWORD) + private val NAMESPACE_KEYS = arrayOf(NAMESPACE) + private val PRIMITIVE_KEYS = arrayOf(PRIMITIVE) + private val COMMENT_KEYS = arrayOf(COMMENT) + private val CLASS_TYPE_KEYS = arrayOf(CLASS_TYPE) + } +} diff --git a/obfuscation-explorer/src/main/kotlin/formats/tsrg2/lang/colors/TSrg2SyntaxHighlighterFactory.kt b/obfuscation-explorer/src/main/kotlin/formats/tsrg2/lang/colors/TSrg2SyntaxHighlighterFactory.kt new file mode 100644 index 000000000..0570765e2 --- /dev/null +++ b/obfuscation-explorer/src/main/kotlin/formats/tsrg2/lang/colors/TSrg2SyntaxHighlighterFactory.kt @@ -0,0 +1,31 @@ +/* + * Minecraft Development for IntelliJ + * + * https://mcdev.io/ + * + * Copyright (C) 2024 minecraft-dev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, version 3.0 only. + * + * 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 Lesser General Public License + * along with this program. If not, see . + */ + +package io.mcdev.obfex.formats.tsrg2.lang.colors + +import com.intellij.openapi.fileTypes.SyntaxHighlighter +import com.intellij.openapi.fileTypes.SyntaxHighlighterFactory +import com.intellij.openapi.project.Project +import com.intellij.openapi.vfs.VirtualFile + +class TSrg2SyntaxHighlighterFactory : SyntaxHighlighterFactory() { + override fun getSyntaxHighlighter(project: Project?, virtualFile: VirtualFile?): SyntaxHighlighter = + TSrg2SyntaxHighlighter() +} diff --git a/obfuscation-explorer/src/main/kotlin/formats/tsrg2/lang/psi/TSrg2ElementType.kt b/obfuscation-explorer/src/main/kotlin/formats/tsrg2/lang/psi/TSrg2ElementType.kt new file mode 100644 index 000000000..b6332331f --- /dev/null +++ b/obfuscation-explorer/src/main/kotlin/formats/tsrg2/lang/psi/TSrg2ElementType.kt @@ -0,0 +1,27 @@ +/* + * Minecraft Development for IntelliJ + * + * https://mcdev.io/ + * + * Copyright (C) 2024 minecraft-dev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, version 3.0 only. + * + * 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 Lesser General Public License + * along with this program. If not, see . + */ + +package io.mcdev.obfex.formats.tsrg2.lang.psi + +import com.intellij.psi.tree.IElementType +import io.mcdev.obfex.formats.tsrg2.lang.TSrg2Language +import org.jetbrains.annotations.NonNls + +class TSrg2ElementType(@NonNls debugName: String) : IElementType(debugName, TSrg2Language) diff --git a/obfuscation-explorer/src/main/kotlin/formats/tsrg2/lang/psi/TSrg2LexerAdapter.kt b/obfuscation-explorer/src/main/kotlin/formats/tsrg2/lang/psi/TSrg2LexerAdapter.kt new file mode 100644 index 000000000..73fa281b5 --- /dev/null +++ b/obfuscation-explorer/src/main/kotlin/formats/tsrg2/lang/psi/TSrg2LexerAdapter.kt @@ -0,0 +1,26 @@ +/* + * Minecraft Development for IntelliJ + * + * https://mcdev.io/ + * + * Copyright (C) 2024 minecraft-dev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, version 3.0 only. + * + * 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 Lesser General Public License + * along with this program. If not, see . + */ + +package io.mcdev.obfex.formats.tsrg2.lang.psi + +import com.intellij.lexer.FlexAdapter +import io.mcdev.obfex.formats.tsrg2.gen.TSrg2Lexer + +class TSrg2LexerAdapter : FlexAdapter(TSrg2Lexer()) diff --git a/obfuscation-explorer/src/main/kotlin/formats/tsrg2/lang/psi/TSrg2TokenType.kt b/obfuscation-explorer/src/main/kotlin/formats/tsrg2/lang/psi/TSrg2TokenType.kt new file mode 100644 index 000000000..4b219b2d2 --- /dev/null +++ b/obfuscation-explorer/src/main/kotlin/formats/tsrg2/lang/psi/TSrg2TokenType.kt @@ -0,0 +1,27 @@ +/* + * Minecraft Development for IntelliJ + * + * https://mcdev.io/ + * + * Copyright (C) 2024 minecraft-dev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, version 3.0 only. + * + * 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 Lesser General Public License + * along with this program. If not, see . + */ + +package io.mcdev.obfex.formats.tsrg2.lang.psi + +import com.intellij.psi.tree.IElementType +import io.mcdev.obfex.formats.tsrg2.lang.TSrg2Language +import org.jetbrains.annotations.NonNls + +class TSrg2TokenType(@NonNls debugName: String) : IElementType(debugName, TSrg2Language) diff --git a/obfuscation-explorer/src/main/kotlin/formats/util/NamespaceRainbowVisitor.kt b/obfuscation-explorer/src/main/kotlin/formats/util/NamespaceRainbowVisitor.kt new file mode 100644 index 000000000..d2597ef1b --- /dev/null +++ b/obfuscation-explorer/src/main/kotlin/formats/util/NamespaceRainbowVisitor.kt @@ -0,0 +1,137 @@ +/* + * Minecraft Development for IntelliJ + * + * https://mcdev.io/ + * + * Copyright (C) 2024 minecraft-dev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, version 3.0 only. + * + * 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 Lesser General Public License + * along with this program. If not, see . + */ + +package io.mcdev.obfex.formats.util + +import com.intellij.codeHighlighting.ColorGenerator +import com.intellij.codeHighlighting.RainbowHighlighter +import com.intellij.codeInsight.daemon.RainbowVisitor +import com.intellij.codeInsight.daemon.impl.HighlightInfo +import com.intellij.codeInsight.daemon.impl.analysis.HighlightInfoHolder +import com.intellij.lang.Language +import com.intellij.openapi.editor.markup.EffectType +import com.intellij.openapi.editor.markup.TextAttributes +import com.intellij.psi.PsiElement +import com.intellij.psi.PsiFile +import java.awt.Color +import java.awt.Font + +abstract class NamespaceRainbowVisitor(private val language: Language) : RainbowVisitor() { + + private var rainbowColors: Array? = null + + override fun suitableForFile(file: PsiFile): Boolean = file.language == language + + override fun analyze( + file: PsiFile, + updateWholeFile: Boolean, + holder: HighlightInfoHolder, + action: Runnable, + ): Boolean { + computeColors(holder) + val res = super.analyze(file, updateWholeFile, holder, action) + rainbowColors = null + return res + } + + protected fun highlightNamespaceDecl(element: PsiElement) { + val index = indexOf(element) + val color = getColorFromIndex(index) + + val attribute = TextAttributes(color, null, null, null, Font.PLAIN) + val highlightInfo = HighlightInfo + .newHighlightInfo(RainbowHighlighter.RAINBOW_ELEMENT) + .textAttributes(attribute) + .range(element) + .create() + addInfo(highlightInfo) + } + + protected fun highlightElement( + element: PsiElement, + index: Int, + type: HighlightType + ) { + val color = getColorFromIndex(index) + + val fontType = when (type) { + HighlightType.CLASS -> Font.ITALIC + HighlightType.METHOD -> Font.BOLD + HighlightType.FIELD, HighlightType.PARAM, HighlightType.LOCAL_VAR -> Font.PLAIN + } + + var attr = TextAttributes(color, null, null, null, fontType) + if (type == HighlightType.FIELD) { + attr = attr.highlight() + } + + val info = HighlightInfo + .newHighlightInfo(RainbowHighlighter.RAINBOW_ELEMENT) + .textAttributes(attr) + .range(element) + .create() + addInfo(info) + } + + protected fun highlightTypeSignature(element: PsiElement) { + val color = getColorFromIndex(0) + + val textAttributes = TextAttributes(null, null, color, EffectType.SLIGHTLY_WIDER_BOX, Font.PLAIN) + val highlightInfo = HighlightInfo + .newHighlightInfo(RainbowHighlighter.RAINBOW_ELEMENT) + .textAttributes(textAttributes) + .range(element) + .create() + addInfo(highlightInfo) + } + + protected fun indexOf(element: PsiElement): Int { + return element.parent.childrenOfType(element::class).indexOf(element) + } + + protected enum class HighlightType { + CLASS, METHOD, FIELD, PARAM, LOCAL_VAR + } + + protected fun getColorFromIndex(index: Int): Color { + val colors = rainbowColors ?: error("rainbowColors is not initialized") + return colors[index % colors.size] + } + + private fun computeColors(holder: HighlightInfoHolder) { + val colorScheme = holder.colorsScheme + val stopRainbowColors = RainbowHighlighter.RAINBOW_COLOR_KEYS.map { + colorScheme.getAttributes(it).foregroundColor + } + + val colors = ColorGenerator.generateLinearColorSequence(stopRainbowColors, 4) + + var startingIndex = 0 + var index = 0 + rainbowColors = Array(colors.size) { + val color = colors[index] + index += 5 + if (index >= colors.size) { + index = ++startingIndex + } + color + } + } +} diff --git a/obfuscation-explorer/src/main/kotlin/formats/util/TabCodeStyleSettingsProvider.kt b/obfuscation-explorer/src/main/kotlin/formats/util/TabCodeStyleSettingsProvider.kt new file mode 100644 index 000000000..9a42e1436 --- /dev/null +++ b/obfuscation-explorer/src/main/kotlin/formats/util/TabCodeStyleSettingsProvider.kt @@ -0,0 +1,36 @@ +/* + * Minecraft Development for IntelliJ + * + * https://mcdev.io/ + * + * Copyright (C) 2024 minecraft-dev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, version 3.0 only. + * + * 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 Lesser General Public License + * along with this program. If not, see . + */ + +package io.mcdev.obfex.formats.util + +import com.intellij.psi.codeStyle.CommonCodeStyleSettings +import com.intellij.psi.codeStyle.LanguageCodeStyleSettingsProvider + +abstract class TabCodeStyleSettingsProvider : LanguageCodeStyleSettingsProvider() { + + override fun customizeDefaults( + commonSettings: CommonCodeStyleSettings, + indentOptions: CommonCodeStyleSettings.IndentOptions + ) { + indentOptions.USE_TAB_CHARACTER = true + indentOptions.TAB_SIZE = 4 + indentOptions.INDENT_SIZE = indentOptions.TAB_SIZE + } +} diff --git a/obfuscation-explorer/src/main/kotlin/formats/util/sigws/SignificantWhitespaceLexer.kt b/obfuscation-explorer/src/main/kotlin/formats/util/sigws/SignificantWhitespaceLexer.kt new file mode 100644 index 000000000..477ef4e9c --- /dev/null +++ b/obfuscation-explorer/src/main/kotlin/formats/util/sigws/SignificantWhitespaceLexer.kt @@ -0,0 +1,144 @@ +/* + * Minecraft Development for IntelliJ + * + * https://mcdev.io/ + * + * Copyright (C) 2024 minecraft-dev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, version 3.0 only. + * + * 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 Lesser General Public License + * along with this program. If not, see . + */ + +package io.mcdev.obfex.formats.util.sigws + +import com.intellij.lexer.Lexer +import com.intellij.lexer.LexerBase +import com.intellij.psi.tree.IElementType +import com.intellij.psi.tree.TokenSet + +abstract class SignificantWhitespaceLexer(private val delegate: Lexer) : LexerBase() { + + private var lookingForIndent: Boolean = true + private var lastSeenTabCount = 0 + private var currentToken: Token? = null + + private val tokenStack = ArrayDeque() + + abstract val newlineTokens: TokenSet + abstract val tabTokens: TokenSet + + abstract val virtualOpenToken: IElementType + abstract val virtualCloseToken: IElementType + + override fun getState(): Int = delegate.state + + override fun getTokenType(): IElementType? = currentToken?.tokenType + override fun getTokenStart(): Int = currentToken?.start ?: 0 + override fun getTokenEnd(): Int = currentToken?.end ?: 0 + + override fun getBufferSequence(): CharSequence = delegate.bufferSequence + override fun getBufferEnd(): Int = delegate.bufferEnd + + override fun start(buffer: CharSequence, startOffset: Int, endOffset: Int, initialState: Int) { + delegate.start(buffer, startOffset, endOffset, initialState) + compute(getCurrentToken()) + } + + override fun advance() { + if (tokenStack.isNotEmpty()) { + currentToken = tokenStack.removeFirst() + return + } + + compute() + } + + private fun compute(firstToken: Token? = null) { + var newToken = firstToken ?: getNextToken() ?: return + val firstNewToken = newToken + + if (!lookingForIndent) { + currentToken = newToken + if (newToken.tokenType in newlineTokens) { + lookingForIndent = true + } + return + } + lookingForIndent = false + + tokenStack.addLast(newToken) + + var tabCount = 0 + while (newToken.tokenType in tabTokens) { + tabCount++ + newToken = getNextToken()?.also { tokenStack.addLast(it) } ?: break + } + + if (newToken.tokenType in newlineTokens) { + // this is a blank line, ignore it + currentToken = tokenStack.removeFirstOrNull() + return + } + + when { + tabCount > lastSeenTabCount -> { + val indentToken = virtualOpen(firstNewToken) + repeat(tabCount - lastSeenTabCount) { + tokenStack.addFirst(indentToken) + } + } + tabCount < lastSeenTabCount -> { + val unindentToken = virtualClose(firstNewToken) + repeat(lastSeenTabCount - tabCount) { + tokenStack.addFirst(unindentToken) + } + } + } + lastSeenTabCount = tabCount + + currentToken = tokenStack.removeFirstOrNull() + } + + private fun getNextToken(): Token? { + delegate.advance() + return getCurrentToken() + } + + private fun getCurrentToken(): Token? { + if (delegate.tokenType == null) { + currentToken = null + return null + } + return Token(delegate.tokenType, delegate.tokenStart, delegate.tokenEnd) + } + + private fun virtualOpen(precedesToken: Token) = Token( + tokenType = virtualOpenToken, + start = precedesToken.start, + end = precedesToken.start, + ) + + private fun virtualClose(precedesToken: Token) = Token( + tokenType = virtualCloseToken, + start = precedesToken.start, + end = precedesToken.start, + ) + + private class Token( + val tokenType: IElementType?, + val start: Int, + val end: Int, + ) { + val isEof: Boolean + get() = tokenType == null + } +} diff --git a/obfuscation-explorer/src/main/kotlin/formats/util/sigws/SignificantWhitespaceParserUtil.kt b/obfuscation-explorer/src/main/kotlin/formats/util/sigws/SignificantWhitespaceParserUtil.kt new file mode 100644 index 000000000..d949400bd --- /dev/null +++ b/obfuscation-explorer/src/main/kotlin/formats/util/sigws/SignificantWhitespaceParserUtil.kt @@ -0,0 +1,54 @@ +/* + * Minecraft Development for IntelliJ + * + * https://mcdev.io/ + * + * Copyright (C) 2024 minecraft-dev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, version 3.0 only. + * + * 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 Lesser General Public License + * along with this program. If not, see . + */ + +package io.mcdev.obfex.formats.util.sigws + +import com.intellij.lang.PsiBuilder +import com.intellij.lang.parser.GeneratedParserUtilBase + +open class SignificantWhitespaceParserUtil : GeneratedParserUtilBase() { + companion object { + @JvmStatic + fun indent0( + builder: PsiBuilder, + level: Int, + virtualOpen: Parser, + virtualClose: Parser, + param: Parser, + ): Boolean { + if (!recursion_guard_(builder, level, "indent0")) { + return false + } + + val marker = enter_section_(builder) + + val res = + // VIRTUAL_OPEN + virtualOpen.parse(builder, level + 1) && + // <> + param.parse(builder, level) && + // (VIRTUAL_CLOSE | <>) + (virtualClose.parse(builder, level + 1) || eof(builder, level + 1)) + + exit_section_(builder, marker, null, res) + return res + } + } +} diff --git a/obfuscation-explorer/src/main/kotlin/formats/util/util.kt b/obfuscation-explorer/src/main/kotlin/formats/util/util.kt new file mode 100644 index 000000000..9d915d951 --- /dev/null +++ b/obfuscation-explorer/src/main/kotlin/formats/util/util.kt @@ -0,0 +1,84 @@ +/* + * Minecraft Development for IntelliJ + * + * https://mcdev.io/ + * + * Copyright (C) 2024 minecraft-dev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, version 3.0 only. + * + * 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 Lesser General Public License + * along with this program. If not, see . + */ + +package io.mcdev.obfex.formats.util + +import com.intellij.lang.annotation.AnnotationHolder +import com.intellij.lang.annotation.HighlightSeverity +import com.intellij.openapi.editor.colors.TextAttributesKey +import com.intellij.openapi.editor.markup.TextAttributes +import com.intellij.psi.PsiElement +import com.intellij.psi.util.PsiTreeUtil +import com.intellij.ui.JBColor +import java.awt.Color +import java.awt.Font +import kotlin.reflect.KClass + +fun TextAttributesKey.highlight(): TextAttributes = defaultAttributes.highlight() +fun TextAttributes.highlight(): TextAttributes = + TextAttributes.merge( + this, + TextAttributes( + null, + foregroundColor.toBackground(), + foregroundColor, + null, + Font.ITALIC + ) + + ) + +fun registerHighlight( + element: PsiElement?, + key: TextAttributesKey, + holder: AnnotationHolder, + highlight: Boolean = false +) { + if (element == null) { + return + } + val builder = holder.newSilentAnnotation(HighlightSeverity.INFORMATION) + .range(element) + .textAttributes(key) + if (highlight) { + builder.enforcedTextAttributes(key.highlight()) + } + builder.create() +} + +private fun Color.toBackground(): Color { + val darkerBase = darker().darker().darker().darker() + val brighterBase = brighter().brighter().brighter().brighter() + return JBColor( + Color(brighterBase.red, brighterBase.green, brighterBase.blue, 128), + Color(darkerBase.red, darkerBase.green, darkerBase.blue, 128) + ) +} + +fun PsiElement.childrenOfType(type: KClass): List { + return PsiTreeUtil.getChildrenOfTypeAsList(this, type.java) +} + +fun Array<*>.indicesFrom(first: Int): IntRange { + if (first >= size) { + return IntRange.EMPTY + } + return first until size +} diff --git a/obfuscation-explorer/src/main/kotlin/formats/xsrg/XSrgMappingsFormatParser.kt b/obfuscation-explorer/src/main/kotlin/formats/xsrg/XSrgMappingsFormatParser.kt new file mode 100644 index 000000000..5af4c9091 --- /dev/null +++ b/obfuscation-explorer/src/main/kotlin/formats/xsrg/XSrgMappingsFormatParser.kt @@ -0,0 +1,224 @@ +/* + * Minecraft Development for IntelliJ + * + * https://mcdev.io/ + * + * Copyright (C) 2024 minecraft-dev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, version 3.0 only. + * + * 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 Lesser General Public License + * along with this program. If not, see . + */ + +package io.mcdev.obfex.formats.xsrg + +import com.intellij.openapi.vfs.VirtualFile +import io.mcdev.obfex.MappingPart +import io.mcdev.obfex.forLines +import io.mcdev.obfex.mappings.FileCoords +import io.mcdev.obfex.mappings.MappingsDefinition +import io.mcdev.obfex.mappings.MappingsDefinitionBuilder +import io.mcdev.obfex.mappings.UnnamedMappingsFormatParser +import io.mcdev.obfex.mappings.clazz +import io.mcdev.obfex.mappings.field +import io.mcdev.obfex.mappings.method +import io.mcdev.obfex.mappings.pack +import io.mcdev.obfex.ref.asClass +import io.mcdev.obfex.ref.asField +import io.mcdev.obfex.ref.asMethod +import io.mcdev.obfex.ref.asMethodDesc +import io.mcdev.obfex.ref.asPackage +import io.mcdev.obfex.ref.asTypeDef +import io.mcdev.obfex.ref.highPriority +import io.mcdev.obfex.ref.lowPriority +import io.mcdev.obfex.splitOnLast + +@Suppress("DuplicatedCode") +class XSrgMappingsFormatParser : UnnamedMappingsFormatParser { + + override val expectedFileExtensions: Array = arrayOf("xsrg") + + override fun parse(file: VirtualFile): MappingsDefinition { + val builder = MappingsDefinitionBuilder(XSrgMappingsFormatType, file) + + file.forLines { lineNum, parts, _ -> + if (parts.isEmpty()) { + return@forLines true + } + + val key = parts.first() + + when (key.value) { + "PK:" -> parsePackageLine(builder, lineNum, parts) + "CL:" -> parseClassLine(builder, lineNum, parts) + "MD:" -> parseMethodLine(builder, lineNum, parts) + "FD:" -> parseFieldLine(builder, lineNum, parts) + else -> builder.error("Unrecognized key: ${key.value.substring(0, 3)}", FileCoords(lineNum)) + } + + return@forLines true + } + + return builder.build() + } + + private fun parsePackageLine(builder: MappingsDefinitionBuilder, lineNum: Int, parts: Array) { + if (parts.size != 3) { + builder.error("Package line is invalid", FileCoords(lineNum)) + return + } + + val (_, leftPack, rightPack) = parts + builder.pack(lineNum.highPriority) { + with(leftPack.asPackage().from) + with(rightPack.asPackage().to) + } + } + + private fun parseClassLine(builder: MappingsDefinitionBuilder, lineNum: Int, parts: Array) { + if (parts.size != 3) { + builder.error("Class line is invalid", FileCoords(lineNum)) + return + } + + val (_, leftClass, rightClass) = parts + builder.clazz(lineNum.highPriority) { + with(leftClass.asClass().from) + with(rightClass.asClass().to) + } + } + + private fun parseFieldLine(builder: MappingsDefinitionBuilder, lineNum: Int, parts: Array) { + when (parts.size) { + 5 -> { + val (leftClass, leftField) = parts[1].splitOnLast('/') + if (leftField == null) { + builder.error( + "Field mapping is invalid, no field is specified after class", + FileCoords(lineNum, parts[1]) + ) + return + } + + val (rightClass, rightField) = parts[3].splitOnLast('/') + if (rightField == null) { + builder.error( + "Field mapping is invalid, no field is specified after class", + FileCoords(lineNum, parts[2]) + ) + return + } + + val leftDesc = parts[2].value.asTypeDef() + if (leftDesc == null) { + builder.error("Field descriptor is invalid", FileCoords(lineNum, parts[2])) + return + } + + val rightDesc = parts[4].value.asTypeDef() + if (rightDesc == null) { + builder.error("Field descriptor is invalid", FileCoords(lineNum, parts[4])) + return + } + + builder.clazz(leftClass.asClass().from, lineNum.lowPriority) { + unlessExists(rightClass.asClass().to) + + field(lineNum.highPriority) { + type = leftDesc + + with(leftField.asField().from) + with(rightField.asField().to) + } + } + } + 3 -> { + val (leftClass, leftField) = parts[1].splitOnLast('/') + if (leftField == null) { + builder.error( + "Field mapping is invalid, no field is specified after class", + FileCoords(lineNum, parts[1]) + ) + return + } + + val (rightClass, rightField) = parts[2].splitOnLast('/') + if (rightField == null) { + builder.error( + "Field mapping is invalid, no field is specified after class", + FileCoords(lineNum, parts[2]) + ) + return + } + + builder.clazz(leftClass.asClass().from, lineNum.lowPriority) { + unlessExists(rightClass.asClass().to) + + field(lineNum.highPriority) { + with(leftField.asField().from) + with(rightField.asField().to) + } + } + } + else -> { + builder.error("Field line is invalid", FileCoords(lineNum)) + } + } + } + + private fun parseMethodLine(builder: MappingsDefinitionBuilder, lineNum: Int, parts: Array) { + if (parts.size != 5) { + builder.error("Method line is invalid", FileCoords(lineNum)) + return + } + + val (leftClass, leftMethod) = parts[1].splitOnLast('/') + if (leftMethod == null) { + builder.error( + "Method mapping is invalid, no method is specified after class", + FileCoords(lineNum, parts[1]) + ) + return + } + + val (rightClass, rightMethod) = parts[3].splitOnLast('/') + if (rightMethod == null) { + builder.error( + "Method mapping is invalid, no method is specified after class", + FileCoords(lineNum, parts[3]) + ) + return + } + + val leftDesc = parts[2].value.asMethodDesc() + if (leftDesc == null) { + builder.error("Method descriptor is invalid", FileCoords(lineNum, parts[2])) + return + } + + val rightDesc = parts[4].value.asMethodDesc() + if (rightDesc == null) { + builder.error("Method descriptor is invalid", FileCoords(lineNum, parts[4])) + return + } + + builder.clazz(leftClass.asClass().from, lineNum.lowPriority) { + unlessExists(rightClass.asClass().to) + + method(lineNum.highPriority) { + desc = leftDesc + + with(leftMethod.asMethod().from) + with(rightMethod.asMethod().to) + } + } + } +} diff --git a/obfuscation-explorer/src/main/kotlin/formats/xsrg/XSrgMappingsFormatType.kt b/obfuscation-explorer/src/main/kotlin/formats/xsrg/XSrgMappingsFormatType.kt new file mode 100644 index 000000000..8df9c243f --- /dev/null +++ b/obfuscation-explorer/src/main/kotlin/formats/xsrg/XSrgMappingsFormatType.kt @@ -0,0 +1,31 @@ +/* + * Minecraft Development for IntelliJ + * + * https://mcdev.io/ + * + * Copyright (C) 2024 minecraft-dev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, version 3.0 only. + * + * 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 Lesser General Public License + * along with this program. If not, see . + */ + +package io.mcdev.obfex.formats.xsrg + +import io.mcdev.obfex.ObfIcons +import io.mcdev.obfex.formats.MappingsFormatType +import javax.swing.Icon + +object XSrgMappingsFormatType : MappingsFormatType("XSRG") { + + override val icon: Icon = ObfIcons.XSRG_ICON + override val name: String = "XSRG" +} diff --git a/obfuscation-explorer/src/main/kotlin/formats/xsrg/lang/XSrgFile.kt b/obfuscation-explorer/src/main/kotlin/formats/xsrg/lang/XSrgFile.kt new file mode 100644 index 000000000..f4498f9d4 --- /dev/null +++ b/obfuscation-explorer/src/main/kotlin/formats/xsrg/lang/XSrgFile.kt @@ -0,0 +1,30 @@ +/* + * Minecraft Development for IntelliJ + * + * https://mcdev.io/ + * + * Copyright (C) 2024 minecraft-dev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, version 3.0 only. + * + * 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 Lesser General Public License + * along with this program. If not, see . + */ + +package io.mcdev.obfex.formats.xsrg.lang + +import com.intellij.extapi.psi.PsiFileBase +import com.intellij.openapi.fileTypes.FileType +import com.intellij.psi.FileViewProvider + +class XSrgFile(viewProvider: FileViewProvider) : PsiFileBase(viewProvider, XSrgLanguage) { + override fun getFileType(): FileType = XSrgFileType + override fun toString(): String = XSrgFileType.description +} diff --git a/obfuscation-explorer/src/main/kotlin/formats/xsrg/lang/XSrgFileType.kt b/obfuscation-explorer/src/main/kotlin/formats/xsrg/lang/XSrgFileType.kt new file mode 100644 index 000000000..e8a3035e3 --- /dev/null +++ b/obfuscation-explorer/src/main/kotlin/formats/xsrg/lang/XSrgFileType.kt @@ -0,0 +1,33 @@ +/* + * Minecraft Development for IntelliJ + * + * https://mcdev.io/ + * + * Copyright (C) 2024 minecraft-dev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, version 3.0 only. + * + * 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 Lesser General Public License + * along with this program. If not, see . + */ + +package io.mcdev.obfex.formats.xsrg.lang + +import com.intellij.openapi.fileTypes.LanguageFileType +import io.mcdev.obfex.ObfIcons +import javax.swing.Icon + +object XSrgFileType : LanguageFileType(XSrgLanguage) { + + override fun getName(): String = "XSRG" + override fun getDescription(): String = "XSRG obfuscation mapping file" + override fun getDefaultExtension(): String = "xsrg" + override fun getIcon(): Icon = ObfIcons.XSRG_ICON +} diff --git a/obfuscation-explorer/src/main/kotlin/formats/xsrg/lang/XSrgLanguage.kt b/obfuscation-explorer/src/main/kotlin/formats/xsrg/lang/XSrgLanguage.kt new file mode 100644 index 000000000..8eaff90e5 --- /dev/null +++ b/obfuscation-explorer/src/main/kotlin/formats/xsrg/lang/XSrgLanguage.kt @@ -0,0 +1,25 @@ +/* + * Minecraft Development for IntelliJ + * + * https://mcdev.io/ + * + * Copyright (C) 2024 minecraft-dev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, version 3.0 only. + * + * 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 Lesser General Public License + * along with this program. If not, see . + */ + +package io.mcdev.obfex.formats.xsrg.lang + +import com.intellij.lang.Language + +object XSrgLanguage : Language("XSRG") diff --git a/obfuscation-explorer/src/main/kotlin/formats/xsrg/lang/XSrgParserDefinition.kt b/obfuscation-explorer/src/main/kotlin/formats/xsrg/lang/XSrgParserDefinition.kt new file mode 100644 index 000000000..d938233d0 --- /dev/null +++ b/obfuscation-explorer/src/main/kotlin/formats/xsrg/lang/XSrgParserDefinition.kt @@ -0,0 +1,50 @@ +/* + * Minecraft Development for IntelliJ + * + * https://mcdev.io/ + * + * Copyright (C) 2024 minecraft-dev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, version 3.0 only. + * + * 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 Lesser General Public License + * along with this program. If not, see . + */ + +package io.mcdev.obfex.formats.xsrg.lang + +import com.intellij.lang.ASTNode +import com.intellij.lang.Language +import com.intellij.lang.ParserDefinition +import com.intellij.lang.PsiParser +import com.intellij.lexer.Lexer +import com.intellij.openapi.project.Project +import com.intellij.psi.FileViewProvider +import com.intellij.psi.PsiElement +import com.intellij.psi.PsiFile +import com.intellij.psi.tree.IFileElementType +import com.intellij.psi.tree.TokenSet +import io.mcdev.obfex.formats.srg.gen.SrgParser +import io.mcdev.obfex.formats.srg.gen.psi.SrgTypes +import io.mcdev.obfex.formats.srg.lang.psi.SrgLexerAdapter + +class XSrgParserDefinition : ParserDefinition { + + override fun createLexer(project: Project): Lexer = SrgLexerAdapter() + override fun getCommentTokens(): TokenSet = COMMENTS + override fun getStringLiteralElements(): TokenSet = TokenSet.EMPTY + override fun createParser(project: Project): PsiParser = SrgParser() + override fun getFileNodeType(): IFileElementType = FILE + override fun createFile(viewProvider: FileViewProvider): PsiFile = XSrgFile(viewProvider) + override fun createElement(node: ASTNode): PsiElement = SrgTypes.Factory.createElement(node) +} + +private val COMMENTS = TokenSet.create(SrgTypes.COMMENT) +private val FILE = IFileElementType(Language.findInstance(XSrgLanguage::class.java)) diff --git a/obfuscation-explorer/src/main/kotlin/lookup/HashLookupIndex.kt b/obfuscation-explorer/src/main/kotlin/lookup/HashLookupIndex.kt new file mode 100644 index 000000000..42fe83f98 --- /dev/null +++ b/obfuscation-explorer/src/main/kotlin/lookup/HashLookupIndex.kt @@ -0,0 +1,87 @@ +/* + * Minecraft Development for IntelliJ + * + * https://mcdev.io/ + * + * Copyright (C) 2024 minecraft-dev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, version 3.0 only. + * + * 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 Lesser General Public License + * along with this program. If not, see . + */ + +package io.mcdev.obfex.lookup + +import it.unimi.dsi.fastutil.objects.Object2ObjectFunction +import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap +import it.unimi.dsi.fastutil.objects.ObjectLinkedOpenHashSet +import java.lang.invoke.MethodHandles +import kotlin.reflect.KClass + +class HashLookupIndex( + private val table: HashLookupTable, + private val transformer: LookupIndexTransformer, +) : LookupIndex { + + private var map: Object2ObjectOpenHashMap>? = null + + override fun query(key: K?): MultiLookupTable { + return map?.get(key) ?: MultiLookupTable.empty() + } + + @Suppress("unused") // indirectly accessed by HashLookupTable + private fun rebuild() { + @Suppress("UNCHECKED_CAST") + val store = tableList.get(table.unwrap(HashLookupTable::class)) as ObjectLinkedOpenHashSet + + map = Object2ObjectOpenHashMap(store.size) + + for (t in store) { + add(t) + } + } + + private fun add(value: T) { + val keys = transformer(value) + + for (key in keys) { + map?.computeIfAbsent(key, Object2ObjectFunction { HashLookupTable() })?.add(value) + } + } + + @Suppress("unused") // indirectly accessed by HashLookupTable + private fun remove(value: T) { + val keys = transformer(value) + + for (key in keys) { + val list = map?.get(key) + list?.remove(value) + + if (list?.isEmpty() == true) { + map?.remove(key) + } + } + } + + override fun > unwrap(type: KClass): L? { + if (type.isInstance(this)) { + @Suppress("UNCHECKED_CAST") + return this as L + } + return null + } + + private companion object { + @JvmStatic + private val tableList = MethodHandles.privateLookupIn(HashLookupTable::class.java, MethodHandles.lookup()) + .findVarHandle(HashLookupTable::class.java, "store", ObjectLinkedOpenHashSet::class.java) + } +} diff --git a/obfuscation-explorer/src/main/kotlin/lookup/HashLookupTable.kt b/obfuscation-explorer/src/main/kotlin/lookup/HashLookupTable.kt new file mode 100644 index 000000000..b5db39719 --- /dev/null +++ b/obfuscation-explorer/src/main/kotlin/lookup/HashLookupTable.kt @@ -0,0 +1,117 @@ +/* + * Minecraft Development for IntelliJ + * + * https://mcdev.io/ + * + * Copyright (C) 2024 minecraft-dev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, version 3.0 only. + * + * 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 Lesser General Public License + * along with this program. If not, see . + */ + +package io.mcdev.obfex.lookup + +import it.unimi.dsi.fastutil.objects.ObjectLinkedOpenHashSet +import java.lang.invoke.MethodHandles +import java.lang.invoke.MethodType +import java.util.Collections +import kotlin.reflect.KClass + +/** + * [MultiLookupTable] implementation based on hash tables. + */ +open class HashLookupTable : MultiLookupTable { + + private val store: ObjectLinkedOpenHashSet = ObjectLinkedOpenHashSet() + + private val indices: MutableList> = mutableListOf() + + override fun seq(): Sequence = store.asSequence() + + override fun list(): List = Collections.unmodifiableList(store.toList()) + + override val size: Int + get() = store.size + + override fun indexMulti(transformer: LookupIndexTransformer): LookupIndex { + for (index in indices) { + if (lookupIndexTransformer.get(index) === transformer) { + @Suppress("UNCHECKED_CAST") + return index as LookupIndex + } + } + + val index = HashLookupIndex(this, transformer) + rebuildIndex.invoke(index) + indices += index + + return index + } + + override fun add(value: T) { + if (store.add(value)) { + for (index in indices) { + addToIndex.invoke(index, value) + } + } + } + + override fun remove(value: T) { + if (store.remove(value)) { + for (index in indices) { + removeFromIndex.invoke(index, value) + } + } + } + + override fun > unwrap(type: KClass): L? { + if (type.isInstance(this)) { + @Suppress("UNCHECKED_CAST") + return this as L + } + return null + } + + companion object { + @JvmStatic + private val lookupIndexTransformer = + MethodHandles.privateLookupIn(HashLookupIndex::class.java, MethodHandles.lookup()) + .findVarHandle(HashLookupIndex::class.java, "transformer", Function1::class.java) + + @JvmStatic + private val rebuildIndex = + MethodHandles.privateLookupIn(HashLookupIndex::class.java, MethodHandles.lookup()) + .findVirtual( + HashLookupIndex::class.java, + "rebuild", + MethodType.methodType(Void::class.javaPrimitiveType) + ) + + @JvmStatic + private val addToIndex = + MethodHandles.privateLookupIn(HashLookupIndex::class.java, MethodHandles.lookup()) + .findVirtual( + HashLookupIndex::class.java, + "add", + MethodType.methodType(Void::class.javaPrimitiveType, Any::class.java) + ) + + @JvmStatic + private val removeFromIndex = + MethodHandles.privateLookupIn(HashLookupIndex::class.java, MethodHandles.lookup()) + .findVirtual( + HashLookupIndex::class.java, + "remove", + MethodType.methodType(Void::class.javaPrimitiveType, Any::class.java) + ) + } +} diff --git a/obfuscation-explorer/src/main/kotlin/lookup/LookupIndex.kt b/obfuscation-explorer/src/main/kotlin/lookup/LookupIndex.kt new file mode 100644 index 000000000..fd7b1a235 --- /dev/null +++ b/obfuscation-explorer/src/main/kotlin/lookup/LookupIndex.kt @@ -0,0 +1,36 @@ +/* + * Minecraft Development for IntelliJ + * + * https://mcdev.io/ + * + * Copyright (C) 2024 minecraft-dev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, version 3.0 only. + * + * 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 Lesser General Public License + * along with this program. If not, see . + */ + +package io.mcdev.obfex.lookup + +import kotlin.reflect.KClass + +/** + * Index for optimized queries from [MultiLookupTable]. + */ +interface LookupIndex { + + /** + * Query the index using the given key. + */ + fun query(key: K?): MultiLookupTable + + fun > unwrap(type: KClass): L? +} diff --git a/obfuscation-explorer/src/main/kotlin/lookup/MultiLookupTable.kt b/obfuscation-explorer/src/main/kotlin/lookup/MultiLookupTable.kt new file mode 100644 index 000000000..2d59acb5d --- /dev/null +++ b/obfuscation-explorer/src/main/kotlin/lookup/MultiLookupTable.kt @@ -0,0 +1,96 @@ +/* + * Minecraft Development for IntelliJ + * + * https://mcdev.io/ + * + * Copyright (C) 2024 minecraft-dev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, version 3.0 only. + * + * 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 Lesser General Public License + * along with this program. If not, see . + */ + +package io.mcdev.obfex.lookup + +import java.util.Collections +import kotlin.reflect.KClass + +typealias SingleLookupIndexTransformer = (T) -> K? +typealias LookupIndexTransformer = (T) -> List + +/** + * Data structure which uses [indices][LookupIndex] to improve query performance of multiple distinct lookups. + */ +interface MultiLookupTable { + + /** + * Get the backing store for this table. This list should not be modified, use [add] or [remove] methods on + * this class instead. + */ + fun seq(): Sequence + + fun list(): List + + fun firstOrNull(): T? { + return seq().firstOrNull() + } + + fun singleOrNull(): T? { + return seq().singleOrNull() + } + + /** + * Returns `true` if this table is empty. + */ + fun isEmpty(): Boolean = size == 0 + + /** + * Returns the number of elements in this table. + */ + val size: Int + + /** + * Create a new [index][LookupIndex] to allow for efficient queries to the data store. Lookup tables with more + * indices have worse write performance, as the indices have to be rebuilt, so a balance is a good idea. + */ + fun indexMulti(transformer: LookupIndexTransformer): LookupIndex + + fun index(transformer: SingleLookupIndexTransformer): LookupIndex = indexMulti { + listOfNotNull(transformer(it)) + } + + /** + * Add a new value to the table. + */ + fun add(value: T) + + /** + * Remove a value from the table. + */ + fun remove(value: T) + + fun > unwrap(type: KClass): L? + + companion object { + private val emptyTable = HashLookupTable() + + @Suppress("UNCHECKED_CAST") + fun empty(): MultiLookupTable = emptyTable as MultiLookupTable + } +} + +/** + * Naive query, not using any optimization, simply look through the raw store for any matches for the given + * predicate and return the result. For repeated queries consider using an [index][MultiLookupTable.index] to improve + * performance. + */ +inline fun MultiLookupTable.query(predicate: (T) -> Boolean): List = + Collections.unmodifiableList(list().filter(predicate)) diff --git a/obfuscation-explorer/src/main/kotlin/mappings/MappingElement.kt b/obfuscation-explorer/src/main/kotlin/mappings/MappingElement.kt new file mode 100644 index 000000000..85a1eed66 --- /dev/null +++ b/obfuscation-explorer/src/main/kotlin/mappings/MappingElement.kt @@ -0,0 +1,394 @@ +/* + * Minecraft Development for IntelliJ + * + * https://mcdev.io/ + * + * Copyright (C) 2024 minecraft-dev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, version 3.0 only. + * + * 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 Lesser General Public License + * along with this program. If not, see . + */ + +@file:Suppress("MemberVisibilityCanBePrivate", "DuplicatedCode") + +package io.mcdev.obfex.mappings + +import com.intellij.util.containers.map2Array +import io.mcdev.obfex.Tristate +import io.mcdev.obfex.ref.FieldName +import io.mcdev.obfex.ref.LocalFieldRef +import io.mcdev.obfex.ref.LocalMethodRef +import io.mcdev.obfex.ref.LocalVarIndex +import io.mcdev.obfex.ref.LvtIndex +import io.mcdev.obfex.ref.MethodDescriptor +import io.mcdev.obfex.ref.MethodName +import io.mcdev.obfex.ref.ParamIndex +import io.mcdev.obfex.ref.ParamMap +import io.mcdev.obfex.ref.TypeDef +import io.mcdev.obfex.ref.asFieldRef +import io.mcdev.obfex.ref.asMethodRef +import io.mcdev.obfex.ref.asParamIndex +import io.mcdev.obfex.ref.asRef +import it.unimi.dsi.fastutil.objects.ObjectArrayList +import java.lang.IllegalArgumentException +import java.util.ArrayList +import java.util.Collections + +sealed interface MappingElement { + val names: Array + val location: MappingLocation + val mappingSet: MappingSet + + fun name(index: Int): String? = names[index] + fun name(ns: MappingNamespace): String? = name(index(ns)) + + fun index(ns: MappingNamespace): Int = mappingSet.namespaceIndex(ns) +} + +val MappingElement.unnamedFrom: String? + get() = name(MappingNamespace.unnamedFrom(mappingSet)) +val MappingElement.unnamedTo: String? + get() = name(MappingNamespace.unnamedTo(mappingSet)) + +class PackageMappingElement( + override val names: Array, + override val mappingSet: MappingSet, + override val location: MappingLocation, +) : MappingElement + +class ClassMappingElement( + override val names: Array, + val parent: ClassMappingElement? = null, + override val mappingSet: MappingSet, + override val location: MappingLocation = UnknownLocation, +) : MappingElement { + + // classes generally have relatively few members (< 1000) so the naive approach for storing and querying them is + // probably better + private val fieldStore = ObjectArrayList() + private val methodStore = ObjectArrayList() + + fun fullyQualifiedName(index: Int): String? = when (val p = parent) { + is ClassMappingElement -> p.fullyQualifiedName(index) + "." + name(index) + else -> name(index) + } + + fun fullyQualifiedName(ns: MappingNamespace): String? = fullyQualifiedName(mappingSet.namespaceIndex(ns)) + + // fields + + fun fields(): List { + return Collections.unmodifiableList(fieldStore) + } + + fun fields(ref: LocalFieldRef): List { + if (ref.type == null) { + return fieldStore.filter { it.names.contains(ref.name.name) } + } + + // We don't know which namespace this field type is, so attempt to map for each namespace + val mappedTypes = if (ref.type.isMappable()) { + mappingSet.namespaces.map2Array { mappingSet.mapTypeFrom(it, ref.type) } + } else { + Array(mappingSet.namespaces.size) { ref.type } + } + + return fieldStore.filter { mapping -> + mappedTypes.withIndex().any { (i, type) -> + mapping.name(i) == ref.name.name && type == mapping.type + } + } + } + + fun fields(name: FieldName, type: TypeDef? = null): List = + fields(name.asRef(type)) + + fun fields(name: String, type: TypeDef? = null): List = + fields(name.asFieldRef(type)) + + fun fields(ns: MappingNamespace, ref: LocalFieldRef): List { + if (ref.type == null) { + return fieldStore.filter { it.name(ns) == ref.name.name } + } + + val type = if (ns != mappingSet.typeNamespace && ref.type.isMappable()) { + mappingSet.mapTypeFrom(ns, ref.type) + } else { + ref.type + } + + return fieldStore.filter { it.name(ns) == ref.name.name && it.type == type } + } + + fun fields(ns: MappingNamespace, name: FieldName, type: TypeDef? = null): List = + fields(ns, name.asRef(type)) + + fun fields(ns: MappingNamespace, name: String, type: TypeDef? = null): List = + fields(ns, name.asFieldRef(type)) + + fun field(ns: MappingNamespace, ref: LocalFieldRef): FieldMappingElement? = + fields(ns, ref).firstOrNull() + + fun field(ns: MappingNamespace, name: FieldName, type: TypeDef? = null): FieldMappingElement? = + field(ns, name.asRef(type)) + + fun field(ns: MappingNamespace, name: String, type: TypeDef? = null): FieldMappingElement? = + field(ns, name.asFieldRef(type)) + + fun field(ref: LocalFieldRef): FieldMappingElement? { + return mappingSet.namespaces.firstNotNullOfOrNull { ns -> field(ns, ref) } + } + + fun field(name: FieldName, type: TypeDef? = null): FieldMappingElement? = field(name.asRef(type)) + fun field(name: String, type: TypeDef? = null): FieldMappingElement? = field(name.asFieldRef(type)) + + fun addFieldMapping( + names: Array, + location: MappingLocation, + type: TypeDef? = null, + metadata: MemberMetadata = MemberMetadata.UNKNOWN, + ): FieldMappingElement { + val element = FieldMappingElement(names, this, type, metadata, mappingSet, location) + fieldStore.add(element) + return element + } + + // methods + + fun methods(): List { + return Collections.unmodifiableList(methodStore) + } + + fun methods(ref: LocalMethodRef): List { + if (ref.desc == null) { + return methodStore.filter { it.names.contains(ref.name.name) } + } + + // We don't know which namespace this field type is, so attempt to map for each namespace + val mappedTypes = if (ref.desc.isMappable()) { + mappingSet.namespaces.map2Array { mappingSet.mapTypeFrom(it, ref.desc) } + } else { + Array(mappingSet.namespaces.size) { ref.desc } + } + + return methodStore.filter { mapping -> + mappedTypes.withIndex().any { (i, type) -> + mapping.name(i) == ref.name.name && type == mapping.descriptor + } + } + } + + fun methods(name: MethodName, desc: MethodDescriptor? = null): List = + methods(name.asRef(desc)) + + fun methods(name: String, desc: MethodDescriptor? = null): List = + methods(name.asMethodRef(desc)) + + fun methods(ns: MappingNamespace, ref: LocalMethodRef): List { + if (ref.desc == null) { + return methodStore.filter { it.name(ns) == ref.name.name } + } + + val desc = if (ns != mappingSet.typeNamespace) { + mappingSet.mapTypeFrom(ns, ref.desc) + } else { + ref.desc + } + + return methodStore.filter { it.descriptor == desc } + } + + fun methods( + ns: MappingNamespace, + name: MethodName, + desc: MethodDescriptor? = null + ): List = methods(ns, name.asRef(desc)) + + fun methods( + ns: MappingNamespace, + name: String, + desc: MethodDescriptor? = null + ): List = methods(ns, name.asMethodRef(desc)) + + fun method(ns: MappingNamespace, ref: LocalMethodRef): MethodMappingElement? = + methods(ns, ref).firstOrNull() + + fun method(ns: MappingNamespace, name: MethodName, desc: MethodDescriptor? = null): MethodMappingElement? = + method(ns, name.asRef(desc)) + + fun method(ns: MappingNamespace, name: String, desc: MethodDescriptor? = null): MethodMappingElement? = + method(ns, name.asMethodRef(desc)) + + fun method(ref: LocalMethodRef): MethodMappingElement? { + return mappingSet.namespaces.firstNotNullOfOrNull { ns -> method(ns, ref) } + } + + fun method(name: MethodName, desc: MethodDescriptor? = null): MethodMappingElement? = + method(name.asRef(desc)) + + fun method(name: String, desc: MethodDescriptor? = null): MethodMappingElement? = + method(name.asMethodRef(desc)) + + fun addMethodMapping( + names: Array, + location: MappingLocation, + descriptor: MethodDescriptor, + metadata: MemberMetadata = MemberMetadata.UNKNOWN, + ): MethodMappingElement { + val element = MethodMappingElement(names, this, descriptor, metadata, mappingSet, location) + methodStore.add(element) + return element + } +} + +class FieldMappingElement( + override val names: Array, + val containingClass: ClassMappingElement, + val type: TypeDef? = null, + val metadata: MemberMetadata = MemberMetadata.UNKNOWN, + override val mappingSet: MappingSet = containingClass.mappingSet, + override val location: MappingLocation = UnknownLocation, +) : MappingElement + +class MethodMappingElement( + override val names: Array, + val containingClass: ClassMappingElement, + val descriptor: MethodDescriptor, + val metadata: MemberMetadata = MemberMetadata.UNKNOWN, + override val mappingSet: MappingSet = containingClass.mappingSet, + override val location: MappingLocation = UnknownLocation, +) : MappingElement { + + private val paramMappings = ParamMap() // keys should be ParameterRef + private val localVarMappings = ArrayList() // keys should be LvtRef + + fun descriptor(ns: MappingNamespace): String = + "(" + descriptor.params.joinToString("") { mappingSet.mapTypeTo(ns, it).descriptor } + ")" + + mappingSet.mapTypeTo(ns, descriptor.returnType).descriptor + + fun params(): Collection { + return Collections.unmodifiableCollection(paramMappings.values) + } + + fun param(index: ParamIndex): MethodParameterMappingElement? { + return paramMappings[index] + } + + fun param(index: Int): MethodParameterMappingElement? = param(index.asParamIndex()) + + fun addParamMapping( + names: Array, + location: MappingLocation, + paramIndex: ParamIndex, + lvtIndex: LvtIndex = LvtIndex.UNKNOWN, + type: TypeDef? = null, + ): MethodParameterMappingElement { + val existing = paramMappings[paramIndex] + + if (existing != null && location.priority <= existing.location.priority) { + // existing takes precedence, so ignore + return existing + } else { + val element = MethodParameterMappingElement(names, this, paramIndex, lvtIndex, type, mappingSet, location) + paramMappings[paramIndex] = element + return element + } + } + + fun localVars(): Collection { + return Collections.unmodifiableCollection(localVarMappings) + } + + fun localVars( + lvtIndex: LvtIndex = LvtIndex.UNKNOWN, + localVarIndex: LocalVarIndex = LocalVarIndex.UNKNOWN + ): List { + return localVarMappings.filter { + // only filter by values which are known + (!lvtIndex.isKnown || it.lvtIndex == lvtIndex) && + (!localVarIndex.index.isKnown || localVarIndex.index == it.localVarIndex.index) && + (!localVarIndex.localStart.isKnown || localVarIndex.localStart == it.localVarIndex.localStart) && + (!localVarIndex.localEnd.isKnown || localVarIndex.localEnd == it.localVarIndex.localEnd) + } + } + + fun localVars(index: LocalVarIndex): List { + return localVars(localVarIndex = index) + } + + fun localVar(index: LocalVarIndex): LocalVariableMappingElement? { + return localVars(index).firstOrNull() + } + + fun localVars(index: LvtIndex): List { + return localVars(lvtIndex = index) + } + + fun localVar(index: LvtIndex): LocalVariableMappingElement? { + return localVars(index).firstOrNull() + } + + fun addLocalVarMapping( + names: Array, + location: MappingLocation, + localVarIndex: LocalVarIndex = LocalVarIndex.UNKNOWN, + lvtIndex: LvtIndex = LvtIndex.UNKNOWN, + type: TypeDef? = null, + ): LocalVariableMappingElement { + val existing = localVars(localVarIndex = localVarIndex, lvtIndex = lvtIndex) + if (existing.isNotEmpty() && location.priority <= existing.first().location.priority) { + // existing takes precedence, so ignore + return existing.first() + } else { + val element = LocalVariableMappingElement(names, this, localVarIndex, lvtIndex, type, mappingSet, location) + localVarMappings += element + return element + } + } +} + +class MethodParameterMappingElement( + override val names: Array, + val containingMethod: MethodMappingElement, + val index: ParamIndex, + val lvtIndex: LvtIndex = LvtIndex.UNKNOWN, + val type: TypeDef? = null, + override val mappingSet: MappingSet = containingMethod.mappingSet, + override val location: MappingLocation = UnknownLocation, +) : MappingElement { + fun hasLvtIndex() = lvtIndex.isKnown +} + +data class MemberMetadata( + val isStatic: Tristate, + val isSynthetic: Tristate, +) { + companion object { + val UNKNOWN = MemberMetadata(Tristate.UNKNOWN, Tristate.UNKNOWN) + } +} + +class LocalVariableMappingElement( + override val names: Array, + val containingMethod: MethodMappingElement, + val localVarIndex: LocalVarIndex, + val lvtIndex: LvtIndex, + val type: TypeDef? = null, + override val mappingSet: MappingSet = containingMethod.mappingSet, + override val location: MappingLocation = UnknownLocation, +) : MappingElement { + init { + if (!localVarIndex.isKnown && !lvtIndex.isKnown) { + throw IllegalArgumentException("Cannot create LocalVariableMappingElement with unknown indices") + } + } +} diff --git a/obfuscation-explorer/src/main/kotlin/mappings/MappingLocation.kt b/obfuscation-explorer/src/main/kotlin/mappings/MappingLocation.kt new file mode 100644 index 000000000..5edd1c9d5 --- /dev/null +++ b/obfuscation-explorer/src/main/kotlin/mappings/MappingLocation.kt @@ -0,0 +1,72 @@ +/* + * Minecraft Development for IntelliJ + * + * https://mcdev.io/ + * + * Copyright (C) 2024 minecraft-dev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, version 3.0 only. + * + * 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 Lesser General Public License + * along with this program. If not, see . + */ + +package io.mcdev.obfex.mappings + +import io.mcdev.obfex.MappingPart +import io.mcdev.obfex.ref.LinePriority + +/** + * The location inside a [MappingSetSource] for a particular mapping. + * + * [priority] allows for different occurrences of a mapping to specify whether they should take precedence over another. + * + * For example, if a member mapping exists which indirectly defines a class mapping, that location would be considered + * lower priority than the direct class mapping itself. The priority system allows the indirect mapping to still be used + * if no direct mapping exists. + */ +sealed interface MappingLocation { + /** + * Numeric priority for this mapping, where high values are considered higher priority. + */ + val priority: Int + + fun withPriority(priority: Int): MappingLocation { + return when (this) { + is FileCoords -> copy(priority = priority) + is UnknownLocation -> this + } + } +} + +/** + * A [MappingLocation] within a mapping file. Used in conjunction with a [MappingsFile] + */ +data class FileCoords(val line: Int, val col: Int = 1, override val priority: Int = 0) : MappingLocation { + constructor(line: LinePriority?, col: Int = 1) : this(line?.coord ?: -1, col, line?.priority ?: 0) + constructor(line: LinePriority?, part: MappingPart) : this(line, part.col) + constructor(line: Int, part: MappingPart) : this(line, part.col) + + override fun withPriority(priority: Int): FileCoords { + return super.withPriority(priority) as FileCoords + } +} + +/** + * An unknown [MappingLocation]. + */ +data object UnknownLocation : MappingLocation { + override val priority: Int + get() = Int.MIN_VALUE + + override fun withPriority(priority: Int): MappingLocation { + return this + } +} diff --git a/obfuscation-explorer/src/main/kotlin/mappings/MappingNamespace.kt b/obfuscation-explorer/src/main/kotlin/mappings/MappingNamespace.kt new file mode 100644 index 000000000..3f7a391d3 --- /dev/null +++ b/obfuscation-explorer/src/main/kotlin/mappings/MappingNamespace.kt @@ -0,0 +1,39 @@ +/* + * Minecraft Development for IntelliJ + * + * https://mcdev.io/ + * + * Copyright (C) 2024 minecraft-dev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, version 3.0 only. + * + * 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 Lesser General Public License + * along with this program. If not, see . + */ + +package io.mcdev.obfex.mappings + +/** + * A namespace is a collection of names in a mapping. A single mapping set can have an arbitrary number of namespaces, + * each namespace providing a different name for the same unique element. + */ +data class MappingNamespace( + val name: String, + val index: Int, + val associatedSet: MappingSet?, +) { + companion object { + const val UNNAMED_FROM: String = "from" + const val UNNAMED_TO: String = "to" + + fun unnamedFrom(set: MappingSet) = set.namespaceOf(UNNAMED_FROM) + fun unnamedTo(set: MappingSet) = set.namespaceOf(UNNAMED_TO) + } +} diff --git a/obfuscation-explorer/src/main/kotlin/mappings/MappingSet.kt b/obfuscation-explorer/src/main/kotlin/mappings/MappingSet.kt new file mode 100644 index 000000000..c97dde96e --- /dev/null +++ b/obfuscation-explorer/src/main/kotlin/mappings/MappingSet.kt @@ -0,0 +1,234 @@ +/* + * Minecraft Development for IntelliJ + * + * https://mcdev.io/ + * + * Copyright (C) 2024 minecraft-dev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, version 3.0 only. + * + * 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 Lesser General Public License + * along with this program. If not, see . + */ + +package io.mcdev.obfex.mappings + +import io.mcdev.obfex.mappings.store.MappingLookupTable +import io.mcdev.obfex.ref.ArrayTypeDef +import io.mcdev.obfex.ref.ClassName +import io.mcdev.obfex.ref.ClassTypeDef +import io.mcdev.obfex.ref.FieldRef +import io.mcdev.obfex.ref.MethodDescriptor +import io.mcdev.obfex.ref.MethodRef +import io.mcdev.obfex.ref.PackName +import io.mcdev.obfex.ref.PrimitiveTypeDef +import io.mcdev.obfex.ref.ReturnTypeDef +import io.mcdev.obfex.ref.VoidTypeDef +import io.mcdev.obfex.ref.asClass +import io.mcdev.obfex.ref.asPackage + +@Suppress("MemberVisibilityCanBePrivate") +class MappingSet(namespaceNames: Iterable = emptyList()) { + + /** + * All type definitions for each element is stored using this namespace. + */ + val typeNamespace: MappingNamespace + + /** + * The namespaces present in this mapping set. + */ + val namespaces: Array + + private val packageStore = MappingLookupTable(this) + private val classStore = MappingLookupTable(this) + + init { + val names = namespaceNames.toList() + if (names.isEmpty()) { + typeNamespace = MappingNamespace(MappingNamespace.UNNAMED_FROM, 0, this) + namespaces = arrayOf(typeNamespace, MappingNamespace(MappingNamespace.UNNAMED_TO, 1, this)) + } else if (names.size >= 2) { + namespaces = Array(names.size) { index -> MappingNamespace(names[index], index, this) } + typeNamespace = namespaces[0] + } else { + throw IllegalArgumentException("namespaceNames must contain at least 2 namespaces: $names") + } + } + + fun namespaceOf(name: String): MappingNamespace = namespaceOfOrNull(name) + ?: throw IllegalArgumentException("Could not find namespace: $name") + + fun namespaceOfOrNull(name: String): MappingNamespace? = namespaces.find { it.name == name } + + private fun findNamespace(ns: MappingNamespace): MappingNamespace = if (ns.associatedSet === this) { + ns + } else { + namespaces.find { it.name == ns.name } + ?: throw IllegalArgumentException( + "Provided namespace is not associated with the current MappingSet: ${ns.name}" + ) + } + + fun namespaceIndex(name: String): Int { + return namespaceIndex(namespaceOf(name)) + } + + fun namespaceIndex(ns: MappingNamespace): Int = findNamespace(ns).index + + fun mapType(fromNs: MappingNamespace, toNs: MappingNamespace, typeDef: T): T { + when (typeDef) { + is VoidTypeDef, is PrimitiveTypeDef -> return typeDef + } + + val fromNamespace = findNamespace(fromNs) + val toNamespace = findNamespace(toNs) + if (fromNamespace === toNamespace) { + return typeDef + } + + @Suppress("UNCHECKED_CAST") + return when (typeDef) { + is ArrayTypeDef -> ArrayTypeDef( + mapType(fromNamespace, toNamespace, typeDef.componentType), typeDef.dimension + ) as T + + is ClassTypeDef -> { + val name = classStore.forNamespace(fromNamespace) + .query(typeDef.className.name) + .firstOrNull() + ?.fullyQualifiedName(toNamespace) + ?.asClass() + ?: typeDef.className // fallback to provided name if not found + ClassTypeDef(name) as T + } + + else -> throw IllegalStateException("Unexpected typedef: " + typeDef.javaClass.name) + } + } + + fun mapType(fromNs: MappingNamespace, toNs: MappingNamespace, desc: MethodDescriptor): MethodDescriptor { + return MethodDescriptor(desc.params.map { mapType(fromNs, toNs, it) }, mapType(fromNs, toNs, desc.returnType)) + } + + /** + * Map the given type to the [typeNamespace]. + */ + fun mapTypeTo(ns: MappingNamespace, typeDef: T): T { + return mapType(typeNamespace, ns, typeDef) + } + + /** + * Map the given type to the [typeNamespace]. + */ + fun mapTypeTo(ns: MappingNamespace, desc: MethodDescriptor): MethodDescriptor { + return mapType(typeNamespace, ns, desc) + } + + /** + * Map the given type from the [typeNamespace]. + */ + fun mapTypeFrom(ns: MappingNamespace, typeDef: T): T { + return mapType(ns, typeNamespace, typeDef) + } + + /** + * Map the given type from the [typeNamespace]. + */ + fun mapTypeFrom(ns: MappingNamespace, desc: MethodDescriptor): MethodDescriptor { + return mapType(ns, typeNamespace, desc) + } + + // package + fun packs(): List { + return packageStore.list() + } + + fun packs(name: PackName): List { + return packageStore.byName.query(name.name).list() + } + fun packs(name: String): List = packs(name.asPackage()) + + fun pack(name: PackName): PackageMappingElement? { + return packageStore.byName.query(name.name).firstOrNull() + } + fun pack(name: String): PackageMappingElement? = pack(name.asPackage()) + + fun packs(ns: MappingNamespace, name: PackName): List { + return packageStore.forNamespace(ns).query(name.name).list() + } + fun packs(ns: MappingNamespace, name: String): List = + packs(ns, name.asPackage()) + + fun pack(ns: MappingNamespace, name: PackName): PackageMappingElement? { + return packageStore.forNamespace(ns).query(name.name).firstOrNull() + } + fun pack(ns: MappingNamespace, name: String): PackageMappingElement? = pack(ns, name.asPackage()) + + fun addPackageMapping(names: Array, location: MappingLocation): PackageMappingElement { + val element = PackageMappingElement(names, this, location) + packageStore.add(element) + return element + } + + // class + fun classes(): List { + return classStore.list() + } + + fun classes(name: ClassName): List { + return classStore.byName.query(name.name).list() + } + fun classes(name: String): List = classes(name.asClass()) + + fun clazz(name: ClassName): ClassMappingElement? { + return classStore.byName.query(name.name).firstOrNull() + } + fun clazz(name: String): ClassMappingElement? = clazz(name.asClass()) + + fun classes(ns: MappingNamespace, name: ClassName): List { + return classStore.forNamespace(ns).query(name.name).list() + } + fun classes(ns: MappingNamespace, name: String): List = classes(ns, name.asClass()) + + fun clazz(ns: MappingNamespace, name: ClassName): ClassMappingElement? { + return classStore.forNamespace(ns).query(name.name).firstOrNull() + } + fun clazz(ns: MappingNamespace, name: String): ClassMappingElement? = clazz(ns, name.asClass()) + + fun addClassMapping( + names: Array, + parent: ClassMappingElement?, + location: MappingLocation + ): ClassMappingElement { + val element = ClassMappingElement(names, parent, this, location) + classStore.add(element) + return element + } + + // members + fun fieldMapping(field: FieldRef, ns: MappingNamespace): FieldMappingElement? { + val clazz = clazz(ns, field.containingClass) ?: return null + return clazz.field(ns, field.field) + } + + fun fieldMapping(field: FieldRef): FieldMappingElement? { + return namespaces.firstNotNullOfOrNull { ns -> fieldMapping(field, ns) } + } + + fun methodMapping(method: MethodRef, ns: MappingNamespace): MethodMappingElement? { + val clazz = clazz(ns, method.containingClass) ?: return null + return clazz.method(ns, method.method) + } + + fun methodMapping(method: MethodRef): MethodMappingElement? { + return namespaces.firstNotNullOfOrNull { ns -> methodMapping(method, ns) } + } +} diff --git a/obfuscation-explorer/src/main/kotlin/mappings/MappingSetBuilder.kt b/obfuscation-explorer/src/main/kotlin/mappings/MappingSetBuilder.kt new file mode 100644 index 000000000..cd6b68901 --- /dev/null +++ b/obfuscation-explorer/src/main/kotlin/mappings/MappingSetBuilder.kt @@ -0,0 +1,412 @@ +/* + * Minecraft Development for IntelliJ + * + * https://mcdev.io/ + * + * Copyright (C) 2024 minecraft-dev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, version 3.0 only. + * + * 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 Lesser General Public License + * along with this program. If not, see . + */ + +@file:Suppress("MemberVisibilityCanBePrivate") + +package io.mcdev.obfex.mappings + +import com.intellij.util.containers.map2Array +import io.mcdev.obfex.mappings.MappingNamespace.Companion.UNNAMED_FROM +import io.mcdev.obfex.mappings.MappingNamespace.Companion.UNNAMED_TO +import io.mcdev.obfex.ref.ClassName +import io.mcdev.obfex.ref.FieldName +import io.mcdev.obfex.ref.LinePriority +import io.mcdev.obfex.ref.LocalMemberRef +import io.mcdev.obfex.ref.LocalMethodRef +import io.mcdev.obfex.ref.LocalVarIndex +import io.mcdev.obfex.ref.LocalVarName +import io.mcdev.obfex.ref.LvtIndex +import io.mcdev.obfex.ref.MemberName +import io.mcdev.obfex.ref.MethodDescriptor +import io.mcdev.obfex.ref.MethodName +import io.mcdev.obfex.ref.PackName +import io.mcdev.obfex.ref.ParamIndex +import io.mcdev.obfex.ref.ParamName +import io.mcdev.obfex.ref.TypeDef +import it.unimi.dsi.fastutil.ints.Int2ObjectFunction +import it.unimi.dsi.fastutil.ints.Int2ObjectLinkedOpenHashMap + +interface MappingSetBuilderCore { + fun pack(line: LinePriority? = null, col: Int = 1): PackageMappingBuilder + fun clazz(line: LinePriority? = null, col: Int = 1): ClassMappingBuilder + + fun clazz(ref: NS, line: LinePriority? = null, col: Int = 1): ClassMappingBuilder +} + +class MappingSetBuilder( + val issues: MappingIssuesRegistry, + vararg namespaces: String, +) : MappingSetBuilderCore, MappingIssuesRegistry by issues { + + val namespaces: Array = if (namespaces.isEmpty()) { + arrayOf(UNNAMED_FROM, UNNAMED_TO) + } else { + // Kotlin wants `namespaces` to be Array + @Suppress("UNCHECKED_CAST") + namespaces as Array + } + + val packageMappings = ArrayList() + val classMappings = ArrayList() + + // mapping files often keep mappings lines for the same class together, so this is an easy way to optimize that case + private var lastClassMapping: ClassMappingBuilder? = null + + override fun pack(line: LinePriority?, col: Int): PackageMappingBuilder { + return PackageMappingBuilder(namespaces, this, FileCoords(line, col)).also { packageMappings += it } + } + + override fun clazz(line: LinePriority?, col: Int): ClassMappingBuilder { + return ClassMappingBuilder(namespaces, this, FileCoords(line, col)).also { classMappings += it } + } + + override fun clazz(ref: NS, line: LinePriority?, col: Int): ClassMappingBuilder { + if (lastClassMapping?.names?.get(ref.ns) == ref.v.name) { + return lastClassMapping!! + } + val res = + classMappings.firstOrNull { it.names[ref.ns] == ref.v.name } + ?: ClassMappingBuilder(namespaces, this, FileCoords(line, col)).also { + classMappings += it + it.with(ref) + } + lastClassMapping = res + return res + } + + fun build(): MappingSet { + val set = MappingSet(namespaces.asIterable()) + + packageMappings.forEach { it.build(set, issues) } + classMappings.forEach { it.build(set, issues) } + + return set + } +} + +class PackageMappingBuilder( + namespaces: Array, + val parent: MappingSetBuilder, + var coords: FileCoords, +) : NamingBuilder(namespaces) { + + override fun addName(ref: NS) { + names[ref.ns] = ref.v + } + + fun build(set: MappingSet, issues: MappingIssuesRegistry) { + val n = names.map2Array { name -> + if (name == null) { + issues.warning("Package mapping found without target name", coords) + } + name?.takeIf { it.isNotBlank() } + } + if (n.all { it == null }) { + issues.error("Package mapping found with no target names", coords) + } + + set.addPackageMapping(n, coords) + } +} + +class ClassMappingBuilder( + namespaces: Array, + val parent: MappingSetBuilder, + var coords: FileCoords, +) : NamingBuilder(namespaces), MappingIssuesRegistry by parent { + + val fieldMappings = ArrayList() + val methodMappings = ArrayList() + + private var lastMethodMapping: MethodMappingBuilder? = null + + val set: MappingSetBuilder + get() = parent + + fun field(line: LinePriority? = null, col: Int = 1): FieldMappingBuilder { + return FieldMappingBuilder(namespaces, this, FileCoords(line, col)).also { fieldMappings += it } + } + + fun method(line: LinePriority? = null, col: Int = 1): MethodMappingBuilder { + return MethodMappingBuilder(namespaces, this, FileCoords(line, col)).also { methodMappings += it } + } + + fun method(ref: NS, line: LinePriority? = null, col: Int = 1): MethodMappingBuilder { + if (lastMethodMapping?.names?.get(ref.ns) == ref.v.name.name && lastMethodMapping?.desc == ref.v.desc) { + return lastMethodMapping!! + } + val res = + methodMappings.firstOrNull { it.names[ref.ns] == ref.v.name.name } + ?: MethodMappingBuilder(namespaces, this, FileCoords(line, col)).also { + methodMappings += it + it.desc = ref.v.desc + it.with(ref.withValue { name }) + } + lastMethodMapping = res + return res + } + + override fun addName(ref: NS) { + names[ref.ns] = ref.v + } + + fun build(set: MappingSet, issues: MappingIssuesRegistry, parentElement: ClassMappingElement? = null) { + val n = names.map2Array { name -> + if (name == null) { + issues.warning("Class mapping found without target name", coords) + } + name?.takeIf { it.isNotBlank() } + } + if (n.all { it == null }) { + issues.error("Class mapping found with no target names", coords) + } + + val element = set.addClassMapping(n, parentElement, coords) + + fieldMappings.forEach { it.build(issues, element) } + methodMappings.forEach { it.build(issues, element) } + } +} + +class FieldMappingBuilder( + namespaces: Array, + val parent: ClassMappingBuilder, + var coords: FileCoords, +) : NamingBuilder(namespaces), MappingIssuesRegistry by parent { + + var type: TypeDef? = null + + var meta: MemberMetadata = MemberMetadata.UNKNOWN + + override fun addName(ref: NS) { + names[ref.ns] = ref.v + } + + fun build(issues: MappingIssuesRegistry, parent: ClassMappingElement) { + val n = names.map2Array { name -> + if (name == null) { + issues.warning("Field mapping found without target name", coords) + } + name?.takeIf { it.isNotBlank() } + } + if (n.all { it == null }) { + issues.error("Field mapping found with no target names", coords) + } + + parent.addFieldMapping(n, coords, type, meta) + } +} + +class MethodMappingBuilder( + namespaces: Array, + val parent: ClassMappingBuilder, + var coords: FileCoords, +) : NamingBuilder(namespaces), MappingIssuesRegistry by parent { + + var desc: MethodDescriptor? = null + + val paramMappings = Int2ObjectLinkedOpenHashMap() + val localVarMappings = ArrayList() + + var meta: MemberMetadata = MemberMetadata.UNKNOWN + + fun param(index: ParamIndex, line: LinePriority? = null, col: Int = 1): ParamMappingBuilder { + return paramMappings.compIfAbsent(index.index) { + ParamMappingBuilder(namespaces, this, index, FileCoords(line, col)) + } + } + + // tiny-v2 is the only format that I know of that supports locals, it only requires a local var index, but I could + // see other formats might have different requirements, so this stays as flexible as possible, not requiring any + fun localVar( + localVarIndex: LocalVarIndex = LocalVarIndex.UNKNOWN, + lvtIndex: LvtIndex = LvtIndex.UNKNOWN, + line: LinePriority? = null, + col: Int = 1 + ): LocalVarMappingBuilder { + return localVarMappings.firstOrNull { mapping -> + mapping.localVarIndex == localVarIndex && mapping.lvtIndex == lvtIndex + } ?: LocalVarMappingBuilder(namespaces, this, localVarIndex, lvtIndex, FileCoords(line, col)) + .also { localVarMappings += it } + } + + override fun addName(ref: NS) { + names[ref.ns] = ref.v + } + + fun build(issues: MappingIssuesRegistry, parent: ClassMappingElement) { + val n = names.map2Array { name -> + if (name == null) { + issues.warning("Method mapping found without target name", coords) + } + name?.takeIf { it.isNotBlank() } + } + if (n.all { it == null }) { + issues.error("Method mapping found with no target names", coords) + } + + val desc = this.desc ?: return issues.error("Method mappings must have a descriptor", coords) + + val element = parent.addMethodMapping(n, coords, desc, meta) + + paramMappings.values.forEach { it.build(issues, element) } + localVarMappings.forEach { it.build(issues, element) } + } +} + +class ParamMappingBuilder( + namespaces: Array, + val parent: MethodMappingBuilder, + val index: ParamIndex, + var coords: FileCoords, +) : NamingBuilder(namespaces), MappingIssuesRegistry by parent { + + var lvtRef: LvtIndex? = LvtIndex.UNKNOWN + + var type: TypeDef? = null + + override fun addName(ref: NS) { + names[ref.ns] = ref.v + } + + fun build(issues: MappingIssuesRegistry, parent: MethodMappingElement) { + val n = names.map2Array { name -> name?.takeIf { it.isNotBlank() } } + if (n.all { it == null }) { + issues.error("Method parameter mapping found with no target names", coords) + } + + parent.addParamMapping(n, coords, index, lvtRef ?: LvtIndex.UNKNOWN, type) + } +} + +class LocalVarMappingBuilder( + namespaces: Array, + val parent: MethodMappingBuilder, + var localVarIndex: LocalVarIndex, + var lvtIndex: LvtIndex, + var coords: FileCoords, +) : NamingBuilder(namespaces), MappingIssuesRegistry by parent { + + var type: TypeDef? = null + + override fun addName(ref: NS) { + names[ref.ns] = ref.v + } + + fun build(issues: MappingIssuesRegistry, parent: MethodMappingElement) { + if (!localVarIndex.isKnown && !lvtIndex.isKnown) { + return issues.error("Method local var mapping found without known any known indices", coords) + } + + val n = names.map2Array { name -> name?.takeIf { it.isNotBlank() } } + if (n.all { it == null }) { + issues.error("Method local var mapping found with no target names", coords) + } + + parent.addLocalVarMapping(n, coords, localVarIndex, lvtIndex, type) + } +} + +abstract class NamingBuilder(val namespaces: Array) { + + val names: Array = arrayOfNulls(namespaces.size) + + operator fun set(ns: String, name: T) { + set(namespaces.indexOf(ns), name) + } + + operator fun set(index: Int, name: T) { + with(NS(name, index)) + } + + fun with(ref: NS) { + addName(ref.forName()) + } + + fun unlessExists(ref: NS) { + if (names[ref.ns] != null) { + return + } + addName(ref.forName()) + } + + protected abstract fun addName(ref: NS) +} + +// as in "NameSpace" +data class NS(val v: V, val ns: Int) { + fun withValue(newV: T): NS = NS(newV, ns) + inline fun withValue(block: V.() -> T): NS = NS(v.block(), ns) +} +fun NS.forName(): NS = NS(v.name, ns) + +fun T.ns(ns: Int): NS = NS(this, ns) +fun > T.ns(ns: Int): NS = NS(this, ns) + +// helpers + +private fun M.compIfAbsent(key: Int, func: Int2ObjectFunction): V + where M : Int2ObjectLinkedOpenHashMap = computeIfAbsent(key, func) + +inline fun MappingSetBuilderCore.pack( + line: LinePriority? = null, + col: Int = 1, + conf: PackageMappingBuilder.() -> Unit, +) = pack(line, col).apply(conf) + +inline fun MappingSetBuilderCore.clazz( + line: LinePriority? = null, + col: Int = 1, + conf: ClassMappingBuilder.() -> Unit, +) = clazz(line, col).apply(conf) + +inline fun MappingSetBuilderCore.clazz( + ref: NS, + line: LinePriority? = null, + col: Int = 1, + conf: ClassMappingBuilder.() -> Unit, +) = clazz(ref, line, col).apply(conf) + +inline fun ClassMappingBuilder.field( + line: LinePriority? = null, + col: Int = 1, + conf: FieldMappingBuilder.() -> Unit, +) = field(line, col).apply(conf) + +inline fun ClassMappingBuilder.method( + line: LinePriority? = null, + col: Int = 1, + conf: MethodMappingBuilder.() -> Unit, +) = method(line, col).apply(conf) + +inline fun MethodMappingBuilder.param( + index: ParamIndex, + line: LinePriority? = null, + col: Int = 1, + conf: ParamMappingBuilder.() -> Unit, +) = param(index, line, col).apply(conf) + +inline fun MethodMappingBuilder.localVar( + localVarIndex: LocalVarIndex = LocalVarIndex.UNKNOWN, + lvtIndex: LvtIndex = LvtIndex.UNKNOWN, + line: LinePriority? = null, + col: Int = 1, + conf: LocalVarMappingBuilder.() -> Unit, +) = localVar(localVarIndex, lvtIndex, line, col).apply(conf) diff --git a/obfuscation-explorer/src/main/kotlin/mappings/MappingSetSource.kt b/obfuscation-explorer/src/main/kotlin/mappings/MappingSetSource.kt new file mode 100644 index 000000000..d33b6bc25 --- /dev/null +++ b/obfuscation-explorer/src/main/kotlin/mappings/MappingSetSource.kt @@ -0,0 +1,69 @@ +/* + * Minecraft Development for IntelliJ + * + * https://mcdev.io/ + * + * Copyright (C) 2024 minecraft-dev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, version 3.0 only. + * + * 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 Lesser General Public License + * along with this program. If not, see . + */ + +package io.mcdev.obfex.mappings + +import com.intellij.openapi.vfs.VirtualFile +import io.mcdev.obfex.formats.MappingsFormatType + +/** + * Defines an issue (warning / error) related to a parsed mapping file. Ideally the [coords] are provided as well to + * identify where the issue occurs. + */ +data class MappingParseIssue(val message: String, val coords: FileCoords?) + +/** + * Builder interface for registering new issues, shared between several builders. + */ +interface MappingIssuesRegistry { + fun error(message: String, coords: FileCoords? = null) + fun warning(message: String, coords: FileCoords? = null) +} + +sealed interface MappingSetSource { + val errors: List + val warnings: List +} + +class MappingsFile( + val file: VirtualFile, + val type: MappingsFormatType, + override val errors: List = emptyList(), + override val warnings: List = emptyList(), +) : MappingSetSource + +class MappingsFileBuilder( + val file: VirtualFile, + val type: MappingsFormatType, +) : MappingIssuesRegistry { + + val errors: MutableList = mutableListOf() + val warnings: MutableList = mutableListOf() + + override fun error(message: String, coords: FileCoords?) { + errors.add(MappingParseIssue(message, coords)) + } + + override fun warning(message: String, coords: FileCoords?) { + warnings.add(MappingParseIssue(message, coords)) + } + + fun build() = MappingsFile(file, type, errors.toList(), warnings.toList()) +} diff --git a/obfuscation-explorer/src/main/kotlin/mappings/MappingsDefinition.kt b/obfuscation-explorer/src/main/kotlin/mappings/MappingsDefinition.kt new file mode 100644 index 000000000..4ddc678d1 --- /dev/null +++ b/obfuscation-explorer/src/main/kotlin/mappings/MappingsDefinition.kt @@ -0,0 +1,54 @@ +/* + * Minecraft Development for IntelliJ + * + * https://mcdev.io/ + * + * Copyright (C) 2024 minecraft-dev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, version 3.0 only. + * + * 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 Lesser General Public License + * along with this program. If not, see . + */ + +package io.mcdev.obfex.mappings + +import com.intellij.openapi.vfs.VirtualFile +import io.mcdev.obfex.formats.MappingsFormatType + +/** + * Complete top-level definition for a collection of mappings, which includes the mappings themselves and their source. + */ +class MappingsDefinition( + val mappings: MappingSet, + val source: MappingSetSource, +) + +class MappingsDefinitionBuilder( + val mappings: MappingSetBuilder, + val source: MappingsFileBuilder, +) : MappingSetBuilderCore by mappings, MappingIssuesRegistry by mappings.issues { + + constructor(fileBuilder: MappingsFileBuilder, vararg namespaces: String) : + this(MappingSetBuilder(fileBuilder, *namespaces), fileBuilder) + + constructor(type: MappingsFormatType, file: VirtualFile, vararg namespaces: String) : + this(MappingsFileBuilder(file, type), *namespaces) + + val namespaces: Array + get() = mappings.namespaces + + val issues: MappingIssuesRegistry + get() = mappings.issues + + fun ns(namespace: String): Int = namespace.indexOf(namespace) + + fun build() = MappingsDefinition(mappings.build(), source.build()) +} diff --git a/obfuscation-explorer/src/main/kotlin/mappings/MappingsFormatParser.kt b/obfuscation-explorer/src/main/kotlin/mappings/MappingsFormatParser.kt new file mode 100644 index 000000000..09537e1c9 --- /dev/null +++ b/obfuscation-explorer/src/main/kotlin/mappings/MappingsFormatParser.kt @@ -0,0 +1,88 @@ +/* + * Minecraft Development for IntelliJ + * + * https://mcdev.io/ + * + * Copyright (C) 2024 minecraft-dev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, version 3.0 only. + * + * 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 Lesser General Public License + * along with this program. If not, see . + */ + +package io.mcdev.obfex.mappings + +import com.intellij.openapi.extensions.ExtensionPointName +import com.intellij.openapi.vfs.VirtualFile +import io.mcdev.obfex.mappings.MappingsFormatParser.Companion.EP_NAME +import io.mcdev.obfex.ref.LocalMemberRef +import io.mcdev.obfex.ref.MemberName + +/** + * A parser for Java obfuscation mapping file formats. Provide implementations using the [EP_NAME] extension point. + */ +interface MappingsFormatParser { + + /** + * File extensions this parser expects to see. This will be the first thing checked to determine the file type, if + * multiple parsers share the same extension match then [isSupportedFile] will be used as a fallback. + */ + val expectedFileExtensions: Array + + /** + * Quickly check the file to determine if this parser supports it. Only used as a fallback if multiple parsers + * match the same [expectedFileExtensions]. + */ + fun isSupportedFile(file: VirtualFile): Boolean = false + + /** + * Attempt to parse the given file into a [MappingsDefinition]. If the file type is unknown or not supported by this + * parser, or the file contents are too corrupt to parse, return `null`. + * + * It's generally preferable to return a _partial_ [MappingsDefinition] when the mappings file contains errors (that + * is, it's preferable to skip over errors rather than fail completely). Record parse errors in the + * [MappingsDefinition] so they can be presented to the user. + */ + fun parse(file: VirtualFile): MappingsDefinition? + + companion object { + @JvmStatic + val EP_NAME = ExtensionPointName.create("io.mcdev.obfex.mappingsFormatParser") + } +} + +/** + * A [MappingsFormatParser] for mapping formats without namespaces. + */ +interface UnnamedMappingsFormatParser : MappingsFormatParser { + + /** + * Helper property for unnamed mapping formats. + */ + val unnamedFrom: Int + get() = 0 + + /** + * Helper property for unnamed mapping formats. + */ + val unnamedTo: Int + get() = 1 + + val T.from: NS + get() = ns(unnamedFrom) + val T.to: NS + get() = ns(unnamedTo) + + val > T.from: NS + get() = ns(unnamedFrom) + val > T.to: NS + get() = ns(unnamedTo) +} diff --git a/obfuscation-explorer/src/main/kotlin/mappings/store/MappingLookupIndex.kt b/obfuscation-explorer/src/main/kotlin/mappings/store/MappingLookupIndex.kt new file mode 100644 index 000000000..a9a9c4ec5 --- /dev/null +++ b/obfuscation-explorer/src/main/kotlin/mappings/store/MappingLookupIndex.kt @@ -0,0 +1,44 @@ +/* + * Minecraft Development for IntelliJ + * + * https://mcdev.io/ + * + * Copyright (C) 2024 minecraft-dev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, version 3.0 only. + * + * 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 Lesser General Public License + * along with this program. If not, see . + */ + +package io.mcdev.obfex.mappings.store + +import io.mcdev.obfex.lookup.LookupIndex +import io.mcdev.obfex.mappings.MappingElement +import io.mcdev.obfex.mappings.MappingSet +import kotlin.reflect.KClass + +class MappingLookupIndex( + private val set: MappingSet, + private val index: LookupIndex +) : LookupIndex by index { + + override fun query(key: K?): MappingLookupTable { + return MappingLookupTable(set, index.query(key)) + } + + override fun > unwrap(type: KClass): L? { + if (type.isInstance(this)) { + @Suppress("UNCHECKED_CAST") + return this as L + } + return index.unwrap(type) + } +} diff --git a/obfuscation-explorer/src/main/kotlin/mappings/store/MappingLookupTable.kt b/obfuscation-explorer/src/main/kotlin/mappings/store/MappingLookupTable.kt new file mode 100644 index 000000000..0a906b45c --- /dev/null +++ b/obfuscation-explorer/src/main/kotlin/mappings/store/MappingLookupTable.kt @@ -0,0 +1,75 @@ +/* + * Minecraft Development for IntelliJ + * + * https://mcdev.io/ + * + * Copyright (C) 2024 minecraft-dev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, version 3.0 only. + * + * 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 Lesser General Public License + * along with this program. If not, see . + */ + +package io.mcdev.obfex.mappings.store + +import io.mcdev.obfex.lookup.HashLookupTable +import io.mcdev.obfex.lookup.LookupIndexTransformer +import io.mcdev.obfex.lookup.MultiLookupTable +import io.mcdev.obfex.lookup.SingleLookupIndexTransformer +import io.mcdev.obfex.mappings.MappingElement +import io.mcdev.obfex.mappings.MappingNamespace +import io.mcdev.obfex.mappings.MappingSet +import kotlin.reflect.KClass + +open class MappingLookupTable( + private val set: MappingSet, + private val table: MultiLookupTable = HashLookupTable() +) : MultiLookupTable by table { + + // lookups based on namespace + private val namespaceIndices = mutableListOf>() + + fun forNamespace(namespaceIndex: Int): MappingLookupIndex { + val lookup = namespaceIndices.firstOrNull { it.index == namespaceIndex } + if (lookup != null) { + return lookup.lookup + } + + val newLookup = index { it.name(namespaceIndex) } + namespaceIndices += NamespaceLookup(namespaceIndex, newLookup) + return newLookup + } + + fun forNamespace(namespace: MappingNamespace): MappingLookupIndex { + val index = set.namespaceIndex(namespace) + return forNamespace(index) + } + + val byName: MappingLookupIndex by lazy { + MappingLookupIndex(set, indexMulti { it.names.filterNotNull() }) + } + + override fun index(transformer: SingleLookupIndexTransformer): MappingLookupIndex { + return MappingLookupIndex(set, table.index(transformer)) + } + + override fun indexMulti(transformer: LookupIndexTransformer): MappingLookupIndex { + return MappingLookupIndex(set, table.indexMulti(transformer)) + } + + override fun > unwrap(type: KClass): L? { + if (type.isInstance(this)) { + @Suppress("UNCHECKED_CAST") + return this as L + } + return table.unwrap(type) + } +} diff --git a/obfuscation-explorer/src/main/kotlin/mappings/store/NamespaceLookup.kt b/obfuscation-explorer/src/main/kotlin/mappings/store/NamespaceLookup.kt new file mode 100644 index 000000000..02d48661c --- /dev/null +++ b/obfuscation-explorer/src/main/kotlin/mappings/store/NamespaceLookup.kt @@ -0,0 +1,25 @@ +/* + * Minecraft Development for IntelliJ + * + * https://mcdev.io/ + * + * Copyright (C) 2024 minecraft-dev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, version 3.0 only. + * + * 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 Lesser General Public License + * along with this program. If not, see . + */ + +package io.mcdev.obfex.mappings.store + +import io.mcdev.obfex.mappings.MappingElement + +data class NamespaceLookup(val index: Int, val lookup: MappingLookupIndex) diff --git a/obfuscation-explorer/src/main/kotlin/ref/ref.kt b/obfuscation-explorer/src/main/kotlin/ref/ref.kt new file mode 100644 index 000000000..06cbf58e6 --- /dev/null +++ b/obfuscation-explorer/src/main/kotlin/ref/ref.kt @@ -0,0 +1,224 @@ +/* + * Minecraft Development for IntelliJ + * + * https://mcdev.io/ + * + * Copyright (C) 2024 minecraft-dev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, version 3.0 only. + * + * 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 Lesser General Public License + * along with this program. If not, see . + */ + +package io.mcdev.obfex.ref + +import io.mcdev.obfex.MappingPart +import it.unimi.dsi.fastutil.ints.Int2ObjectLinkedOpenHashMap + +/* + * Mapping names are transparent wrappers around plain strings. The benefit they provide is by allowing transformations + * of the input string in the `as` methods that create them. + */ + +interface MemberName { + val name: String +} + +@JvmInline +value class PackName(override val name: String) : MemberName +@JvmInline +value class ClassName(override val name: String) : MemberName +@JvmInline +value class FieldName(override val name: String) : MemberName +@JvmInline +value class MethodName(override val name: String) : MemberName +@JvmInline +value class ParamName(override val name: String) : MemberName +@JvmInline +value class LocalVarName(override val name: String) : MemberName + +fun String.asPackage(): PackName { + val replaced = this.replace('.', '/') + return if (replaced == "//") { + PackName("/") + } else { + PackName(replaced) + } +} +fun String.asClass(): ClassName = ClassName(this.replace('.', '/')) +fun String.asField(): FieldName = FieldName(this) +fun String.asMethod(): MethodName = MethodName(this) +fun String.asParam(): ParamName = ParamName(this) +fun String.asLocal(): LocalVarName = LocalVarName(this) + +// Most of the time we want to go directly from a MappingPart, so these are just convenience wrappers for that + +fun MappingPart.asPackage(): PackName = value.asPackage() +fun MappingPart.asClass(): ClassName = value.asClass() +fun MappingPart.asField(): FieldName = value.asField() +fun MappingPart.asMethod(): MethodName = value.asMethod() +fun MappingPart.asParam(): ParamName = value.asParam() +fun MappingPart.asLocal(): LocalVarName = value.asLocal() + +/* + * References for Java members. + */ + +interface LocalMemberRef { + val name: T +} + +/** + * Local field reference, no class. + */ +data class LocalFieldRef(override val name: FieldName, val type: TypeDef? = null) : LocalMemberRef { + fun withType(type: TypeDef?): LocalFieldRef = copy(type = type) + fun withoutType(): LocalFieldRef = withType(null) +} + +/** + * Absolute field reference, including class. + */ +data class FieldRef(val containingClass: ClassName, val field: LocalFieldRef) + +// Utilities for creating field refs +fun FieldName.asRef(type: TypeDef? = null): LocalFieldRef = LocalFieldRef(this, type) +fun String.asFieldRef(type: TypeDef? = null): LocalFieldRef = this.asField().asRef(type) +fun String.asFieldRef(type: String?): LocalFieldRef = this.asField().asRef(type?.asTypeDef()) + +fun ClassName.field(name: FieldName, type: TypeDef? = null): FieldRef = FieldRef(this, name.asRef(type)) +fun ClassName.field(ref: LocalFieldRef): FieldRef = FieldRef(this, ref) +/** + * Local method reference, no class. + */ +data class LocalMethodRef(override val name: MethodName, val desc: MethodDescriptor?) : LocalMemberRef { + fun withDesc(desc: MethodDescriptor): LocalMethodRef = copy(desc = desc) +} + +/** + * Absolute method reference, including class. + */ +data class MethodRef(val containingClass: ClassName, val method: LocalMethodRef) + +// Utilities for creating method refs +fun MethodName.asRef(desc: MethodDescriptor? = null): LocalMethodRef = LocalMethodRef(this, desc) +fun String.asMethodRef(desc: MethodDescriptor? = null): LocalMethodRef = this.asMethod().asRef(desc) +fun String.asMethodRef(desc: String?): LocalMethodRef = this.asMethod().asRef(desc?.asMethodDesc()) + +fun ClassName.method(name: MethodName, desc: MethodDescriptor? = null): MethodRef = MethodRef(this, name.asRef(desc)) +fun ClassName.method(ref: LocalMethodRef): MethodRef = MethodRef(this, ref) + +// Similar concept to ParamName and LvtName, but wrapping integers for indices. + +@JvmInline +value class ParamIndex(val index: Int) + +data class LocalVarIndex(val index: Local, val localStart: Local = Local.UNKNOWN, val localEnd: Local = Local.UNKNOWN) { + val isKnown: Boolean + get() = index.isKnown + + companion object { + @JvmStatic + val UNKNOWN = LocalVarIndex(Local.UNKNOWN) + } +} + +@JvmInline +value class LvtIndex(val index: Local) { + val isKnown: Boolean + get() = this.index.isKnown + + companion object { + @JvmStatic + val UNKNOWN = LvtIndex(Local.UNKNOWN) + } +} + +@JvmInline +value class Local(val index: Int) { + val isKnown: Boolean + get() = this.index != -1 + + companion object { + @JvmStatic + val UNKNOWN = Local(-1) + } +} + +fun Int.asParamIndex(): ParamIndex = ParamIndex(this) +fun Int.asLocal(): Local = Local(this) +fun Local.asLocalVar(startIndex: Local = Local.UNKNOWN, endIndex: Local = Local.UNKNOWN): LocalVarIndex = + LocalVarIndex(this, startIndex, endIndex) +fun Int.asLocal(startIndex: Local = Local.UNKNOWN, endIndex: Local = Local.UNKNOWN): LocalVarIndex = + this.asLocal().asLocalVar(startIndex, endIndex) +fun Local.asLvtIndex(): LvtIndex = LvtIndex(this) +fun Int.asLvtIndex(): LvtIndex = this.asLocal().asLvtIndex() + +// LinePriority helps us keep track of which line number we want to use for new elements + +data class LinePriority(val coord: Int, val priority: Int) + +private const val BASE_PRIORITY_VALUE = 0 +private const val HIGH_PRIORITY_VALUE = 100 +private const val LOW_PRIORITY_VALUE = -100 + +val Int.basePriority: LinePriority + get() = priority(BASE_PRIORITY_VALUE) +val LinePriority.basePriority: LinePriority + get() = if (priority == BASE_PRIORITY_VALUE) { + this + } else { + coord.basePriority + } + +val Int.highPriority: LinePriority + get() = priority(HIGH_PRIORITY_VALUE) +val LinePriority.highPriority: LinePriority + get() = if (priority == HIGH_PRIORITY_VALUE) { + this + } else { + coord.highPriority + } + +val Int.lowPriority: LinePriority + get() = priority(LOW_PRIORITY_VALUE) +val LinePriority.lowPriority: LinePriority + get() = if (priority == LOW_PRIORITY_VALUE) { + this + } else { + coord.lowPriority + } + +fun Int.priority(priority: Int) = LinePriority(this, priority) + +// ParamIndex and LvtIndex helpers +class ParamMap( + private val backing: Int2ObjectLinkedOpenHashMap = Int2ObjectLinkedOpenHashMap() +) : MutableMap by backing { + @Deprecated(message = "Use ParameterRef to access", replaceWith = ReplaceWith("this.get(key.asParam())")) + override fun get(key: Int): T? { + return backing.get(key) + } + + @Deprecated(message = "Use ParameterRef to access", replaceWith = ReplaceWith("this.put(key.asParam(), value)")) + override fun put(key: Int, value: T): T? { + return backing.put(key, value) + } + @Deprecated(message = "Use ParameterRef to access", replaceWith = ReplaceWith("this.set(key.asParam(), value)")) + operator fun set(key: Int, value: T): T? { + return backing.put(key, value) + } + + operator fun get(key: ParamIndex): T? = backing.get(key.index) + operator fun set(key: ParamIndex, value: T) { + backing.put(key.index, value) + } +} diff --git a/obfuscation-explorer/src/main/kotlin/ref/types.kt b/obfuscation-explorer/src/main/kotlin/ref/types.kt new file mode 100644 index 000000000..fe9caf87b --- /dev/null +++ b/obfuscation-explorer/src/main/kotlin/ref/types.kt @@ -0,0 +1,199 @@ +/* + * Minecraft Development for IntelliJ + * + * https://mcdev.io/ + * + * Copyright (C) 2024 minecraft-dev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, version 3.0 only. + * + * 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 Lesser General Public License + * along with this program. If not, see . + */ + +package io.mcdev.obfex.ref + +import io.mcdev.obfex.MappingPart +import java.lang.IllegalStateException + +sealed interface ReturnTypeDef { + val descriptor: String + + fun isMappable(): Boolean + + companion object { + fun parse(desc: String) = desc.asReturnTypeDef() + } +} + +object VoidTypeDef : ReturnTypeDef { + override val descriptor = "V" + + override fun isMappable(): Boolean = false + + override fun toString() = descriptor +} + +sealed interface TypeDef : ReturnTypeDef { + override fun isMappable(): Boolean = when (this) { + is PrimitiveTypeDef -> false + is ClassTypeDef -> true + is ArrayTypeDef -> componentType.isMappable() + } + + companion object { + fun parse(desc: String) = desc.asTypeDef() + } +} + +enum class PrimitiveTypeDef(override val descriptor: String) : TypeDef { + BOOLEAN("Z"), + CHAR("C"), + BYTE("B"), + SHORT("S"), + INT("I"), + LONG("J"), + FLOAT("F"), + DOUBLE("D"); + + override fun toString() = descriptor + + companion object { + fun fromChar(c: Char): PrimitiveTypeDef? { + return when (c) { + 'Z' -> BOOLEAN + 'C' -> CHAR + 'B' -> BYTE + 'S' -> SHORT + 'I' -> INT + 'J' -> LONG + 'F' -> FLOAT + 'D' -> DOUBLE + else -> null + } + } + } +} + +data class ClassTypeDef(val className: ClassName) : TypeDef { + override val descriptor: String + get() = "L${className.name};" + + override fun toString(): String = descriptor +} + +data class ArrayTypeDef(val componentType: TypeDef, val dimension: Int) : TypeDef { + override val descriptor: String + get() = "[".repeat(dimension) + componentType.descriptor + + override fun toString() = descriptor +} + +fun String.asReturnTypeDef(): ReturnTypeDef? = parseType(this, 0)?.type +fun String.asTypeDef(): TypeDef? = parseType(this, 0)?.type as? TypeDef + +private data class TypeReturn(val index: Int, val type: ReturnTypeDef) + +private fun parseType(text: String, index: Int): TypeReturn? { + return when (val c = text[index]) { + 'L' -> { + val endIndex = text.indexOf(';', index + 1) + if (endIndex == -1) { + return null + } + TypeReturn(endIndex + 1, ClassTypeDef(ClassName(text.substring(index + 1, endIndex)))) + } + '[' -> { + var endIndex: Int = -1 + for (i in index + 1 until text.length) { + val next = text[i] + if (next != '[') { + endIndex = i + break + } + } + if (endIndex == -1) { + return null + } + + val componentType = parseType(text, endIndex) ?: return null + if (componentType.type is ArrayTypeDef) { + // should be impossible + throw IllegalStateException() + } else if (componentType.type == VoidTypeDef) { + return null + } + + return TypeReturn( + componentType.index, + ArrayTypeDef(componentType.type as TypeDef, endIndex - index) + ) + } + 'V' -> TypeReturn(index + 1, VoidTypeDef) + else -> PrimitiveTypeDef.fromChar(c)?.let { TypeReturn(index + 1, it) } + } +} + +data class MethodDescriptor(val params: List, val returnType: ReturnTypeDef) { + + fun isMappable(): Boolean = params.any { it.isMappable() } || returnType.isMappable() + + override fun toString(): String = buildString { + append('(') + for (param in params) { + append(param.descriptor) + } + append(')') + append(returnType.descriptor) + } + + companion object { + fun parse(text: MappingPart?, startIndex: Int = 0) = parse(text?.value, startIndex) + fun parse(text: String?, startIndex: Int = 0): MethodDescriptor? { + if (text.isNullOrBlank()) { + return null + } + + if (text.length <= startIndex || text[startIndex] != '(') { + return null + } + + val paramTypes = mutableListOf() + + var index = startIndex + 1 + while (true) { + if (text.length <= index) { + return null + } + if (text[index] == ')') { + index++ + break + } + + val type = parseType(text, index) ?: return null + if (type.type == VoidTypeDef) { + // void not allowed as a param type + return null + } + paramTypes.add(type.type as TypeDef) + index = type.index + } + + val returnType = parseType(text, index) ?: return null + if (text.length > returnType.index) { + return null + } + + return MethodDescriptor(paramTypes, returnType.type) + } + } +} + +fun String.asMethodDesc(): MethodDescriptor? = MethodDescriptor.parse(this) diff --git a/obfuscation-explorer/src/main/kotlin/util.kt b/obfuscation-explorer/src/main/kotlin/util.kt new file mode 100644 index 000000000..7ffedd78d --- /dev/null +++ b/obfuscation-explorer/src/main/kotlin/util.kt @@ -0,0 +1,310 @@ +/* + * Minecraft Development for IntelliJ + * + * https://mcdev.io/ + * + * Copyright (C) 2024 minecraft-dev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, version 3.0 only. + * + * 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 Lesser General Public License + * along with this program. If not, see . + */ + +package io.mcdev.obfex + +import com.google.common.base.CharMatcher +import com.intellij.openapi.vfs.VirtualFile +import com.intellij.util.ArrayUtil +import kotlin.contracts.contract + +enum class Tristate { + TRUE, + FALSE, + UNKNOWN, +} + +inline fun hash(func: Hasher.() -> Unit): Int { + val hasher = Hasher() + hasher.func() + return hasher.code +} + +class Hasher { + var code: Int = 1 + private set + + private fun updateHash(value: Int) { + code = 31 * code + value + } + + operator fun Any?.unaryPlus() { + updateHash(this?.hashCode() ?: 0) + } + operator fun Array<*>?.unaryPlus() { + updateHash(contentHashCode()) + } + operator fun ByteArray?.unaryPlus() { + updateHash(contentHashCode()) + } + operator fun ShortArray?.unaryPlus() { + updateHash(contentHashCode()) + } + operator fun IntArray?.unaryPlus() { + updateHash(contentHashCode()) + } + operator fun LongArray?.unaryPlus() { + updateHash(contentHashCode()) + } + operator fun FloatArray?.unaryPlus() { + updateHash(contentHashCode()) + } + operator fun DoubleArray?.unaryPlus() { + updateHash(contentHashCode()) + } + operator fun BooleanArray?.unaryPlus() { + updateHash(contentHashCode()) + } + operator fun CharArray?.unaryPlus() { + updateHash(contentHashCode()) + } + + operator fun Byte?.invoke() { + updateHash(this?.hashCode() ?: 0) + } + operator fun Byte.invoke() { + updateHash(this.hashCode()) + } + operator fun Short?.invoke() { + updateHash(this?.hashCode() ?: 0) + } + operator fun Short.invoke() { + updateHash(this.hashCode()) + } + operator fun Int?.invoke() { + updateHash(this?.hashCode() ?: 0) + } + operator fun Int.invoke() { + updateHash(this.hashCode()) + } + operator fun Long?.invoke() { + updateHash(this?.hashCode() ?: 0) + } + operator fun Long.invoke() { + updateHash(this.hashCode()) + } + operator fun Float?.invoke() { + updateHash(this?.hashCode() ?: 0) + } + operator fun Float.invoke() { + updateHash(this.hashCode()) + } + operator fun Double?.invoke() { + updateHash(this?.hashCode() ?: 0) + } + operator fun Double.invoke() { + updateHash(this.hashCode()) + } + operator fun Boolean?.invoke() { + updateHash(this?.hashCode() ?: 0) + } + operator fun Boolean.invoke() { + updateHash(this.hashCode()) + } + operator fun Char?.invoke() { + updateHash(this?.hashCode() ?: 0) + } + operator fun Char.invoke() { + updateHash(this.hashCode()) + } +} + +private fun emptyArrayUtil(): Array { + /* + * re-use an existing empty array, rather than allocating like emptyArray() does + */ + @Suppress("UNCHECKED_CAST") + return ArrayUtil.EMPTY_OBJECT_ARRAY as Array +} + +data class MappingPart(val col: Int, val value: String) + +private val whitespace = CharMatcher.whitespace() +private val nonWhitespace = whitespace.negate() + +fun String.splitMappingLine(preserveBlank: Boolean = false): Array { + var index = 0 + if (this.isNotEmpty() && whitespace.matches(this[0])) { + index = nonWhitespace.indexIn(this) + if (index == -1) { + return emptyArrayUtil() + } + } + + if (this[index] == '#') { + return emptyArrayUtil() + } + + var arrayIndex = 0 + // 6 should be big enough to handle most lines with a single allocation + var array = arrayOfNulls(6) + while (true) { + if (preserveBlank) { + // With preserveBlank enabled, we don't treat 2 consecutive whitespaces as a group, instead that would + // result in a blank + val nextPart = if (index >= this.length) { + break + } else if (whitespace.matches(this[index])) { + // immediately hit whitespace, so this is blank + "" + } else { + val endIndex = whitespace.indexIn(this, index) + if (endIndex == -1) { + this.substring(index, this.length) + } else { + this.substring(index, endIndex) + } + } + + if (arrayIndex >= array.size) { + array = array.copyOf(array.size * 2) + } + + array[arrayIndex++] = MappingPart(index, nextPart) + index += nextPart.length + 1 + } else { + val startIndex = nonWhitespace.indexIn(this, index) + if (this[startIndex] == '#') { + break + } + + if (arrayIndex >= array.size) { + array = array.copyOf(array.size * 2) + } + + val endIndex = whitespace.indexIn(this, startIndex + 1) + if (endIndex == -1) { + array[arrayIndex++] = MappingPart(startIndex, this.substring(startIndex)) + break + } else { + array[arrayIndex++] = MappingPart(startIndex, this.substring(startIndex, endIndex)) + index = endIndex + } + } + } + + if (arrayIndex == 0) { + return emptyArrayUtil() + } + + @Suppress("UNCHECKED_CAST") + return array.copyOf(arrayIndex) as Array +} + +fun String.isJavaInternalIdentifier(): Boolean { + if (this.isEmpty()) { + return false + } + + var isStart = true + var index = 0 + while (true) { + if (index >= this.length) { + break + } + + val c = this.codePointAt(index++) + if (Character.isSupplementaryCodePoint(c)) { + index++ + } + + when { + isStart -> { + isStart = false + if (!Character.isJavaIdentifierStart(c)) { + return false + } + } + c == '/'.code || c == '.'.code -> { + isStart = true + continue + } + else -> { + if (!Character.isJavaIdentifierPart(c)) { + return false + } + } + } + } + + return true +} + +typealias LineBlock = (lineNum: Int, parts: Array, line: String) -> Boolean +inline fun VirtualFile.forLines(skipLines: Int = 0, preserveBlank: Boolean = false, block: LineBlock) { + contract { + callsInPlace(block) + } + + this.inputStream.bufferedReader(this.charset).use { reader -> + var lineNum = 0 + while (true) { + lineNum++ + val line = reader.readLine() ?: break + if (lineNum <= skipLines) { + continue + } + + val parts = line.splitMappingLine(preserveBlank = preserveBlank) + if (parts.isEmpty()) { + continue + } + + if (!block(lineNum, parts, line)) { + break + } + } + } +} + +/* + * Impl note: For simplicity this considers any single whitespace character as a single indent. That means it treats + * a single space and a single tab character as the same indent level. This lets it handle indent levels for files + * which use tabs or spaces interchangeably, however it leaves files which mix tabs and spaces for indentation in the + * category of "undefined behavior". + */ +typealias IndentedLineBlock = (indent: Int, lineNum: Int, parts: Array, line: String) -> Boolean +inline fun VirtualFile.forLinesIndent(skipLines: Int = 0, preserveBlank: Boolean = false, block: IndentedLineBlock) { + contract { + callsInPlace(block) + } + + this.forLines(skipLines = skipLines, preserveBlank = preserveBlank) { lineNum, parts, line -> + val index = line.firstNonWhitespaceIndex() + if (index < 0) { + // blank line - forLines should skip these, but handle this case anyway + return@forLines true + } + + return@forLines block(index, lineNum, parts, line) + } +} + +fun String.firstNonWhitespaceIndex(): Int = nonWhitespace.indexIn(this) + +fun String.splitOnLast(delimiter: Char): Pair { + val lastIndex = lastIndexOf(delimiter) + if (lastIndex < 0) { + return this to null + } + + return substring(0, lastIndex) to substring(lastIndex + 1) +} +fun MappingPart.splitOnLast(delimiter: Char): Pair = value.splitOnLast(delimiter) diff --git a/obfuscation-explorer/src/main/resources/META-INF/plugin.xml b/obfuscation-explorer/src/main/resources/META-INF/plugin.xml new file mode 100644 index 000000000..2ba487bbe --- /dev/null +++ b/obfuscation-explorer/src/main/resources/META-INF/plugin.xml @@ -0,0 +1,110 @@ + + + + io.mcdev.obfex + Obfuscation Explorer + DemonWav + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/obfuscation-explorer/src/main/resources/META-INF/pluginIcon.svg b/obfuscation-explorer/src/main/resources/META-INF/pluginIcon.svg new file mode 100644 index 000000000..4b4e349cf --- /dev/null +++ b/obfuscation-explorer/src/main/resources/META-INF/pluginIcon.svg @@ -0,0 +1,9 @@ + +Obfuscation Mapper Icon + + + + + + + diff --git a/obfuscation-explorer/src/main/resources/fileTypes/csrg.svg b/obfuscation-explorer/src/main/resources/fileTypes/csrg.svg new file mode 100644 index 000000000..6941f63f2 --- /dev/null +++ b/obfuscation-explorer/src/main/resources/fileTypes/csrg.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/obfuscation-explorer/src/main/resources/fileTypes/enigma.svg b/obfuscation-explorer/src/main/resources/fileTypes/enigma.svg new file mode 100644 index 000000000..e77409a8b --- /dev/null +++ b/obfuscation-explorer/src/main/resources/fileTypes/enigma.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/obfuscation-explorer/src/main/resources/fileTypes/jam.svg b/obfuscation-explorer/src/main/resources/fileTypes/jam.svg new file mode 100644 index 000000000..e7c1b1f71 --- /dev/null +++ b/obfuscation-explorer/src/main/resources/fileTypes/jam.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/obfuscation-explorer/src/main/resources/fileTypes/obfex.svg b/obfuscation-explorer/src/main/resources/fileTypes/obfex.svg new file mode 100644 index 000000000..4b4e349cf --- /dev/null +++ b/obfuscation-explorer/src/main/resources/fileTypes/obfex.svg @@ -0,0 +1,9 @@ + +Obfuscation Mapper Icon + + + + + + + diff --git a/obfuscation-explorer/src/main/resources/fileTypes/proguard.svg b/obfuscation-explorer/src/main/resources/fileTypes/proguard.svg new file mode 100644 index 000000000..366519ef1 --- /dev/null +++ b/obfuscation-explorer/src/main/resources/fileTypes/proguard.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/obfuscation-explorer/src/main/resources/fileTypes/srg.svg b/obfuscation-explorer/src/main/resources/fileTypes/srg.svg new file mode 100644 index 000000000..60200e055 --- /dev/null +++ b/obfuscation-explorer/src/main/resources/fileTypes/srg.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/obfuscation-explorer/src/main/resources/fileTypes/tinyv1.svg b/obfuscation-explorer/src/main/resources/fileTypes/tinyv1.svg new file mode 100644 index 000000000..bcca2cb7e --- /dev/null +++ b/obfuscation-explorer/src/main/resources/fileTypes/tinyv1.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/obfuscation-explorer/src/main/resources/fileTypes/tinyv2.svg b/obfuscation-explorer/src/main/resources/fileTypes/tinyv2.svg new file mode 100644 index 000000000..e7e11a4d0 --- /dev/null +++ b/obfuscation-explorer/src/main/resources/fileTypes/tinyv2.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/obfuscation-explorer/src/main/resources/fileTypes/tsrg.svg b/obfuscation-explorer/src/main/resources/fileTypes/tsrg.svg new file mode 100644 index 000000000..e4d4b4a7a --- /dev/null +++ b/obfuscation-explorer/src/main/resources/fileTypes/tsrg.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/obfuscation-explorer/src/main/resources/fileTypes/tsrg2.svg b/obfuscation-explorer/src/main/resources/fileTypes/tsrg2.svg new file mode 100644 index 000000000..d72c717e8 --- /dev/null +++ b/obfuscation-explorer/src/main/resources/fileTypes/tsrg2.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/obfuscation-explorer/src/main/resources/fileTypes/xsrg.svg b/obfuscation-explorer/src/main/resources/fileTypes/xsrg.svg new file mode 100644 index 000000000..6d1cd84a7 --- /dev/null +++ b/obfuscation-explorer/src/main/resources/fileTypes/xsrg.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/obfuscation-explorer/src/test/kotlin/TypeParsingTest.kt b/obfuscation-explorer/src/test/kotlin/TypeParsingTest.kt new file mode 100644 index 000000000..81bd79678 --- /dev/null +++ b/obfuscation-explorer/src/test/kotlin/TypeParsingTest.kt @@ -0,0 +1,221 @@ +/* + * Minecraft Development for IntelliJ + * + * https://mcdev.io/ + * + * Copyright (C) 2024 minecraft-dev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, version 3.0 only. + * + * 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 Lesser General Public License + * along with this program. If not, see . + */ + +package io.mcdev.obfex + +import io.mcdev.obfex.ref.ArrayTypeDef +import io.mcdev.obfex.ref.ClassTypeDef +import io.mcdev.obfex.ref.MethodDescriptor +import io.mcdev.obfex.ref.PrimitiveTypeDef +import io.mcdev.obfex.ref.ReturnTypeDef +import io.mcdev.obfex.ref.VoidTypeDef +import io.mcdev.obfex.ref.asClass +import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.Assertions.assertNull +import org.junit.jupiter.api.Test + +class TypeParsingTest { + + @Test + fun testPrimitiveTypes() { + assertEquals(PrimitiveTypeDef.BOOLEAN, ReturnTypeDef.parse("Z")) + assertEquals(PrimitiveTypeDef.CHAR, ReturnTypeDef.parse("C")) + assertEquals(PrimitiveTypeDef.BYTE, ReturnTypeDef.parse("B")) + assertEquals(PrimitiveTypeDef.SHORT, ReturnTypeDef.parse("S")) + assertEquals(PrimitiveTypeDef.INT, ReturnTypeDef.parse("I")) + assertEquals(PrimitiveTypeDef.LONG, ReturnTypeDef.parse("J")) + assertEquals(PrimitiveTypeDef.FLOAT, ReturnTypeDef.parse("F")) + assertEquals(PrimitiveTypeDef.DOUBLE, ReturnTypeDef.parse("D")) + assertEquals(VoidTypeDef, ReturnTypeDef.parse("V")) + } + + @Test + fun testArrayPrimitiveTypes() { + repeat(10) { i -> + val dim = i + 1 + assertEquals(ArrayTypeDef(PrimitiveTypeDef.BOOLEAN, dim), ReturnTypeDef.parse("[".repeat(dim) + 'Z')) + assertEquals(ArrayTypeDef(PrimitiveTypeDef.CHAR, dim), ReturnTypeDef.parse("[".repeat(dim) + 'C')) + assertEquals(ArrayTypeDef(PrimitiveTypeDef.BYTE, dim), ReturnTypeDef.parse("[".repeat(dim) + 'B')) + assertEquals(ArrayTypeDef(PrimitiveTypeDef.SHORT, dim), ReturnTypeDef.parse("[".repeat(dim) + 'S')) + assertEquals(ArrayTypeDef(PrimitiveTypeDef.INT, dim), ReturnTypeDef.parse("[".repeat(dim) + 'I')) + assertEquals(ArrayTypeDef(PrimitiveTypeDef.LONG, dim), ReturnTypeDef.parse("[".repeat(dim) + 'J')) + assertEquals(ArrayTypeDef(PrimitiveTypeDef.FLOAT, dim), ReturnTypeDef.parse("[".repeat(dim) + 'F')) + assertEquals(ArrayTypeDef(PrimitiveTypeDef.DOUBLE, dim), ReturnTypeDef.parse("[".repeat(dim) + 'D')) + } + } + + @Test + fun testVoidArray() { + assertNull(ReturnTypeDef.parse("[V")) + } + + @Test + fun testClassType() { + assertEquals(ClassTypeDef("java/lang/String".asClass()), ReturnTypeDef.parse("Ljava/lang/String;")) + assertEquals(ClassTypeDef("a".asClass()), ReturnTypeDef.parse("La;")) + } + + @Test + fun testClassArray() { + assertEquals( + ArrayTypeDef(ClassTypeDef("java/lang/String".asClass()), 1), + ReturnTypeDef.parse("[Ljava/lang/String;") + ) + assertEquals( + ArrayTypeDef(ClassTypeDef("java/lang/String".asClass()), 3), + ReturnTypeDef.parse("[[[Ljava/lang/String;") + ) + assertEquals(ArrayTypeDef(ClassTypeDef("a".asClass()), 2), ReturnTypeDef.parse("[[La;")) + } + + @Test + fun testEmptyMethodDesc() { + assertEquals(MethodDescriptor(emptyList(), VoidTypeDef), MethodDescriptor.parse("()V")) + } + + @Test + fun testPrimitiveMethodDesc() { + assertEquals(MethodDescriptor(listOf(PrimitiveTypeDef.INT), VoidTypeDef), MethodDescriptor.parse("(I)V")) + assertEquals( + MethodDescriptor(listOf(PrimitiveTypeDef.INT, PrimitiveTypeDef.INT, PrimitiveTypeDef.INT), VoidTypeDef), + MethodDescriptor.parse("(III)V") + ) + + assertEquals( + MethodDescriptor( + listOf( + PrimitiveTypeDef.CHAR, + PrimitiveTypeDef.BOOLEAN, + PrimitiveTypeDef.BYTE, + PrimitiveTypeDef.SHORT, + PrimitiveTypeDef.INT, + PrimitiveTypeDef.LONG, + PrimitiveTypeDef.FLOAT, + PrimitiveTypeDef.DOUBLE, + ), + VoidTypeDef + ), + MethodDescriptor.parse("(CZBSIJFD)V") + ) + + assertEquals( + MethodDescriptor( + listOf( + PrimitiveTypeDef.CHAR, + PrimitiveTypeDef.BOOLEAN, + PrimitiveTypeDef.BYTE, + PrimitiveTypeDef.SHORT, + PrimitiveTypeDef.INT, + PrimitiveTypeDef.LONG, + PrimitiveTypeDef.FLOAT, + PrimitiveTypeDef.DOUBLE, + ), + PrimitiveTypeDef.CHAR + ), + MethodDescriptor.parse("(CZBSIJFD)C") + ) + } + + @Test + fun testArrayMethodDesc() { + assertEquals( + MethodDescriptor(listOf(ArrayTypeDef(PrimitiveTypeDef.LONG, 2)), ArrayTypeDef(PrimitiveTypeDef.FLOAT, 1)), + MethodDescriptor.parse("([[J)[F") + ) + + assertEquals( + MethodDescriptor( + listOf( + ArrayTypeDef(PrimitiveTypeDef.LONG, 2), + ArrayTypeDef(PrimitiveTypeDef.INT, 1), + ArrayTypeDef(PrimitiveTypeDef.BOOLEAN, 3), + ), + ArrayTypeDef(PrimitiveTypeDef.FLOAT, 1) + ), + MethodDescriptor.parse("([[J[I[[[Z)[F") + ) + } + + @Test + fun testClassMethodDesc() { + assertEquals( + MethodDescriptor( + listOf( + ClassTypeDef("a".asClass()), + ClassTypeDef("b".asClass()), + ClassTypeDef("c".asClass()), + ), + ClassTypeDef("d".asClass()) + ), + MethodDescriptor.parse("(La;Lb;Lc;)Ld;") + ) + + assertEquals( + MethodDescriptor( + listOf( + ClassTypeDef("java.lang.String".asClass()), + ClassTypeDef("java.lang.Object".asClass()), + ), + ClassTypeDef("d".asClass()) + ), + MethodDescriptor.parse("(Ljava/lang/String;Ljava/lang/Object;)Ld;") + ) + + assertEquals( + MethodDescriptor( + listOf( + ClassTypeDef("java.lang.String".asClass()), + ArrayTypeDef(ClassTypeDef("java.lang.Object".asClass()), 2), + ), + ArrayTypeDef(ClassTypeDef("d".asClass()), 1) + ), + MethodDescriptor.parse("(Ljava/lang/String;[[Ljava/lang/Object;)[Ld;") + ) + + assertEquals( + MethodDescriptor( + listOf( + ClassTypeDef("java.lang.String".asClass()), + ArrayTypeDef(ClassTypeDef("java.lang.Object".asClass()), 2), + PrimitiveTypeDef.INT, + PrimitiveTypeDef.INT, + PrimitiveTypeDef.INT, + ), + VoidTypeDef + ), + MethodDescriptor.parse("(Ljava/lang/String;[[Ljava/lang/Object;III)V") + ) + + assertEquals( + MethodDescriptor( + listOf( + ClassTypeDef("java.lang.String".asClass()), + PrimitiveTypeDef.LONG, + ArrayTypeDef(ClassTypeDef("java.lang.Object".asClass()), 2), + PrimitiveTypeDef.INT, + ArrayTypeDef(PrimitiveTypeDef.INT, 1), + PrimitiveTypeDef.INT, + ), + VoidTypeDef + ), + MethodDescriptor.parse("(Ljava/lang/String;J[[Ljava/lang/Object;I[II)V") + ) + } +} diff --git a/obfuscation-explorer/src/test/kotlin/formats/ParserFixture.kt b/obfuscation-explorer/src/test/kotlin/formats/ParserFixture.kt new file mode 100644 index 000000000..d4beddcf0 --- /dev/null +++ b/obfuscation-explorer/src/test/kotlin/formats/ParserFixture.kt @@ -0,0 +1,60 @@ +/* + * Minecraft Development for IntelliJ + * + * https://mcdev.io/ + * + * Copyright (C) 2024 minecraft-dev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, version 3.0 only. + * + * 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 Lesser General Public License + * along with this program. If not, see . + */ + +package io.mcdev.obfex.formats + +import com.intellij.openapi.fileTypes.PlainTextLanguage +import com.intellij.testFramework.ReadOnlyLightVirtualFile +import com.intellij.testFramework.fixtures.IdeaTestFixtureFactory +import io.mcdev.obfex.mappings.MappingsDefinition +import io.mcdev.obfex.mappings.MappingsFormatParser +import org.junit.jupiter.api.AfterAll +import org.junit.jupiter.api.BeforeAll +import org.junit.jupiter.api.TestInstance + +@TestInstance(TestInstance.Lifecycle.PER_CLASS) +abstract class ParserFixture { + + private val fixture = IdeaTestFixtureFactory.getFixtureFactory().createBareFixture() + + lateinit var def: MappingsDefinition + + abstract val parser: MappingsFormatParser + + abstract val text: String + + @BeforeAll + fun setup() { + fixture.setUp() + + val file = ReadOnlyLightVirtualFile( + "test.${parser.expectedFileExtensions.first()}", + PlainTextLanguage.INSTANCE, + text + ) + + def = parser.parse(file)!! + } + + @AfterAll + fun teardown() { + fixture.tearDown() + } +} diff --git a/obfuscation-explorer/src/test/kotlin/formats/csrg/CSrgMappingsFormatParserTest.kt b/obfuscation-explorer/src/test/kotlin/formats/csrg/CSrgMappingsFormatParserTest.kt new file mode 100644 index 000000000..7fdfa83e7 --- /dev/null +++ b/obfuscation-explorer/src/test/kotlin/formats/csrg/CSrgMappingsFormatParserTest.kt @@ -0,0 +1,153 @@ +/* + * Minecraft Development for IntelliJ + * + * https://mcdev.io/ + * + * Copyright (C) 2024 minecraft-dev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, version 3.0 only. + * + * 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 Lesser General Public License + * along with this program. If not, see . + */ + +package io.mcdev.obfex.formats.csrg + +import io.mcdev.obfex.formats.ParserFixture +import io.mcdev.obfex.mappings.MappingsFormatParser +import io.mcdev.obfex.mappings.unnamedFrom +import io.mcdev.obfex.mappings.unnamedTo +import io.mcdev.obfex.ref.ClassTypeDef +import io.mcdev.obfex.ref.MethodDescriptor +import io.mcdev.obfex.ref.PrimitiveTypeDef +import io.mcdev.obfex.ref.asClass +import io.mcdev.obfex.ref.asFieldRef +import io.mcdev.obfex.ref.asMethodRef +import io.mcdev.obfex.ref.field +import io.mcdev.obfex.ref.method +import org.intellij.lang.annotations.Language +import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.Test + +class CSrgMappingsFormatParserTest : ParserFixture() { + + override val parser: MappingsFormatParser = CSrgMappingsFormatParser() + + @Language("csrg") + override val text: String = """ + net/minecraft/CommentTest uih + net/minecraft/Test${'$'}Example ght${'$'}ds + net/minecraft/tags/TagsItem af CANDLES + net/minecraft/tags/TagsItem ag READ + com/mojang/math/Divisor a (II)Ljava/lang/Iterable; asIterable + net/minecraft/BlockUtil a (Ljava/util/function/Predicate;Lnet/minecraft/core/BlockPosition${'$'}MutableBlockPosition;Lnet/minecraft/core/EnumDirection;I)I getLimit + net/minecraft/BlockUtil a (Lnet/minecraft/core/BlockPosition;Lnet/minecraft/core/EnumDirection${'$'}EnumAxis;ILnet/minecraft/core/EnumDirection${'$'}EnumAxis;ILjava/util/function/Predicate;)Lnet/minecraft/BlockUtil${'$'}Rectangle; getLargestRectangleAround + net/minecraft/BlockUtil a (Lnet/minecraft/world/level/IBlockAccess;Lnet/minecraft/core/BlockPosition;Lnet/minecraft/world/level/block/Block;Lnet/minecraft/core/EnumDirection;Lnet/minecraft/world/level/block/Block;)Ljava/util/Optional; getTopConnectedBlock + """.trimIndent() + + @Test + fun testField1() { + val mapping = def.mappings.fieldMapping( + "net.minecraft.tags.TagsItem".asClass().field("af".asFieldRef()), + )!! + val fromName = mapping.unnamedFrom + val toName = mapping.unnamedTo + + assertEquals("af", fromName) + assertEquals("CANDLES", toName) + } + + @Test + fun testField2() { + val mapping = def.mappings.fieldMapping( + "net.minecraft.tags.TagsItem".asClass().field("ag".asFieldRef()), + )!! + val fromName = mapping.unnamedFrom + val toName = mapping.unnamedTo + + assertEquals("ag", fromName) + assertEquals("READ", toName) + } + + @Test + fun testClass() { + val clazz = def.mappings.clazz( + "net.minecraft.CommentTest".asClass(), + )!! + + val fromName = clazz.unnamedFrom + val toName = clazz.unnamedTo + + assertEquals("net/minecraft/CommentTest", fromName) + assertEquals("uih", toName) + } + + @Test + fun testMethod() { + val desc = MethodDescriptor( + listOf( + ClassTypeDef("net/minecraft/core/BlockPosition".asClass()), + ClassTypeDef("net/minecraft/core/EnumDirection\$EnumAxis".asClass()), + PrimitiveTypeDef.INT, + ClassTypeDef("net/minecraft/core/EnumDirection\$EnumAxis".asClass()), + PrimitiveTypeDef.INT, + ClassTypeDef("java/util/function/Predicate".asClass()), + ), + ClassTypeDef("net.minecraft.BlockUtil\$Rectangle".asClass()) + ) + + val method = def.mappings.methodMapping( + "net.minecraft.BlockUtil".asClass().method("a".asMethodRef(desc)), + )!! + + val fromName = method.unnamedFrom + val toName = method.unnamedTo + + assertEquals("a", fromName) + assertEquals("getLargestRectangleAround", toName) + } + + @Test + fun testMethodOtherSide() { + val desc = MethodDescriptor( + listOf( + ClassTypeDef("net/minecraft/world/level/IBlockAccess".asClass()), + ClassTypeDef("net/minecraft/core/BlockPosition".asClass()), + ClassTypeDef("net/minecraft/world/level/block/Block".asClass()), + ClassTypeDef("net/minecraft/core/EnumDirection".asClass()), + ClassTypeDef("net/minecraft/world/level/block/Block".asClass()), + ), + ClassTypeDef("java/util/Optional".asClass()) + ) + + val method = def.mappings.methodMapping( + "net.minecraft.BlockUtil".asClass().method("getTopConnectedBlock".asMethodRef(desc)), + )!! + + val fromName = method.unnamedFrom + val toName = method.unnamedTo + + assertEquals("a", fromName) + assertEquals("getTopConnectedBlock", toName) + } + + @Test + fun testCounts() { + assertEquals(5, def.mappings.classes().size) + assertEquals(2, def.mappings.classes().sumOf { it.fields().size }) + assertEquals(4, def.mappings.classes().sumOf { it.methods().size }) + } + + @Test + fun testWarnings() { + // Missing class mapping warnings + assertEquals(3, def.source.warnings.size) + } +} diff --git a/obfuscation-explorer/src/test/kotlin/formats/enigma/EnigmaLexerTest.kt b/obfuscation-explorer/src/test/kotlin/formats/enigma/EnigmaLexerTest.kt new file mode 100644 index 000000000..718edfe2d --- /dev/null +++ b/obfuscation-explorer/src/test/kotlin/formats/enigma/EnigmaLexerTest.kt @@ -0,0 +1,46 @@ +/* + * Minecraft Development for IntelliJ + * + * https://mcdev.io/ + * + * Copyright (C) 2024 minecraft-dev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, version 3.0 only. + * + * 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 Lesser General Public License + * along with this program. If not, see . + */ + +package io.mcdev.obfex.formats.enigma + +import com.intellij.testFramework.LexerTestCase +import io.mcdev.obfex.formats.enigma.lang.EnigmaLayoutLexer +import org.junit.jupiter.api.Assertions +import org.junit.jupiter.api.DisplayName +import org.junit.jupiter.api.Test + +@DisplayName("Enigma Lexing Test") +class EnigmaLexerTest { + + @Test + @DisplayName("Enigma Lexing Test") + fun test() { + val caller = EnigmaLexerTest::class.java + val basePath = "lexer/fixtures/test.mapping" + val text = caller.getResource(basePath)?.readText()?.trim() ?: Assertions.fail("no test data found") + + val expected = caller.getResource("${basePath.substringBeforeLast('.')}.txt")?.readText()?.trim() + ?: Assertions.fail("no expected data found") + val actual = LexerTestCase.printTokens(text.filter { it != '\r' }, 0, EnigmaLayoutLexer()).trim() + + Assertions.assertEquals(expected, actual) + } + } + \ No newline at end of file diff --git a/obfuscation-explorer/src/test/kotlin/formats/enigma/EnigmaMappingsFormatParserTest.kt b/obfuscation-explorer/src/test/kotlin/formats/enigma/EnigmaMappingsFormatParserTest.kt new file mode 100644 index 000000000..c47fef12f --- /dev/null +++ b/obfuscation-explorer/src/test/kotlin/formats/enigma/EnigmaMappingsFormatParserTest.kt @@ -0,0 +1,195 @@ +/* + * Minecraft Development for IntelliJ + * + * https://mcdev.io/ + * + * Copyright (C) 2024 minecraft-dev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, version 3.0 only. + * + * 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 Lesser General Public License + * along with this program. If not, see . + */ + +package io.mcdev.obfex.formats.enigma + +import io.mcdev.obfex.formats.ParserFixture +import io.mcdev.obfex.mappings.MappingNamespace +import io.mcdev.obfex.mappings.MappingsFormatParser +import io.mcdev.obfex.ref.asParamIndex +import io.mcdev.obfex.ref.asTypeDef +import org.intellij.lang.annotations.Language +import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.Test + +class EnigmaMappingsFormatParserTest : ParserFixture() { + + override val parser: MappingsFormatParser = EnigmaMappingsFormatParser() + @Language("Enigma") + override val text: String = """ + CLASS net/minecraft/class_2019 net/minecraft/predicate/DamagePredicate + COMMENT asdfasdf + COMMENT asdfasdfasdfa + FIELD field_9520 ANY Lnet/minecraft/class_2019; + FIELD field_9521 sourceEntity Lnet/minecraft/class_2048; + COMMENT sdafasdfasdf + METHOD (Lnet/minecraft/class_2096${'$'}class_2099;Lnet/minecraft/class_2096${'$'}class_2099;Lnet/minecraft/class_2048;Ljava/lang/Boolean;Lnet/minecraft/class_2022;)V + COMMENT asdfasfasdf + ARG 1 dealt + ARG 2 taken + ARG 3 sourceEntity + ARG 4 blocked + ARG 5 type + METHOD method_8839 fromJson (Lcom/google/gson/JsonElement;)Lnet/minecraft/class_2019; + ARG 0 json + CLASS class_2020 Builder + COMMENT asdfasdfa + FIELD field_9526 blocked Ljava/lang/Boolean; + FIELD field_9527 taken Lnet/minecraft/class_2096${'$'}class_2099; + COMMENT asdfasdfadsf + FIELD field_9530 dealt Lnet/minecraft/class_2096${'$'}class_2099; + METHOD method_8841 blocked (Ljava/lang/Boolean;)Lnet/minecraft/class_2019${'$'}class_2020; + COMMENT asfasdf + COMMENT asfasdfasdfasdfsad + ARG 1 blocked + METHOD method_8842 type (Lnet/minecraft/class_2022${'$'}class_2023;)Lnet/minecraft/class_2019${'$'}class_2020; + ARG 1 builder + METHOD method_8843 build ()Lnet/minecraft/class_2019; + CLASS net/minecraft/class_9323 net/minecraft/component/ComponentMap + FIELD field_49584 EMPTY Lnet/minecraft/class_9323; + METHOD method_57828 filtered (Ljava/util/function/Predicate;)Lnet/minecraft/class_9323; + ARG 1 predicate + METHOD method_57833 stream ()Ljava/util/stream/Stream; + CLASS class_9324 Builder + FIELD field_49587 components Lit/unimi/dsi/fastutil/objects/Reference2ObjectMap; + METHOD method_57838 build ()Lnet/minecraft/class_9323; + METHOD method_57840 add (Lnet/minecraft/class_9331;Ljava/lang/Object;)Lnet/minecraft/class_9323${'$'}class_9324; + ARG 1 type + ARG 2 value + METHOD method_58756 put (Lnet/minecraft/class_9331;Ljava/lang/Object;)V + ARG 1 type + ARG 2 value + CLASS class_9325 SimpleComponentMap + """.trimIndent() + .replace(" ", "\t") + + private val from: MappingNamespace + get() = MappingNamespace.unnamedFrom(def.mappings) + private val to: MappingNamespace + get() = MappingNamespace.unnamedTo(def.mappings) + + @Test + fun testClasses() { + assertEquals(5, def.mappings.classes().size) + + val class1 = def.mappings.clazz("net/minecraft/class_2019")!! + val class2 = def.mappings.clazz("net/minecraft/class_2019\$class_2020")!! + val class3 = def.mappings.clazz("net/minecraft/class_9323")!! + val class4 = def.mappings.clazz("net/minecraft/class_9323\$class_9324")!! + val class5 = def.mappings.clazz("net/minecraft/class_9323\$class_9324\$class_9325")!! + + assertEquals("net/minecraft/class_2019", class1.name(from)) + assertEquals("net/minecraft/predicate/DamagePredicate", class1.name(to)) + + assertEquals("net/minecraft/class_2019\$class_2020", class2.name(from)) + assertEquals("net/minecraft/predicate/DamagePredicate\$Builder", class2.name(to)) + + assertEquals("net/minecraft/class_9323", class3.name(from)) + assertEquals("net/minecraft/component/ComponentMap", class3.name(to)) + + assertEquals("net/minecraft/class_9323\$class_9324", class4.name(from)) + assertEquals("net/minecraft/component/ComponentMap\$Builder", class4.name(to)) + + assertEquals("net/minecraft/class_9323\$class_9324\$class_9325", class5.name(from)) + assertEquals("net/minecraft/component/ComponentMap\$Builder\$SimpleComponentMap", class5.name(to)) + } + + @Test + fun testMethods() { + assertEquals(2, def.mappings.clazz("net/minecraft/class_2019")?.methods()?.size) + assertEquals(3, def.mappings.clazz("net/minecraft/class_2019\$class_2020")?.methods()?.size) + assertEquals(2, def.mappings.clazz("net/minecraft/class_9323")?.methods()?.size) + assertEquals(3, def.mappings.clazz("net/minecraft/class_9323\$class_9324")?.methods()?.size) + assertEquals(0, def.mappings.clazz("net/minecraft/class_9323\$class_9324\$class_9325")?.methods()?.size) + + assertEquals("", def.mappings.clazz("net/minecraft/class_2019")?.method("")?.name(to)) + assertEquals("fromJson", def.mappings.clazz("net/minecraft/class_2019")?.method("method_8839")?.name(to)) + + assertEquals( + "blocked", + def.mappings.clazz("net/minecraft/class_2019\$class_2020")?.method("method_8841")?.name(to) + ) + assertEquals( + "type", + def.mappings.clazz("net/minecraft/class_2019\$class_2020")?.method("method_8842")?.name(to) + ) + assertEquals( + "build", + def.mappings.clazz("net/minecraft/class_2019\$class_2020")?.method("method_8843")?.name(to) + ) + + assertEquals("filtered", def.mappings.clazz("net/minecraft/class_9323")?.method("method_57828")?.name(to)) + assertEquals("stream", def.mappings.clazz("net/minecraft/class_9323")?.method("method_57833")?.name(to)) + + assertEquals( + "build", + def.mappings.clazz("net/minecraft/class_9323\$class_9324")?.method("method_57838")?.name(to) + ) + assertEquals( + "add", + def.mappings.clazz("net/minecraft/class_9323\$class_9324")?.method("method_57840")?.name(to) + ) + assertEquals( + "put", + def.mappings.clazz("net/minecraft/class_9323\$class_9324")?.method("method_58756")?.name(to) + ) + } + + @Test + fun testParams() { + val class1 = def.mappings.clazz("net/minecraft/class_2019")!! + val class4 = def.mappings.clazz("net/minecraft/class_9323\$class_9324")!! + + val count = def.mappings.classes() + .flatMap { it.methods() } + .flatMap { it.params() } + .count() + assertEquals(13, count) + + val names = listOf("dealt", "taken", "sourceEntity", "blocked", "type") + val params = class1.method("")!!.params() + for ((i, name) in names.withIndex()) { + val p = params.first { it.index == (i + 1).asParamIndex() } + assertEquals(name, p.name(to)) + } + + val names2 = listOf("type", "value") + val params2 = class4.method("method_57840")!!.params() + for ((i, name) in names2.withIndex()) { + val p = params2.first { it.index == (i + 1).asParamIndex() } + assertEquals(name, p.name(to)) + } + } + + @Test + fun testFields() { + val count = def.mappings.classes() + .flatMap { it.fields() } + .count() + assertEquals(7, count) + + val class1 = def.mappings.clazz("net/minecraft/class_2019")!! + + assertEquals("ANY", class1.field("field_9520")!!.name(to)) + assertEquals("Lnet/minecraft/class_2019;".asTypeDef(), class1.field("field_9520")!!.type) + assertEquals("field_9521", class1.field("sourceEntity")!!.name(from)) + assertEquals("Lnet/minecraft/class_2048;".asTypeDef(), class1.field("sourceEntity")!!.type) + } +} diff --git a/obfuscation-explorer/src/test/kotlin/formats/jam/JamMappingsFormatParserTest.kt b/obfuscation-explorer/src/test/kotlin/formats/jam/JamMappingsFormatParserTest.kt new file mode 100644 index 000000000..f886c1c93 --- /dev/null +++ b/obfuscation-explorer/src/test/kotlin/formats/jam/JamMappingsFormatParserTest.kt @@ -0,0 +1,112 @@ +/* + * Minecraft Development for IntelliJ + * + * https://mcdev.io/ + * + * Copyright (C) 2024 minecraft-dev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, version 3.0 only. + * + * 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 Lesser General Public License + * along with this program. If not, see . + */ + +package io.mcdev.obfex.formats.jam + +import io.mcdev.obfex.formats.ParserFixture +import io.mcdev.obfex.mappings.MappingsFormatParser +import io.mcdev.obfex.mappings.unnamedFrom +import io.mcdev.obfex.mappings.unnamedTo +import io.mcdev.obfex.ref.MethodDescriptor +import io.mcdev.obfex.ref.PrimitiveTypeDef +import io.mcdev.obfex.ref.asClass +import io.mcdev.obfex.ref.asFieldRef +import io.mcdev.obfex.ref.asMethodDesc +import io.mcdev.obfex.ref.asMethodRef +import io.mcdev.obfex.ref.asParamIndex +import io.mcdev.obfex.ref.asTypeDef +import io.mcdev.obfex.ref.field +import io.mcdev.obfex.ref.method +import org.intellij.lang.annotations.Language +import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.Assertions.assertNull +import org.junit.jupiter.api.Assertions.assertSame +import org.junit.jupiter.api.Test + +class JamMappingsFormatParserTest : ParserFixture() { + + override val parser: MappingsFormatParser = JamMappingsFormatParser() + + @Language("jam") + override val text: String = """ + CL ght net/minecraft/Test + CL ght${'$'}a net/minecraft/Test${'$'}Inner + FD ght rft Ljava/util/logging/Logger; log + MD ght hyuip (I)Z isEven + MP ght hyuip (I)Z 0 num + """.trimIndent() + + @Test + fun testClass() { + val from = def.mappings.clazz("ght".asClass()) + val to = def.mappings.clazz("net/minecraft/Test".asClass()) + + assertSame(from, to) + } + + @Test + fun testField() { + val mapping = def.mappings.fieldMapping( + "ght".asClass() + .field("rft".asFieldRef("Ljava/util/logging/Logger;".asTypeDef())) + )!! + + val fromName = mapping.unnamedFrom + val toName = mapping.unnamedTo + + assertEquals("rft", fromName) + assertEquals("log", toName) + } + + @Test + fun testMethod() { + val clazz = def.mappings.clazz("ght".asClass())!! + val mapping = clazz.methods().first { it.unnamedFrom == "hyuip" } + + val fromName = mapping.unnamedFrom + val toName = mapping.unnamedTo + + assertEquals("hyuip", fromName) + assertEquals("isEven", toName) + + assertEquals( + MethodDescriptor( + listOf(PrimitiveTypeDef.INT), + PrimitiveTypeDef.BOOLEAN + ), + mapping.descriptor + ) + } + + @Test + fun testParam() { + val ref = "net/minecraft/Test".asClass() + .method("isEven".asMethodRef("(I)Z".asMethodDesc())) + + val method = def.mappings.methodMapping(ref)!! + val mapping = method.param(0.asParamIndex())!! + + val fromName = mapping.unnamedFrom + val toName = mapping.unnamedTo + + assertNull(fromName) + assertEquals("num", toName) + } +} diff --git a/obfuscation-explorer/src/test/kotlin/formats/proguard/ProGuardMappingsFormatParserTest.kt b/obfuscation-explorer/src/test/kotlin/formats/proguard/ProGuardMappingsFormatParserTest.kt new file mode 100644 index 000000000..dc538c137 --- /dev/null +++ b/obfuscation-explorer/src/test/kotlin/formats/proguard/ProGuardMappingsFormatParserTest.kt @@ -0,0 +1,105 @@ +/* + * Minecraft Development for IntelliJ + * + * https://mcdev.io/ + * + * Copyright (C) 2024 minecraft-dev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, version 3.0 only. + * + * 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 Lesser General Public License + * along with this program. If not, see . + */ + +package io.mcdev.obfex.formats.proguard + +import io.mcdev.obfex.formats.ParserFixture +import io.mcdev.obfex.mappings.MappingsFormatParser +import io.mcdev.obfex.mappings.unnamedFrom +import io.mcdev.obfex.mappings.unnamedTo +import io.mcdev.obfex.ref.asClass +import io.mcdev.obfex.ref.asFieldRef +import io.mcdev.obfex.ref.asMethodRef +import io.mcdev.obfex.ref.asTypeDef +import org.intellij.lang.annotations.Language +import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.Assertions.assertNull +import org.junit.jupiter.api.Test + +class ProGuardMappingsFormatParserTest : ParserFixture() { + + override val parser: MappingsFormatParser = ProGuardMappingsFormatParser() + + @Language("ProGuard") + override val text: String = """ + com.mojang.math.Axis -> a: + com.mojang.math.Axis XN -> a + com.mojang.math.Axis XP -> b + com.mojang.math.Axis YN -> c + 17:17:com.mojang.math.Axis of(org.joml.Vector3f) -> abcd + 23:23:org.joml.Quaternionf rotationDegrees(float) -> rotationDegrees + com.mojang.math.Constants -> b: + float PI -> a + float RAD_TO_DEG -> b + float DEG_TO_RAD -> c + float EPSILON -> d + 3:3:void () -> + """.trimIndent() + + @Test + fun testCounts() { + assertEquals(2, def.mappings.classes().size) + assertEquals(7, def.mappings.classes().sumOf { it.fields().size }) + assertEquals(3, def.mappings.classes().sumOf { it.methods().size }) + } + + @Test + fun testClass() { + val mapping = def.mappings.clazz("a".asClass())!! + + assertEquals("com/mojang/math/Axis", mapping.unnamedFrom) + assertEquals("a", mapping.unnamedTo) + } + + @Test + fun testFields() { + val clazz = def.mappings.clazz("a".asClass())!! + + val axis = "Lcom/mojang/math/Axis;" + val xn = clazz.field("XN".asFieldRef(axis.asTypeDef()))!! + val xp = clazz.field("b".asFieldRef(axis.asTypeDef()))!! + val yn = clazz.field("YN".asFieldRef(axis.asTypeDef()))!! + + assertEquals("XN", xn.unnamedFrom) + assertEquals("a", xn.unnamedTo) + assertEquals("XP", xp.unnamedFrom) + assertEquals("b", xp.unnamedTo) + assertEquals("YN", yn.unnamedFrom) + assertEquals("c", yn.unnamedTo) + } + + @Test + fun testMethods() { + val clazz = def.mappings.clazz("com.mojang.math.Axis".asClass())!! + + val mapping = clazz.method("of".asMethodRef("(Lorg/joml/Vector3f;)Lcom/mojang/math/Axis;"))!! + + assertEquals("of", mapping.unnamedFrom) + assertEquals("abcd", mapping.unnamedTo) + } + + @Test + fun testInitSkipped() { + val clazz = def.mappings.clazz("com.mojang.math.Constants".asClass())!! + val mapping = clazz.method("".asMethodRef("()V")) + + assertNull(mapping) + } +} diff --git a/obfuscation-explorer/src/test/kotlin/formats/srg/SrgLexerTest.kt b/obfuscation-explorer/src/test/kotlin/formats/srg/SrgLexerTest.kt new file mode 100644 index 000000000..8782fd7fd --- /dev/null +++ b/obfuscation-explorer/src/test/kotlin/formats/srg/SrgLexerTest.kt @@ -0,0 +1,47 @@ +/* + * Minecraft Development for IntelliJ + * + * https://mcdev.io/ + * + * Copyright (C) 2024 minecraft-dev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, version 3.0 only. + * + * 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 Lesser General Public License + * along with this program. If not, see . + */ + +package io.mcdev.obfex.formats.srg + +import com.intellij.lexer.FlexAdapter +import com.intellij.testFramework.LexerTestCase +import io.mcdev.obfex.formats.srg.gen.SrgLexer +import org.junit.jupiter.api.Assertions +import org.junit.jupiter.api.DisplayName +import org.junit.jupiter.api.Test + +@DisplayName("SRG Lexing Tests") +class SrgLexerTest { + + @Test + @DisplayName("SRG Lexing Test") + fun test() { + val caller = SrgLexerTest::class.java + val basePath = "lexer/fixtures/test.srg" + val text = caller.getResource(basePath)?.readText()?.trim() ?: Assertions.fail("no test data found") + + val expected = caller.getResource("${basePath.substringBeforeLast('.')}.txt")?.readText()?.trim() + ?: Assertions.fail("no expected data found") + val actual = LexerTestCase.printTokens(text.filter { it != '\r' }, 0, FlexAdapter(SrgLexer())).trim() + + Assertions.assertEquals(expected, actual) + } + } + \ No newline at end of file diff --git a/obfuscation-explorer/src/test/kotlin/formats/srg/SrgMappingsFormatParserTest.kt b/obfuscation-explorer/src/test/kotlin/formats/srg/SrgMappingsFormatParserTest.kt new file mode 100644 index 000000000..48c54afd1 --- /dev/null +++ b/obfuscation-explorer/src/test/kotlin/formats/srg/SrgMappingsFormatParserTest.kt @@ -0,0 +1,124 @@ +/* + * Minecraft Development for IntelliJ + * + * https://mcdev.io/ + * + * Copyright (C) 2024 minecraft-dev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, version 3.0 only. + * + * 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 Lesser General Public License + * along with this program. If not, see . + */ + +package io.mcdev.obfex.formats.srg + +import io.mcdev.obfex.formats.ParserFixture +import io.mcdev.obfex.mappings.MappingsFormatParser +import io.mcdev.obfex.mappings.unnamedFrom +import io.mcdev.obfex.mappings.unnamedTo +import io.mcdev.obfex.ref.MethodDescriptor +import io.mcdev.obfex.ref.PrimitiveTypeDef +import io.mcdev.obfex.ref.asClass +import io.mcdev.obfex.ref.asFieldRef +import io.mcdev.obfex.ref.asPackage +import io.mcdev.obfex.ref.field +import org.intellij.lang.annotations.Language +import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.Assertions.assertSame +import org.junit.jupiter.api.Test + +class SrgMappingsFormatParserTest : ParserFixture() { + + override val parser: MappingsFormatParser = SrgMappingsFormatParser() + + @Language("srg") + override val text: String = """ + PK: ./ net/minecraft/server + CL: ght net/minecraft/Test + FD: ght/rft net/minecraft/Test/log + FD: ght${'$'}ds/juh net/minecraft/Test${'$'}Example/server + MD: ght/hyuip (I)Z net/minecraft/Test/isEven (I)Z + MD: ght${'$'}ds/hyuip (I)Z net/minecraft/Test${'$'}Example/isOdd (I)Z + """.trimIndent() + + @Test + fun testPackage() { + val mapping = def.mappings.pack("/".asPackage())!! + + val fromName = mapping.unnamedFrom + val toName = mapping.unnamedTo + + assertEquals("/", fromName) + assertEquals("net/minecraft/server", toName) + } + + @Test + fun testPackageOpposite() { + val mapping = def.mappings.pack("net/minecraft/server".asPackage())!! + + val fromName = mapping.unnamedFrom + val toName = mapping.unnamedTo + + assertEquals("/", fromName) + assertEquals("net/minecraft/server", toName) + } + + @Test + fun testClass() { + val from = def.mappings.clazz("ght".asClass()) + val to = def.mappings.clazz("net/minecraft/Test".asClass()) + + assertSame(from, to) + } + + @Test + fun testField1() { + val mapping = def.mappings.fieldMapping("ght".asClass().field("rft".asFieldRef()))!! + + val fromName = mapping.unnamedFrom + val toName = mapping.unnamedTo + + assertEquals("rft", fromName) + assertEquals("log", toName) + } + + @Test + fun testField2() { + val mapping = + def.mappings.fieldMapping("net/minecraft/Test\$Example".asClass().field("server".asFieldRef()))!! + + val fromName = mapping.unnamedFrom + val toName = mapping.unnamedTo + + assertEquals("juh", fromName) + assertEquals("server", toName) + } + + @Test + fun testMethod() { + val clazz = def.mappings.clazz("ght".asClass())!! + val mapping = clazz.methods().first { it.unnamedFrom == "hyuip" } + + val fromName = mapping.unnamedFrom + val toName = mapping.unnamedTo + + assertEquals("hyuip", fromName) + assertEquals("isEven", toName) + + assertEquals( + MethodDescriptor( + listOf(PrimitiveTypeDef.INT), + PrimitiveTypeDef.BOOLEAN + ), + mapping.descriptor + ) + } +} diff --git a/obfuscation-explorer/src/test/kotlin/formats/tinyv1/TinyV1MappingsFormatParserTest.kt b/obfuscation-explorer/src/test/kotlin/formats/tinyv1/TinyV1MappingsFormatParserTest.kt new file mode 100644 index 000000000..3cfe9da72 --- /dev/null +++ b/obfuscation-explorer/src/test/kotlin/formats/tinyv1/TinyV1MappingsFormatParserTest.kt @@ -0,0 +1,90 @@ +/* + * Minecraft Development for IntelliJ + * + * https://mcdev.io/ + * + * Copyright (C) 2024 minecraft-dev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, version 3.0 only. + * + * 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 Lesser General Public License + * along with this program. If not, see . + */ + +package io.mcdev.obfex.formats.tinyv1 + +import io.mcdev.obfex.formats.ParserFixture +import io.mcdev.obfex.mappings.MappingNamespace +import io.mcdev.obfex.mappings.MappingsFormatParser +import io.mcdev.obfex.ref.asClass +import io.mcdev.obfex.ref.asFieldRef +import io.mcdev.obfex.ref.asMethodRef +import org.intellij.lang.annotations.Language +import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.Assertions.assertNull +import org.junit.jupiter.api.Test + +class TinyV1MappingsFormatParserTest : ParserFixture() { + + override val parser: MappingsFormatParser = TinyV1MappingsFormatParser() + + @Language("TinyV1") + override val text: String = """ + v1 official intermediary test + CLASS a net/minecraft/class_7833 com/example/Test + FIELD a La; a field_40713 + FIELD a La; b abc + METHOD a (F)Lorg/joml/Quaternionf; a method_46349 + METHOD a (Lorg/joml/Vector3f;F)Lorg/joml/Quaternionf; a method_46350 xyz + METHOD a (F)Lorg/joml/Quaternionf; method_46351 + """.trimIndent() + .replace(" ", "\t") + + private val official: MappingNamespace + get() = def.mappings.namespaceOf("official") + private val intermediary: MappingNamespace + get() = def.mappings.namespaceOf("intermediary") + private val test: MappingNamespace + get() = def.mappings.namespaceOf("test") + + @Test + fun testClass() { + val mapping = def.mappings.clazz("a".asClass())!! + + assertEquals(3, mapping.names.size) + assertEquals("a", mapping.name(official)) + assertEquals("net/minecraft/class_7833", mapping.name(intermediary)) + assertEquals("com/example/Test", mapping.name(test)) + } + + @Test + fun testField() { + val clazz = def.mappings.clazz("net/minecraft/class_7833".asClass())!! + + val field = clazz.field("field_40713".asFieldRef("Lnet/minecraft/class_7833;"))!! + + assertEquals(3, field.names.size) + assertEquals("a", field.name(official)) + assertEquals("field_40713", field.name(intermediary)) + assertNull(field.name(test)) + } + + @Test + fun testMethod() { + val clazz = def.mappings.clazz("com/example/Test".asClass())!! + + val method = clazz.method("method_46351".asMethodRef())!! + + assertEquals(3, method.names.size) + assertNull(method.name(official)) + assertEquals("method_46351", method.name(intermediary)) + assertNull(method.name(test)) + } +} diff --git a/obfuscation-explorer/src/test/kotlin/formats/tinyv2/TinyV2LexerTest.kt b/obfuscation-explorer/src/test/kotlin/formats/tinyv2/TinyV2LexerTest.kt new file mode 100644 index 000000000..694d4d67b --- /dev/null +++ b/obfuscation-explorer/src/test/kotlin/formats/tinyv2/TinyV2LexerTest.kt @@ -0,0 +1,46 @@ +/* + * Minecraft Development for IntelliJ + * + * https://mcdev.io/ + * + * Copyright (C) 2024 minecraft-dev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, version 3.0 only. + * + * 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 Lesser General Public License + * along with this program. If not, see . + */ + +package io.mcdev.obfex.formats.tinyv2 + +import com.intellij.testFramework.LexerTestCase +import io.mcdev.obfex.formats.tinyv2.lang.psi.TinyV2LexerAdapter +import org.junit.jupiter.api.Assertions +import org.junit.jupiter.api.DisplayName +import org.junit.jupiter.api.Test + +@DisplayName("TinyV2 Lexer Test") +class TinyV2LexerTest { + + @Test + @DisplayName("TinyV2 Lexer Test") + fun test() { + val caller = TinyV2LexerTest::class.java + val basePath = "lexer/fixtures/test.tiny" + val text = caller.getResource(basePath)?.readText()?.trim() ?: Assertions.fail("no test data found") + + val expected = caller.getResource("${basePath.substringBeforeLast('.')}.txt")?.readText()?.trim() + ?: Assertions.fail("no test data found") + val actual = LexerTestCase.printTokens(text.filter { it != '\r' }, 0, TinyV2LexerAdapter()).trim() + + Assertions.assertEquals(expected, actual) + } + } + \ No newline at end of file diff --git a/obfuscation-explorer/src/test/kotlin/formats/tinyv2/TinyV2MappingsFormatParserTest.kt b/obfuscation-explorer/src/test/kotlin/formats/tinyv2/TinyV2MappingsFormatParserTest.kt new file mode 100644 index 000000000..6e20e59cc --- /dev/null +++ b/obfuscation-explorer/src/test/kotlin/formats/tinyv2/TinyV2MappingsFormatParserTest.kt @@ -0,0 +1,124 @@ +/* + * Minecraft Development for IntelliJ + * + * https://mcdev.io/ + * + * Copyright (C) 2024 minecraft-dev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, version 3.0 only. + * + * 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 Lesser General Public License + * along with this program. If not, see . + */ + +package io.mcdev.obfex.formats.tinyv2 + +import io.mcdev.obfex.formats.ParserFixture +import io.mcdev.obfex.mappings.MappingNamespace +import io.mcdev.obfex.mappings.MappingsFormatParser +import io.mcdev.obfex.ref.LvtIndex +import io.mcdev.obfex.ref.PrimitiveTypeDef +import io.mcdev.obfex.ref.asLocal +import io.mcdev.obfex.ref.asLvtIndex +import io.mcdev.obfex.ref.asParamIndex +import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.Test + +class TinyV2MappingsFormatParserTest : ParserFixture() { + + override val parser: MappingsFormatParser = TinyV2MappingsFormatParser() + + override val text: String = """ + tiny 2 0 source target target2 + c class_1 class1Ns0Rename class1Ns1Rename + f I field_1 field1Ns0Rename field1Ns1Rename + m ()I method_1 method1Ns0Rename method1Ns1Rename + p 1 param_1 param1Ns0Rename param1Ns1Rename + v 2 3 4 var_1 var1Ns0Rename var1Ns1Rename + v 5 6 var_2 var2Ns0Rename var2Ns1Rename + c class_1${'$'}class_2 class1Ns0Rename${'$'}class2Ns0Rename class1Ns1Rename${'$'}class2Ns1Rename + f I field_2 field2Ns0Rename field2Ns1Rename + c class_3 class3Ns0Rename class3Ns1Rename + """.trimIndent() + .replace(" ", "\t") + + private val source: MappingNamespace + get() = def.mappings.namespaceOf("source") + private val target: MappingNamespace + get() = def.mappings.namespaceOf("target") + private val target2: MappingNamespace + get() = def.mappings.namespaceOf("target2") + + @Test + fun testClasses() { + assertEquals(3, def.mappings.classes().count()) + + val class1 = def.mappings.clazz("class1Ns0Rename")!! + assertEquals("class_1", class1.name(source)) + assertEquals("class1Ns0Rename", class1.name(target)) + assertEquals("class1Ns1Rename", class1.name(target2)) + + val class2 = def.mappings.clazz("class3Ns1Rename")!! + assertEquals("class_3", class2.name(source)) + assertEquals("class3Ns0Rename", class2.name(target)) + assertEquals("class3Ns1Rename", class2.name(target2)) + } + + @Test + fun testField() { + val class1 = def.mappings.clazz("class_1")!! + val field = class1.fields().single() + + assertEquals("field_1", field.name(source)) + assertEquals("field1Ns0Rename", field.name(target)) + assertEquals("field1Ns1Rename", field.name(target2)) + assertEquals(PrimitiveTypeDef.INT, field.type) + } + + @Test + fun testMethod() { + val class1 = def.mappings.clazz("class1Ns1Rename")!! + val method = class1.methods().single() + + assertEquals("method_1", method.name(source)) + assertEquals("method1Ns0Rename", method.name(target)) + assertEquals("method1Ns1Rename", method.name(target2)) + } + + @Test + fun testParam() { + val method = def.mappings.clazz("class1Ns1Rename")!!.methods().single() + val param = method.params().single() + + assertEquals(1.asParamIndex(), param.index) + assertEquals("param_1", param.name(source)) + assertEquals("param1Ns0Rename", param.name(target)) + assertEquals("param1Ns1Rename", param.name(target2)) + } + + @Test + fun testLocalVar() { + val method = def.mappings.clazz("class1Ns1Rename")!!.methods().single() + val firstLocal = method.localVars().single { it.lvtIndex == 4.asLvtIndex() } + val secondLocal = method.localVars().single { it.localVarIndex.index == 5.asLocal() } + + assertEquals(2.asLocal(startIndex = 3.asLocal()), firstLocal.localVarIndex) + assertEquals(4.asLvtIndex(), firstLocal.lvtIndex) + assertEquals("var_1", firstLocal.name(source)) + assertEquals("var1Ns0Rename", firstLocal.name(target)) + assertEquals("var1Ns1Rename", firstLocal.name(target2)) + + assertEquals(5.asLocal(startIndex = 6.asLocal()), secondLocal.localVarIndex) + assertEquals(LvtIndex.UNKNOWN, secondLocal.lvtIndex) + assertEquals("var_2", secondLocal.name(source)) + assertEquals("var2Ns0Rename", secondLocal.name(target)) + assertEquals("var2Ns1Rename", secondLocal.name(target2)) + } +} diff --git a/obfuscation-explorer/src/test/kotlin/formats/tsrg/TSrgMappingsFormatParserTest.kt b/obfuscation-explorer/src/test/kotlin/formats/tsrg/TSrgMappingsFormatParserTest.kt new file mode 100644 index 000000000..d3765b7b6 --- /dev/null +++ b/obfuscation-explorer/src/test/kotlin/formats/tsrg/TSrgMappingsFormatParserTest.kt @@ -0,0 +1,133 @@ +/* + * Minecraft Development for IntelliJ + * + * https://mcdev.io/ + * + * Copyright (C) 2024 minecraft-dev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, version 3.0 only. + * + * 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 Lesser General Public License + * along with this program. If not, see . + */ + +package io.mcdev.obfex.formats.tsrg + +import io.mcdev.obfex.formats.ParserFixture +import io.mcdev.obfex.mappings.MappingNamespace +import io.mcdev.obfex.mappings.MappingsFormatParser +import io.mcdev.obfex.mappings.unnamedFrom +import io.mcdev.obfex.mappings.unnamedTo +import io.mcdev.obfex.ref.ClassTypeDef +import io.mcdev.obfex.ref.MethodDescriptor +import io.mcdev.obfex.ref.asClass +import io.mcdev.obfex.ref.asFieldRef +import io.mcdev.obfex.ref.asMethodDesc +import io.mcdev.obfex.ref.asMethodRef +import io.mcdev.obfex.ref.asTypeDef +import io.mcdev.obfex.ref.field +import io.mcdev.obfex.ref.method +import org.intellij.lang.annotations.Language +import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.Assertions.assertNotNull +import org.junit.jupiter.api.Test + +class TSrgMappingsFormatParserTest : ParserFixture() { + + override val parser: MappingsFormatParser = TSrgMappingsFormatParser() + + @Language("tsrg") + override val text: String = """ + a net/minecraft/util/text/TextFormatting + a BLACK + b DARK_BLUE + c DARK_GREEN + d DARK_AQUA + e DARK_RED + a (C)La; func_211165_a + a (I)La; func_175744_a + a (La;)La; func_199747_a + b net/minecraft/crash/CrashReport + a field_147150_a + b field_71513_a + a ()Ljava/lang/String; func_71501_a + a (Ljava/io/File;)Z func_147149_a + """.trimIndent() + .replace(" ", "\t") + + @Test + fun testCounts() { + assertEquals(2, def.mappings.classes().size) + val clazzA = def.mappings.clazz("a".asClass())!! + val clazzB = def.mappings.clazz("b".asClass())!! + + assertEquals(5, clazzA.fields().size) + assertEquals(3, clazzA.methods().size) + assertEquals(2, clazzB.fields().size) + assertEquals(2, clazzB.methods().size) + } + + @Test + fun testTypeMapping() { + val mapping = def.mappings.clazz("a".asClass())!! + + val aMethods = mapping.methods().filter { it.unnamedFrom == "a" } + + val returnType = "Lnet/minecraft/util/text/TextFormatting;".asTypeDef() + + for (aMethod in aMethods) { + assertEquals( + returnType, + def.mappings.mapTypeTo(MappingNamespace.unnamedTo(def.mappings), aMethod.descriptor.returnType) + ) + } + } + + @Test + fun testByMappedType() { + val ref = "net/minecraft/util/text/TextFormatting".asClass() + .method("func_211165_a".asMethodRef("(C)Lnet/minecraft/util/text/TextFormatting;".asMethodDesc())) + val mapping = def.mappings.methodMapping(ref) + + assertNotNull(mapping) + } + + @Test + fun testField() { + val mapping = def.mappings.fieldMapping("a".asClass().field("a".asFieldRef()))!! + + val fromName = mapping.unnamedFrom + val toName = mapping.unnamedTo + + assertEquals("a", fromName) + assertEquals("BLACK", toName) + } + + @Test + fun testMethod() { + val ref = "b".asClass() + .method( + "a".asMethodRef( + MethodDescriptor( + listOf(), + ClassTypeDef("java/lang/String".asClass()) + ) + ) + ) + + val mapping = def.mappings.methodMapping(ref)!! + + val fromName = mapping.unnamedFrom + val toName = mapping.unnamedTo + + assertEquals("a", fromName) + assertEquals("func_71501_a", toName) + } +} diff --git a/obfuscation-explorer/src/test/kotlin/formats/tsrg2/TSrg2LexerTest.kt b/obfuscation-explorer/src/test/kotlin/formats/tsrg2/TSrg2LexerTest.kt new file mode 100644 index 000000000..ac9a18118 --- /dev/null +++ b/obfuscation-explorer/src/test/kotlin/formats/tsrg2/TSrg2LexerTest.kt @@ -0,0 +1,46 @@ +/* + * Minecraft Development for IntelliJ + * + * https://mcdev.io/ + * + * Copyright (C) 2024 minecraft-dev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, version 3.0 only. + * + * 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 Lesser General Public License + * along with this program. If not, see . + */ + +package io.mcdev.obfex.formats.tsrg2 + +import com.intellij.testFramework.LexerTestCase +import io.mcdev.obfex.formats.tsrg2.lang.TSrg2LayoutLexer +import org.junit.jupiter.api.Assertions +import org.junit.jupiter.api.DisplayName +import org.junit.jupiter.api.Test + +@DisplayName("TSrg2 Lexer Test") +class TSrg2LexerTest { + + @Test + @DisplayName("TSrg2 Lexer Test") + fun test() { + val caller = TSrg2LexerTest::class.java + val basePath = "lexer/fixtures/test.tsrg2" + val text = caller.getResource(basePath)?.readText()?.trim() ?: Assertions.fail("no test data found") + + val expected = caller.getResource("${basePath.substringBeforeLast('.')}.txt")?.readText()?.trim() + ?: Assertions.fail("no test data found") + val actual = LexerTestCase.printTokens(text.filter { it != '\r' }, 0, TSrg2LayoutLexer()).trim() + + Assertions.assertEquals(expected, actual) + } + } + \ No newline at end of file diff --git a/obfuscation-explorer/src/test/kotlin/formats/tsrg2/TSrg2MappingsFormatParserTest.kt b/obfuscation-explorer/src/test/kotlin/formats/tsrg2/TSrg2MappingsFormatParserTest.kt new file mode 100644 index 000000000..24655271b --- /dev/null +++ b/obfuscation-explorer/src/test/kotlin/formats/tsrg2/TSrg2MappingsFormatParserTest.kt @@ -0,0 +1,161 @@ +/* + * Minecraft Development for IntelliJ + * + * https://mcdev.io/ + * + * Copyright (C) 2024 minecraft-dev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, version 3.0 only. + * + * 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 Lesser General Public License + * along with this program. If not, see . + */ + +package io.mcdev.obfex.formats.tsrg2 + +import io.mcdev.obfex.Tristate +import io.mcdev.obfex.formats.ParserFixture +import io.mcdev.obfex.mappings.MappingNamespace +import io.mcdev.obfex.mappings.MappingsFormatParser +import org.intellij.lang.annotations.Language +import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.Assertions.assertNotNull +import org.junit.jupiter.api.Test + +class TSrg2MappingsFormatParserTest : ParserFixture() { + + override val parser: MappingsFormatParser = TSrg2MappingsFormatParser() + + @Language("tsrg2") + override val text: String = """ + tsrg2 left right + com/mojang/blaze3d/Blaze3D com/mojang/blaze3d/Blaze3D + ()V + getTime ()D m_83640_ + static + process (Lcom/mojang/blaze3d/pipeline/RenderPipeline;F)V m_166118_ + static + 0 p_166119_ p_166119_ + 1 p_166120_ p_166120_ + com/mojang/blaze3d/audio/Channel com/mojang/blaze3d/audio/ChannelTest + BUFFER_DURATION_SECONDS f_166124_ + LOGGER f_83641_ + QUEUED_BUFFER_COUNT f_166125_ + ()V + static + (I)V + 0 p_83648_ p_83648_ + attachBufferStream (Lnet/minecraft/client/sounds/AudioStream;)V m_83658_ + 0 p_83659_ p_83659_ + initialized f_83643_ + attachStaticBuffer (Lcom/mojang/blaze3d/audio/SoundBuffer;)V m_83656_ + 0 p_83657_ TEST + calculateBufferSize (Ljavax/sound/sampled/AudioFormat;I)I m_83660_ + static + 0 p_83661_ p_83661_test + 1 p_83662_ p_83662_test + """.trimIndent() + .replace(" ", "\t") + + private val left: MappingNamespace + get() = def.mappings.namespaceOf("left") + private val right: MappingNamespace + get() = def.mappings.namespaceOf("right") + + @Test + fun testNamespaces() { + assertEquals(2, def.mappings.namespaces.size) + assertEquals("left", def.mappings.namespaces[0].name) + assertEquals("right", def.mappings.namespaces[1].name) + } + + @Test + fun testClasses() { + assertEquals(2, def.mappings.classes().size) + val blaze3d = def.mappings.clazz("com.mojang.blaze3d.Blaze3D") + val channel = def.mappings.clazz("com.mojang.blaze3d.audio.Channel") + + assertNotNull(blaze3d, "blaze3d") + assertNotNull(channel, "channel") + + assertEquals("com/mojang/blaze3d/audio/ChannelTest", channel!!.name(right)) + } + + @Test + fun testMethods() { + val method1 = def.mappings.clazz("com.mojang.blaze3d.Blaze3D")!! + .method("getTime")!! + + assertEquals("getTime", method1.name(left)) + assertEquals("m_83640_", method1.name(right)) + + val method2 = def.mappings.clazz("com.mojang.blaze3d.Blaze3D")!! + .method("process")!! + + assertEquals("process", method2.name(left)) + assertEquals("m_166118_", method2.name(right)) + + val method3 = def.mappings.clazz("com/mojang/blaze3d/audio/Channel")!! + .method("m_83658_")!! + + assertEquals("attachBufferStream", method3.name(left)) + assertEquals("m_83658_", method3.name(right)) + } + + @Test + fun testStatics() { + val method1 = def.mappings.clazz("com.mojang.blaze3d.Blaze3D")!! + .method("getTime")!! + assertEquals(Tristate.TRUE, method1.metadata.isStatic) + + val method2 = def.mappings.clazz("com/mojang/blaze3d/audio/Channel")!! + .method("")!! + assertEquals(Tristate.TRUE, method2.metadata.isStatic) + + val method3 = def.mappings.clazz("com/mojang/blaze3d/audio/Channel")!! + .method("")!! + assertEquals(Tristate.FALSE, method3.metadata.isStatic) + } + + @Test + fun testParams() { + val method1 = def.mappings.clazz("com.mojang.blaze3d.Blaze3D")!! + .method("getTime")!! + + assertEquals(0, method1.params().size) + + val method2 = def.mappings.clazz("com/mojang/blaze3d/audio/Channel")!! + .method("attachStaticBuffer")!! + + assertEquals(1, method2.params().size) + assertEquals("p_83657_", method2.param(0)!!.name(left)) + assertEquals("TEST", method2.param(0)!!.name(right)) + + val method3 = def.mappings.clazz("com/mojang/blaze3d/audio/ChannelTest")!! + .method("calculateBufferSize")!! + + assertEquals(Tristate.TRUE, method3.metadata.isStatic) + assertEquals(2, method3.params().size) + assertEquals("p_83661_", method3.param(0)!!.name(left)) + assertEquals("p_83661_test", method3.param(0)!!.name(right)) + assertEquals("p_83662_", method3.param(1)!!.name(left)) + assertEquals("p_83662_test", method3.param(1)!!.name(right)) + } + + @Test + fun testFields() { + val c = def.mappings.clazz("com/mojang/blaze3d/audio/Channel")!! + + assertEquals(4, c.fields().size) + assertEquals("BUFFER_DURATION_SECONDS", c.field("f_166124_")!!.name(left)) + assertEquals("f_166125_", c.field("QUEUED_BUFFER_COUNT")!!.name(right)) + assertEquals("initialized", c.field("f_83643_")!!.name(left)) + } +} diff --git a/obfuscation-explorer/src/test/kotlin/formats/xsrg/XSrgMappingsFormatParserTest.kt b/obfuscation-explorer/src/test/kotlin/formats/xsrg/XSrgMappingsFormatParserTest.kt new file mode 100644 index 000000000..a9fceb440 --- /dev/null +++ b/obfuscation-explorer/src/test/kotlin/formats/xsrg/XSrgMappingsFormatParserTest.kt @@ -0,0 +1,128 @@ +/* + * Minecraft Development for IntelliJ + * + * https://mcdev.io/ + * + * Copyright (C) 2024 minecraft-dev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, version 3.0 only. + * + * 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 Lesser General Public License + * along with this program. If not, see . + */ + +package io.mcdev.obfex.formats.xsrg + +import io.mcdev.obfex.formats.ParserFixture +import io.mcdev.obfex.mappings.MappingsFormatParser +import io.mcdev.obfex.mappings.unnamedFrom +import io.mcdev.obfex.mappings.unnamedTo +import io.mcdev.obfex.ref.MethodDescriptor +import io.mcdev.obfex.ref.PrimitiveTypeDef +import io.mcdev.obfex.ref.asClass +import io.mcdev.obfex.ref.asFieldRef +import io.mcdev.obfex.ref.asPackage +import io.mcdev.obfex.ref.asTypeDef +import io.mcdev.obfex.ref.field +import org.intellij.lang.annotations.Language +import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.Assertions.assertSame +import org.junit.jupiter.api.Test + +class XSrgMappingsFormatParserTest : ParserFixture() { + + override val parser: MappingsFormatParser = XSrgMappingsFormatParser() + + @Language("xsrg") + override val text: String = """ + PK: ./ net/minecraft/server + CL: ght net/minecraft/Test + FD: ght/rft net/minecraft/Test/log + FD: ght${'$'}ds/juh Ljava/lang/Object; net/minecraft/Test${'$'}Example/server Ljava/lang/Object; + MD: ght/hyuip (I)Z net/minecraft/Test/isEven (I)Z + MD: ght${'$'}ds/hyuip (I)Z net/minecraft/Test${'$'}Example/isOdd (I)Z + """.trimIndent() + + @Test + fun testPackage() { + val mapping = def.mappings.pack("/".asPackage())!! + + val fromName = mapping.unnamedFrom + val toName = mapping.unnamedTo + + assertEquals("/", fromName) + assertEquals("net/minecraft/server", toName) + } + + @Test + fun testPackageOpposite() { + val mapping = def.mappings.pack("net/minecraft/server".asPackage())!! + + val fromName = mapping.unnamedFrom + val toName = mapping.unnamedTo + + assertEquals("/", fromName) + assertEquals("net/minecraft/server", toName) + } + + @Test + fun testClass() { + val from = def.mappings.clazz("ght".asClass()) + val to = def.mappings.clazz("net/minecraft/Test".asClass()) + + assertSame(from, to) + } + + @Test + fun testField1() { + val mapping = def.mappings.fieldMapping("ght".asClass().field("rft".asFieldRef()))!! + + val fromName = mapping.unnamedFrom + val toName = mapping.unnamedTo + + assertEquals("rft", fromName) + assertEquals("log", toName) + } + + @Test + fun testField2() { + val mapping = + def.mappings.fieldMapping( + "net/minecraft/Test\$Example".asClass() + .field("server".asFieldRef("Ljava/lang/Object;".asTypeDef())) + )!! + + val fromName = mapping.unnamedFrom + val toName = mapping.unnamedTo + + assertEquals("juh", fromName) + assertEquals("server", toName) + } + + @Test + fun testMethod() { + val clazz = def.mappings.clazz("ght".asClass())!! + val mapping = clazz.methods().first { it.unnamedFrom == "hyuip" } + + val fromName = mapping.unnamedFrom + val toName = mapping.unnamedTo + + assertEquals("hyuip", fromName) + assertEquals("isEven", toName) + + assertEquals( + MethodDescriptor( + listOf(PrimitiveTypeDef.INT), + PrimitiveTypeDef.BOOLEAN + ), + mapping.descriptor + ) + } +} diff --git a/obfuscation-explorer/src/test/resources/io/mcdev/obfex/formats/enigma/lexer/fixtures/test.mapping b/obfuscation-explorer/src/test/resources/io/mcdev/obfex/formats/enigma/lexer/fixtures/test.mapping new file mode 100644 index 000000000..7c19f556d --- /dev/null +++ b/obfuscation-explorer/src/test/resources/io/mcdev/obfex/formats/enigma/lexer/fixtures/test.mapping @@ -0,0 +1,34 @@ +CLASS net/minecraft/class_2019 net/minecraft/predicate/DamagePredicate + FIELD field_9520 ANY Lnet/minecraft/class_2019; + FIELD field_9521 sourceEntity Lnet/minecraft/class_2048; + FIELD field_9522 blocked Ljava/lang/Boolean; + FIELD field_9523 dealt Lnet/minecraft/class_2096$class_2099; + FIELD field_9524 taken Lnet/minecraft/class_2096$class_2099; + FIELD field_9525 type Lnet/minecraft/class_2022; + METHOD (Lnet/minecraft/class_2096$class_2099;Lnet/minecraft/class_2096$class_2099;Lnet/minecraft/class_2048;Ljava/lang/Boolean;Lnet/minecraft/class_2022;)V + ARG 1 dealt + ARG 2 taken + ARG 3 sourceEntity + ARG 4 blocked + ARG 5 type + METHOD method_8838 test (Lnet/minecraft/class_3222;Lnet/minecraft/class_1282;FFZ)Z + ARG 1 player + ARG 2 source + ARG 3 dealt + ARG 4 taken + ARG 5 blocked + METHOD method_8839 fromJson (Lcom/google/gson/JsonElement;)Lnet/minecraft/class_2019; + ARG 0 json + METHOD method_8840 toJson ()Lcom/google/gson/JsonElement; + CLASS class_2020 Builder + FIELD field_9526 blocked Ljava/lang/Boolean; + FIELD field_9527 taken Lnet/minecraft/class_2096$class_2099; + FIELD field_9528 sourceEntity Lnet/minecraft/class_2048; + FIELD field_9529 type Lnet/minecraft/class_2022; + FIELD field_9530 dealt Lnet/minecraft/class_2096$class_2099; + METHOD method_8841 blocked (Ljava/lang/Boolean;)Lnet/minecraft/class_2019$class_2020; + ARG 1 blocked + METHOD method_8842 type (Lnet/minecraft/class_2022$class_2023;)Lnet/minecraft/class_2019$class_2020; + ARG 1 builder + METHOD method_8843 build ()Lnet/minecraft/class_2019; + METHOD method_8844 create ()Lnet/minecraft/class_2019$class_2020; diff --git a/obfuscation-explorer/src/test/resources/io/mcdev/obfex/formats/enigma/lexer/fixtures/test.txt b/obfuscation-explorer/src/test/resources/io/mcdev/obfex/formats/enigma/lexer/fixtures/test.txt new file mode 100644 index 000000000..dc1ba8081 --- /dev/null +++ b/obfuscation-explorer/src/test/resources/io/mcdev/obfex/formats/enigma/lexer/fixtures/test.txt @@ -0,0 +1,347 @@ +CLASS_KEY ('CLASS') +WHITE_SPACE (' ') +NAME_ELEMENT ('net') +SLASH ('/') +NAME_ELEMENT ('minecraft') +SLASH ('/') +NAME_ELEMENT ('class_2019') +WHITE_SPACE (' ') +NAME_ELEMENT ('net') +SLASH ('/') +NAME_ELEMENT ('minecraft') +SLASH ('/') +NAME_ELEMENT ('predicate') +SLASH ('/') +NAME_ELEMENT ('DamagePredicate') +CRLF ('\n') +VIRTUAL_OPEN ('') +TAB (' ') +FIELD_KEY ('FIELD') +WHITE_SPACE (' ') +NAME_ELEMENT ('field_9520') +WHITE_SPACE (' ') +NAME_ELEMENT ('ANY') +WHITE_SPACE (' ') +CLASS_TYPE ('Lnet/minecraft/class_2019;') +CRLF ('\n') +TAB (' ') +FIELD_KEY ('FIELD') +WHITE_SPACE (' ') +NAME_ELEMENT ('field_9521') +WHITE_SPACE (' ') +NAME_ELEMENT ('sourceEntity') +WHITE_SPACE (' ') +CLASS_TYPE ('Lnet/minecraft/class_2048;') +CRLF ('\n') +TAB (' ') +FIELD_KEY ('FIELD') +WHITE_SPACE (' ') +NAME_ELEMENT ('field_9522') +WHITE_SPACE (' ') +NAME_ELEMENT ('blocked') +WHITE_SPACE (' ') +CLASS_TYPE ('Ljava/lang/Boolean;') +CRLF ('\n') +TAB (' ') +FIELD_KEY ('FIELD') +WHITE_SPACE (' ') +NAME_ELEMENT ('field_9523') +WHITE_SPACE (' ') +NAME_ELEMENT ('dealt') +WHITE_SPACE (' ') +CLASS_TYPE ('Lnet/minecraft/class_2096$class_2099;') +CRLF ('\n') +TAB (' ') +FIELD_KEY ('FIELD') +WHITE_SPACE (' ') +NAME_ELEMENT ('field_9524') +WHITE_SPACE (' ') +NAME_ELEMENT ('taken') +WHITE_SPACE (' ') +CLASS_TYPE ('Lnet/minecraft/class_2096$class_2099;') +CRLF ('\n') +TAB (' ') +FIELD_KEY ('FIELD') +WHITE_SPACE (' ') +NAME_ELEMENT ('field_9525') +WHITE_SPACE (' ') +NAME_ELEMENT ('type') +WHITE_SPACE (' ') +CLASS_TYPE ('Lnet/minecraft/class_2022;') +CRLF ('\n') +TAB (' ') +METHOD_KEY ('METHOD') +WHITE_SPACE (' ') +CONSTRUCTOR ('') +WHITE_SPACE (' ') +OPEN_PAREN ('(') +CLASS_TYPE ('Lnet/minecraft/class_2096$class_2099;') +CLASS_TYPE ('Lnet/minecraft/class_2096$class_2099;') +CLASS_TYPE ('Lnet/minecraft/class_2048;') +CLASS_TYPE ('Ljava/lang/Boolean;') +CLASS_TYPE ('Lnet/minecraft/class_2022;') +CLOSE_PAREN (')') +PRIMITIVE ('V') +CRLF ('\n') +VIRTUAL_OPEN ('') +TAB (' ') +TAB (' ') +ARG_KEY ('ARG') +WHITE_SPACE (' ') +INDEX ('1') +WHITE_SPACE (' ') +NAME_ELEMENT ('dealt') +CRLF ('\n') +TAB (' ') +TAB (' ') +ARG_KEY ('ARG') +WHITE_SPACE (' ') +INDEX ('2') +WHITE_SPACE (' ') +NAME_ELEMENT ('taken') +CRLF ('\n') +TAB (' ') +TAB (' ') +ARG_KEY ('ARG') +WHITE_SPACE (' ') +INDEX ('3') +WHITE_SPACE (' ') +NAME_ELEMENT ('sourceEntity') +CRLF ('\n') +TAB (' ') +TAB (' ') +ARG_KEY ('ARG') +WHITE_SPACE (' ') +INDEX ('4') +WHITE_SPACE (' ') +NAME_ELEMENT ('blocked') +CRLF ('\n') +TAB (' ') +TAB (' ') +ARG_KEY ('ARG') +WHITE_SPACE (' ') +INDEX ('5') +WHITE_SPACE (' ') +NAME_ELEMENT ('type') +CRLF ('\n') +VIRTUAL_CLOSE ('') +TAB (' ') +METHOD_KEY ('METHOD') +WHITE_SPACE (' ') +NAME_ELEMENT ('method_8838') +WHITE_SPACE (' ') +NAME_ELEMENT ('test') +WHITE_SPACE (' ') +OPEN_PAREN ('(') +CLASS_TYPE ('Lnet/minecraft/class_3222;') +CLASS_TYPE ('Lnet/minecraft/class_1282;') +PRIMITIVE ('F') +PRIMITIVE ('F') +PRIMITIVE ('Z') +CLOSE_PAREN (')') +PRIMITIVE ('Z') +CRLF ('\n') +VIRTUAL_OPEN ('') +TAB (' ') +TAB (' ') +ARG_KEY ('ARG') +WHITE_SPACE (' ') +INDEX ('1') +WHITE_SPACE (' ') +NAME_ELEMENT ('player') +CRLF ('\n') +TAB (' ') +TAB (' ') +ARG_KEY ('ARG') +WHITE_SPACE (' ') +INDEX ('2') +WHITE_SPACE (' ') +NAME_ELEMENT ('source') +CRLF ('\n') +TAB (' ') +TAB (' ') +ARG_KEY ('ARG') +WHITE_SPACE (' ') +INDEX ('3') +WHITE_SPACE (' ') +NAME_ELEMENT ('dealt') +CRLF ('\n') +TAB (' ') +TAB (' ') +ARG_KEY ('ARG') +WHITE_SPACE (' ') +INDEX ('4') +WHITE_SPACE (' ') +NAME_ELEMENT ('taken') +CRLF ('\n') +TAB (' ') +TAB (' ') +ARG_KEY ('ARG') +WHITE_SPACE (' ') +INDEX ('5') +WHITE_SPACE (' ') +NAME_ELEMENT ('blocked') +CRLF ('\n') +VIRTUAL_CLOSE ('') +TAB (' ') +METHOD_KEY ('METHOD') +WHITE_SPACE (' ') +NAME_ELEMENT ('method_8839') +WHITE_SPACE (' ') +NAME_ELEMENT ('fromJson') +WHITE_SPACE (' ') +OPEN_PAREN ('(') +CLASS_TYPE ('Lcom/google/gson/JsonElement;') +CLOSE_PAREN (')') +CLASS_TYPE ('Lnet/minecraft/class_2019;') +CRLF ('\n') +VIRTUAL_OPEN ('') +TAB (' ') +TAB (' ') +ARG_KEY ('ARG') +WHITE_SPACE (' ') +INDEX ('0') +WHITE_SPACE (' ') +NAME_ELEMENT ('json') +CRLF ('\n') +VIRTUAL_CLOSE ('') +TAB (' ') +METHOD_KEY ('METHOD') +WHITE_SPACE (' ') +NAME_ELEMENT ('method_8840') +WHITE_SPACE (' ') +NAME_ELEMENT ('toJson') +WHITE_SPACE (' ') +OPEN_PAREN ('(') +CLOSE_PAREN (')') +CLASS_TYPE ('Lcom/google/gson/JsonElement;') +CRLF ('\n') +TAB (' ') +CLASS_KEY ('CLASS') +WHITE_SPACE (' ') +NAME_ELEMENT ('class_2020') +WHITE_SPACE (' ') +NAME_ELEMENT ('Builder') +CRLF ('\n') +VIRTUAL_OPEN ('') +TAB (' ') +TAB (' ') +FIELD_KEY ('FIELD') +WHITE_SPACE (' ') +NAME_ELEMENT ('field_9526') +WHITE_SPACE (' ') +NAME_ELEMENT ('blocked') +WHITE_SPACE (' ') +CLASS_TYPE ('Ljava/lang/Boolean;') +CRLF ('\n') +TAB (' ') +TAB (' ') +FIELD_KEY ('FIELD') +WHITE_SPACE (' ') +NAME_ELEMENT ('field_9527') +WHITE_SPACE (' ') +NAME_ELEMENT ('taken') +WHITE_SPACE (' ') +CLASS_TYPE ('Lnet/minecraft/class_2096$class_2099;') +CRLF ('\n') +TAB (' ') +TAB (' ') +FIELD_KEY ('FIELD') +WHITE_SPACE (' ') +NAME_ELEMENT ('field_9528') +WHITE_SPACE (' ') +NAME_ELEMENT ('sourceEntity') +WHITE_SPACE (' ') +CLASS_TYPE ('Lnet/minecraft/class_2048;') +CRLF ('\n') +TAB (' ') +TAB (' ') +FIELD_KEY ('FIELD') +WHITE_SPACE (' ') +NAME_ELEMENT ('field_9529') +WHITE_SPACE (' ') +NAME_ELEMENT ('type') +WHITE_SPACE (' ') +CLASS_TYPE ('Lnet/minecraft/class_2022;') +CRLF ('\n') +TAB (' ') +TAB (' ') +FIELD_KEY ('FIELD') +WHITE_SPACE (' ') +NAME_ELEMENT ('field_9530') +WHITE_SPACE (' ') +NAME_ELEMENT ('dealt') +WHITE_SPACE (' ') +CLASS_TYPE ('Lnet/minecraft/class_2096$class_2099;') +CRLF ('\n') +TAB (' ') +TAB (' ') +METHOD_KEY ('METHOD') +WHITE_SPACE (' ') +NAME_ELEMENT ('method_8841') +WHITE_SPACE (' ') +NAME_ELEMENT ('blocked') +WHITE_SPACE (' ') +OPEN_PAREN ('(') +CLASS_TYPE ('Ljava/lang/Boolean;') +CLOSE_PAREN (')') +CLASS_TYPE ('Lnet/minecraft/class_2019$class_2020;') +CRLF ('\n') +VIRTUAL_OPEN ('') +TAB (' ') +TAB (' ') +TAB (' ') +ARG_KEY ('ARG') +WHITE_SPACE (' ') +INDEX ('1') +WHITE_SPACE (' ') +NAME_ELEMENT ('blocked') +CRLF ('\n') +VIRTUAL_CLOSE ('') +TAB (' ') +TAB (' ') +METHOD_KEY ('METHOD') +WHITE_SPACE (' ') +NAME_ELEMENT ('method_8842') +WHITE_SPACE (' ') +NAME_ELEMENT ('type') +WHITE_SPACE (' ') +OPEN_PAREN ('(') +CLASS_TYPE ('Lnet/minecraft/class_2022$class_2023;') +CLOSE_PAREN (')') +CLASS_TYPE ('Lnet/minecraft/class_2019$class_2020;') +CRLF ('\n') +VIRTUAL_OPEN ('') +TAB (' ') +TAB (' ') +TAB (' ') +ARG_KEY ('ARG') +WHITE_SPACE (' ') +INDEX ('1') +WHITE_SPACE (' ') +NAME_ELEMENT ('builder') +CRLF ('\n') +VIRTUAL_CLOSE ('') +TAB (' ') +TAB (' ') +METHOD_KEY ('METHOD') +WHITE_SPACE (' ') +NAME_ELEMENT ('method_8843') +WHITE_SPACE (' ') +NAME_ELEMENT ('build') +WHITE_SPACE (' ') +OPEN_PAREN ('(') +CLOSE_PAREN (')') +CLASS_TYPE ('Lnet/minecraft/class_2019;') +CRLF ('\n') +TAB (' ') +TAB (' ') +METHOD_KEY ('METHOD') +WHITE_SPACE (' ') +NAME_ELEMENT ('method_8844') +WHITE_SPACE (' ') +NAME_ELEMENT ('create') +WHITE_SPACE (' ') +OPEN_PAREN ('(') +CLOSE_PAREN (')') +CLASS_TYPE ('Lnet/minecraft/class_2019$class_2020;') diff --git a/obfuscation-explorer/src/test/resources/io/mcdev/obfex/formats/srg/lexer/fixtures/test.srg b/obfuscation-explorer/src/test/resources/io/mcdev/obfex/formats/srg/lexer/fixtures/test.srg new file mode 100644 index 000000000..c3a12d2b2 --- /dev/null +++ b/obfuscation-explorer/src/test/resources/io/mcdev/obfex/formats/srg/lexer/fixtures/test.srg @@ -0,0 +1,10 @@ +# CL: yu net/minecraft/Comment +PK: ./ net/minecraft/server +CL: uih net/minecraft/CommentTest # CL: op uk/jr/Operator +CL: ght net/minecraft/Test +CL: ght$ds net/minecraft/Test$Example +CL: ght$ds$bg net/minecraft/Test$Example$Inner +FD: ght/rft net/minecraft/Test/log +FD: ght$ds/juh net/minecraft/Test$Example/server +MD: ght/hyuip (I)Z net/minecraft/Test/isEven (I)Z +MD: ght$ds/hyuip (I)Z net/minecraft/Test$Example/isOdd (I)Z diff --git a/obfuscation-explorer/src/test/resources/io/mcdev/obfex/formats/srg/lexer/fixtures/test.txt b/obfuscation-explorer/src/test/resources/io/mcdev/obfex/formats/srg/lexer/fixtures/test.txt new file mode 100644 index 000000000..11ec5d9d1 --- /dev/null +++ b/obfuscation-explorer/src/test/resources/io/mcdev/obfex/formats/srg/lexer/fixtures/test.txt @@ -0,0 +1,124 @@ +COMMENT ('# CL: yu net/minecraft/Comment') +CRLF ('\n') +PACKAGE_KEY ('PK:') +WHITE_SPACE (' ') +PACKAGE_NAME ('./') +WHITE_SPACE (' ') +PACKAGE_NAME ('net/minecraft/server') +CRLF ('\n') +CLASS_KEY ('CL:') +WHITE_SPACE (' ') +NAME_ELEMENT ('uih') +WHITE_SPACE (' ') +NAME_ELEMENT ('net') +SLASH ('/') +NAME_ELEMENT ('minecraft') +SLASH ('/') +NAME_ELEMENT ('CommentTest') +COMMENT (' # CL: op uk/jr/Operator') +CRLF ('\n') +CLASS_KEY ('CL:') +WHITE_SPACE (' ') +NAME_ELEMENT ('ght') +WHITE_SPACE (' ') +NAME_ELEMENT ('net') +SLASH ('/') +NAME_ELEMENT ('minecraft') +SLASH ('/') +NAME_ELEMENT ('Test') +CRLF ('\n') +CLASS_KEY ('CL:') +WHITE_SPACE (' ') +NAME_ELEMENT ('ght$ds') +WHITE_SPACE (' ') +NAME_ELEMENT ('net') +SLASH ('/') +NAME_ELEMENT ('minecraft') +SLASH ('/') +NAME_ELEMENT ('Test$Example') +CRLF ('\n') +CLASS_KEY ('CL:') +WHITE_SPACE (' ') +NAME_ELEMENT ('ght$ds$bg') +WHITE_SPACE (' ') +NAME_ELEMENT ('net') +SLASH ('/') +NAME_ELEMENT ('minecraft') +SLASH ('/') +NAME_ELEMENT ('Test$Example$Inner') +CRLF ('\n') +FIELD_KEY ('FD:') +WHITE_SPACE (' ') +NAME_ELEMENT ('ght') +SLASH ('/') +NAME_ELEMENT ('rft') +WHITE_SPACE (' ') +NAME_ELEMENT ('net') +SLASH ('/') +NAME_ELEMENT ('minecraft') +SLASH ('/') +NAME_ELEMENT ('Test') +SLASH ('/') +NAME_ELEMENT ('log') +CRLF ('\n') +FIELD_KEY ('FD:') +WHITE_SPACE (' ') +NAME_ELEMENT ('ght$ds') +SLASH ('/') +NAME_ELEMENT ('juh') +WHITE_SPACE (' ') +NAME_ELEMENT ('net') +SLASH ('/') +NAME_ELEMENT ('minecraft') +SLASH ('/') +NAME_ELEMENT ('Test$Example') +SLASH ('/') +NAME_ELEMENT ('server') +CRLF ('\n') +METHOD_KEY ('MD:') +WHITE_SPACE (' ') +NAME_ELEMENT ('ght') +SLASH ('/') +NAME_ELEMENT ('hyuip') +WHITE_SPACE (' ') +OPEN_PAREN ('(') +PRIMITIVE ('I') +CLOSE_PAREN (')') +PRIMITIVE ('Z') +WHITE_SPACE (' ') +NAME_ELEMENT ('net') +SLASH ('/') +NAME_ELEMENT ('minecraft') +SLASH ('/') +NAME_ELEMENT ('Test') +SLASH ('/') +NAME_ELEMENT ('isEven') +WHITE_SPACE (' ') +OPEN_PAREN ('(') +PRIMITIVE ('I') +CLOSE_PAREN (')') +PRIMITIVE ('Z') +CRLF ('\n') +METHOD_KEY ('MD:') +WHITE_SPACE (' ') +NAME_ELEMENT ('ght$ds') +SLASH ('/') +NAME_ELEMENT ('hyuip') +WHITE_SPACE (' ') +OPEN_PAREN ('(') +PRIMITIVE ('I') +CLOSE_PAREN (')') +PRIMITIVE ('Z') +WHITE_SPACE (' ') +NAME_ELEMENT ('net') +SLASH ('/') +NAME_ELEMENT ('minecraft') +SLASH ('/') +NAME_ELEMENT ('Test$Example') +SLASH ('/') +NAME_ELEMENT ('isOdd') +WHITE_SPACE (' ') +OPEN_PAREN ('(') +PRIMITIVE ('I') +CLOSE_PAREN (')') +PRIMITIVE ('Z') diff --git a/obfuscation-explorer/src/test/resources/io/mcdev/obfex/formats/tinyv2/lexer/fixtures/test.tiny b/obfuscation-explorer/src/test/resources/io/mcdev/obfex/formats/tinyv2/lexer/fixtures/test.tiny new file mode 100644 index 000000000..a8049946b --- /dev/null +++ b/obfuscation-explorer/src/test/resources/io/mcdev/obfex/formats/tinyv2/lexer/fixtures/test.tiny @@ -0,0 +1,15 @@ +tiny 2 0 official intermediary named + someProperty someValue + anotherProperty +c a class_123 pkg/SomeClass + c Class comment + m (III)V a method_456 someMethod + p 1 param_0 x + c Param comment + p 2 param_1 y + p 3 param_2 z + c Method comment + f [I a field_789 someField + c Field comment +c b class_234 pkg/xy/AnotherClass + m (Ljava/lang/String;)I a method_567 anotherMethod diff --git a/obfuscation-explorer/src/test/resources/io/mcdev/obfex/formats/tinyv2/lexer/fixtures/test.txt b/obfuscation-explorer/src/test/resources/io/mcdev/obfex/formats/tinyv2/lexer/fixtures/test.txt new file mode 100644 index 000000000..36eb8e874 --- /dev/null +++ b/obfuscation-explorer/src/test/resources/io/mcdev/obfex/formats/tinyv2/lexer/fixtures/test.txt @@ -0,0 +1,135 @@ +TINY_KEY ('tiny') +WHITE_SPACE (' ') +VERSION_NUM ('2') +WHITE_SPACE (' ') +VERSION_NUM ('0') +WHITE_SPACE (' ') +NAMESPACE_KEY ('official') +WHITE_SPACE (' ') +NAMESPACE_KEY ('intermediary') +WHITE_SPACE (' ') +NAMESPACE_KEY ('named') +CRLF ('\n') +WHITE_SPACE (' ') +PROPERTY_KEY ('someProperty') +WHITE_SPACE (' ') +PROPERTY_VALUE ('someValue') +CRLF ('\n') +WHITE_SPACE (' ') +PROPERTY_KEY ('anotherProperty') +CRLF ('\n') +CLASS_KEY ('c') +WHITE_SPACE (' ') +NAME_ELEMENT ('a') +WHITE_SPACE (' ') +NAME_ELEMENT ('class_123') +WHITE_SPACE (' ') +NAME_ELEMENT ('pkg') +SLASH ('/') +NAME_ELEMENT ('SomeClass') +CRLF ('\n') +WHITE_SPACE (' ') +COMMENT_KEY ('c') +DOC_TEXT (' Class comment') +CRLF ('\n') +WHITE_SPACE (' ') +METHOD_KEY ('m') +WHITE_SPACE (' ') +OPEN_PAREN ('(') +PRIMITIVE ('I') +PRIMITIVE ('I') +PRIMITIVE ('I') +CLOSE_PAREN (')') +PRIMITIVE ('V') +WHITE_SPACE (' ') +NAME_ELEMENT ('a') +WHITE_SPACE (' ') +NAME_ELEMENT ('method_456') +WHITE_SPACE (' ') +NAME_ELEMENT ('someMethod') +CRLF ('\n') +WHITE_SPACE (' ') +WHITE_SPACE (' ') +PARAM_KEY ('p') +WHITE_SPACE (' ') +DIGIT ('1') +WHITE_SPACE (' ') +WHITE_SPACE (' ') +NAME_ELEMENT ('param_0') +WHITE_SPACE (' ') +NAME_ELEMENT ('x') +CRLF ('\n') +WHITE_SPACE (' ') +WHITE_SPACE (' ') +WHITE_SPACE (' ') +COMMENT_KEY ('c') +DOC_TEXT (' Param comment') +CRLF ('\n') +WHITE_SPACE (' ') +WHITE_SPACE (' ') +PARAM_KEY ('p') +WHITE_SPACE (' ') +DIGIT ('2') +WHITE_SPACE (' ') +WHITE_SPACE (' ') +NAME_ELEMENT ('param_1') +WHITE_SPACE (' ') +NAME_ELEMENT ('y') +CRLF ('\n') +WHITE_SPACE (' ') +WHITE_SPACE (' ') +PARAM_KEY ('p') +WHITE_SPACE (' ') +DIGIT ('3') +WHITE_SPACE (' ') +WHITE_SPACE (' ') +NAME_ELEMENT ('param_2') +WHITE_SPACE (' ') +NAME_ELEMENT ('z') +CRLF ('\n') +WHITE_SPACE (' ') +WHITE_SPACE (' ') +COMMENT_KEY ('c') +DOC_TEXT (' Method comment') +CRLF ('\n') +WHITE_SPACE (' ') +FIELD_KEY ('f') +WHITE_SPACE (' ') +CLASS_TYPE ('[I') +WHITE_SPACE (' ') +NAME_ELEMENT ('a') +WHITE_SPACE (' ') +NAME_ELEMENT ('field_789') +WHITE_SPACE (' ') +NAME_ELEMENT ('someField') +CRLF ('\n') +WHITE_SPACE (' ') +WHITE_SPACE (' ') +COMMENT_KEY ('c') +DOC_TEXT (' Field comment') +CRLF ('\n') +CLASS_KEY ('c') +WHITE_SPACE (' ') +NAME_ELEMENT ('b') +WHITE_SPACE (' ') +NAME_ELEMENT ('class_234') +WHITE_SPACE (' ') +NAME_ELEMENT ('pkg') +SLASH ('/') +NAME_ELEMENT ('xy') +SLASH ('/') +NAME_ELEMENT ('AnotherClass') +CRLF ('\n') +WHITE_SPACE (' ') +METHOD_KEY ('m') +WHITE_SPACE (' ') +OPEN_PAREN ('(') +CLASS_TYPE ('Ljava/lang/String;') +CLOSE_PAREN (')') +PRIMITIVE ('I') +WHITE_SPACE (' ') +NAME_ELEMENT ('a') +WHITE_SPACE (' ') +NAME_ELEMENT ('method_567') +WHITE_SPACE (' ') +NAME_ELEMENT ('anotherMethod') diff --git a/obfuscation-explorer/src/test/resources/io/mcdev/obfex/formats/tsrg2/lexer/fixtures/test.tsrg2 b/obfuscation-explorer/src/test/resources/io/mcdev/obfex/formats/tsrg2/lexer/fixtures/test.tsrg2 new file mode 100644 index 000000000..ac67f947a --- /dev/null +++ b/obfuscation-explorer/src/test/resources/io/mcdev/obfex/formats/tsrg2/lexer/fixtures/test.tsrg2 @@ -0,0 +1,26 @@ +tsrg2 left right +com/mojang/blaze3d/Blaze3D com/mojang/blaze3d/Blaze3D + ()V + getTime ()D m_83640_ + static + process (Lcom/mojang/blaze3d/pipeline/RenderPipeline;F)V m_166118_ + static + 0 p_166119_ p_166119_ + 1 p_166120_ p_166120_ +com/mojang/blaze3d/audio/Channel com/mojang/blaze3d/audio/ChannelTest + BUFFER_DURATION_SECONDS f_166124_ + LOGGER f_83641_ + QUEUED_BUFFER_COUNT f_166125_ + ()V + static + (I)V + 0 p_83648_ p_83648_ + attachBufferStream (Lnet/minecraft/client/sounds/AudioStream;)V m_83658_ + 0 p_83659_ p_83659_ + initialized f_83643_ + attachStaticBuffer (Lcom/mojang/blaze3d/audio/SoundBuffer;)V m_83656_ + 0 p_83657_ TEST + calculateBufferSize (Ljavax/sound/sampled/AudioFormat;I)I m_83660_ + static + 0 p_83661_ p_83661_test + 1 p_83662_ p_83662_test diff --git a/obfuscation-explorer/src/test/resources/io/mcdev/obfex/formats/tsrg2/lexer/fixtures/test.txt b/obfuscation-explorer/src/test/resources/io/mcdev/obfex/formats/tsrg2/lexer/fixtures/test.txt new file mode 100644 index 000000000..a66abbf8f --- /dev/null +++ b/obfuscation-explorer/src/test/resources/io/mcdev/obfex/formats/tsrg2/lexer/fixtures/test.txt @@ -0,0 +1,228 @@ +TSRG2_KEY ('tsrg2') +WHITE_SPACE (' ') +NAMESPACE_KEY ('left') +WHITE_SPACE (' ') +NAMESPACE_KEY ('right') +CRLF ('\n') +NAME_ELEMENT ('com') +SLASH ('/') +NAME_ELEMENT ('mojang') +SLASH ('/') +NAME_ELEMENT ('blaze3d') +SLASH ('/') +NAME_ELEMENT ('Blaze3D') +WHITE_SPACE (' ') +NAME_ELEMENT ('com') +SLASH ('/') +NAME_ELEMENT ('mojang') +SLASH ('/') +NAME_ELEMENT ('blaze3d') +SLASH ('/') +NAME_ELEMENT ('Blaze3D') +CRLF ('\n') +VIRTUAL_OPEN ('') +TAB (' ') +INIT ('') +WHITE_SPACE (' ') +OPEN_PAREN ('(') +CLOSE_PAREN (')') +PRIMITIVE ('V') +WHITE_SPACE (' ') +INIT ('') +CRLF ('\n') +TAB (' ') +NAME_ELEMENT ('getTime') +WHITE_SPACE (' ') +OPEN_PAREN ('(') +CLOSE_PAREN (')') +PRIMITIVE ('D') +WHITE_SPACE (' ') +NAME_ELEMENT ('m_83640_') +CRLF ('\n') +VIRTUAL_OPEN ('') +TAB (' ') +TAB (' ') +STATIC ('static') +CRLF ('\n') +VIRTUAL_CLOSE ('') +TAB (' ') +NAME_ELEMENT ('process') +WHITE_SPACE (' ') +OPEN_PAREN ('(') +CLASS_TYPE ('Lcom/mojang/blaze3d/pipeline/RenderPipeline;') +PRIMITIVE ('F') +CLOSE_PAREN (')') +PRIMITIVE ('V') +WHITE_SPACE (' ') +NAME_ELEMENT ('m_166118_') +CRLF ('\n') +VIRTUAL_OPEN ('') +TAB (' ') +TAB (' ') +STATIC ('static') +CRLF ('\n') +TAB (' ') +TAB (' ') +DIGIT ('0') +WHITE_SPACE (' ') +NAME_ELEMENT ('p_166119_') +WHITE_SPACE (' ') +NAME_ELEMENT ('p_166119_') +CRLF ('\n') +TAB (' ') +TAB (' ') +DIGIT ('1') +WHITE_SPACE (' ') +NAME_ELEMENT ('p_166120_') +WHITE_SPACE (' ') +NAME_ELEMENT ('p_166120_') +CRLF ('\n') +VIRTUAL_CLOSE ('') +VIRTUAL_CLOSE ('') +NAME_ELEMENT ('com') +SLASH ('/') +NAME_ELEMENT ('mojang') +SLASH ('/') +NAME_ELEMENT ('blaze3d') +SLASH ('/') +NAME_ELEMENT ('audio') +SLASH ('/') +NAME_ELEMENT ('Channel') +WHITE_SPACE (' ') +NAME_ELEMENT ('com') +SLASH ('/') +NAME_ELEMENT ('mojang') +SLASH ('/') +NAME_ELEMENT ('blaze3d') +SLASH ('/') +NAME_ELEMENT ('audio') +SLASH ('/') +NAME_ELEMENT ('ChannelTest') +CRLF ('\n') +VIRTUAL_OPEN ('') +TAB (' ') +NAME_ELEMENT ('BUFFER_DURATION_SECONDS') +WHITE_SPACE (' ') +NAME_ELEMENT ('f_166124_') +CRLF ('\n') +TAB (' ') +NAME_ELEMENT ('LOGGER') +WHITE_SPACE (' ') +NAME_ELEMENT ('f_83641_') +CRLF ('\n') +TAB (' ') +NAME_ELEMENT ('QUEUED_BUFFER_COUNT') +WHITE_SPACE (' ') +NAME_ELEMENT ('f_166125_') +CRLF ('\n') +TAB (' ') +CLINIT ('') +WHITE_SPACE (' ') +OPEN_PAREN ('(') +CLOSE_PAREN (')') +PRIMITIVE ('V') +WHITE_SPACE (' ') +CLINIT ('') +CRLF ('\n') +VIRTUAL_OPEN ('') +TAB (' ') +TAB (' ') +STATIC ('static') +CRLF ('\n') +VIRTUAL_CLOSE ('') +TAB (' ') +INIT ('') +WHITE_SPACE (' ') +OPEN_PAREN ('(') +PRIMITIVE ('I') +CLOSE_PAREN (')') +PRIMITIVE ('V') +WHITE_SPACE (' ') +INIT ('') +CRLF ('\n') +VIRTUAL_OPEN ('') +TAB (' ') +TAB (' ') +DIGIT ('0') +WHITE_SPACE (' ') +NAME_ELEMENT ('p_83648_') +WHITE_SPACE (' ') +NAME_ELEMENT ('p_83648_') +CRLF ('\n') +VIRTUAL_CLOSE ('') +TAB (' ') +NAME_ELEMENT ('attachBufferStream') +WHITE_SPACE (' ') +OPEN_PAREN ('(') +CLASS_TYPE ('Lnet/minecraft/client/sounds/AudioStream;') +CLOSE_PAREN (')') +PRIMITIVE ('V') +WHITE_SPACE (' ') +NAME_ELEMENT ('m_83658_') +CRLF ('\n') +VIRTUAL_OPEN ('') +TAB (' ') +TAB (' ') +DIGIT ('0') +WHITE_SPACE (' ') +NAME_ELEMENT ('p_83659_') +WHITE_SPACE (' ') +NAME_ELEMENT ('p_83659_') +CRLF ('\n') +VIRTUAL_CLOSE ('') +TAB (' ') +NAME_ELEMENT ('initialized') +WHITE_SPACE (' ') +NAME_ELEMENT ('f_83643_') +CRLF ('\n') +TAB (' ') +NAME_ELEMENT ('attachStaticBuffer') +WHITE_SPACE (' ') +OPEN_PAREN ('(') +CLASS_TYPE ('Lcom/mojang/blaze3d/audio/SoundBuffer;') +CLOSE_PAREN (')') +PRIMITIVE ('V') +WHITE_SPACE (' ') +NAME_ELEMENT ('m_83656_') +CRLF ('\n') +VIRTUAL_OPEN ('') +TAB (' ') +TAB (' ') +DIGIT ('0') +WHITE_SPACE (' ') +NAME_ELEMENT ('p_83657_') +WHITE_SPACE (' ') +NAME_ELEMENT ('TEST') +CRLF ('\n') +VIRTUAL_CLOSE ('') +TAB (' ') +NAME_ELEMENT ('calculateBufferSize') +WHITE_SPACE (' ') +OPEN_PAREN ('(') +CLASS_TYPE ('Ljavax/sound/sampled/AudioFormat;') +PRIMITIVE ('I') +CLOSE_PAREN (')') +PRIMITIVE ('I') +WHITE_SPACE (' ') +NAME_ELEMENT ('m_83660_') +CRLF ('\n') +VIRTUAL_OPEN ('') +TAB (' ') +TAB (' ') +STATIC ('static') +CRLF ('\n') +TAB (' ') +TAB (' ') +DIGIT ('0') +WHITE_SPACE (' ') +NAME_ELEMENT ('p_83661_') +WHITE_SPACE (' ') +NAME_ELEMENT ('p_83661_test') +CRLF ('\n') +TAB (' ') +TAB (' ') +DIGIT ('1') +WHITE_SPACE (' ') +NAME_ELEMENT ('p_83662_') +WHITE_SPACE (' ') +NAME_ELEMENT ('p_83662_test') diff --git a/settings.gradle.kts b/settings.gradle.kts index e277e1d24..4e6b06a62 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -23,6 +23,7 @@ plugins { } rootProject.name = "MinecraftDev" +include("obfuscation-explorer") enableFeaturePreview("TYPESAFE_PROJECT_ACCESSORS") diff --git a/src/main/grammars/AtParser.bnf b/src/main/grammars/AtParser.bnf index 5e93d1337..fcad83735 100644 --- a/src/main/grammars/AtParser.bnf +++ b/src/main/grammars/AtParser.bnf @@ -38,7 +38,7 @@ at_file ::= line* private line ::= !<> entry? COMMENT? end_line -private end_line ::= crlf | <> +private end_line ::= CRLF | <> entry ::= keyword class_name line_value? { mixin="com.demonwav.mcdev.platform.mcp.at.psi.mixins.impl.AtEntryImplMixin" diff --git a/src/main/kotlin/facet/MinecraftFacet.kt b/src/main/kotlin/facet/MinecraftFacet.kt index 9fafe353f..476879f0b 100644 --- a/src/main/kotlin/facet/MinecraftFacet.kt +++ b/src/main/kotlin/facet/MinecraftFacet.kt @@ -251,7 +251,7 @@ class MinecraftFacet( val ID = FacetTypeId(TYPE_ID) val facetType: MinecraftFacetType - get() = FacetTypeRegistry.getInstance().findFacetType(ID) as MinecraftFacetType + get() = facetTypeOrNull as MinecraftFacetType val facetTypeOrNull: MinecraftFacetType? get() = FacetTypeRegistry.getInstance().findFacetType(TYPE_ID) as? MinecraftFacetType diff --git a/src/main/kotlin/nbt/filetype/NbtFileTypeDetector.kt b/src/main/kotlin/nbt/filetype/NbtFileTypeDetector.kt index a0319a833..560b75672 100644 --- a/src/main/kotlin/nbt/filetype/NbtFileTypeDetector.kt +++ b/src/main/kotlin/nbt/filetype/NbtFileTypeDetector.kt @@ -31,6 +31,10 @@ import java.io.EOFException class NbtFileTypeDetector : FileTypeRegistry.FileTypeDetector { override fun detect(file: VirtualFile, firstBytes: ByteSequence, firstCharsIfText: CharSequence?): FileType? { + if (firstCharsIfText != null) { + return null + } + return try { // 20 ms is plenty of time to parse most files // Won't parse very large files, but if we fail on timeout then those files probably are NBT anyways diff --git a/src/main/kotlin/nbt/lang/colors/NbttSyntaxHighlighter.kt b/src/main/kotlin/nbt/lang/colors/NbttSyntaxHighlighter.kt index 4f4bcee29..02c7b2bc2 100644 --- a/src/main/kotlin/nbt/lang/colors/NbttSyntaxHighlighter.kt +++ b/src/main/kotlin/nbt/lang/colors/NbttSyntaxHighlighter.kt @@ -42,7 +42,7 @@ class NbttSyntaxHighlighter : SyntaxHighlighterBase() { NbttTypes.LONG_LITERAL -> LONG_KEYS NbttTypes.FLOAT_LITERAL -> FLOAT_KEYS NbttTypes.DOUBLE_LITERAL -> DOUBLE_KEYS - else -> EMPTY_KEYS + else -> TextAttributesKey.EMPTY_ARRAY } } @@ -70,6 +70,5 @@ class NbttSyntaxHighlighter : SyntaxHighlighterBase() { val LONG_KEYS = arrayOf(LONG) val FLOAT_KEYS = arrayOf(FLOAT) val DOUBLE_KEYS = arrayOf(DOUBLE) - val EMPTY_KEYS = emptyArray() } } diff --git a/src/main/kotlin/platform/mcp/at/AtFile.kt b/src/main/kotlin/platform/mcp/at/AtFile.kt index 3e7b96774..d99d7666c 100644 --- a/src/main/kotlin/platform/mcp/at/AtFile.kt +++ b/src/main/kotlin/platform/mcp/at/AtFile.kt @@ -47,6 +47,6 @@ class AtFile(viewProvider: FileViewProvider) : PsiFileBase(viewProvider, AtLangu } override fun getFileType() = AtFileType - override fun toString() = "Access Transformer File" + override fun toString() = AtFileType.description override fun getIcon(flags: Int) = PlatformAssets.MCP_ICON } diff --git a/src/main/kotlin/platform/mcp/at/AtFileType.kt b/src/main/kotlin/platform/mcp/at/AtFileType.kt index fffb0ecc4..f0ac47347 100644 --- a/src/main/kotlin/platform/mcp/at/AtFileType.kt +++ b/src/main/kotlin/platform/mcp/at/AtFileType.kt @@ -26,7 +26,7 @@ import com.intellij.openapi.fileTypes.LanguageFileType object AtFileType : LanguageFileType(AtLanguage) { override fun getName() = "Access Transformers" - override fun getDescription() = "Access transformers" + override fun getDescription() = "Access Transformers" override fun getDefaultExtension() = "" override fun getIcon() = PlatformAssets.MCP_ICON } diff --git a/src/main/kotlin/platform/mcp/at/AtParserDefinition.kt b/src/main/kotlin/platform/mcp/at/AtParserDefinition.kt index 377ce06fa..135fba57c 100644 --- a/src/main/kotlin/platform/mcp/at/AtParserDefinition.kt +++ b/src/main/kotlin/platform/mcp/at/AtParserDefinition.kt @@ -28,7 +28,6 @@ import com.intellij.lang.ParserDefinition import com.intellij.openapi.project.Project import com.intellij.psi.FileViewProvider import com.intellij.psi.PsiElement -import com.intellij.psi.TokenType import com.intellij.psi.tree.IElementType import com.intellij.psi.tree.IFileElementType import com.intellij.psi.tree.TokenSet @@ -36,7 +35,6 @@ import com.intellij.psi.tree.TokenSet class AtParserDefinition : ParserDefinition { override fun createLexer(project: Project) = AtLexerAdapter() - override fun getWhitespaceTokens() = WHITE_SPACES override fun getCommentTokens() = COMMENTS override fun getStringLiteralElements(): TokenSet = TokenSet.EMPTY override fun createParser(project: Project) = AtParser() @@ -49,7 +47,6 @@ class AtParserDefinition : ParserDefinition { ?: ParserDefinition.SpaceRequirements.MUST_NOT companion object { - private val WHITE_SPACES = TokenSet.create(TokenType.WHITE_SPACE) private val COMMENTS = TokenSet.create(AtTypes.COMMENT) private val FILE = IFileElementType(Language.findInstance(AtLanguage::class.java)) diff --git a/src/main/kotlin/platform/mcp/at/AtSyntaxHighlighter.kt b/src/main/kotlin/platform/mcp/at/AtSyntaxHighlighter.kt index e6a92cec5..27760cc72 100644 --- a/src/main/kotlin/platform/mcp/at/AtSyntaxHighlighter.kt +++ b/src/main/kotlin/platform/mcp/at/AtSyntaxHighlighter.kt @@ -32,7 +32,7 @@ class AtSyntaxHighlighter : SyntaxHighlighterBase() { override fun getHighlightingLexer() = AtLexerAdapter() - override fun getTokenHighlights(tokenType: IElementType) = + override fun getTokenHighlights(tokenType: IElementType?): Array = when (tokenType) { AtTypes.KEYWORD_ELEMENT -> KEYWORD_KEYS AtTypes.CLASS_NAME_ELEMENT -> CLASS_NAME_KEYS @@ -42,7 +42,7 @@ class AtSyntaxHighlighter : SyntaxHighlighterBase() { AtTypes.PRIMITIVE -> PRIMITIVE_KEYS AtTypes.COMMENT -> COMMENT_KEYS TokenType.BAD_CHARACTER -> BAD_CHARACTER_KEYS - else -> EMPTY_KEYS + else -> TextAttributesKey.EMPTY_ARRAY } companion object { @@ -70,6 +70,5 @@ class AtSyntaxHighlighter : SyntaxHighlighterBase() { private val PRIMITIVE_KEYS = arrayOf(PRIMITIVE) private val COMMENT_KEYS = arrayOf(COMMENT) private val BAD_CHARACTER_KEYS = arrayOf(BAD_CHARACTER) - private val EMPTY_KEYS = emptyArray() } } diff --git a/src/main/kotlin/util/psi-utils.kt b/src/main/kotlin/util/psi-utils.kt index 548ea40ed..4c3d33041 100644 --- a/src/main/kotlin/util/psi-utils.kt +++ b/src/main/kotlin/util/psi-utils.kt @@ -103,13 +103,9 @@ private val PsiElement.ancestors: Sequence fun PsiElement.isAncestorOf(child: PsiElement): Boolean = child.ancestors.contains(this) -private inline fun PsiElement.findParent(resolveReferences: Boolean): T? { - return findParent(resolveReferences) { false } -} - private inline fun PsiElement.findParent( resolveReferences: Boolean, - stop: (PsiElement) -> Boolean, + stop: (PsiElement) -> Boolean = { false }, ): T? { var el: PsiElement = this diff --git a/src/main/resources/META-INF/plugin.xml b/src/main/resources/META-INF/plugin.xml index 27a38c6a3..74957336f 100644 --- a/src/main/resources/META-INF/plugin.xml +++ b/src/main/resources/META-INF/plugin.xml @@ -84,7 +84,7 @@ - + @@ -137,7 +137,7 @@ - + diff --git a/src/test/kotlin/framework/test-util.kt b/src/test/kotlin/framework/test-util.kt index 1e0a78dc4..901c82717 100644 --- a/src/test/kotlin/framework/test-util.kt +++ b/src/test/kotlin/framework/test-util.kt @@ -88,38 +88,40 @@ fun testLexer(basePath: String, lexer: Lexer) { val caller = ReflectionUtil.getCallerClass(3)!! val text = caller.getResource(basePath)!!.readText().trim() - val expected = caller.getResource("${basePath.substringBeforeLast('.')}.txt")!!.readText().trim() - val actual = LexerTestCase.printTokens(text.filter { it != '\r' }, 0, lexer) + val expected = caller.getResource("${basePath.substringBeforeLast('.')}.txt")?.readText()?.trim() + ?: Assertions.fail("no test data") + val actual = LexerTestCase.printTokens(text.filter { it != '\r' }, 0, lexer).trim() - val expectedLines = expected.lineSequence().filter { it.isNotBlank() }.toList() - val actualLines = actual.lineSequence().filter { it.isNotBlank() }.toList() - Assertions.assertLinesMatch(expectedLines, actualLines) -} - -fun ProjectBuilderTest.testParser(basePath: String, func: ProjectBuilderFunc) { - val caller = ReflectionUtil.getCallerClass(3)!! - val text = caller.getResource(basePath).readText().trim() - val expected = caller.getResource("${basePath.substringBeforeLast('.')}.txt").readText().trim() - - var file: PsiFile? = null - buildProject { - file = func(basePath.substringAfterLast('/'), text, true, false).toPsiFile() + Assertions.assertEquals(expected, actual) } - val actual = DebugUtil.psiToString(file!!, true, true) + fun ProjectBuilderTest.testParser(basePath: String, func: ProjectBuilderFunc) { + val caller = ReflectionUtil.getCallerClass(3)!! + val text = caller.getResource(basePath)?.readText()?.trim() ?: Assertions.fail("no test data") + val expected = caller.getResource("${basePath.substringBeforeLast('.')}.txt")?.readText()?.trim() + ?: Assertions.fail("no expected data") - val expectedLines = expected.lineSequence().filter { it.isNotBlank() }.toList() - val actualLines = actual.lineSequence().filter { it.isNotBlank() }.toList() - Assertions.assertLinesMatch(expectedLines, actualLines) -} + var file: PsiFile? = null + buildProject { + file = func(basePath.substringAfterLast('/'), text, true, false).toPsiFile() + } -fun testInspectionFix(fixture: JavaCodeInsightTestFixture, basePath: String, fixName: String) { - val caller = ReflectionUtil.getCallerClass(4)!! - val original = caller.getResource("$basePath.java").readText().trim().lineSequence().joinToString("\n") - val expected = caller.getResource("$basePath.after.java").readText().trim().lineSequence().joinToString("\n") + val actual = DebugUtil.psiToString(file!!, true, true).trim() - fixture.configureByText(JavaFileType.INSTANCE, original) - val intention = fixture.findSingleIntention(fixName) - fixture.launchAction(intention) - fixture.checkResult(expected) -} + Assertions.assertEquals(expected, actual) + } + + fun testInspectionFix(fixture: JavaCodeInsightTestFixture, basePath: String, fixName: String) { + val caller = ReflectionUtil.getCallerClass(4)!! + val original = + caller.getResource("$basePath.java")?.readText()?.trim()?.lineSequence()?.joinToString("\n") + ?: Assertions.fail("no test data") + val expected = caller.getResource("$basePath.after.java")?.readText()?.trim()?.lineSequence() + ?.joinToString("\n") ?: Assertions.fail("no expected data") + + fixture.configureByText(JavaFileType.INSTANCE, original) + val intention = fixture.findSingleIntention(fixName) + fixture.launchAction(intention) + fixture.checkResult(expected) + } + \ No newline at end of file diff --git a/src/test/resources/com/demonwav/mcdev/platform/mcp/at/lexer/fixtures/spigot_mappings_at.txt b/src/test/resources/com/demonwav/mcdev/platform/mcp/at/lexer/fixtures/spigot_mappings_at.txt index 845720079..530a7891c 100644 --- a/src/test/resources/com/demonwav/mcdev/platform/mcp/at/lexer/fixtures/spigot_mappings_at.txt +++ b/src/test/resources/com/demonwav/mcdev/platform/mcp/at/lexer/fixtures/spigot_mappings_at.txt @@ -3,7 +3,7 @@ WHITE_SPACE (' ') CLASS_NAME_ELEMENT ('net.minecraft.server.EntityPlayer') WHITE_SPACE (' ') NAME_ELEMENT ('containerCounter') -crlf ('\n') +CRLF ('\n') KEYWORD_ELEMENT ('public') WHITE_SPACE (' ') CLASS_NAME_ELEMENT ('net.minecraft.server.EntityPlayer') @@ -12,35 +12,35 @@ NAME_ELEMENT ('nextContainerCounter') OPEN_PAREN ('(') CLOSE_PAREN (')') PRIMITIVE ('V') -crlf ('\n') +CRLF ('\n') KEYWORD_ELEMENT ('public') WHITE_SPACE (' ') CLASS_NAME_ELEMENT ('net.minecraft.server.ShapelessRecipes') WHITE_SPACE (' ') NAME_ELEMENT ('ingredients') -crlf ('\n') +CRLF ('\n') KEYWORD_ELEMENT ('public') WHITE_SPACE (' ') CLASS_NAME_ELEMENT ('net.minecraft.server.EntityVillager$IMerchantRecipeOption') -crlf ('\n') +CRLF ('\n') KEYWORD_ELEMENT ('protected') WHITE_SPACE (' ') CLASS_NAME_ELEMENT ('bip') WHITE_SPACE (' ') NAME_ELEMENT ('d') -crlf ('\n') +CRLF ('\n') KEYWORD_ELEMENT ('protected') WHITE_SPACE (' ') CLASS_NAME_ELEMENT ('bip') WHITE_SPACE (' ') NAME_ELEMENT ('e') -crlf ('\n') +CRLF ('\n') KEYWORD_ELEMENT ('protected') WHITE_SPACE (' ') CLASS_NAME_ELEMENT ('bip') WHITE_SPACE (' ') NAME_ELEMENT ('g') -crlf ('\n') +CRLF ('\n') KEYWORD_ELEMENT ('protected') WHITE_SPACE (' ') CLASS_NAME_ELEMENT ('bip') @@ -48,13 +48,13 @@ WHITE_SPACE (' ') NAME_ELEMENT ('i') WHITE_SPACE (' ') COMMENT ('#asdf') -crlf ('\n') +CRLF ('\n') KEYWORD_ELEMENT ('protected') WHITE_SPACE (' ') CLASS_NAME_ELEMENT ('bip') WHITE_SPACE (' ') NAME_ELEMENT ('j') -crlf ('\n') +CRLF ('\n') KEYWORD_ELEMENT ('protected') WHITE_SPACE (' ') CLASS_NAME_ELEMENT ('bip') @@ -65,7 +65,7 @@ PRIMITIVE ('I') PRIMITIVE ('Z') CLOSE_PAREN (')') PRIMITIVE ('F') -crlf ('\n') +CRLF ('\n') KEYWORD_ELEMENT ('protected') WHITE_SPACE (' ') CLASS_NAME_ELEMENT ('bip') @@ -76,77 +76,77 @@ PRIMITIVE ('C') PRIMITIVE ('Z') CLOSE_PAREN (')') PRIMITIVE ('F') -crlf ('\n') +CRLF ('\n') KEYWORD_ELEMENT ('protected') WHITE_SPACE (' ') CLASS_NAME_ELEMENT ('biq') WHITE_SPACE (' ') ASTERISK_ELEMENT ('*') -crlf ('\n') +CRLF ('\n') KEYWORD_ELEMENT ('protected') WHITE_SPACE (' ') CLASS_NAME_ELEMENT ('biq') WHITE_SPACE (' ') ASTERISK_ELEMENT ('*()') -crlf ('\n') +CRLF ('\n') KEYWORD_ELEMENT ('public-f') WHITE_SPACE (' ') CLASS_NAME_ELEMENT ('net.minecraft.server.WorldType') WHITE_SPACE (' ') NAME_ELEMENT ('types') -crlf ('\n') +CRLF ('\n') KEYWORD_ELEMENT ('public') WHITE_SPACE (' ') CLASS_NAME_ELEMENT ('bzf') WHITE_SPACE (' ') NAME_ELEMENT ('k') -crlf ('\n') +CRLF ('\n') KEYWORD_ELEMENT ('public') WHITE_SPACE (' ') CLASS_NAME_ELEMENT ('net.minecraft.server.AdvancementDataWorld') WHITE_SPACE (' ') NAME_ELEMENT ('DESERIALIZER') -crlf ('\n') +CRLF ('\n') KEYWORD_ELEMENT ('protected') WHITE_SPACE (' ') CLASS_NAME_ELEMENT ('net.minecraft.server.MinecraftServer') WHITE_SPACE (' ') NAME_ELEMENT ('ab') -crlf ('\n') +CRLF ('\n') KEYWORD_ELEMENT ('public') WHITE_SPACE (' ') CLASS_NAME_ELEMENT ('bja') WHITE_SPACE (' ') NAME_ELEMENT ('f') -crlf ('\n') +CRLF ('\n') KEYWORD_ELEMENT ('public') WHITE_SPACE (' ') CLASS_NAME_ELEMENT ('bja') WHITE_SPACE (' ') NAME_ELEMENT ('g') -crlf ('\n') +CRLF ('\n') KEYWORD_ELEMENT ('public') WHITE_SPACE (' ') CLASS_NAME_ELEMENT ('net.minecraft.server.RecipiesShield$Decoration') -crlf ('\n') +CRLF ('\n') KEYWORD_ELEMENT ('public') WHITE_SPACE (' ') CLASS_NAME_ELEMENT ('net.minecraft.server.PacketStatusOutServerInfo') WHITE_SPACE (' ') NAME_ELEMENT ('a') -crlf ('\n') +CRLF ('\n') KEYWORD_ELEMENT ('public-f') WHITE_SPACE (' ') CLASS_NAME_ELEMENT ('bje') WHITE_SPACE (' ') NAME_ELEMENT ('i') -crlf ('\n') +CRLF ('\n') KEYWORD_ELEMENT ('public-f') WHITE_SPACE (' ') CLASS_NAME_ELEMENT ('bje') WHITE_SPACE (' ') NAME_ELEMENT ('j') -crlf ('\n') +CRLF ('\n') KEYWORD_ELEMENT ('protected') WHITE_SPACE (' ') CLASS_NAME_ELEMENT ('bzu') @@ -156,7 +156,7 @@ OPEN_PAREN ('(') CLASS_VALUE ('Lnet/minecraft/server/ItemStack;') CLOSE_PAREN (')') PRIMITIVE ('I') -crlf ('\n') +CRLF ('\n') KEYWORD_ELEMENT ('public') WHITE_SPACE (' ') CLASS_NAME_ELEMENT ('bjd') @@ -165,67 +165,67 @@ NAME_ELEMENT ('d') OPEN_PAREN ('(') CLOSE_PAREN (')') PRIMITIVE ('V') -crlf ('\n') +CRLF ('\n') KEYWORD_ELEMENT ('public') WHITE_SPACE (' ') CLASS_NAME_ELEMENT ('net.minecraft.server.EntityVillager$MerchantRecipeOptionProcess') -crlf ('\n') +CRLF ('\n') KEYWORD_ELEMENT ('public') WHITE_SPACE (' ') CLASS_NAME_ELEMENT ('net.minecraft.server.EntityVillager$MerchantRecipeOptionEnchant') -crlf ('\n') +CRLF ('\n') KEYWORD_ELEMENT ('public') WHITE_SPACE (' ') CLASS_NAME_ELEMENT ('net.minecraft.server.EntityVillager$MerchantRecipeOptionBuy') -crlf ('\n') +CRLF ('\n') KEYWORD_ELEMENT ('public') WHITE_SPACE (' ') CLASS_NAME_ELEMENT ('bjr') WHITE_SPACE (' ') NAME_ELEMENT ('b') -crlf ('\n') +CRLF ('\n') KEYWORD_ELEMENT ('public') WHITE_SPACE (' ') CLASS_NAME_ELEMENT ('bjr') WHITE_SPACE (' ') NAME_ELEMENT ('c') -crlf ('\n') +CRLF ('\n') KEYWORD_ELEMENT ('public') WHITE_SPACE (' ') CLASS_NAME_ELEMENT ('bjr') WHITE_SPACE (' ') NAME_ELEMENT ('t') -crlf ('\n') +CRLF ('\n') KEYWORD_ELEMENT ('public') WHITE_SPACE (' ') CLASS_NAME_ELEMENT ('bjr') WHITE_SPACE (' ') NAME_ELEMENT ('d') -crlf ('\n') +CRLF ('\n') KEYWORD_ELEMENT ('public') WHITE_SPACE (' ') CLASS_NAME_ELEMENT ('bjr') WHITE_SPACE (' ') NAME_ELEMENT ('e') -crlf ('\n') +CRLF ('\n') KEYWORD_ELEMENT ('public') WHITE_SPACE (' ') CLASS_NAME_ELEMENT ('bjr') WHITE_SPACE (' ') NAME_ELEMENT ('f') -crlf ('\n') +CRLF ('\n') KEYWORD_ELEMENT ('public') WHITE_SPACE (' ') CLASS_NAME_ELEMENT ('bjr') WHITE_SPACE (' ') NAME_ELEMENT ('g') -crlf ('\n') +CRLF ('\n') KEYWORD_ELEMENT ('public') WHITE_SPACE (' ') CLASS_NAME_ELEMENT ('bjr') WHITE_SPACE (' ') NAME_ELEMENT ('h') -crlf ('\n') +CRLF ('\n') KEYWORD_ELEMENT ('public') WHITE_SPACE (' ') CLASS_NAME_ELEMENT ('bsb') @@ -235,7 +235,7 @@ OPEN_PAREN ('(') CLASS_VALUE ('Lnet/minecraft/server/Entity;') CLOSE_PAREN (')') PRIMITIVE ('V') -crlf ('\n') +CRLF ('\n') KEYWORD_ELEMENT ('public') WHITE_SPACE (' ') CLASS_NAME_ELEMENT ('bsb') @@ -245,13 +245,13 @@ OPEN_PAREN ('(') CLASS_VALUE ('Lnet/minecraft/server/Entity;') CLOSE_PAREN (')') PRIMITIVE ('V') -crlf ('\n') +CRLF ('\n') KEYWORD_ELEMENT ('public') WHITE_SPACE (' ') CLASS_NAME_ELEMENT ('net.minecraft.server.BiomeBigHills') WHITE_SPACE (' ') ASTERISK_ELEMENT ('*()') -crlf ('\n') +CRLF ('\n') KEYWORD_ELEMENT ('public') WHITE_SPACE (' ') CLASS_NAME_ELEMENT ('net.minecraft.server.Item') @@ -261,7 +261,7 @@ OPEN_PAREN ('(') PRIMITIVE ('Z') CLOSE_PAREN (')') CLASS_VALUE ('Lnet/minecraft/server/Item;') -crlf ('\n') +CRLF ('\n') KEYWORD_ELEMENT ('public') WHITE_SPACE (' ') CLASS_NAME_ELEMENT ('net.minecraft.server.Item') @@ -271,23 +271,23 @@ OPEN_PAREN ('(') PRIMITIVE ('I') CLOSE_PAREN (')') CLASS_VALUE ('Lnet/minecraft/server/Item;') -crlf ('\n') +CRLF ('\n') KEYWORD_ELEMENT ('public') WHITE_SPACE (' ') CLASS_NAME_ELEMENT ('net.minecraft.server.ContainerAnvil') WHITE_SPACE (' ') NAME_ELEMENT ('k') -crlf ('\n') +CRLF ('\n') KEYWORD_ELEMENT ('public') WHITE_SPACE (' ') CLASS_NAME_ELEMENT ('net.minecraft.server.RecipesBanner$AddRecipe') -crlf ('\n') +CRLF ('\n') KEYWORD_ELEMENT ('public') WHITE_SPACE (' ') CLASS_NAME_ELEMENT ('net.minecraft.server.BiomeIcePlains') WHITE_SPACE (' ') ASTERISK_ELEMENT ('*()') -crlf ('\n') +CRLF ('\n') KEYWORD_ELEMENT ('public') WHITE_SPACE (' ') CLASS_NAME_ELEMENT ('net.minecraft.server.TileEntity') @@ -298,41 +298,41 @@ CLASS_VALUE ('Ljava/lang/String;') CLASS_VALUE ('Ljava/lang/Class;') CLOSE_PAREN (')') PRIMITIVE ('V') -crlf ('\n') +CRLF ('\n') KEYWORD_ELEMENT ('public+f') WHITE_SPACE (' ') CLASS_NAME_ELEMENT ('net.minecraft.server.ShapedRecipes') WHITE_SPACE (' ') NAME_ELEMENT ('width') -crlf ('\n') +CRLF ('\n') KEYWORD_ELEMENT ('public+f') WHITE_SPACE (' ') CLASS_NAME_ELEMENT ('net.minecraft.server.ShapedRecipes') WHITE_SPACE (' ') NAME_ELEMENT ('items') -crlf ('\n') +CRLF ('\n') KEYWORD_ELEMENT ('public+f') WHITE_SPACE (' ') CLASS_NAME_ELEMENT ('net.minecraft.server.ShapedRecipes') WHITE_SPACE (' ') NAME_ELEMENT ('height') -crlf ('\n') +CRLF ('\n') KEYWORD_ELEMENT ('public') WHITE_SPACE (' ') CLASS_NAME_ELEMENT ('net.minecraft.server.WorldGenStrongholdPieces$WorldGenStrongholdPiece') -crlf ('\n') +CRLF ('\n') KEYWORD_ELEMENT ('protected') WHITE_SPACE (' ') CLASS_NAME_ELEMENT ('net.minecraft.server.EntityHuman') WHITE_SPACE (' ') NAME_ELEMENT ('d') -crlf ('\n') +CRLF ('\n') KEYWORD_ELEMENT ('protected') WHITE_SPACE (' ') CLASS_NAME_ELEMENT ('net.minecraft.server.EntityHuman') WHITE_SPACE (' ') NAME_ELEMENT ('e') -crlf ('\n') +CRLF ('\n') KEYWORD_ELEMENT ('public') WHITE_SPACE (' ') CLASS_NAME_ELEMENT ('net.minecraft.server.EntityHuman') @@ -341,7 +341,7 @@ NAME_ELEMENT ('closeInventory') OPEN_PAREN ('(') CLOSE_PAREN (')') PRIMITIVE ('V') -crlf ('\n') +CRLF ('\n') KEYWORD_ELEMENT ('public') WHITE_SPACE (' ') CLASS_NAME_ELEMENT ('net.minecraft.server.EntityHuman') @@ -351,63 +351,63 @@ OPEN_PAREN ('(') CLASS_VALUE ('Lnet/minecraft/server/EntityItem;') CLOSE_PAREN (')') CLASS_VALUE ('Lnet/minecraft/server/ItemStack;') -crlf ('\n') +CRLF ('\n') KEYWORD_ELEMENT ('public') WHITE_SPACE (' ') CLASS_NAME_ELEMENT ('net.minecraft.server.EntityInsentient') WHITE_SPACE (' ') NAME_ELEMENT ('targetSelector') -crlf ('\n') +CRLF ('\n') KEYWORD_ELEMENT ('public') WHITE_SPACE (' ') CLASS_NAME_ELEMENT ('net.minecraft.server.EntityInsentient') WHITE_SPACE (' ') NAME_ELEMENT ('goalSelector') -crlf ('\n') +CRLF ('\n') KEYWORD_ELEMENT ('public') WHITE_SPACE (' ') CLASS_NAME_ELEMENT ('net.minecraft.server.EnumDirection') WHITE_SPACE (' ') NAME_ELEMENT ('n') -crlf ('\n') +CRLF ('\n') KEYWORD_ELEMENT ('public') WHITE_SPACE (' ') CLASS_NAME_ELEMENT ('net.minecraft.server.EnumDirection') WHITE_SPACE (' ') NAME_ELEMENT ('o') -crlf ('\n') +CRLF ('\n') KEYWORD_ELEMENT ('public') WHITE_SPACE (' ') CLASS_NAME_ELEMENT ('net.minecraft.server.BiomePlains') WHITE_SPACE (' ') ASTERISK_ELEMENT ('*()') -crlf ('\n') +CRLF ('\n') KEYWORD_ELEMENT ('protected') WHITE_SPACE (' ') CLASS_NAME_ELEMENT ('net.minecraft.server.RegistryBlockID') WHITE_SPACE (' ') NAME_ELEMENT ('a') -crlf ('\n') +CRLF ('\n') KEYWORD_ELEMENT ('protected') WHITE_SPACE (' ') CLASS_NAME_ELEMENT ('net.minecraft.server.RegistryBlockID') WHITE_SPACE (' ') NAME_ELEMENT ('b') -crlf ('\n') +CRLF ('\n') KEYWORD_ELEMENT ('public') WHITE_SPACE (' ') CLASS_NAME_ELEMENT ('net.minecraft.server.RecipesBanner$DuplicateRecipe') -crlf ('\n') +CRLF ('\n') KEYWORD_ELEMENT ('public') WHITE_SPACE (' ') CLASS_NAME_ELEMENT ('net.minecraft.server.BlockStateList$BlockData') -crlf ('\n') +CRLF ('\n') KEYWORD_ELEMENT ('protected') WHITE_SPACE (' ') CLASS_NAME_ELEMENT ('net.minecraft.server.BlockStateList$BlockData') WHITE_SPACE (' ') NAME_ELEMENT ('c') -crlf ('\n') +CRLF ('\n') KEYWORD_ELEMENT ('protected') WHITE_SPACE (' ') CLASS_NAME_ELEMENT ('net.minecraft.server.BlockStateList$BlockData') @@ -418,7 +418,7 @@ CLASS_VALUE ('Lnet/minecraft/block/Block;') CLASS_VALUE ('Lcom/google/common/collect/ImmutableMap;') CLOSE_PAREN (')') PRIMITIVE ('V') -crlf ('\n') +CRLF ('\n') KEYWORD_ELEMENT ('public') WHITE_SPACE (' ') CLASS_NAME_ELEMENT ('net.minecraft.server.Block') @@ -427,7 +427,7 @@ NAME_ELEMENT ('j') OPEN_PAREN ('(') CLOSE_PAREN (')') CLASS_VALUE ('Lnet/minecraft/server/Block;') -crlf ('\n') +CRLF ('\n') KEYWORD_ELEMENT ('public') WHITE_SPACE (' ') CLASS_NAME_ELEMENT ('net.minecraft.server.Block') @@ -437,7 +437,7 @@ OPEN_PAREN ('(') PRIMITIVE ('Z') CLOSE_PAREN (')') CLASS_VALUE ('Lnet/minecraft/server/Block;') -crlf ('\n') +CRLF ('\n') KEYWORD_ELEMENT ('public') WHITE_SPACE (' ') CLASS_NAME_ELEMENT ('net.minecraft.server.Block') @@ -447,7 +447,7 @@ OPEN_PAREN ('(') PRIMITIVE ('F') CLOSE_PAREN (')') CLASS_VALUE ('Lnet/minecraft/server/Block;') -crlf ('\n') +CRLF ('\n') KEYWORD_ELEMENT ('public') WHITE_SPACE (' ') CLASS_NAME_ELEMENT ('net.minecraft.server.Block') @@ -457,7 +457,7 @@ OPEN_PAREN ('(') PRIMITIVE ('F') CLOSE_PAREN (')') CLASS_VALUE ('Lnet/minecraft/server/Block;') -crlf ('\n') +CRLF ('\n') KEYWORD_ELEMENT ('public') WHITE_SPACE (' ') CLASS_NAME_ELEMENT ('net.minecraft.server.Block') @@ -467,7 +467,7 @@ OPEN_PAREN ('(') PRIMITIVE ('I') CLOSE_PAREN (')') CLASS_VALUE ('Lnet/minecraft/server/Block;') -crlf ('\n') +CRLF ('\n') KEYWORD_ELEMENT ('public') WHITE_SPACE (' ') CLASS_NAME_ELEMENT ('net.minecraft.server.Block') @@ -477,7 +477,7 @@ OPEN_PAREN ('(') CLASS_VALUE ('Lnet/minecraft/block/material/Material;') CLOSE_PAREN (')') PRIMITIVE ('V') -crlf ('\n') +CRLF ('\n') KEYWORD_ELEMENT ('public') WHITE_SPACE (' ') CLASS_NAME_ELEMENT ('net.minecraft.server.Block') @@ -489,7 +489,7 @@ CLASS_VALUE ('Lnet/minecraft/server/BlockPosition;') PRIMITIVE ('I') CLOSE_PAREN (')') PRIMITIVE ('V') -crlf ('\n') +CRLF ('\n') KEYWORD_ELEMENT ('public') WHITE_SPACE (' ') CLASS_NAME_ELEMENT ('net.minecraft.server.Block') @@ -499,7 +499,7 @@ OPEN_PAREN ('(') PRIMITIVE ('F') CLOSE_PAREN (')') CLASS_VALUE ('Lnet/minecraft/server/Block;') -crlf ('\n') +CRLF ('\n') KEYWORD_ELEMENT ('public') WHITE_SPACE (' ') CLASS_NAME_ELEMENT ('net.minecraft.server.WorldGenFactory') @@ -510,7 +510,7 @@ CLASS_VALUE ('Ljava/lang/Class;') CLASS_VALUE ('Ljava/lang/String;') CLOSE_PAREN (')') PRIMITIVE ('V') -crlf ('\n') +CRLF ('\n') KEYWORD_ELEMENT ('public') WHITE_SPACE (' ') CLASS_NAME_ELEMENT ('net.minecraft.server.WorldGenFactory') @@ -521,11 +521,11 @@ CLASS_VALUE ('Ljava/lang/Class;') CLASS_VALUE ('Ljava/lang/String;') CLOSE_PAREN (')') PRIMITIVE ('V') -crlf ('\n') +CRLF ('\n') KEYWORD_ELEMENT ('public') WHITE_SPACE (' ') CLASS_NAME_ELEMENT ('net.minecraft.server.RecipeTippedArrow') -crlf ('\n') +CRLF ('\n') KEYWORD_ELEMENT ('public') WHITE_SPACE (' ') CLASS_NAME_ELEMENT ('net.minecraft.server.WorldServer') @@ -535,7 +535,7 @@ OPEN_PAREN ('(') CLASS_VALUE ('Lnet/minecraft/server/Entity;') CLOSE_PAREN (')') PRIMITIVE ('V') -crlf ('\n') +CRLF ('\n') KEYWORD_ELEMENT ('public') WHITE_SPACE (' ') CLASS_NAME_ELEMENT ('net.minecraft.server.WorldServer') @@ -545,13 +545,13 @@ OPEN_PAREN ('(') CLASS_VALUE ('Lnet/minecraft/server/Entity;') CLOSE_PAREN (')') PRIMITIVE ('V') -crlf ('\n') +CRLF ('\n') KEYWORD_ELEMENT ('public') WHITE_SPACE (' ') CLASS_NAME_ELEMENT ('blk') WHITE_SPACE (' ') NAME_ELEMENT ('j') -crlf ('\n') +CRLF ('\n') KEYWORD_ELEMENT ('public') WHITE_SPACE (' ') CLASS_NAME_ELEMENT ('net.minecraft.server.BlockFire') @@ -561,7 +561,7 @@ OPEN_PAREN ('(') CLASS_VALUE ('Lnet/minecraft/server/Block;') CLOSE_PAREN (')') PRIMITIVE ('I') -crlf ('\n') +CRLF ('\n') KEYWORD_ELEMENT ('public') WHITE_SPACE (' ') CLASS_NAME_ELEMENT ('net.minecraft.server.BlockFire') @@ -571,21 +571,21 @@ OPEN_PAREN ('(') CLASS_VALUE ('Lnet/minecraft/server/Block;') CLOSE_PAREN (')') PRIMITIVE ('I') -crlf ('\n') +CRLF ('\n') KEYWORD_ELEMENT ('public') WHITE_SPACE (' ') CLASS_NAME_ELEMENT ('net.minecraft.server.WeightedRandom$WeightedRandomChoice') WHITE_SPACE (' ') NAME_ELEMENT ('a') -crlf ('\n') +CRLF ('\n') KEYWORD_ELEMENT ('public') WHITE_SPACE (' ') CLASS_NAME_ELEMENT ('net.minecraft.server.EntityVillager$MerchantOptionRandomRange') -crlf ('\n') +CRLF ('\n') KEYWORD_ELEMENT ('public') WHITE_SPACE (' ') CLASS_NAME_ELEMENT ('net.minecraft.server.EntityVillager$MerchantRecipeOptionSell') -crlf ('\n') +CRLF ('\n') KEYWORD_ELEMENT ('protected') WHITE_SPACE (' ') CLASS_NAME_ELEMENT ('net.minecraft.server.EntitySkeletonStray') @@ -594,7 +594,7 @@ NAME_ELEMENT ('p') OPEN_PAREN ('(') CLOSE_PAREN (')') CLASS_VALUE ('Lnet/minecraft/server/SoundEffect;') -crlf ('\n') +CRLF ('\n') KEYWORD_ELEMENT ('public') WHITE_SPACE (' ') CLASS_NAME_ELEMENT ('bud') @@ -604,37 +604,37 @@ OPEN_PAREN ('(') CLASS_VALUE ('Lnet/minecraft/server/EntityItem;') CLOSE_PAREN (')') CLASS_VALUE ('Lnet/minecraft/server/ItemStack;') -crlf ('\n') +CRLF ('\n') KEYWORD_ELEMENT ('public') WHITE_SPACE (' ') CLASS_NAME_ELEMENT ('net.minecraft.server.World') WHITE_SPACE (' ') NAME_ELEMENT ('p') -crlf ('\n') +CRLF ('\n') KEYWORD_ELEMENT ('public') WHITE_SPACE (' ') CLASS_NAME_ELEMENT ('net.minecraft.server.World') WHITE_SPACE (' ') NAME_ELEMENT ('q') -crlf ('\n') +CRLF ('\n') KEYWORD_ELEMENT ('public-f') WHITE_SPACE (' ') CLASS_NAME_ELEMENT ('net.minecraft.server.World') WHITE_SPACE (' ') NAME_ELEMENT ('villages') -crlf ('\n') +CRLF ('\n') KEYWORD_ELEMENT ('public') WHITE_SPACE (' ') CLASS_NAME_ELEMENT ('net.minecraft.server.World') WHITE_SPACE (' ') NAME_ELEMENT ('n') -crlf ('\n') +CRLF ('\n') KEYWORD_ELEMENT ('public') WHITE_SPACE (' ') CLASS_NAME_ELEMENT ('net.minecraft.server.World') WHITE_SPACE (' ') NAME_ELEMENT ('o') -crlf ('\n') +CRLF ('\n') KEYWORD_ELEMENT ('public') WHITE_SPACE (' ') CLASS_NAME_ELEMENT ('net.minecraft.server.World') @@ -644,7 +644,7 @@ OPEN_PAREN ('(') CLASS_VALUE ('Lnet/minecraft/server/BlockPosition;') CLOSE_PAREN (')') PRIMITIVE ('Z') -crlf ('\n') +CRLF ('\n') KEYWORD_ELEMENT ('public') WHITE_SPACE (' ') CLASS_NAME_ELEMENT ('net.minecraft.server.World') @@ -654,7 +654,7 @@ OPEN_PAREN ('(') CLASS_VALUE ('Lnet/minecraft/server/BlockPosition;') CLOSE_PAREN (')') PRIMITIVE ('Z') -crlf ('\n') +CRLF ('\n') KEYWORD_ELEMENT ('public') WHITE_SPACE (' ') CLASS_NAME_ELEMENT ('net.minecraft.server.World') @@ -664,7 +664,7 @@ OPEN_PAREN ('(') CLASS_VALUE ('Lnet/minecraft/server/Entity;') CLOSE_PAREN (')') PRIMITIVE ('V') -crlf ('\n') +CRLF ('\n') KEYWORD_ELEMENT ('public') WHITE_SPACE (' ') CLASS_NAME_ELEMENT ('net.minecraft.server.World') @@ -674,19 +674,19 @@ OPEN_PAREN ('(') CLASS_VALUE ('Lnet/minecraft/server/Entity;') CLOSE_PAREN (')') PRIMITIVE ('V') -crlf ('\n') +CRLF ('\n') KEYWORD_ELEMENT ('public') WHITE_SPACE (' ') CLASS_NAME_ELEMENT ('net.minecraft.server.DataConverterManager') WHITE_SPACE (' ') NAME_ELEMENT ('d') -crlf ('\n') +CRLF ('\n') KEYWORD_ELEMENT ('public') WHITE_SPACE (' ') CLASS_NAME_ELEMENT ('net.minecraft.server.BiomeDecorator') WHITE_SPACE (' ') ASTERISK_ELEMENT ('*') -crlf ('\n') +CRLF ('\n') KEYWORD_ELEMENT ('public') WHITE_SPACE (' ') CLASS_NAME_ELEMENT ('buk') @@ -699,7 +699,7 @@ PRIMITIVE ('I') PRIMITIVE ('I') CLOSE_PAREN (')') PRIMITIVE ('V') -crlf ('\n') +CRLF ('\n') KEYWORD_ELEMENT ('public') WHITE_SPACE (' ') CLASS_NAME_ELEMENT ('buk') @@ -709,19 +709,19 @@ OPEN_PAREN ('(') PRIMITIVE ('I') CLOSE_PAREN (')') PRIMITIVE ('I') -crlf ('\n') +CRLF ('\n') KEYWORD_ELEMENT ('public') WHITE_SPACE (' ') CLASS_NAME_ELEMENT ('net.minecraft.server.BiomeBase$a') WHITE_SPACE (' ') ASTERISK_ELEMENT ('*()') -crlf ('\n') +CRLF ('\n') KEYWORD_ELEMENT ('public-f') WHITE_SPACE (' ') CLASS_NAME_ELEMENT ('net.minecraft.server.WorldGenVillage') WHITE_SPACE (' ') NAME_ELEMENT ('a') -crlf ('\n') +CRLF ('\n') KEYWORD_ELEMENT ('public') WHITE_SPACE (' ') CLASS_NAME_ELEMENT ('buq') @@ -731,65 +731,65 @@ OPEN_PAREN ('(') CLASS_VALUE ('Lnet/minecraft/server/MinecraftKey;') CLOSE_PAREN (')') PRIMITIVE ('V') -crlf ('\n') +CRLF ('\n') KEYWORD_ELEMENT ('public') WHITE_SPACE (' ') CLASS_NAME_ELEMENT ('net.minecraft.server.NBTNumber') -crlf ('\n') +CRLF ('\n') KEYWORD_ELEMENT ('protected') WHITE_SPACE (' ') CLASS_NAME_ELEMENT ('cgb') WHITE_SPACE (' ') NAME_ELEMENT ('p') -crlf ('\n') +CRLF ('\n') KEYWORD_ELEMENT ('public') WHITE_SPACE (' ') CLASS_NAME_ELEMENT ('cgb') WHITE_SPACE (' ') NAME_ELEMENT ('a') -crlf ('\n') +CRLF ('\n') KEYWORD_ELEMENT ('protected') WHITE_SPACE (' ') CLASS_NAME_ELEMENT ('cgb') WHITE_SPACE (' ') NAME_ELEMENT ('r') -crlf ('\n') +CRLF ('\n') KEYWORD_ELEMENT ('protected') WHITE_SPACE (' ') CLASS_NAME_ELEMENT ('cgb') WHITE_SPACE (' ') NAME_ELEMENT ('b') -crlf ('\n') +CRLF ('\n') KEYWORD_ELEMENT ('protected') WHITE_SPACE (' ') CLASS_NAME_ELEMENT ('cgb') WHITE_SPACE (' ') NAME_ELEMENT ('s') -crlf ('\n') +CRLF ('\n') KEYWORD_ELEMENT ('protected') WHITE_SPACE (' ') CLASS_NAME_ELEMENT ('cgb') WHITE_SPACE (' ') NAME_ELEMENT ('g') -crlf ('\n') +CRLF ('\n') KEYWORD_ELEMENT ('protected') WHITE_SPACE (' ') CLASS_NAME_ELEMENT ('cgb') WHITE_SPACE (' ') NAME_ELEMENT ('h') -crlf ('\n') +CRLF ('\n') KEYWORD_ELEMENT ('protected') WHITE_SPACE (' ') CLASS_NAME_ELEMENT ('cgb') WHITE_SPACE (' ') NAME_ELEMENT ('l') -crlf ('\n') +CRLF ('\n') KEYWORD_ELEMENT ('protected') WHITE_SPACE (' ') CLASS_NAME_ELEMENT ('cgb') WHITE_SPACE (' ') NAME_ELEMENT ('m') -crlf ('\n') +CRLF ('\n') KEYWORD_ELEMENT ('protected') WHITE_SPACE (' ') CLASS_NAME_ELEMENT ('cgb') @@ -799,7 +799,7 @@ OPEN_PAREN ('(') CLASS_VALUE ('Lnet/minecraft/server/Item;') CLOSE_PAREN (')') CLASS_VALUE ('Ljava/util/List;') -crlf ('\n') +CRLF ('\n') KEYWORD_ELEMENT ('protected') WHITE_SPACE (' ') CLASS_NAME_ELEMENT ('cgb') @@ -808,7 +808,7 @@ NAME_ELEMENT ('h') OPEN_PAREN ('(') CLOSE_PAREN (')') PRIMITIVE ('V') -crlf ('\n') +CRLF ('\n') KEYWORD_ELEMENT ('protected') WHITE_SPACE (' ') CLASS_NAME_ELEMENT ('cgb') @@ -817,7 +817,7 @@ NAME_ELEMENT ('g') OPEN_PAREN ('(') CLOSE_PAREN (')') PRIMITIVE ('V') -crlf ('\n') +CRLF ('\n') KEYWORD_ELEMENT ('protected') WHITE_SPACE (' ') CLASS_NAME_ELEMENT ('cgb') @@ -826,7 +826,7 @@ NAME_ELEMENT ('f') OPEN_PAREN ('(') CLOSE_PAREN (')') PRIMITIVE ('V') -crlf ('\n') +CRLF ('\n') KEYWORD_ELEMENT ('protected') WHITE_SPACE (' ') CLASS_NAME_ELEMENT ('cgb') @@ -836,7 +836,7 @@ OPEN_PAREN ('(') CLASS_VALUE ('Lbvu;') CLOSE_PAREN (')') PRIMITIVE ('Z') -crlf ('\n') +CRLF ('\n') KEYWORD_ELEMENT ('protected') WHITE_SPACE (' ') CLASS_NAME_ELEMENT ('cgb') @@ -846,7 +846,7 @@ OPEN_PAREN ('(') CLASS_VALUE ('Lbvu;') CLOSE_PAREN (')') PRIMITIVE ('Z') -crlf ('\n') +CRLF ('\n') KEYWORD_ELEMENT ('protected') WHITE_SPACE (' ') CLASS_NAME_ELEMENT ('cgb') @@ -856,7 +856,7 @@ OPEN_PAREN ('(') CLASS_VALUE ('Lnet/minecraft/server/MinecraftKey;') CLOSE_PAREN (')') CLASS_VALUE ('Lnet/minecraft/server/MinecraftKey;') -crlf ('\n') +CRLF ('\n') KEYWORD_ELEMENT ('protected') WHITE_SPACE (' ') CLASS_NAME_ELEMENT ('cgb') @@ -867,7 +867,7 @@ CLASS_VALUE ('Lbvv;') CLASS_VALUE ('Lcgd;') CLOSE_PAREN (')') PRIMITIVE ('V') -crlf ('\n') +CRLF ('\n') KEYWORD_ELEMENT ('protected') WHITE_SPACE (' ') CLASS_NAME_ELEMENT ('cgb') @@ -877,7 +877,7 @@ OPEN_PAREN ('(') CLASS_VALUE ('Lbvu;') CLOSE_PAREN (')') CLASS_VALUE ('Lbvu;') -crlf ('\n') +CRLF ('\n') KEYWORD_ELEMENT ('protected') WHITE_SPACE (' ') CLASS_NAME_ELEMENT ('cgb') @@ -887,7 +887,7 @@ OPEN_PAREN ('(') CLASS_VALUE ('Lnet/minecraft/server/MinecraftKey;') CLOSE_PAREN (')') CLASS_VALUE ('Lbvu;') -crlf ('\n') +CRLF ('\n') KEYWORD_ELEMENT ('protected') WHITE_SPACE (' ') CLASS_NAME_ELEMENT ('cgb') @@ -897,7 +897,7 @@ OPEN_PAREN ('(') CLASS_VALUE ('Ljava/lang/String;') CLOSE_PAREN (')') CLASS_VALUE ('Lnet/minecraft/server/MinecraftKey;') -crlf ('\n') +CRLF ('\n') KEYWORD_ELEMENT ('protected') WHITE_SPACE (' ') CLASS_NAME_ELEMENT ('cgb') @@ -908,7 +908,7 @@ CLASS_VALUE ('Lcgd;') CLASS_VALUE ('Lbwd;') CLOSE_PAREN (')') PRIMITIVE ('V') -crlf ('\n') +CRLF ('\n') KEYWORD_ELEMENT ('protected') WHITE_SPACE (' ') CLASS_NAME_ELEMENT ('cgb') @@ -918,7 +918,7 @@ OPEN_PAREN ('(') CLASS_VALUE ('Lbvu;') CLOSE_PAREN (')') CLASS_VALUE ('Ljava/util/Set;') -crlf ('\n') +CRLF ('\n') KEYWORD_ELEMENT ('protected') WHITE_SPACE (' ') CLASS_NAME_ELEMENT ('cgb') @@ -927,7 +927,7 @@ NAME_ELEMENT ('e') OPEN_PAREN ('(') CLOSE_PAREN (')') PRIMITIVE ('V') -crlf ('\n') +CRLF ('\n') KEYWORD_ELEMENT ('protected') WHITE_SPACE (' ') CLASS_NAME_ELEMENT ('cgb') @@ -936,7 +936,7 @@ NAME_ELEMENT ('c') OPEN_PAREN ('(') CLOSE_PAREN (')') PRIMITIVE ('V') -crlf ('\n') +CRLF ('\n') KEYWORD_ELEMENT ('protected') WHITE_SPACE (' ') CLASS_NAME_ELEMENT ('cgb') @@ -945,7 +945,7 @@ NAME_ELEMENT ('b') OPEN_PAREN ('(') CLOSE_PAREN (')') PRIMITIVE ('V') -crlf ('\n') +CRLF ('\n') KEYWORD_ELEMENT ('protected') WHITE_SPACE (' ') CLASS_NAME_ELEMENT ('cgb') @@ -955,17 +955,17 @@ OPEN_PAREN ('(') CLASS_VALUE ('Lnet/minecraft/server/MinecraftKey;') CLOSE_PAREN (')') CLASS_VALUE ('Lbvv;') -crlf ('\n') +CRLF ('\n') KEYWORD_ELEMENT ('public') WHITE_SPACE (' ') CLASS_NAME_ELEMENT ('net.minecraft.server.PathfinderGoalSelector$PathfinderGoalSelectorItem') -crlf ('\n') +CRLF ('\n') KEYWORD_ELEMENT ('protected') WHITE_SPACE (' ') CLASS_NAME_ELEMENT ('buv') WHITE_SPACE (' ') NAME_ELEMENT ('c') -crlf ('\n') +CRLF ('\n') KEYWORD_ELEMENT ('protected') WHITE_SPACE (' ') CLASS_NAME_ELEMENT ('net.minecraft.server.EntitySkeleton') @@ -974,19 +974,19 @@ NAME_ELEMENT ('p') OPEN_PAREN ('(') CLOSE_PAREN (')') CLASS_VALUE ('Lnet/minecraft/server/SoundEffect;') -crlf ('\n') +CRLF ('\n') KEYWORD_ELEMENT ('public') WHITE_SPACE (' ') CLASS_NAME_ELEMENT ('net.minecraft.server.BiomeTaiga') WHITE_SPACE (' ') ASTERISK_ELEMENT ('*()') -crlf ('\n') +CRLF ('\n') KEYWORD_ELEMENT ('public') WHITE_SPACE (' ') CLASS_NAME_ELEMENT ('net.minecraft.server.BiomeMesa') WHITE_SPACE (' ') ASTERISK_ELEMENT ('*()') -crlf ('\n') +CRLF ('\n') KEYWORD_ELEMENT ('public') WHITE_SPACE (' ') CLASS_NAME_ELEMENT ('net.minecraft.server.TileEntityHopper') @@ -995,7 +995,7 @@ NAME_ELEMENT ('K') OPEN_PAREN ('(') CLOSE_PAREN (')') PRIMITIVE ('Z') -crlf ('\n') +CRLF ('\n') KEYWORD_ELEMENT ('protected') WHITE_SPACE (' ') CLASS_NAME_ELEMENT ('net.minecraft.server.TileEntityHopper') @@ -1004,7 +1004,7 @@ NAME_ELEMENT ('o') OPEN_PAREN ('(') CLOSE_PAREN (')') PRIMITIVE ('Z') -crlf ('\n') +CRLF ('\n') KEYWORD_ELEMENT ('public') WHITE_SPACE (' ') CLASS_NAME_ELEMENT ('net.minecraft.server.TileEntityHopper') @@ -1014,25 +1014,25 @@ OPEN_PAREN ('(') PRIMITIVE ('I') CLOSE_PAREN (')') PRIMITIVE ('V') -crlf ('\n') +CRLF ('\n') KEYWORD_ELEMENT ('public') WHITE_SPACE (' ') CLASS_NAME_ELEMENT ('bvu') WHITE_SPACE (' ') NAME_ELEMENT ('c') -crlf ('\n') +CRLF ('\n') KEYWORD_ELEMENT ('public') WHITE_SPACE (' ') CLASS_NAME_ELEMENT ('bvu') WHITE_SPACE (' ') NAME_ELEMENT ('d') -crlf ('\n') +CRLF ('\n') KEYWORD_ELEMENT ('public') WHITE_SPACE (' ') CLASS_NAME_ELEMENT ('bvu') WHITE_SPACE (' ') NAME_ELEMENT ('i') -crlf ('\n') +CRLF ('\n') KEYWORD_ELEMENT ('public') WHITE_SPACE (' ') CLASS_NAME_ELEMENT ('bvu') @@ -1041,37 +1041,37 @@ NAME_ELEMENT ('f') OPEN_PAREN ('(') CLOSE_PAREN (')') CLASS_VALUE ('Ljava/util/List;') -crlf ('\n') +CRLF ('\n') KEYWORD_ELEMENT ('private-f') WHITE_SPACE (' ') CLASS_NAME_ELEMENT ('net.minecraft.server.LootSelector') WHITE_SPACE (' ') NAME_ELEMENT ('c') -crlf ('\n') +CRLF ('\n') KEYWORD_ELEMENT ('private-f') WHITE_SPACE (' ') CLASS_NAME_ELEMENT ('net.minecraft.server.LootSelector') WHITE_SPACE (' ') NAME_ELEMENT ('d') -crlf ('\n') +CRLF ('\n') KEYWORD_ELEMENT ('protected') WHITE_SPACE (' ') CLASS_NAME_ELEMENT ('net.minecraft.server.PortalTravelAgent') WHITE_SPACE (' ') NAME_ELEMENT ('b') -crlf ('\n') +CRLF ('\n') KEYWORD_ELEMENT ('protected') WHITE_SPACE (' ') CLASS_NAME_ELEMENT ('net.minecraft.server.PortalTravelAgent') WHITE_SPACE (' ') NAME_ELEMENT ('world') -crlf ('\n') +CRLF ('\n') KEYWORD_ELEMENT ('protected') WHITE_SPACE (' ') CLASS_NAME_ELEMENT ('net.minecraft.server.PortalTravelAgent') WHITE_SPACE (' ') NAME_ELEMENT ('c') -crlf ('\n') +CRLF ('\n') KEYWORD_ELEMENT ('protected') WHITE_SPACE (' ') CLASS_NAME_ELEMENT ('net.minecraft.server.RecipeItemStack') @@ -1081,25 +1081,25 @@ OPEN_PAREN ('(') CLASS_VALUE ('[Lnet/minecraft/item/ItemStack;') CLOSE_PAREN (')') PRIMITIVE ('V') -crlf ('\n') +CRLF ('\n') KEYWORD_ELEMENT ('public') WHITE_SPACE (' ') CLASS_NAME_ELEMENT ('chm') WHITE_SPACE (' ') NAME_ELEMENT ('d') -crlf ('\n') +CRLF ('\n') KEYWORD_ELEMENT ('public') WHITE_SPACE (' ') CLASS_NAME_ELEMENT ('net.minecraft.server.PacketPlayOutBlockChange') WHITE_SPACE (' ') NAME_ELEMENT ('block') -crlf ('\n') +CRLF ('\n') KEYWORD_ELEMENT ('public') WHITE_SPACE (' ') CLASS_NAME_ELEMENT ('net.minecraft.server.DamageSource') WHITE_SPACE (' ') ASTERISK_ELEMENT ('*()') -crlf ('\n') +CRLF ('\n') KEYWORD_ELEMENT ('protected') WHITE_SPACE (' ') CLASS_NAME_ELEMENT ('net.minecraft.server.EntitySkeletonAbstract') @@ -1108,7 +1108,7 @@ NAME_ELEMENT ('p') OPEN_PAREN ('(') CLOSE_PAREN (')') CLASS_VALUE ('Lnet/minecraft/server/SoundEffect;') -crlf ('\n') +CRLF ('\n') KEYWORD_ELEMENT ('protected') WHITE_SPACE (' ') CLASS_NAME_ELEMENT ('net.minecraft.server.EntitySkeletonWither') @@ -1117,53 +1117,53 @@ NAME_ELEMENT ('p') OPEN_PAREN ('(') CLOSE_PAREN (')') CLASS_VALUE ('Lnet/minecraft/server/SoundEffect;') -crlf ('\n') +CRLF ('\n') KEYWORD_ELEMENT ('public') WHITE_SPACE (' ') CLASS_NAME_ELEMENT ('net.minecraft.server.DedicatedServer') WHITE_SPACE (' ') NAME_ELEMENT ('serverCommandQueue') -crlf ('\n') +CRLF ('\n') KEYWORD_ELEMENT ('public') WHITE_SPACE (' ') CLASS_NAME_ELEMENT ('net.minecraft.server.WorldGenVillagePieces$WorldGenVillagePiece') -crlf ('\n') +CRLF ('\n') KEYWORD_ELEMENT ('public') WHITE_SPACE (' ') CLASS_NAME_ELEMENT ('net.minecraft.server.BiomeBase') WHITE_SPACE (' ') ASTERISK_ELEMENT ('*()') -crlf ('\n') +CRLF ('\n') KEYWORD_ELEMENT ('public') WHITE_SPACE (' ') CLASS_NAME_ELEMENT ('net.minecraft.server.ChunkProviderServer') WHITE_SPACE (' ') NAME_ELEMENT ('chunkGenerator') -crlf ('\n') +CRLF ('\n') KEYWORD_ELEMENT ('public') WHITE_SPACE (' ') CLASS_NAME_ELEMENT ('net.minecraft.server.ChunkProviderServer') WHITE_SPACE (' ') NAME_ELEMENT ('world') -crlf ('\n') +CRLF ('\n') KEYWORD_ELEMENT ('public') WHITE_SPACE (' ') CLASS_NAME_ELEMENT ('net.minecraft.server.ChunkProviderServer') WHITE_SPACE (' ') NAME_ELEMENT ('chunkLoader') -crlf ('\n') +CRLF ('\n') KEYWORD_ELEMENT ('public') WHITE_SPACE (' ') CLASS_NAME_ELEMENT ('net.minecraft.server.ChunkProviderServer') WHITE_SPACE (' ') NAME_ELEMENT ('chunks') -crlf ('\n') +CRLF ('\n') KEYWORD_ELEMENT ('public') WHITE_SPACE (' ') CLASS_NAME_ELEMENT ('net.minecraft.server.ChunkRegionLoader') WHITE_SPACE (' ') NAME_ELEMENT ('d') -crlf ('\n') +CRLF ('\n') KEYWORD_ELEMENT ('public') WHITE_SPACE (' ') CLASS_NAME_ELEMENT ('caa') @@ -1173,97 +1173,97 @@ OPEN_PAREN ('(') CLASS_VALUE ('Lccg;') CLOSE_PAREN (')') PRIMITIVE ('Z') -crlf ('\n') +CRLF ('\n') KEYWORD_ELEMENT ('public') WHITE_SPACE (' ') CLASS_NAME_ELEMENT ('bwx') WHITE_SPACE (' ') NAME_ELEMENT ('n') -crlf ('\n') +CRLF ('\n') KEYWORD_ELEMENT ('public') WHITE_SPACE (' ') CLASS_NAME_ELEMENT ('bwx') WHITE_SPACE (' ') NAME_ELEMENT ('o') -crlf ('\n') +CRLF ('\n') KEYWORD_ELEMENT ('private-f') WHITE_SPACE (' ') CLASS_NAME_ELEMENT ('net.minecraft.server.PlayerChunk') WHITE_SPACE (' ') NAME_ELEMENT ('dirtyBlocks') -crlf ('\n') +CRLF ('\n') KEYWORD_ELEMENT ('private-f') WHITE_SPACE (' ') CLASS_NAME_ELEMENT ('net.minecraft.server.ChunkProviderGenerate') WHITE_SPACE (' ') NAME_ELEMENT ('A') -crlf ('\n') +CRLF ('\n') KEYWORD_ELEMENT ('private-f') WHITE_SPACE (' ') CLASS_NAME_ELEMENT ('net.minecraft.server.ChunkProviderGenerate') WHITE_SPACE (' ') NAME_ELEMENT ('B') -crlf ('\n') +CRLF ('\n') KEYWORD_ELEMENT ('private-f') WHITE_SPACE (' ') CLASS_NAME_ELEMENT ('net.minecraft.server.ChunkProviderGenerate') WHITE_SPACE (' ') NAME_ELEMENT ('C') -crlf ('\n') +CRLF ('\n') KEYWORD_ELEMENT ('private-f') WHITE_SPACE (' ') CLASS_NAME_ELEMENT ('net.minecraft.server.ChunkProviderGenerate') WHITE_SPACE (' ') NAME_ELEMENT ('v') -crlf ('\n') +CRLF ('\n') KEYWORD_ELEMENT ('private-f') WHITE_SPACE (' ') CLASS_NAME_ELEMENT ('net.minecraft.server.ChunkProviderGenerate') WHITE_SPACE (' ') NAME_ELEMENT ('w') -crlf ('\n') +CRLF ('\n') KEYWORD_ELEMENT ('private-f') WHITE_SPACE (' ') CLASS_NAME_ELEMENT ('net.minecraft.server.ChunkProviderGenerate') WHITE_SPACE (' ') NAME_ELEMENT ('x') -crlf ('\n') +CRLF ('\n') KEYWORD_ELEMENT ('private-f') WHITE_SPACE (' ') CLASS_NAME_ELEMENT ('net.minecraft.server.ChunkProviderGenerate') WHITE_SPACE (' ') NAME_ELEMENT ('y') -crlf ('\n') +CRLF ('\n') KEYWORD_ELEMENT ('private-f') WHITE_SPACE (' ') CLASS_NAME_ELEMENT ('net.minecraft.server.ChunkProviderGenerate') WHITE_SPACE (' ') NAME_ELEMENT ('j') -crlf ('\n') +CRLF ('\n') KEYWORD_ELEMENT ('private-f') WHITE_SPACE (' ') CLASS_NAME_ELEMENT ('net.minecraft.server.ChunkProviderGenerate') WHITE_SPACE (' ') NAME_ELEMENT ('z') -crlf ('\n') +CRLF ('\n') KEYWORD_ELEMENT ('private-f') WHITE_SPACE (' ') CLASS_NAME_ELEMENT ('net.minecraft.server.ChunkProviderGenerate') WHITE_SPACE (' ') NAME_ELEMENT ('k') -crlf ('\n') +CRLF ('\n') KEYWORD_ELEMENT ('private-f') WHITE_SPACE (' ') CLASS_NAME_ELEMENT ('net.minecraft.server.ChunkProviderGenerate') WHITE_SPACE (' ') NAME_ELEMENT ('l') -crlf ('\n') +CRLF ('\n') KEYWORD_ELEMENT ('private-f') WHITE_SPACE (' ') CLASS_NAME_ELEMENT ('net.minecraft.server.ChunkProviderGenerate') WHITE_SPACE (' ') NAME_ELEMENT ('m') -crlf ('\n') +CRLF ('\n') KEYWORD_ELEMENT ('public') WHITE_SPACE (' ') CLASS_NAME_ELEMENT ('net.minecraft.server.PotionBrewer') @@ -1273,7 +1273,7 @@ OPEN_PAREN ('(') CLASS_VALUE ('Lnet/minecraft/server/ItemPotion;') CLOSE_PAREN (')') PRIMITIVE ('V') -crlf ('\n') +CRLF ('\n') KEYWORD_ELEMENT ('public') WHITE_SPACE (' ') CLASS_NAME_ELEMENT ('net.minecraft.server.PotionBrewer') @@ -1285,7 +1285,7 @@ CLASS_VALUE ('Lnet/minecraft/server/Item;') CLASS_VALUE ('Lnet/minecraft/server/PotionRegistry;') CLOSE_PAREN (')') PRIMITIVE ('V') -crlf ('\n') +CRLF ('\n') KEYWORD_ELEMENT ('public') WHITE_SPACE (' ') CLASS_NAME_ELEMENT ('net.minecraft.server.PotionBrewer') @@ -1297,7 +1297,7 @@ CLASS_VALUE ('Lnet/minecraft/server/Item;') CLASS_VALUE ('Lnet/minecraft/server/ItemPotion;') CLOSE_PAREN (')') PRIMITIVE ('V') -crlf ('\n') +CRLF ('\n') KEYWORD_ELEMENT ('public') WHITE_SPACE (' ') CLASS_NAME_ELEMENT ('net.minecraft.server.PotionBrewer') @@ -1309,7 +1309,7 @@ CLASS_VALUE ('Lnet/minecraft/server/RecipeItemStack;') CLASS_VALUE ('Lnet/minecraft/server/PotionRegistry;') CLOSE_PAREN (')') PRIMITIVE ('V') -crlf ('\n') +CRLF ('\n') KEYWORD_ELEMENT ('public') WHITE_SPACE (' ') CLASS_NAME_ELEMENT ('net.minecraft.server.CriterionTriggers') @@ -1319,135 +1319,135 @@ OPEN_PAREN ('(') CLASS_VALUE ('Lnet/minecraft/server/CriterionTrigger;') CLOSE_PAREN (')') CLASS_VALUE ('Lnet/minecraft/server/CriterionTrigger;') -crlf ('\n') +CRLF ('\n') KEYWORD_ELEMENT ('public') WHITE_SPACE (' ') CLASS_NAME_ELEMENT ('net.minecraft.server.EntityVillager$MerchantRecipeOptionBook') -crlf ('\n') +CRLF ('\n') KEYWORD_ELEMENT ('public-f') WHITE_SPACE (' ') CLASS_NAME_ELEMENT ('net.minecraft.server.CreativeModeTab') WHITE_SPACE (' ') NAME_ELEMENT ('a') -crlf ('\n') +CRLF ('\n') KEYWORD_ELEMENT ('private-f') WHITE_SPACE (' ') CLASS_NAME_ELEMENT ('net.minecraft.server.ChunkProviderHell') WHITE_SPACE (' ') NAME_ELEMENT ('u') -crlf ('\n') +CRLF ('\n') KEYWORD_ELEMENT ('private-f') WHITE_SPACE (' ') CLASS_NAME_ELEMENT ('net.minecraft.server.ChunkProviderHell') WHITE_SPACE (' ') NAME_ELEMENT ('v') -crlf ('\n') +CRLF ('\n') KEYWORD_ELEMENT ('public-f') WHITE_SPACE (' ') CLASS_NAME_ELEMENT ('net.minecraft.server.ChunkProviderHell') WHITE_SPACE (' ') NAME_ELEMENT ('g') -crlf ('\n') +CRLF ('\n') KEYWORD_ELEMENT ('private-f') WHITE_SPACE (' ') CLASS_NAME_ELEMENT ('net.minecraft.server.ChunkProviderHell') WHITE_SPACE (' ') NAME_ELEMENT ('w') -crlf ('\n') +CRLF ('\n') KEYWORD_ELEMENT ('public-f') WHITE_SPACE (' ') CLASS_NAME_ELEMENT ('net.minecraft.server.ChunkProviderHell') WHITE_SPACE (' ') NAME_ELEMENT ('h') -crlf ('\n') +CRLF ('\n') KEYWORD_ELEMENT ('private-f') WHITE_SPACE (' ') CLASS_NAME_ELEMENT ('net.minecraft.server.ChunkProviderHell') WHITE_SPACE (' ') NAME_ELEMENT ('x') -crlf ('\n') +CRLF ('\n') KEYWORD_ELEMENT ('private-f') WHITE_SPACE (' ') CLASS_NAME_ELEMENT ('net.minecraft.server.ChunkProviderHell') WHITE_SPACE (' ') NAME_ELEMENT ('I') -crlf ('\n') +CRLF ('\n') KEYWORD_ELEMENT ('private-f') WHITE_SPACE (' ') CLASS_NAME_ELEMENT ('net.minecraft.server.ChunkProviderHell') WHITE_SPACE (' ') NAME_ELEMENT ('y') -crlf ('\n') +CRLF ('\n') KEYWORD_ELEMENT ('private-f') WHITE_SPACE (' ') CLASS_NAME_ELEMENT ('net.minecraft.server.ChunkProviderHell') WHITE_SPACE (' ') NAME_ELEMENT ('J') -crlf ('\n') +CRLF ('\n') KEYWORD_ELEMENT ('public') WHITE_SPACE (' ') CLASS_NAME_ELEMENT ('net.minecraft.server.BiomeSavanna') WHITE_SPACE (' ') ASTERISK_ELEMENT ('*()') -crlf ('\n') +CRLF ('\n') KEYWORD_ELEMENT ('private-f') WHITE_SPACE (' ') CLASS_NAME_ELEMENT ('net.minecraft.server.ChunkProviderTheEnd') WHITE_SPACE (' ') NAME_ELEMENT ('p') -crlf ('\n') +CRLF ('\n') KEYWORD_ELEMENT ('private-f') WHITE_SPACE (' ') CLASS_NAME_ELEMENT ('net.minecraft.server.ChunkProviderTheEnd') WHITE_SPACE (' ') NAME_ELEMENT ('i') -crlf ('\n') +CRLF ('\n') KEYWORD_ELEMENT ('private-f') WHITE_SPACE (' ') CLASS_NAME_ELEMENT ('net.minecraft.server.ChunkProviderTheEnd') WHITE_SPACE (' ') NAME_ELEMENT ('j') -crlf ('\n') +CRLF ('\n') KEYWORD_ELEMENT ('private-f') WHITE_SPACE (' ') CLASS_NAME_ELEMENT ('net.minecraft.server.ChunkProviderTheEnd') WHITE_SPACE (' ') NAME_ELEMENT ('k') -crlf ('\n') +CRLF ('\n') KEYWORD_ELEMENT ('private-f') WHITE_SPACE (' ') CLASS_NAME_ELEMENT ('net.minecraft.server.ChunkProviderTheEnd') WHITE_SPACE (' ') NAME_ELEMENT ('o') -crlf ('\n') +CRLF ('\n') KEYWORD_ELEMENT ('public') WHITE_SPACE (' ') CLASS_NAME_ELEMENT ('net.minecraft.server.BiomeForest') WHITE_SPACE (' ') ASTERISK_ELEMENT ('*()') -crlf ('\n') +CRLF ('\n') KEYWORD_ELEMENT ('public') WHITE_SPACE (' ') CLASS_NAME_ELEMENT ('net.minecraft.server.EntityTrackerEntry') WHITE_SPACE (' ') NAME_ELEMENT ('trackedPlayers') -crlf ('\n') +CRLF ('\n') KEYWORD_ELEMENT ('public+f') WHITE_SPACE (' ') CLASS_NAME_ELEMENT ('net.minecraft.server.WorldGenStronghold') WHITE_SPACE (' ') NAME_ELEMENT ('a') -crlf ('\n') +CRLF ('\n') KEYWORD_ELEMENT ('public') WHITE_SPACE (' ') CLASS_NAME_ELEMENT ('net.minecraft.server.PotionBrewer$PredicatedCombination') -crlf ('\n') +CRLF ('\n') KEYWORD_ELEMENT ('public') WHITE_SPACE (' ') CLASS_NAME_ELEMENT ('net.minecraft.server.WorldLoader') WHITE_SPACE (' ') NAME_ELEMENT ('a') -crlf ('\n') +CRLF ('\n') KEYWORD_ELEMENT ('public') WHITE_SPACE (' ') CLASS_NAME_ELEMENT ('net.minecraft.server.MinecraftKey') @@ -1457,37 +1457,37 @@ OPEN_PAREN ('(') CLASS_VALUE ('Ljava/lang/String;') CLOSE_PAREN (')') CLASS_VALUE ('[Ljava/lang/String;') -crlf ('\n') +CRLF ('\n') KEYWORD_ELEMENT ('default') WHITE_SPACE (' ') CLASS_NAME_ELEMENT ('net.minecraft.server.ItemStack') WHITE_SPACE (' ') NAME_ELEMENT ('damage') -crlf ('\n') +CRLF ('\n') KEYWORD_ELEMENT ('public') WHITE_SPACE (' ') CLASS_NAME_ELEMENT ('net.minecraft.server.EntityExperienceOrb') WHITE_SPACE (' ') NAME_ELEMENT ('value') -crlf ('\n') +CRLF ('\n') KEYWORD_ELEMENT ('public') WHITE_SPACE (' ') CLASS_NAME_ELEMENT ('net.minecraft.server.EntityMinecartContainer') WHITE_SPACE (' ') NAME_ELEMENT ('b') -crlf ('\n') +CRLF ('\n') KEYWORD_ELEMENT ('public') WHITE_SPACE (' ') CLASS_NAME_ELEMENT ('bib') WHITE_SPACE (' ') NAME_ELEMENT ('Q') -crlf ('\n') +CRLF ('\n') KEYWORD_ELEMENT ('public') WHITE_SPACE (' ') CLASS_NAME_ELEMENT ('bib') WHITE_SPACE (' ') NAME_ELEMENT ('aE') -crlf ('\n') +CRLF ('\n') KEYWORD_ELEMENT ('public') WHITE_SPACE (' ') CLASS_NAME_ELEMENT ('bib') @@ -1498,7 +1498,7 @@ CLASS_VALUE ('Lnet/minecraft/server/ItemStack;') CLASS_VALUE ('Lnet/minecraft/server/TileEntity;') CLOSE_PAREN (')') CLASS_VALUE ('Lnet/minecraft/server/ItemStack;') -crlf ('\n') +CRLF ('\n') KEYWORD_ELEMENT ('public') WHITE_SPACE (' ') CLASS_NAME_ELEMENT ('bib') @@ -1509,7 +1509,7 @@ PRIMITIVE ('I') PRIMITIVE ('I') CLOSE_PAREN (')') PRIMITIVE ('V') -crlf ('\n') +CRLF ('\n') KEYWORD_ELEMENT ('public') WHITE_SPACE (' ') CLASS_NAME_ELEMENT ('bib') @@ -1518,7 +1518,7 @@ NAME_ELEMENT ('ar') OPEN_PAREN ('(') CLOSE_PAREN (')') PRIMITIVE ('V') -crlf ('\n') +CRLF ('\n') KEYWORD_ELEMENT ('public') WHITE_SPACE (' ') CLASS_NAME_ELEMENT ('bib') @@ -1528,13 +1528,13 @@ OPEN_PAREN ('(') CLASS_VALUE ('Lcdr;') CLOSE_PAREN (')') PRIMITIVE ('V') -crlf ('\n') +CRLF ('\n') KEYWORD_ELEMENT ('public') WHITE_SPACE (' ') CLASS_NAME_ELEMENT ('net.minecraft.server.PathfinderGoalSelector') WHITE_SPACE (' ') NAME_ELEMENT ('b') -crlf ('\n') +CRLF ('\n') KEYWORD_ELEMENT ('public') WHITE_SPACE (' ') CLASS_NAME_ELEMENT ('net.minecraft.server.PlayerSelector') diff --git a/src/test/resources/com/demonwav/mcdev/platform/mcp/at/parser/fixtures/asterisks_at.txt b/src/test/resources/com/demonwav/mcdev/platform/mcp/at/parser/fixtures/asterisks_at.txt index 28501b937..e7329fe7d 100644 --- a/src/test/resources/com/demonwav/mcdev/platform/mcp/at/parser/fixtures/asterisks_at.txt +++ b/src/test/resources/com/demonwav/mcdev/platform/mcp/at/parser/fixtures/asterisks_at.txt @@ -1,4 +1,4 @@ -Access Transformer File(0,127) +Access Transformers(0,127) AtEntryImpl(ENTRY)(0,56) AtKeywordImpl(KEYWORD)(0,6) PsiElement(KEYWORD_ELEMENT)('public')(0,6) @@ -8,7 +8,7 @@ Access Transformer File(0,127) PsiWhiteSpace(' ')(54,55) AtAsteriskImpl(ASTERISK)(55,56) PsiElement(ASTERISK_ELEMENT)('*')(55,56) - PsiElement(crlf)('\n')(56,57) + PsiElement(CRLF)('\n')(56,57) AtEntryImpl(ENTRY)(57,127) AtKeywordImpl(KEYWORD)(57,63) PsiElement(KEYWORD_ELEMENT)('public')(57,63) diff --git a/src/test/resources/com/demonwav/mcdev/platform/mcp/at/parser/fixtures/comments_at.txt b/src/test/resources/com/demonwav/mcdev/platform/mcp/at/parser/fixtures/comments_at.txt index b5a313999..018140e13 100644 --- a/src/test/resources/com/demonwav/mcdev/platform/mcp/at/parser/fixtures/comments_at.txt +++ b/src/test/resources/com/demonwav/mcdev/platform/mcp/at/parser/fixtures/comments_at.txt @@ -1,8 +1,8 @@ -Access Transformer File(0,350) +Access Transformers(0,350) PsiComment(COMMENT)('# Comment one')(0,13) - PsiElement(crlf)('\n')(13,14) + PsiElement(CRLF)('\n')(13,14) PsiComment(COMMENT)('# Comment two')(14,27) - PsiElement(crlf)('\n')(27,28) + PsiElement(CRLF)('\n')(27,28) AtEntryImpl(ENTRY)(28,111) AtKeywordImpl(KEYWORD)(28,37) PsiElement(KEYWORD_ELEMENT)('private-f')(28,37) @@ -14,7 +14,7 @@ Access Transformer File(0,350) PsiElement(NAME_ELEMENT)('field_151334_b')(97,111) PsiWhiteSpace(' ')(111,112) PsiComment(COMMENT)('# Comment after method')(112,134) - PsiElement(crlf)('\n')(134,135) + PsiElement(CRLF)('\n')(134,135) AtEntryImpl(ENTRY)(135,172) AtKeywordImpl(KEYWORD)(135,143) PsiElement(KEYWORD_ELEMENT)('public-f')(135,143) @@ -23,7 +23,7 @@ Access Transformer File(0,350) PsiElement(CLASS_NAME_ELEMENT)('net.minecraft.item.ItemStack')(144,172) PsiWhiteSpace(' ')(172,173) PsiComment(COMMENT)('# Comment after no value')(173,197) - PsiElement(crlf)('\n')(197,198) + PsiElement(CRLF)('\n')(197,198) AtEntryImpl(ENTRY)(198,254) AtKeywordImpl(KEYWORD)(198,204) PsiElement(KEYWORD_ELEMENT)('public')(198,204) @@ -35,7 +35,7 @@ Access Transformer File(0,350) PsiElement(ASTERISK_ELEMENT)('*')(253,254) PsiWhiteSpace(' ')(254,255) PsiComment(COMMENT)('# Comment after asterisk')(255,279) - PsiElement(crlf)('\n')(279,280) + PsiElement(CRLF)('\n')(279,280) AtEntryImpl(ENTRY)(280,328) AtKeywordImpl(KEYWORD)(280,286) PsiElement(KEYWORD_ELEMENT)('public')(280,286) diff --git a/src/test/resources/com/demonwav/mcdev/platform/mcp/at/parser/fixtures/fields_at.txt b/src/test/resources/com/demonwav/mcdev/platform/mcp/at/parser/fixtures/fields_at.txt index 05e609bf2..b80dcd2a6 100644 --- a/src/test/resources/com/demonwav/mcdev/platform/mcp/at/parser/fixtures/fields_at.txt +++ b/src/test/resources/com/demonwav/mcdev/platform/mcp/at/parser/fixtures/fields_at.txt @@ -1,4 +1,4 @@ -Access Transformer File(0,728) +Access Transformers(0,728) AtEntryImpl(ENTRY)(0,63) AtKeywordImpl(KEYWORD)(0,6) PsiElement(KEYWORD_ELEMENT)('public')(0,6) @@ -10,7 +10,7 @@ Access Transformer File(0,728) PsiElement(NAME_ELEMENT)('field_184827_bp')(48,63) PsiWhiteSpace(' ')(63,64) PsiComment(COMMENT)('# PLAYER_MODEL_FLAG')(64,83) - PsiElement(crlf)('\n')(83,84) + PsiElement(CRLF)('\n')(83,84) AtEntryImpl(ENTRY)(84,147) AtKeywordImpl(KEYWORD)(84,90) PsiElement(KEYWORD_ELEMENT)('public')(84,90) @@ -22,7 +22,7 @@ Access Transformer File(0,728) PsiElement(NAME_ELEMENT)('field_184828_bq')(132,147) PsiWhiteSpace(' ')(147,148) PsiComment(COMMENT)('# MAIN_HAND')(148,159) - PsiElement(crlf)('\n')(159,160) + PsiElement(CRLF)('\n')(159,160) AtEntryImpl(ENTRY)(160,222) AtKeywordImpl(KEYWORD)(160,166) PsiElement(KEYWORD_ELEMENT)('public')(160,166) @@ -34,7 +34,7 @@ Access Transformer File(0,728) PsiElement(NAME_ELEMENT)('field_184829_a')(208,222) PsiWhiteSpace(' ')(222,223) PsiComment(COMMENT)('# field_184829_a')(223,239) - PsiElement(crlf)('\n')(239,240) + PsiElement(CRLF)('\n')(239,240) AtEntryImpl(ENTRY)(240,302) AtKeywordImpl(KEYWORD)(240,246) PsiElement(KEYWORD_ELEMENT)('public')(240,246) @@ -46,7 +46,7 @@ Access Transformer File(0,728) PsiElement(NAME_ELEMENT)('field_184830_b')(288,302) PsiWhiteSpace(' ')(302,303) PsiComment(COMMENT)('# PLAYER_SCORE')(303,317) - PsiElement(crlf)('\n')(317,318) + PsiElement(CRLF)('\n')(317,318) AtEntryImpl(ENTRY)(318,379) AtKeywordImpl(KEYWORD)(318,324) PsiElement(KEYWORD_ELEMENT)('public')(318,324) @@ -58,7 +58,7 @@ Access Transformer File(0,728) PsiElement(NAME_ELEMENT)('field_71078_a')(366,379) PsiWhiteSpace(' ')(379,380) PsiComment(COMMENT)('# theInventoryEnderChest')(380,404) - PsiElement(crlf)('\n')(404,405) + PsiElement(CRLF)('\n')(404,405) AtEntryImpl(ENTRY)(405,469) AtKeywordImpl(KEYWORD)(405,411) PsiElement(KEYWORD_ELEMENT)('public')(405,411) @@ -70,7 +70,7 @@ Access Transformer File(0,728) PsiElement(NAME_ELEMENT)('field_71144_ck')(455,469) PsiWhiteSpace(' ')(469,470) PsiComment(COMMENT)('# lastExperience')(470,486) - PsiElement(crlf)('\n')(486,487) + PsiElement(CRLF)('\n')(486,487) AtEntryImpl(ENTRY)(487,552) AtKeywordImpl(KEYWORD)(487,493) PsiElement(KEYWORD_ELEMENT)('public')(487,493) @@ -82,7 +82,7 @@ Access Transformer File(0,728) PsiElement(NAME_ELEMENT)('field_184440_g')(538,552) PsiWhiteSpace(' ')(552,553) PsiComment(COMMENT)('# main_inventory')(553,569) - PsiElement(crlf)('\n')(569,570) + PsiElement(CRLF)('\n')(569,570) AtEntryImpl(ENTRY)(570,637) AtKeywordImpl(KEYWORD)(570,576) PsiElement(KEYWORD_ELEMENT)('public')(570,576) @@ -94,7 +94,7 @@ Access Transformer File(0,728) PsiElement(NAME_ELEMENT)('field_75096_f')(624,637) PsiWhiteSpace(' ')(637,638) PsiComment(COMMENT)('# flySpeed')(638,648) - PsiElement(crlf)('\n')(648,649) + PsiElement(CRLF)('\n')(648,649) AtEntryImpl(ENTRY)(649,716) AtKeywordImpl(KEYWORD)(649,655) PsiElement(KEYWORD_ELEMENT)('public')(649,655) diff --git a/src/test/resources/com/demonwav/mcdev/platform/mcp/at/parser/fixtures/funcs_at.txt b/src/test/resources/com/demonwav/mcdev/platform/mcp/at/parser/fixtures/funcs_at.txt index c35f54305..581e83350 100644 --- a/src/test/resources/com/demonwav/mcdev/platform/mcp/at/parser/fixtures/funcs_at.txt +++ b/src/test/resources/com/demonwav/mcdev/platform/mcp/at/parser/fixtures/funcs_at.txt @@ -1,4 +1,4 @@ -Access Transformer File(0,2318) +Access Transformers(0,2318) AtEntryImpl(ENTRY)(0,64) AtKeywordImpl(KEYWORD)(0,6) PsiElement(KEYWORD_ELEMENT)('public')(0,6) @@ -15,7 +15,7 @@ Access Transformer File(0,2318) PsiElement(PRIMITIVE)('V')(63,64) PsiWhiteSpace(' ')(64,65) PsiComment(COMMENT)('# removeExpired')(65,80) - PsiElement(crlf)('\n')(80,81) + PsiElement(CRLF)('\n')(80,81) AtEntryImpl(ENTRY)(81,194) AtKeywordImpl(KEYWORD)(81,87) PsiElement(KEYWORD_ELEMENT)('public')(81,87) @@ -34,7 +34,7 @@ Access Transformer File(0,2318) PsiElement(CLASS_VALUE)('Ljava/lang/String;')(176,194) PsiWhiteSpace(' ')(194,195) PsiComment(COMMENT)('# getObjectKey')(195,209) - PsiElement(crlf)('\n')(209,210) + PsiElement(CRLF)('\n')(209,210) AtEntryImpl(ENTRY)(210,309) AtKeywordImpl(KEYWORD)(210,216) PsiElement(KEYWORD_ELEMENT)('public')(210,216) @@ -53,7 +53,7 @@ Access Transformer File(0,2318) PsiElement(CLASS_VALUE)('Ljava/lang/String;')(291,309) PsiWhiteSpace(' ')(309,310) PsiComment(COMMENT)('# getObjectKey')(310,324) - PsiElement(crlf)('\n')(324,325) + PsiElement(CRLF)('\n')(324,325) AtEntryImpl(ENTRY)(325,403) AtKeywordImpl(KEYWORD)(325,331) PsiElement(KEYWORD_ELEMENT)('public')(325,331) @@ -68,7 +68,7 @@ Access Transformer File(0,2318) PsiElement(CLOSE_PAREN)(')')(387,388) AtReturnValueImpl(RETURN_VALUE)(388,403) PsiElement(CLASS_VALUE)('Ljava/util/Map;')(388,403) - PsiElement(crlf)('\n')(403,404) + PsiElement(CRLF)('\n')(403,404) AtEntryImpl(ENTRY)(404,521) AtKeywordImpl(KEYWORD)(404,410) PsiElement(KEYWORD_ELEMENT)('public')(404,410) @@ -87,7 +87,7 @@ Access Transformer File(0,2318) PsiElement(CLASS_VALUE)('Ljava/lang/String;')(503,521) PsiWhiteSpace(' ')(521,522) PsiComment(COMMENT)('# getObjectKey')(522,536) - PsiElement(crlf)('\n')(536,537) + PsiElement(CRLF)('\n')(536,537) AtEntryImpl(ENTRY)(537,640) AtKeywordImpl(KEYWORD)(537,543) PsiElement(KEYWORD_ELEMENT)('public')(537,543) @@ -106,7 +106,7 @@ Access Transformer File(0,2318) PsiElement(CLASS_VALUE)('Ljava/lang/String;')(622,640) PsiWhiteSpace(' ')(640,641) PsiComment(COMMENT)('# getObjectKey')(641,655) - PsiElement(crlf)('\n')(655,656) + PsiElement(CRLF)('\n')(655,656) AtEntryImpl(ENTRY)(656,742) AtKeywordImpl(KEYWORD)(656,662) PsiElement(KEYWORD_ELEMENT)('public')(656,662) @@ -123,7 +123,7 @@ Access Transformer File(0,2318) PsiElement(CLASS_VALUE)('Ljava/lang/Object;')(724,742) PsiWhiteSpace(' ')(742,743) PsiComment(COMMENT)('# getValue')(743,753) - PsiElement(crlf)('\n')(753,754) + PsiElement(CRLF)('\n')(753,754) AtEntryImpl(ENTRY)(754,865) AtKeywordImpl(KEYWORD)(754,760) PsiElement(KEYWORD_ELEMENT)('public')(754,760) @@ -142,7 +142,7 @@ Access Transformer File(0,2318) PsiElement(CLASS_VALUE)('Ljava/lang/String;')(847,865) PsiWhiteSpace(' ')(865,866) PsiComment(COMMENT)('# addressToString')(866,883) - PsiElement(crlf)('\n')(883,884) + PsiElement(CRLF)('\n')(883,884) AtEntryImpl(ENTRY)(884,1000) AtKeywordImpl(KEYWORD)(884,890) PsiElement(KEYWORD_ELEMENT)('public')(884,890) @@ -161,7 +161,7 @@ Access Transformer File(0,2318) PsiElement(CLASS_VALUE)('Ljava/lang/String;')(982,1000) PsiWhiteSpace(' ')(1000,1001) PsiComment(COMMENT)('# getObjectKey')(1001,1015) - PsiElement(crlf)('\n')(1015,1016) + PsiElement(CRLF)('\n')(1015,1016) AtEntryImpl(ENTRY)(1016,1118) AtKeywordImpl(KEYWORD)(1016,1022) PsiElement(KEYWORD_ELEMENT)('public')(1016,1022) @@ -180,7 +180,7 @@ Access Transformer File(0,2318) PsiElement(CLASS_VALUE)('Ljava/lang/String;')(1100,1118) PsiWhiteSpace(' ')(1118,1119) PsiComment(COMMENT)('# getObjectKey')(1119,1133) - PsiElement(crlf)('\n')(1133,1134) + PsiElement(CRLF)('\n')(1133,1134) AtEntryImpl(ENTRY)(1134,1256) AtKeywordImpl(KEYWORD)(1134,1140) PsiElement(KEYWORD_ELEMENT)('public')(1134,1140) @@ -199,7 +199,7 @@ Access Transformer File(0,2318) PsiElement(CLASS_VALUE)('Ljava/lang/String;')(1238,1256) PsiWhiteSpace(' ')(1256,1257) PsiComment(COMMENT)('# getObjectKey')(1257,1271) - PsiElement(crlf)('\n')(1271,1272) + PsiElement(CRLF)('\n')(1271,1272) AtEntryImpl(ENTRY)(1272,1380) AtKeywordImpl(KEYWORD)(1272,1278) PsiElement(KEYWORD_ELEMENT)('public')(1272,1278) @@ -218,7 +218,7 @@ Access Transformer File(0,2318) PsiElement(CLASS_VALUE)('Ljava/lang/String;')(1362,1380) PsiWhiteSpace(' ')(1380,1381) PsiComment(COMMENT)('# getObjectKey')(1381,1395) - PsiElement(crlf)('\n')(1395,1396) + PsiElement(CRLF)('\n')(1395,1396) AtEntryImpl(ENTRY)(1396,1483) AtKeywordImpl(KEYWORD)(1396,1402) PsiElement(KEYWORD_ELEMENT)('public')(1396,1402) @@ -235,7 +235,7 @@ Access Transformer File(0,2318) PsiElement(CLASS_VALUE)('Lnet/minecraft/util/DamageSource;')(1450,1483) PsiWhiteSpace(' ')(1483,1484) PsiComment(COMMENT)('# setDamageIsAbsolute')(1484,1505) - PsiElement(crlf)('\n')(1505,1506) + PsiElement(CRLF)('\n')(1505,1506) AtEntryImpl(ENTRY)(1506,1592) AtKeywordImpl(KEYWORD)(1506,1512) PsiElement(KEYWORD_ELEMENT)('public')(1506,1512) @@ -252,7 +252,7 @@ Access Transformer File(0,2318) PsiElement(CLASS_VALUE)('Lnet/minecraft/util/DamageSource;')(1559,1592) PsiWhiteSpace(' ')(1592,1593) PsiComment(COMMENT)('# setDamageBypassesArmor')(1593,1617) - PsiElement(crlf)('\n')(1617,1618) + PsiElement(CRLF)('\n')(1617,1618) AtEntryImpl(ENTRY)(1618,1704) AtKeywordImpl(KEYWORD)(1618,1624) PsiElement(KEYWORD_ELEMENT)('public')(1618,1624) @@ -269,7 +269,7 @@ Access Transformer File(0,2318) PsiElement(CLASS_VALUE)('Lnet/minecraft/util/DamageSource;')(1671,1704) PsiWhiteSpace(' ')(1704,1705) PsiComment(COMMENT)('# setDamageAllowedInCreativeMode')(1705,1737) - PsiElement(crlf)('\n')(1737,1738) + PsiElement(CRLF)('\n')(1737,1738) AtEntryImpl(ENTRY)(1738,1824) AtKeywordImpl(KEYWORD)(1738,1744) PsiElement(KEYWORD_ELEMENT)('public')(1738,1744) @@ -286,7 +286,7 @@ Access Transformer File(0,2318) PsiElement(CLASS_VALUE)('Lnet/minecraft/util/DamageSource;')(1791,1824) PsiWhiteSpace(' ')(1824,1825) PsiComment(COMMENT)('# setFireDamage')(1825,1840) - PsiElement(crlf)('\n')(1840,1841) + PsiElement(CRLF)('\n')(1840,1841) AtEntryImpl(ENTRY)(1841,1907) AtKeywordImpl(KEYWORD)(1841,1847) PsiElement(KEYWORD_ELEMENT)('public')(1841,1847) @@ -305,7 +305,7 @@ Access Transformer File(0,2318) PsiElement(PRIMITIVE)('V')(1906,1907) PsiWhiteSpace(' ')(1907,1908) PsiComment(COMMENT)('# protected DamageSource (String damageTypeIn)')(1908,1954) - PsiElement(crlf)('\n')(1954,1955) + PsiElement(CRLF)('\n')(1954,1955) AtEntryImpl(ENTRY)(1955,2011) AtKeywordImpl(KEYWORD)(1955,1961) PsiElement(KEYWORD_ELEMENT)('public')(1955,1961) @@ -336,7 +336,7 @@ Access Transformer File(0,2318) PsiElement(PRIMITIVE)('Z')(2010,2011) PsiWhiteSpace(' ')(2011,2012) PsiComment(COMMENT)('# isAreaLoaded')(2012,2026) - PsiElement(crlf)('\n')(2026,2027) + PsiElement(CRLF)('\n')(2026,2027) AtEntryImpl(ENTRY)(2027,2147) AtKeywordImpl(KEYWORD)(2027,2033) PsiElement(KEYWORD_ELEMENT)('public')(2027,2033) @@ -357,7 +357,7 @@ Access Transformer File(0,2318) PsiElement(PRIMITIVE)('V')(2146,2147) PsiWhiteSpace(' ')(2147,2148) PsiComment(COMMENT)('# setResourcePackFromWorld')(2148,2174) - PsiElement(crlf)('\n')(2174,2175) + PsiElement(CRLF)('\n')(2174,2175) AtEntryImpl(ENTRY)(2175,2301) AtKeywordImpl(KEYWORD)(2175,2181) PsiElement(KEYWORD_ELEMENT)('public')(2175,2181) diff --git a/src/test/resources/com/demonwav/mcdev/platform/mcp/at/parser/fixtures/keywords_at.txt b/src/test/resources/com/demonwav/mcdev/platform/mcp/at/parser/fixtures/keywords_at.txt index 20cac812a..8a7643d3a 100644 --- a/src/test/resources/com/demonwav/mcdev/platform/mcp/at/parser/fixtures/keywords_at.txt +++ b/src/test/resources/com/demonwav/mcdev/platform/mcp/at/parser/fixtures/keywords_at.txt @@ -1,4 +1,4 @@ -Access Transformer File(0,354) +Access Transformers(0,354) AtEntryImpl(ENTRY)(0,26) AtKeywordImpl(KEYWORD)(0,6) PsiElement(KEYWORD_ELEMENT)('public')(0,6) @@ -8,7 +8,7 @@ Access Transformer File(0,354) PsiWhiteSpace(' ')(20,21) AtFieldNameImpl(FIELD_NAME)(21,26) PsiElement(NAME_ELEMENT)('field')(21,26) - PsiElement(crlf)('\n')(26,27) + PsiElement(CRLF)('\n')(26,27) AtEntryImpl(ENTRY)(27,55) AtKeywordImpl(KEYWORD)(27,35) PsiElement(KEYWORD_ELEMENT)('public-f')(27,35) @@ -18,7 +18,7 @@ Access Transformer File(0,354) PsiWhiteSpace(' ')(49,50) AtFieldNameImpl(FIELD_NAME)(50,55) PsiElement(NAME_ELEMENT)('field')(50,55) - PsiElement(crlf)('\n')(55,56) + PsiElement(CRLF)('\n')(55,56) AtEntryImpl(ENTRY)(56,84) AtKeywordImpl(KEYWORD)(56,64) PsiElement(KEYWORD_ELEMENT)('public+f')(56,64) @@ -28,7 +28,7 @@ Access Transformer File(0,354) PsiWhiteSpace(' ')(78,79) AtFieldNameImpl(FIELD_NAME)(79,84) PsiElement(NAME_ELEMENT)('field')(79,84) - PsiElement(crlf)('\n')(84,85) + PsiElement(CRLF)('\n')(84,85) AtEntryImpl(ENTRY)(85,112) AtKeywordImpl(KEYWORD)(85,92) PsiElement(KEYWORD_ELEMENT)('private')(85,92) @@ -38,7 +38,7 @@ Access Transformer File(0,354) PsiWhiteSpace(' ')(106,107) AtFieldNameImpl(FIELD_NAME)(107,112) PsiElement(NAME_ELEMENT)('field')(107,112) - PsiElement(crlf)('\n')(112,113) + PsiElement(CRLF)('\n')(112,113) AtEntryImpl(ENTRY)(113,142) AtKeywordImpl(KEYWORD)(113,122) PsiElement(KEYWORD_ELEMENT)('private-f')(113,122) @@ -48,7 +48,7 @@ Access Transformer File(0,354) PsiWhiteSpace(' ')(136,137) AtFieldNameImpl(FIELD_NAME)(137,142) PsiElement(NAME_ELEMENT)('field')(137,142) - PsiElement(crlf)('\n')(142,143) + PsiElement(CRLF)('\n')(142,143) AtEntryImpl(ENTRY)(143,172) AtKeywordImpl(KEYWORD)(143,152) PsiElement(KEYWORD_ELEMENT)('private+f')(143,152) @@ -58,7 +58,7 @@ Access Transformer File(0,354) PsiWhiteSpace(' ')(166,167) AtFieldNameImpl(FIELD_NAME)(167,172) PsiElement(NAME_ELEMENT)('field')(167,172) - PsiElement(crlf)('\n')(172,173) + PsiElement(CRLF)('\n')(172,173) AtEntryImpl(ENTRY)(173,202) AtKeywordImpl(KEYWORD)(173,182) PsiElement(KEYWORD_ELEMENT)('protected')(173,182) @@ -68,7 +68,7 @@ Access Transformer File(0,354) PsiWhiteSpace(' ')(196,197) AtFieldNameImpl(FIELD_NAME)(197,202) PsiElement(NAME_ELEMENT)('field')(197,202) - PsiElement(crlf)('\n')(202,203) + PsiElement(CRLF)('\n')(202,203) AtEntryImpl(ENTRY)(203,234) AtKeywordImpl(KEYWORD)(203,214) PsiElement(KEYWORD_ELEMENT)('protected-f')(203,214) @@ -78,7 +78,7 @@ Access Transformer File(0,354) PsiWhiteSpace(' ')(228,229) AtFieldNameImpl(FIELD_NAME)(229,234) PsiElement(NAME_ELEMENT)('field')(229,234) - PsiElement(crlf)('\n')(234,235) + PsiElement(CRLF)('\n')(234,235) AtEntryImpl(ENTRY)(235,266) AtKeywordImpl(KEYWORD)(235,246) PsiElement(KEYWORD_ELEMENT)('protected+f')(235,246) @@ -88,7 +88,7 @@ Access Transformer File(0,354) PsiWhiteSpace(' ')(260,261) AtFieldNameImpl(FIELD_NAME)(261,266) PsiElement(NAME_ELEMENT)('field')(261,266) - PsiElement(crlf)('\n')(266,267) + PsiElement(CRLF)('\n')(266,267) AtEntryImpl(ENTRY)(267,294) AtKeywordImpl(KEYWORD)(267,274) PsiElement(KEYWORD_ELEMENT)('default')(267,274) @@ -98,7 +98,7 @@ Access Transformer File(0,354) PsiWhiteSpace(' ')(288,289) AtFieldNameImpl(FIELD_NAME)(289,294) PsiElement(NAME_ELEMENT)('field')(289,294) - PsiElement(crlf)('\n')(294,295) + PsiElement(CRLF)('\n')(294,295) AtEntryImpl(ENTRY)(295,324) AtKeywordImpl(KEYWORD)(295,304) PsiElement(KEYWORD_ELEMENT)('default-f')(295,304) @@ -108,7 +108,7 @@ Access Transformer File(0,354) PsiWhiteSpace(' ')(318,319) AtFieldNameImpl(FIELD_NAME)(319,324) PsiElement(NAME_ELEMENT)('field')(319,324) - PsiElement(crlf)('\n')(324,325) + PsiElement(CRLF)('\n')(324,325) AtEntryImpl(ENTRY)(325,354) AtKeywordImpl(KEYWORD)(325,334) PsiElement(KEYWORD_ELEMENT)('default+f')(325,334) diff --git a/src/test/resources/com/demonwav/mcdev/platform/mcp/at/parser/fixtures/no_value_at.txt b/src/test/resources/com/demonwav/mcdev/platform/mcp/at/parser/fixtures/no_value_at.txt index fa68192f4..44bc02727 100644 --- a/src/test/resources/com/demonwav/mcdev/platform/mcp/at/parser/fixtures/no_value_at.txt +++ b/src/test/resources/com/demonwav/mcdev/platform/mcp/at/parser/fixtures/no_value_at.txt @@ -1,18 +1,18 @@ -Access Transformer File(0,173) +Access Transformers(0,173) AtEntryImpl(ENTRY)(0,37) AtKeywordImpl(KEYWORD)(0,8) PsiElement(KEYWORD_ELEMENT)('public-f')(0,8) PsiWhiteSpace(' ')(8,9) AtClassNameImpl(CLASS_NAME)(9,37) PsiElement(CLASS_NAME_ELEMENT)('net.minecraft.item.ItemStack')(9,37) - PsiElement(crlf)('\n')(37,38) + PsiElement(CRLF)('\n')(37,38) AtEntryImpl(ENTRY)(38,110) AtKeywordImpl(KEYWORD)(38,44) PsiElement(KEYWORD_ELEMENT)('public')(38,44) PsiWhiteSpace(' ')(44,45) AtClassNameImpl(CLASS_NAME)(45,110) PsiElement(CLASS_NAME_ELEMENT)('net.minecraft.block.state.BlockStateContainer$StateImplementation')(45,110) - PsiElement(crlf)('\n')(110,111) + PsiElement(CRLF)('\n')(110,111) AtEntryImpl(ENTRY)(111,173) AtKeywordImpl(KEYWORD)(111,117) PsiElement(KEYWORD_ELEMENT)('public')(111,117)