├── .gitignore ├── .gitmodules ├── CC0-LICENSE ├── LICENSE ├── MCP-LICENSE ├── README.md ├── build.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── settings.gradle ├── src └── main │ └── kotlin │ ├── main.kt │ ├── provider │ ├── mcp.kt │ ├── mojang.kt │ ├── spigot.kt │ └── yarn.kt │ ├── tiny │ └── tiny.kt │ ├── tsrgUtil.kt │ └── version.kt └── viewer ├── .gitignore ├── README.md ├── babel.config.js ├── package.json ├── public ├── favicon.ico └── index.html ├── src ├── App.vue ├── main.js └── router.js └── yarn.lock /.gitignore: -------------------------------------------------------------------------------- 1 | mappings/ 2 | bin 3 | cache 4 | .idea 5 | *.iml 6 | __pycache__ 7 | .atom_jvm_classpath 8 | build/ 9 | .gradle/ 10 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "MCPConfig"] 2 | path = MCPConfig 3 | url = https://github.com/MinecraftForge/MCPConfig/ 4 | [submodule "yarn"] 5 | path = yarn 6 | url = https://github.com/FabricMC/yarn/ 7 | -------------------------------------------------------------------------------- /CC0-LICENSE: -------------------------------------------------------------------------------- 1 | Creative Commons Legal Code 2 | 3 | CC0 1.0 Universal 4 | 5 | CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE 6 | LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN 7 | ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS 8 | INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES 9 | REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS 10 | PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM 11 | THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED 12 | HEREUNDER. 13 | 14 | Statement of Purpose 15 | 16 | The laws of most jurisdictions throughout the world automatically confer 17 | exclusive Copyright and Related Rights (defined below) upon the creator 18 | and subsequent owner(s) (each and all, an "owner") of an original work of 19 | authorship and/or a database (each, a "Work"). 20 | 21 | Certain owners wish to permanently relinquish those rights to a Work for 22 | the purpose of contributing to a commons of creative, cultural and 23 | scientific works ("Commons") that the public can reliably and without fear 24 | of later claims of infringement build upon, modify, incorporate in other 25 | works, reuse and redistribute as freely as possible in any form whatsoever 26 | and for any purposes, including without limitation commercial purposes. 27 | These owners may contribute to the Commons to promote the ideal of a free 28 | culture and the further production of creative, cultural and scientific 29 | works, or to gain reputation or greater distribution for their Work in 30 | part through the use and efforts of others. 31 | 32 | For these and/or other purposes and motivations, and without any 33 | expectation of additional consideration or compensation, the person 34 | associating CC0 with a Work (the "Affirmer"), to the extent that he or she 35 | is an owner of Copyright and Related Rights in the Work, voluntarily 36 | elects to apply CC0 to the Work and publicly distribute the Work under its 37 | terms, with knowledge of his or her Copyright and Related Rights in the 38 | Work and the meaning and intended legal effect of CC0 on those rights. 39 | 40 | 1. Copyright and Related Rights. A Work made available under CC0 may be 41 | protected by copyright and related or neighboring rights ("Copyright and 42 | Related Rights"). Copyright and Related Rights include, but are not 43 | limited to, the following: 44 | 45 | i. the right to reproduce, adapt, distribute, perform, display, 46 | communicate, and translate a Work; 47 | ii. moral rights retained by the original author(s) and/or performer(s); 48 | iii. publicity and privacy rights pertaining to a person's image or 49 | likeness depicted in a Work; 50 | iv. rights protecting against unfair competition in regards to a Work, 51 | subject to the limitations in paragraph 4(a), below; 52 | v. rights protecting the extraction, dissemination, use and reuse of data 53 | in a Work; 54 | vi. database rights (such as those arising under Directive 96/9/EC of the 55 | European Parliament and of the Council of 11 March 1996 on the legal 56 | protection of databases, and under any national implementation 57 | thereof, including any amended or successor version of such 58 | directive); and 59 | vii. other similar, equivalent or corresponding rights throughout the 60 | world based on applicable law or treaty, and any national 61 | implementations thereof. 62 | 63 | 2. Waiver. To the greatest extent permitted by, but not in contravention 64 | of, applicable law, Affirmer hereby overtly, fully, permanently, 65 | irrevocably and unconditionally waives, abandons, and surrenders all of 66 | Affirmer's Copyright and Related Rights and associated claims and causes 67 | of action, whether now known or unknown (including existing as well as 68 | future claims and causes of action), in the Work (i) in all territories 69 | worldwide, (ii) for the maximum duration provided by applicable law or 70 | treaty (including future time extensions), (iii) in any current or future 71 | medium and for any number of copies, and (iv) for any purpose whatsoever, 72 | including without limitation commercial, advertising or promotional 73 | purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each 74 | member of the public at large and to the detriment of Affirmer's heirs and 75 | successors, fully intending that such Waiver shall not be subject to 76 | revocation, rescission, cancellation, termination, or any other legal or 77 | equitable action to disrupt the quiet enjoyment of the Work by the public 78 | as contemplated by Affirmer's express Statement of Purpose. 79 | 80 | 3. Public License Fallback. Should any part of the Waiver for any reason 81 | be judged legally invalid or ineffective under applicable law, then the 82 | Waiver shall be preserved to the maximum extent permitted taking into 83 | account Affirmer's express Statement of Purpose. In addition, to the 84 | extent the Waiver is so judged Affirmer hereby grants to each affected 85 | person a royalty-free, non transferable, non sublicensable, non exclusive, 86 | irrevocable and unconditional license to exercise Affirmer's Copyright and 87 | Related Rights in the Work (i) in all territories worldwide, (ii) for the 88 | maximum duration provided by applicable law or treaty (including future 89 | time extensions), (iii) in any current or future medium and for any number 90 | of copies, and (iv) for any purpose whatsoever, including without 91 | limitation commercial, advertising or promotional purposes (the 92 | "License"). The License shall be deemed effective as of the date CC0 was 93 | applied by Affirmer to the Work. Should any part of the License for any 94 | reason be judged legally invalid or ineffective under applicable law, such 95 | partial invalidity or ineffectiveness shall not invalidate the remainder 96 | of the License, and in such case Affirmer hereby affirms that he or she 97 | will not (i) exercise any of his or her remaining Copyright and Related 98 | Rights in the Work or (ii) assert any associated claims and causes of 99 | action with respect to the Work, in either case contrary to Affirmer's 100 | express Statement of Purpose. 101 | 102 | 4. Limitations and Disclaimers. 103 | 104 | a. No trademark or patent rights held by Affirmer are waived, abandoned, 105 | surrendered, licensed or otherwise affected by this document. 106 | b. Affirmer offers the Work as-is and makes no representations or 107 | warranties of any kind concerning the Work, express, implied, 108 | statutory or otherwise, including without limitation warranties of 109 | title, merchantability, fitness for a particular purpose, non 110 | infringement, or the absence of latent or other defects, accuracy, or 111 | the present or absence of errors, whether or not discoverable, all to 112 | the greatest extent permissible under applicable law. 113 | c. Affirmer disclaims responsibility for clearing rights of other persons 114 | that may apply to the Work or any use thereof, including without 115 | limitation any person's Copyright and Related Rights in the Work. 116 | Further, Affirmer disclaims responsibility for obtaining any necessary 117 | consents, permissions or other rights required for any use of the 118 | Work. 119 | d. Affirmer understands and acknowledges that Creative Commons is not a 120 | party to this document and has no duty or obligation with respect to 121 | this CC0 or use of the Work. 122 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) Jadon Fowler 4 | Copyright (c) contributors 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in 14 | all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | THE SOFTWARE. -------------------------------------------------------------------------------- /MCP-LICENSE: -------------------------------------------------------------------------------- 1 | License and terms of use. 2 | ========================= 3 | 4 | No warranties. If MCP does not work for you, or causes any damage, it's your problem. Use it at own risk. 5 | 6 | You are allowed to: 7 | - Use MCP to decompile the Minecraft client and server jar files. 8 | - Use the decompiled source code to create mods for Minecraft. 9 | - Recompile modified versions of Minecraft. 10 | - Reobfuscate the classes of your mod for Minecraft. 11 | 12 | You are NOT allowed to: 13 | - Use MCP to do anything that violated Mojangs terms of use for Minecraft. 14 | - Release Minecraft versions or modifications that allow you to play without having bought Minecraft from Mojang. 15 | - Release modified or unmodified versions of MCP anywhere. 16 | - Use any of MCPs scripts, tools or data files without explicit written permission. 17 | - Make money with anything based on MCP (excluding Minecraft mods created by using MCP). 18 | - Use MCP to create clients that are used for griefing or exploiting server bugs. 19 | - Release the decompiled source code of Minecraft in any way. 20 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Minecraft Mappings 2 | 3 | This tool creates transitive mappings for various Minecraft versions. 4 | 5 | All the mappings can be translated to one another for a given version: `spigot2mcp.srg`, `srg2obf.mcp`, etc. 6 | 7 | Supported versions: 8 | 9 | | | Spigot | MCP | Searge | Yarn | Intermediary | Mojang | 10 | |--------|----------|----------|----------|----------|--------------|----------| 11 | | 1.15.2 | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | 12 | | 1.15.1 | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | 13 | | 1.15 | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | 14 | | 1.14.4 | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | 15 | | 1.14.3 | ✓ | ✓ | ✓ | ✓ | ✓ | | 16 | | 1.14.2 | ✓ | ✓ | ✓ | ✓ | ✓ | | 17 | | 1.14.1 | ✓ | ✓ | ✓ | ✓ | ✓ | | 18 | | 1.14 | ✓ | ✓ | ✓ | ✓ | ✓ | | 19 | | 1.13.2 | ✓ | ✓ | ✓ | | | | 20 | | 1.13.1 | ✓ | ✓ | ✓ | | | | 21 | | 1.13 | ✓ | ✓ | ✓ | | | | 22 | | 1.12.2 | ✓ | ✓ | ✓ | | | | 23 | | 1.12 | ✓ | ✓ | ✓ | | | | 24 | | 1.11 | ✓ | ✓ | ✓ | | | | 25 | | 1.10.2 | ✓ | ✓ | ✓ | | | | 26 | | 1.9.4 | ✓ | ✓ | ✓ | | | | 27 | | 1.9 | ✓ | ✓ | ✓ | | | | 28 | | 1.8.9 | | ✓ | ✓ | | | | 29 | | 1.8.8 | ✓ | ✓ | ✓ | | | | 30 | | 1.8 | ✓ | ✓ | ✓ | | | | 31 | | 1.7.10 | | ✓ | ✓ | | | | 32 | 33 | Supported formats: 34 | 35 | - SRG 36 | - CSRG 37 | - TSRG 38 | - Tiny (fields descriptors don't exist) 39 | - JSON 40 | 41 | These mappings were made possible by @Techcable, the MCP team, Bukkit, SpigotMC, FabricMC, Mojang, and various other people. 42 | 43 | ## TODO 44 | 45 | - [ ] [older versions](https://github.com/agaricusb/MinecraftRemapping) 46 | - [ ] < 1.8 CraftBukkit mappings? 47 | - [ ] snapshots? 48 | 49 | ## License 50 | 51 | * All Kotlin scripts are MIT Licensed. 52 | 53 | * The MCP mappings are the property of the MCP Team and are released under the MCP License. 54 | 55 | * The Spigot mappings are copyright SpigotMC Pty. Ltd. 56 | 57 | * The Yarn mappings are licensed under the Creative Commons Zero license. 58 | 59 | * The Mojang mappings are copyright Microsoft. 60 | -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'java' 3 | id 'org.jetbrains.kotlin.jvm' version '1.3.21' 4 | } 5 | 6 | group 'io.jadon' 7 | version '1.0-SNAPSHOT' 8 | 9 | sourceCompatibility = 1.8 10 | 11 | repositories { 12 | mavenCentral() 13 | maven { url 'https://jitpack.io' } 14 | maven { 15 | name "Modmuss50 Repository" 16 | url 'https://maven.modmuss50.me' 17 | } 18 | } 19 | 20 | dependencies { 21 | implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8" 22 | implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.2.1' 23 | testCompile group: 'junit', name: 'junit', version: '4.12' 24 | 25 | compile 'com.github.phase:SrgLib:master-SNAPSHOT' 26 | compile "com.opencsv:opencsv:3.9" 27 | compile 'com.google.code.gson:gson:2.8.6' 28 | compile "com.google.guava:guava:21.0" 29 | compile "net.md-5:SpecialSource:1.8.5" 30 | compile "cuchaz:enigma:0.13.1.+:all" 31 | compile 'org.cadixdev:lorenz:0.5.2' 32 | compile 'org.cadixdev:lorenz-io-proguard:0.5.2' 33 | } 34 | 35 | compileKotlin { 36 | kotlinOptions.jvmTarget = "1.8" 37 | } 38 | compileTestKotlin { 39 | kotlinOptions.jvmTarget = "1.8" 40 | } -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | kotlin.code.style=official -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/phase/MinecraftMappings/411df6765ac0902fd05659398e6986e72ace3161/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-4.10.3-bin.zip 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Attempt to set APP_HOME 10 | # Resolve links: $0 may be a link 11 | PRG="$0" 12 | # Need this for relative symlinks. 13 | while [ -h "$PRG" ] ; do 14 | ls=`ls -ld "$PRG"` 15 | link=`expr "$ls" : '.*-> \(.*\)$'` 16 | if expr "$link" : '/.*' > /dev/null; then 17 | PRG="$link" 18 | else 19 | PRG=`dirname "$PRG"`"/$link" 20 | fi 21 | done 22 | SAVED="`pwd`" 23 | cd "`dirname \"$PRG\"`/" >/dev/null 24 | APP_HOME="`pwd -P`" 25 | cd "$SAVED" >/dev/null 26 | 27 | APP_NAME="Gradle" 28 | APP_BASE_NAME=`basename "$0"` 29 | 30 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 31 | DEFAULT_JVM_OPTS="" 32 | 33 | # Use the maximum available, or set MAX_FD != -1 to use that value. 34 | MAX_FD="maximum" 35 | 36 | warn () { 37 | echo "$*" 38 | } 39 | 40 | die () { 41 | echo 42 | echo "$*" 43 | echo 44 | exit 1 45 | } 46 | 47 | # OS specific support (must be 'true' or 'false'). 48 | cygwin=false 49 | msys=false 50 | darwin=false 51 | nonstop=false 52 | case "`uname`" in 53 | CYGWIN* ) 54 | cygwin=true 55 | ;; 56 | Darwin* ) 57 | darwin=true 58 | ;; 59 | MINGW* ) 60 | msys=true 61 | ;; 62 | NONSTOP* ) 63 | nonstop=true 64 | ;; 65 | esac 66 | 67 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 68 | 69 | # Determine the Java command to use to start the JVM. 70 | if [ -n "$JAVA_HOME" ] ; then 71 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 72 | # IBM's JDK on AIX uses strange locations for the executables 73 | JAVACMD="$JAVA_HOME/jre/sh/java" 74 | else 75 | JAVACMD="$JAVA_HOME/bin/java" 76 | fi 77 | if [ ! -x "$JAVACMD" ] ; then 78 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 79 | 80 | Please set the JAVA_HOME variable in your environment to match the 81 | location of your Java installation." 82 | fi 83 | else 84 | JAVACMD="java" 85 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 86 | 87 | Please set the JAVA_HOME variable in your environment to match the 88 | location of your Java installation." 89 | fi 90 | 91 | # Increase the maximum file descriptors if we can. 92 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then 93 | MAX_FD_LIMIT=`ulimit -H -n` 94 | if [ $? -eq 0 ] ; then 95 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 96 | MAX_FD="$MAX_FD_LIMIT" 97 | fi 98 | ulimit -n $MAX_FD 99 | if [ $? -ne 0 ] ; then 100 | warn "Could not set maximum file descriptor limit: $MAX_FD" 101 | fi 102 | else 103 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 104 | fi 105 | fi 106 | 107 | # For Darwin, add options to specify how the application appears in the dock 108 | if $darwin; then 109 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 110 | fi 111 | 112 | # For Cygwin, switch paths to Windows format before running java 113 | if $cygwin ; then 114 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 115 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 116 | JAVACMD=`cygpath --unix "$JAVACMD"` 117 | 118 | # We build the pattern for arguments to be converted via cygpath 119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 120 | SEP="" 121 | for dir in $ROOTDIRSRAW ; do 122 | ROOTDIRS="$ROOTDIRS$SEP$dir" 123 | SEP="|" 124 | done 125 | OURCYGPATTERN="(^($ROOTDIRS))" 126 | # Add a user-defined pattern to the cygpath arguments 127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 129 | fi 130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 131 | i=0 132 | for arg in "$@" ; do 133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 135 | 136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 138 | else 139 | eval `echo args$i`="\"$arg\"" 140 | fi 141 | i=$((i+1)) 142 | done 143 | case $i in 144 | (0) set -- ;; 145 | (1) set -- "$args0" ;; 146 | (2) set -- "$args0" "$args1" ;; 147 | (3) set -- "$args0" "$args1" "$args2" ;; 148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 154 | esac 155 | fi 156 | 157 | # Escape application args 158 | save () { 159 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done 160 | echo " " 161 | } 162 | APP_ARGS=$(save "$@") 163 | 164 | # Collect all arguments for the java command, following the shell quoting and substitution rules 165 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" 166 | 167 | # by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong 168 | if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then 169 | cd "$(dirname "$0")" 170 | fi 171 | 172 | exec "$JAVACMD" "$@" 173 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | set DIRNAME=%~dp0 12 | if "%DIRNAME%" == "" set DIRNAME=. 13 | set APP_BASE_NAME=%~n0 14 | set APP_HOME=%DIRNAME% 15 | 16 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 17 | set DEFAULT_JVM_OPTS= 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windows variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | 53 | :win9xME_args 54 | @rem Slurp the command line arguments. 55 | set CMD_LINE_ARGS= 56 | set _SKIP=2 57 | 58 | :win9xME_args_slurp 59 | if "x%~1" == "x" goto execute 60 | 61 | set CMD_LINE_ARGS=%* 62 | 63 | :execute 64 | @rem Setup the command line 65 | 66 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 67 | 68 | @rem Execute Gradle 69 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 70 | 71 | :end 72 | @rem End local scope for the variables with windows NT shell 73 | if "%ERRORLEVEL%"=="0" goto mainEnd 74 | 75 | :fail 76 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 77 | rem the _cmd.exe /c_ return code! 78 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 79 | exit /b 1 80 | 81 | :mainEnd 82 | if "%OS%"=="Windows_NT" endlocal 83 | 84 | :omega 85 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | rootProject.name = 'mapping-generator' 2 | 3 | -------------------------------------------------------------------------------- /src/main/kotlin/main.kt: -------------------------------------------------------------------------------- 1 | import kotlinx.coroutines.GlobalScope 2 | import kotlinx.coroutines.launch 3 | import kotlinx.coroutines.runBlocking 4 | import java.io.File 5 | 6 | val GLOBAL_FOLDER = File("mappings") 7 | 8 | fun main() { 9 | val time = System.currentTimeMillis() 10 | GLOBAL_FOLDER.mkdirs() 11 | // MinecraftVersion.values().map { 12 | // GlobalScope.launch { 13 | // println("Starting ${it.mcVersion}") 14 | // it.write(GLOBAL_FOLDER) 15 | // } 16 | // }.forEach { runBlocking { it.join() } } 17 | MinecraftVersion.V1_14_4.write(GLOBAL_FOLDER) 18 | val elapsed = (System.currentTimeMillis() - time) / 1000.0 19 | println("Done. Took ${elapsed / 60}m (${elapsed}s)") 20 | } 21 | -------------------------------------------------------------------------------- /src/main/kotlin/provider/mcp.kt: -------------------------------------------------------------------------------- 1 | package provider 2 | 3 | import TSrgUtil 4 | import com.google.gson.Gson 5 | import com.google.gson.JsonObject 6 | import com.google.gson.stream.JsonReader 7 | import com.opencsv.CSVReader 8 | import net.techcable.srglib.FieldData 9 | import net.techcable.srglib.JavaType 10 | import net.techcable.srglib.MethodData 11 | import net.techcable.srglib.format.MappingsFormat 12 | import net.techcable.srglib.mappings.Mappings 13 | import java.io.File 14 | import java.io.IOException 15 | import java.net.URL 16 | import java.util.* 17 | import java.util.zip.ZipInputStream 18 | import kotlin.system.exitProcess 19 | 20 | fun downloadMcpMappings(srgMappings: Mappings, mappingsVersion: String): Mappings { 21 | val cacheFile = File("cache/mcp_$mappingsVersion/mcp.json") 22 | val fieldNames = HashMap() 23 | val methodNames = HashMap() 24 | if (!cacheFile.exists()) { 25 | cacheFile.parentFile.mkdirs() 26 | check(cacheFile.createNewFile()) 27 | // Validate and compute the mapping version information 28 | val mappingsVersions: Map>> = 29 | JsonReader(URL("http://export.mcpbot.bspk.rs/versions.json").openStream().reader()).use { reader -> 30 | val result = HashMap>>() 31 | // We have to parse this by hand or else things don't work 32 | reader.beginObject() 33 | while (reader.hasNext()) { 34 | val version = reader.nextName() 35 | val byChannel = HashMap>() 36 | reader.beginObject() 37 | while (reader.hasNext()) { 38 | val channelName = reader.nextName() 39 | val channelVersions = ArrayList() 40 | reader.beginArray() 41 | while (reader.hasNext()) { 42 | channelVersions.add(reader.nextInt()) 43 | } 44 | reader.endArray() 45 | byChannel[channelName] = channelVersions 46 | } 47 | reader.endObject() 48 | result[version] = byChannel 49 | } 50 | reader.endObject() 51 | result 52 | } 53 | val mappingsChannel = mappingsVersion.substring(0, mappingsVersion.indexOf('_')) 54 | val isNodoc = "_nodoc_" in mappingsVersion 55 | val fullMappingsChannel = if (isNodoc) mappingsChannel + "_nodoc" else mappingsChannel 56 | val mappingsId = mappingsVersion.substring(mappingsVersion.lastIndexOf('_') + 1).toInt() 57 | var minecraftVersion: String? = null 58 | for ((version, byChannel) in mappingsVersions.entries) { 59 | if (mappingsChannel !in byChannel) { 60 | System.err.println("Unknown channel $mappingsChannel for version $version") 61 | exitProcess(1) 62 | } 63 | if (mappingsId in byChannel[mappingsChannel]!!) { 64 | println("Found mappings $mappingsId for version $version") 65 | minecraftVersion = version 66 | break 67 | } 68 | } 69 | if (minecraftVersion == null) { 70 | System.err.println("Unable to find mappings: $mappingsVersion") 71 | exitProcess(1) 72 | } 73 | // Parse the mappings data files 74 | try { 75 | val url = 76 | URL("http://export.mcpbot.bspk.rs/mcp_$fullMappingsChannel/$mappingsId-$minecraftVersion/mcp_$fullMappingsChannel-$mappingsId-$minecraftVersion.zip") 77 | println("Downloading MCP mappings from: $url") 78 | ZipInputStream(url.openStream()).use { 79 | var entry = it.nextEntry 80 | do { 81 | when (entry.name) { 82 | "fields.csv" -> { 83 | CSVReader(it.reader()).forEachLine { 84 | val original = it[0] 85 | val renamed = it[1] 86 | fieldNames[original] = renamed 87 | } 88 | } 89 | "methods.csv" -> { 90 | CSVReader(it.reader()).forEachLine { 91 | val original = it[0] 92 | val renamed = it[1] 93 | methodNames[original] = renamed 94 | } 95 | } 96 | } 97 | entry = it.nextEntry 98 | } while (entry != null) 99 | } 100 | if (fieldNames.isEmpty() || methodNames.isEmpty()) { 101 | System.err.println("Unable to download MCP mappings $mappingsVersion: Unable to locate info in the zip file") 102 | exitProcess(1) 103 | } 104 | } catch (e: IOException) { 105 | System.err.println("Unable to download MCP mappings $mappingsVersion:") 106 | e.printStackTrace() 107 | exitProcess(1) 108 | } 109 | val json = JsonObject() 110 | json["fields"] = Gson().toJsonTree(fieldNames) 111 | json["methods"] = Gson().toJsonTree(methodNames) 112 | cacheFile.writeText(json.toString()) 113 | } else { 114 | println("Reading cache $cacheFile") 115 | JsonReader(cacheFile.reader()).use { 116 | it.beginObject() 117 | while (it.hasNext()) { 118 | val name = it.nextName() 119 | if (name == "fields" || name == "methods") { 120 | it.beginObject() 121 | while (it.hasNext()) { 122 | val original = it.nextName() 123 | val renamed = it.nextString() 124 | when (name) { 125 | "fields" -> fieldNames[original] = renamed 126 | "methods" -> methodNames[original] = renamed 127 | } 128 | } 129 | it.endObject() 130 | } 131 | } 132 | it.endObject() 133 | } 134 | } 135 | return Mappings.createRenamingMappings( 136 | { oldType: JavaType -> oldType }, 137 | { srgMethod: MethodData -> methodNames[srgMethod.name] ?: srgMethod.name }, 138 | { srgField: FieldData -> fieldNames[srgField.name] ?: srgField.name } 139 | ).transform(srgMappings.inverted()) 140 | } 141 | 142 | fun downloadSrgMappings(minecraftVersion: String): Mappings { 143 | val cacheFile = File("cache/mcp-$minecraftVersion-joined.srg") 144 | if (!cacheFile.exists()) { 145 | cacheFile.parentFile.mkdirs() 146 | try { 147 | val url = 148 | URL("http://files.minecraftforge.net/maven/de/oceanlabs/mcp/mcp/$minecraftVersion/mcp-$minecraftVersion-srg.zip") 149 | ZipInputStream(url.openStream()).use { zipStream -> 150 | var entry = zipStream.nextEntry 151 | while (entry != null) { 152 | if (entry.name == "joined.srg") { 153 | check(cacheFile.createNewFile()) 154 | cacheFile.outputStream().use { output -> zipStream.copyTo(output) } 155 | } 156 | entry = zipStream.nextEntry 157 | } 158 | } 159 | if (!cacheFile.exists()) { 160 | System.err.println("Unable to download SRG mappings for $minecraftVersion: Unable to locate joined.srg in the zip file") 161 | exitProcess(1) 162 | } 163 | } catch (e: IOException) { 164 | System.err.println("Unable to download SRG mappings for $minecraftVersion:") 165 | e.printStackTrace() 166 | exitProcess(1) 167 | } 168 | } 169 | return MappingsFormat.SEARGE_FORMAT.parseFile(cacheFile) 170 | } 171 | 172 | // use MinecraftForge/MCPConfig to mappings > 1.13 173 | 174 | fun getMCPConfigMappings(minecraftVersion: String): Mappings { 175 | val cacheFolder = File("cache/") 176 | 177 | val obf2srgFile = File(cacheFolder, "mcpconfig-$minecraftVersion-joined.srg") 178 | 179 | if (!obf2srgFile.exists()) { 180 | println("mcpconfig $minecraftVersion: generating srg from tsrg") 181 | val tsrgFile = File("MCPConfig/versions/release/$minecraftVersion/joined.tsrg") 182 | TSrgUtil.toSrg(tsrgFile, obf2srgFile) 183 | } 184 | 185 | return MappingsFormat.SEARGE_FORMAT.parseFile(obf2srgFile) 186 | } 187 | -------------------------------------------------------------------------------- /src/main/kotlin/provider/mojang.kt: -------------------------------------------------------------------------------- 1 | package provider 2 | 3 | import com.google.gson.JsonParser 4 | import net.techcable.srglib.format.MappingsFormat 5 | import net.techcable.srglib.mappings.Mappings 6 | import org.cadixdev.lorenz.MappingSet 7 | import org.cadixdev.lorenz.io.proguard.ProGuardFormat 8 | import org.cadixdev.lorenz.io.srg.SrgWriter 9 | import java.io.* 10 | import java.net.URL 11 | import java.nio.charset.StandardCharsets 12 | 13 | /** 14 | * Reads the client mappings from the URLs stored in the client json. 15 | * 16 | * Written in Java and translated to Kotlin 17 | * 18 | * @author phase 19 | */ 20 | object MojangMappings { 21 | 22 | fun getMappings(version: String): Mappings { 23 | val cacheDir = File("cache/mojang/") 24 | cacheDir.mkdirs() 25 | download(version, cacheDir) 26 | val clientMappingFile = File(cacheDir, version + "_client.srg") 27 | return MappingsFormat.SEARGE_FORMAT.parseLines(clientMappingFile.readLines().filter { 28 | !it.contains("package-info") 29 | && !".*(\\$\\d+).*".toRegex().matches(it) 30 | }) 31 | } 32 | 33 | fun download(version: String, dir: File?) { 34 | val clientMappingFile = File(dir, version + "_client.srg") 35 | val serverMappingFile = File(dir, version + "_server.srg") 36 | 37 | if (clientMappingFile.exists() && serverMappingFile.exists()) return; 38 | 39 | val mappings = readMappings(version) 40 | 41 | val clientWriter = PrintWriter(FileWriter(clientMappingFile)) 42 | SrgWriter(clientWriter).write(mappings.clientMappings) 43 | clientWriter.close() 44 | 45 | val serverWriter = PrintWriter(FileWriter(serverMappingFile)) 46 | SrgWriter(serverWriter).write(mappings.serverMappings) 47 | serverWriter.close() 48 | } 49 | 50 | fun readMappings(version: String): LorenzMappings { 51 | val versionJsonFile = 52 | File(minecraftFolder, "/versions/$version/$version.json") 53 | val versionJson = 54 | JsonParser().parse(FileReader(versionJsonFile)).asJsonObject 55 | val downloads = versionJson.getAsJsonObject("downloads") 56 | val clientMappingUrl = 57 | URL(downloads.getAsJsonObject("client_mappings")["url"].asString) 58 | val serverMappingUrl = 59 | URL(downloads.getAsJsonObject("server_mappings")["url"].asString) 60 | return LorenzMappings(readUrl(clientMappingUrl), readUrl(serverMappingUrl)) 61 | } 62 | 63 | fun readUrl(url: URL): MappingSet { 64 | println("Downloading from $url") 65 | val conn = url.openConnection() 66 | BufferedReader( 67 | InputStreamReader( 68 | conn.getInputStream(), 69 | StandardCharsets.UTF_8 70 | ) 71 | ).use { reader -> return ProGuardFormat().createReader(reader).read().reverse() } 72 | } 73 | 74 | /** 75 | * @return .minecraft folder for the current OS 76 | */ 77 | val minecraftFolder: File 78 | get() { 79 | val os = System.getProperty("os.name").toLowerCase() 80 | return if (os.contains("win")) { 81 | File(File(System.getenv("APPDATA")), ".minecraft") 82 | } else if (os.contains("mac")) { 83 | File( 84 | File(System.getProperty("user.home")), 85 | "Library/Application Support/minecraft" 86 | ) 87 | } else if (os.contains("linux")) { 88 | File(File(System.getProperty("user.home")), ".minecraft/") 89 | } else { 90 | throw RuntimeException("Failed to determine Minecraft directory for OS: $os") 91 | } 92 | } 93 | 94 | 95 | data class LorenzMappings( 96 | val clientMappings: MappingSet, 97 | val serverMappings: MappingSet 98 | ) 99 | } -------------------------------------------------------------------------------- /src/main/kotlin/provider/spigot.kt: -------------------------------------------------------------------------------- 1 | package provider 2 | 3 | import com.google.common.collect.ImmutableBiMap 4 | import com.google.common.collect.ImmutableList 5 | import com.google.common.collect.ImmutableMap 6 | import com.google.gson.Gson 7 | import com.google.gson.JsonElement 8 | import com.google.gson.JsonObject 9 | import com.google.gson.JsonParser 10 | import com.google.gson.reflect.TypeToken 11 | import com.google.gson.stream.JsonReader 12 | import com.google.gson.stream.JsonWriter 13 | import com.opencsv.CSVReader 14 | import net.techcable.srglib.FieldData 15 | import net.techcable.srglib.JavaType 16 | import net.techcable.srglib.MethodData 17 | import net.techcable.srglib.format.MappingsFormat 18 | import net.techcable.srglib.mappings.ImmutableMappings 19 | import net.techcable.srglib.mappings.Mappings 20 | import java.io.File 21 | import java.io.Reader 22 | import java.net.URL 23 | import java.util.* 24 | 25 | /** 26 | * Thank you Techcable for being awesome <3 27 | * @author Techcable (MIT License) 28 | */ 29 | 30 | inline fun Gson.fromJson(reader: Reader): T = this.fromJson(reader, (object : TypeToken() {}).type) 31 | 32 | inline fun Gson.fromJson(element: JsonElement): T = 33 | this.fromJson(element, (object : TypeToken() {}).type) 34 | 35 | inline fun CSVReader.forEachLine(action: (Array) -> Unit) { 36 | var line = this.readNext() 37 | while (line != null) { 38 | action(line) 39 | line = this.readNext() 40 | } 41 | } 42 | 43 | fun URL.loadJson(): JsonElement = this.openStream().reader().use { JsonParser().parse(it) } 44 | fun URL.downloadTo(target: File) { 45 | check(target.createNewFile()) { "Unable to download ${this} to $target: File already exists" } 46 | this.openStream().use { input -> 47 | target.outputStream().use { output -> 48 | input.copyTo(output) 49 | } 50 | } 51 | } 52 | 53 | @Suppress("NOTHING_TO_INLINE") 54 | inline operator fun JsonObject.set(key: String, value: JsonElement) = this.add(key, value) 55 | 56 | fun stripDuplicates(mappings: Mappings): ImmutableMappings { 57 | val classes = HashMap() 58 | val fields = HashMap() 59 | val methods = HashMap() 60 | mappings.forEachClass { original, renamed -> 61 | if (original != renamed) { 62 | classes[original] = renamed 63 | } 64 | } 65 | mappings.forEachField { original, renamed -> 66 | if (original.name != renamed.name) { 67 | fields[original] = renamed 68 | } 69 | } 70 | mappings.forEachMethod { original, renamed -> 71 | if (original.name != renamed.name) { 72 | methods[original] = renamed 73 | } 74 | } 75 | return ImmutableMappings.create( 76 | ImmutableBiMap.copyOf(classes), 77 | ImmutableBiMap.copyOf(methods), 78 | ImmutableBiMap.copyOf(fields) 79 | ) 80 | } 81 | 82 | class CacheInfo(val buildDataCommits: MutableMap = HashMap()) { 83 | fun saveTo(file: File) = file.writer().use { Gson().toJson(this, it) } 84 | 85 | companion object { 86 | fun loadFrom(file: File) = file.reader().use { Gson().fromJson(it) } 87 | } 88 | } 89 | 90 | /** 91 | * Get the build data commit from the specified spigot revision 92 | * 93 | * The Spigot revision is specified using '--rev=1.8' as a buildtools option 94 | * Available revisions: https://hub.spigotmc.org/versions/ 95 | * @param spigotVersion the build data revision 96 | * @return the build data commit-id for this revision 97 | */ 98 | fun getBuildDataCommit(spigotVersion: String): String { 99 | val cacheFile = File("cache/info.json") 100 | val cacheInfo = if (cacheFile.exists()) CacheInfo.loadFrom(cacheFile) else CacheInfo() 101 | return cacheInfo.buildDataCommits.getOrElse(spigotVersion) { 102 | val url = URL("https://hub.spigotmc.org/versions/$spigotVersion.json") 103 | val json = url.loadJson().asJsonObject 104 | val buildDataCommit = json.getAsJsonObject("refs").get("BuildData").asString 105 | // Store it in the cache 106 | cacheInfo.buildDataCommits[spigotVersion] = buildDataCommit 107 | cacheInfo.saveTo(cacheFile) 108 | buildDataCommit 109 | } 110 | } 111 | 112 | /** 113 | * Syntax errors in the srg files that SpecialSource swallows silently 114 | */ 115 | val brokenLines = setOf( 116 | "IDispenseBehavior a(LISourceBlock;LItemStack;)LItemStack; dispense", 117 | "nv ServerStatisticManager#", 118 | "ql ServerStatisticManager#", 119 | "qn ServerStatisticManager#" 120 | ) 121 | 122 | fun stripBrokenLines(lines: List) = lines.filter { it !in brokenLines && "" !in it } 123 | 124 | fun downloadSpigotMappings(buildDataCommit: String): Mappings { 125 | val baseUrl = "https://hub.spigotmc.org/stash/projects/SPIGOT/repos/builddata/browse/" 126 | val cacheDir = File("cache/spigot_$buildDataCommit") 127 | val classMappingsFile = File(cacheDir, "classes.csrg") 128 | val memberMappingsFile = File(cacheDir, "members.csrg") 129 | val packageMappingsFile = File(cacheDir, "packages.json") 130 | if (!classMappingsFile.exists() || !memberMappingsFile.exists() || !packageMappingsFile.exists()) { 131 | cacheDir.mkdirs() 132 | val info = URL("$baseUrl/info.json?at=$buildDataCommit&raw").loadJson().asJsonObject 133 | val classMappingsLocation = info.get("classMappings").asString 134 | val memberMappingsLocation = info.get("memberMappings").asString 135 | val packageMappingsLocation = info.get("packageMappings").asString 136 | if (!classMappingsFile.exists()) { 137 | URL("$baseUrl/mappings/$classMappingsLocation/?at=$buildDataCommit&raw").downloadTo(classMappingsFile) 138 | } 139 | if (!memberMappingsFile.exists()) { 140 | URL("$baseUrl/mappings/$memberMappingsLocation/?at=$buildDataCommit&raw").downloadTo(memberMappingsFile) 141 | } 142 | if (!packageMappingsFile.exists()) { 143 | val packages = HashMap() 144 | for (line in URL("$baseUrl/mappings/$packageMappingsLocation/?at=$buildDataCommit&raw").openStream().bufferedReader().lineSequence()) { 145 | if (line.trim().startsWith("#") || line.isEmpty()) { 146 | continue 147 | } 148 | val split = line.trim().split(" ") 149 | var original = split[0] 150 | var renamed = split[1] 151 | if (!original.endsWith("/") || !renamed.endsWith("/")) { 152 | throw RuntimeException("Not a package: $line") 153 | } 154 | // Strip trailing '/' 155 | original = original.substring(0, original.length - 1) 156 | renamed = renamed.substring(0, renamed.length - 1) 157 | // Strip leading '.' if present 158 | if (original.startsWith(".")) { 159 | original = original.substring(1) 160 | } 161 | if (renamed.startsWith(".")) { 162 | renamed = renamed.substring(1) 163 | } 164 | original = original.replace('/', '.') 165 | renamed = renamed.replace('/', '.') 166 | packages[original] = renamed 167 | } 168 | JsonWriter(packageMappingsFile.writer()).use { 169 | it.beginObject() 170 | for ((original, renamed) in packages) { 171 | it.name(original) 172 | it.value(renamed) 173 | } 174 | it.endObject() 175 | } 176 | } 177 | } 178 | val classMappings = MappingsFormat.COMPACT_SEARGE_FORMAT.parseLines(stripBrokenLines(classMappingsFile.readLines())) 179 | val memberMappings = 180 | MappingsFormat.COMPACT_SEARGE_FORMAT.parseLines(stripBrokenLines(memberMappingsFile.readLines())) 181 | val packages = JsonReader(packageMappingsFile.reader()).use { 182 | val builder = ImmutableMap.builder() 183 | it.beginObject() 184 | while (it.hasNext()) { 185 | builder.put(it.nextName(), it.nextString()) 186 | } 187 | it.endObject() 188 | builder.build() 189 | } 190 | return Mappings.chain(ImmutableList.of(classMappings, memberMappings, Mappings.createPackageMappings(packages))) 191 | } 192 | -------------------------------------------------------------------------------- /src/main/kotlin/provider/yarn.kt: -------------------------------------------------------------------------------- 1 | package provider 2 | 3 | import cuchaz.enigma.ProgressListener 4 | import cuchaz.enigma.translation.mapping.serde.MappingFormat 5 | import net.techcable.srglib.format.MappingsFormat 6 | import net.techcable.srglib.mappings.Mappings 7 | import java.io.File 8 | import java.net.URL 9 | import java.util.concurrent.TimeUnit 10 | import java.util.zip.GZIPInputStream 11 | 12 | fun String.runCommand(workingDir: File) { 13 | ProcessBuilder(*split(" ").toTypedArray()) 14 | .directory(workingDir) 15 | .redirectOutput(ProcessBuilder.Redirect.INHERIT) 16 | .redirectError(ProcessBuilder.Redirect.INHERIT) 17 | .start() 18 | .waitFor(60, TimeUnit.MINUTES) 19 | } 20 | 21 | fun getYarnVersion(minecraftVersion: String): String { 22 | val url = URL("https://meta.fabricmc.net/v1/versions/mappings/$minecraftVersion") 23 | val versionJson = url.loadJson().asJsonArray[0].asJsonObject 24 | return versionJson.get("version").asString 25 | } 26 | 27 | fun downloadYarn(yarnVersion: String, file: File) { 28 | URL("https://maven.fabricmc.net/net/fabricmc/yarn/$yarnVersion/yarn-$yarnVersion-tiny.gz").downloadTo(file) 29 | } 30 | 31 | fun getYarnMappings(minecraftVersion: String): Map { 32 | val yarnMavenVersion = getYarnVersion(minecraftVersion) 33 | val yarnZip = File("cache/yarn-$yarnMavenVersion.gz") 34 | if (!yarnZip.exists()) { 35 | downloadYarn(yarnMavenVersion, yarnZip) 36 | } 37 | val tinyMappings = tiny.Mappings() 38 | GZIPInputStream(yarnZip.inputStream()).use { zip -> 39 | var namespaces = listOf() 40 | zip.reader().readLines().forEach { line -> 41 | val parts = line.split("\t") 42 | when (true) { 43 | line.startsWith("v1") -> { 44 | namespaces = parts.subList(2, parts.size) 45 | tinyMappings.namespaces = namespaces.toMutableList() 46 | } 47 | line.startsWith("CLASS\t") -> { 48 | val clazz = tinyMappings.getClass(parts[1]) 49 | parts.forEachIndexed { index, mapped -> 50 | if (index >= 2) { 51 | val namespace = namespaces[index - 2] 52 | if (namespace != "official") { 53 | clazz.add(namespace, mapped) 54 | } 55 | } 56 | } 57 | } 58 | line.startsWith("FIELD\t") -> { 59 | val field = tinyMappings.getField(parts[1], parts[3], parts[2]) 60 | parts.forEachIndexed { index, mapped -> 61 | if (index >= 4) { 62 | val namespace = namespaces[index - 4] 63 | if (namespace != "official") { 64 | field.add(namespace, mapped) 65 | } 66 | } 67 | } 68 | } 69 | line.startsWith("METHOD\t") -> { 70 | val method = tinyMappings.getMethod(parts[1], parts[3], parts[2]) 71 | parts.forEachIndexed { index, mapped -> 72 | if (index >= 4) { 73 | val namespace = namespaces[index - 4] 74 | if (namespace != "official") { 75 | try { 76 | method.add(namespace, mapped) 77 | } catch (e: Exception) { 78 | e.printStackTrace() 79 | println(line) 80 | } 81 | } 82 | } 83 | } 84 | } 85 | } 86 | } 87 | } 88 | tinyMappings.toMappings().forEach { namespace, mappings -> 89 | println("yarn $minecraftVersion $namespace: parsed class=${mappings.classes().size} method=${mappings.fields().size} field=${mappings.methods().size}") 90 | } 91 | return tinyMappings.toMappings() 92 | } 93 | 94 | fun getYarnMappingsFromSubmodule(minecraftVersion: String): Mappings { 95 | val yarnFolder = File("yarn") 96 | val mappingsFile = File("cache/yarn-$minecraftVersion.srg") 97 | if (mappingsFile.exists()) { 98 | println("yarn $minecraftVersion: yarn-$minecraftVersion.srg already exists") 99 | return MappingsFormat.SEARGE_FORMAT.parseFile(mappingsFile) 100 | } 101 | 102 | println("yarn $minecraftVersion: checking out branch $minecraftVersion") 103 | "git checkout $minecraftVersion".runCommand(yarnFolder) 104 | 105 | println("yarn $minecraftVersion: reading mappings directory") 106 | val entryTree = MappingFormat.ENIGMA_DIRECTORY.read(File(yarnFolder, "mappings").toPath(), ProgressListener.VOID) 107 | println("yarn $minecraftVersion: writing mappings to srg") 108 | MappingFormat.SRG_FILE.write(entryTree, mappingsFile.toPath(), ProgressListener.VOID) 109 | 110 | // TODO: fix these 111 | val brokenClasses = listOf( 112 | "", "WoodlandMansionGenerator", 113 | "VoxelSet", "ParticleManager", "PointOfInterestDebugRenderer", "NumberRange", "ServerLightingProvider", 114 | "SpellcastingIllagerEntity", "NetherFortressGenerator", "TextureUtil" 115 | ) 116 | mappingsFile.writeText(mappingsFile.readLines().filter { 117 | brokenClasses.map { b -> !it.contains(b) }.foldRight(true) { a, b -> a && b } 118 | }.joinToString("\n")) 119 | 120 | return MappingsFormat.SEARGE_FORMAT.parseFile(mappingsFile) 121 | } 122 | -------------------------------------------------------------------------------- /src/main/kotlin/tiny/tiny.kt: -------------------------------------------------------------------------------- 1 | package tiny 2 | 3 | import com.google.common.collect.ImmutableBiMap 4 | import net.techcable.srglib.FieldData 5 | import net.techcable.srglib.JavaType 6 | import net.techcable.srglib.MethodData 7 | import net.techcable.srglib.MethodSignature 8 | import net.techcable.srglib.mappings.ImmutableMappings 9 | import net.techcable.srglib.mappings.Mappings 10 | 11 | /** 12 | * Merge multiple srglib.Mappings into one tiny.Mappings 13 | */ 14 | 15 | class Mappings( 16 | var namespaces: MutableList, 17 | val classes: MutableList, 18 | val fields: MutableList, 19 | val methods: MutableList 20 | ) { 21 | 22 | constructor() : this(mutableListOf(), mutableListOf(), mutableListOf(), mutableListOf()) 23 | 24 | fun addMappings(namespace: String, mappings: Mappings) { 25 | namespaces.add(namespace) 26 | println("tiny: starting conversion for $namespace") 27 | mappings.forEachClass { obf, mapped -> 28 | getClass(obf.name).add(namespace, mapped.name) 29 | } 30 | mappings.forEachField { obf, mapped -> 31 | getField(obf.declaringType.name, obf.name, "Lunk;").add(namespace, mapped.name) 32 | } 33 | mappings.forEachMethod { obf, mapped -> 34 | getMethod(obf.declaringType.name, obf.name, obf.signature.descriptor).add(namespace, mapped.name) 35 | } 36 | } 37 | 38 | fun getClass(source: String): ClassMapping { 39 | var maybeClass = classes.firstOrNull { it.source == source } 40 | if (maybeClass == null) { 41 | maybeClass = ClassMapping(source, mutableMapOf()) 42 | classes.add(maybeClass) 43 | } 44 | return maybeClass 45 | } 46 | 47 | fun getField(sourceClass: String, source: String, desc: String): FieldMapping { 48 | var maybeField = fields.firstOrNull { it.sourceClass == sourceClass && it.source == source && it.desc == desc } 49 | if (maybeField == null) { 50 | maybeField = FieldMapping(sourceClass, source, desc, mutableMapOf()) 51 | fields.add(maybeField) 52 | } 53 | return maybeField 54 | } 55 | 56 | fun getMethod(sourceClass: String, source: String, desc: String): MethodMapping { 57 | var maybeMethod = 58 | methods.firstOrNull { it.sourceClass == sourceClass && it.source == source && it.desc == desc } 59 | if (maybeMethod == null) { 60 | maybeMethod = MethodMapping(sourceClass, source, desc, mutableMapOf()) 61 | methods.add(maybeMethod) 62 | } 63 | return maybeMethod 64 | } 65 | 66 | fun toStrings(): List { 67 | val entryMappings = mutableListOf("v1\tofficial\t${namespaces.joinToString("\t")}") 68 | entryMappings.addAll(classes.map { it.toString(namespaces) }) 69 | entryMappings.addAll(fields.map { it.toString(namespaces) }) 70 | entryMappings.addAll(methods.map { it.toString(namespaces) }) 71 | return entryMappings 72 | } 73 | 74 | fun toMappings(): Map = namespaces.map { namespace -> 75 | val classMappings = ImmutableBiMap.copyOf(classes.mapNotNull { 76 | try { 77 | Pair( 78 | JavaType.fromDescriptor("L${it.source};"), 79 | JavaType.fromDescriptor("L${(it[namespace] ?: it.source).replace('/', '.')};") 80 | ) 81 | } catch (e: Exception) { 82 | e.printStackTrace() 83 | null 84 | } 85 | }.toMap()) 86 | val fieldDatas = fields.mapNotNull { 87 | try { 88 | it to FieldData.create(JavaType.fromDescriptor("L${it.sourceClass};"), it.source) 89 | } catch (e: Exception) { 90 | e.printStackTrace() 91 | null 92 | } 93 | } 94 | val methodDatas = methods.mapNotNull { 95 | try { 96 | it to MethodData.create( 97 | JavaType.fromDescriptor("L${it.sourceClass};"), 98 | it.source, 99 | MethodSignature.fromDescriptor(it.desc) 100 | ) 101 | } catch (e: Exception) { 102 | // e.printStackTrace() // yarn data has numbers in it for some reason? 103 | null 104 | } 105 | } 106 | val fieldMappings = ImmutableBiMap.copyOf(fieldDatas.map { (field, fieldData) -> 107 | Pair(fieldData, fieldData.mapTypes { classMappings[it] ?: it }.withName(field[namespace] ?: field.source)) 108 | }.toMap()) 109 | val methodMappings = ImmutableBiMap.copyOf(methodDatas.map { (method, methodData) -> 110 | Pair( 111 | methodData, 112 | methodData.mapTypes { classMappings[it] ?: it }.withName(method[namespace] ?: method.source) 113 | ) 114 | }.toMap()) 115 | val namespace = when (namespace) { 116 | "named" -> "yarn" 117 | else -> namespace 118 | } 119 | namespace to ImmutableMappings.create(classMappings, methodMappings, fieldMappings) 120 | }.toMap() 121 | } 122 | 123 | interface EntryMapping { 124 | val mappings: MutableMap 125 | val source: String 126 | 127 | fun add(namespace: String, value: String): EntryMapping { 128 | mappings[namespace] = value 129 | return this 130 | } 131 | 132 | operator fun get(namespace: String): String? = mappings[namespace] 133 | 134 | fun toString(namespaces: List): String { 135 | val line = namespaces.joinToString("\t") { get(it) ?: source } 136 | val kind = when (this) { 137 | is ClassMapping -> "CLASS" 138 | is FieldMapping -> "FIELD" 139 | is MethodMapping -> "METHOD" 140 | else -> "UNKNOWN" 141 | } 142 | return "$kind\t${toString()}\t$line".replace('.', '/') 143 | } 144 | } 145 | 146 | class ClassMapping( 147 | override val source: String, 148 | override val mappings: MutableMap 149 | ) : EntryMapping { 150 | override fun toString(): String = source 151 | } 152 | 153 | class FieldMapping( 154 | val sourceClass: String, 155 | override val source: String, 156 | val desc: String, 157 | override val mappings: MutableMap 158 | ) : EntryMapping { 159 | override fun toString(): String = "$sourceClass\t$desc\t$source" 160 | } 161 | 162 | class MethodMapping( 163 | val sourceClass: String, 164 | override val source: String, 165 | val desc: String, 166 | override val mappings: MutableMap 167 | ) : EntryMapping { 168 | override fun toString(): String = "$sourceClass\t$desc\t$source" 169 | } 170 | -------------------------------------------------------------------------------- /src/main/kotlin/tsrgUtil.kt: -------------------------------------------------------------------------------- 1 | import java.io.File 2 | 3 | /** 4 | * Various utility functions for dealing with TSRG. 5 | * This code is super ugly. 6 | * 7 | * @author phase 8 | */ 9 | object TSrgUtil { 10 | 11 | // these classes are using data based on the TSRG format, not the SRG format 12 | 13 | data class Clazz( 14 | var obf: String, var deobf: String, 15 | val fields: MutableList = mutableListOf(), 16 | val methods: MutableList = mutableListOf() 17 | ) { 18 | override fun toString(): String = "$obf $deobf" 19 | } 20 | 21 | data class Field(var obf: String, var deobf: String) { 22 | override fun toString(): String = "$obf $deobf" 23 | } 24 | 25 | data class Method(var obf: String, var obfSig: String, var deobf: String) { 26 | override fun toString(): String = "$obf $obfSig $deobf" 27 | 28 | fun getDeobfSig(classNames: Map): String { 29 | // find what classes need to be replaced in the obfuscated string 30 | val classesToReplace = mutableListOf() 31 | var buffer = "" 32 | var state = false 33 | obfSig.forEach { 34 | when (it) { 35 | 'L' -> { 36 | if (!state) { 37 | buffer = "" 38 | state = true 39 | } else { 40 | // the obfSig contains the letter L, like "GLX" 41 | buffer += it 42 | } 43 | } 44 | ';' -> { 45 | classesToReplace.add(buffer) 46 | state = false 47 | } 48 | else -> { 49 | if (state) { 50 | buffer += it 51 | } 52 | } 53 | } 54 | } 55 | 56 | // replace the obfuscated classes 57 | var deobfSig = obfSig 58 | classesToReplace.forEach { obfClassName -> 59 | if (classNames.containsKey(obfClassName)) { 60 | deobfSig = deobfSig.replace("L$obfClassName;", "L${classNames[obfClassName]!!};") 61 | } 62 | } 63 | return deobfSig 64 | } 65 | } 66 | 67 | fun parseTSrg(lines: List): List { 68 | val classes = mutableListOf() 69 | var currentClass: Clazz? = null 70 | 71 | // parse the lines 72 | lines.forEachIndexed { index, line -> 73 | if (line.startsWith("#") || line.trim().isEmpty()) { // comment 74 | } else if (line.startsWith("\t") || line.startsWith(" ")) { 75 | if (currentClass == null) throw RuntimeException("Parse error on line $index: no class\n$line") 76 | val l = line.trim() 77 | val parts = l.split(" ") 78 | when (parts.size) { 79 | 2 -> { 80 | // field 81 | val obf = parts[0] 82 | val deobf = parts[1] 83 | currentClass!!.fields.add(Field(obf, deobf)) 84 | } 85 | 3 -> { 86 | // method 87 | val obf = parts[0] 88 | val obfSig = parts[1] 89 | val deobf = parts[2] 90 | currentClass!!.methods.add(Method(obf, obfSig, deobf)) 91 | } 92 | else -> throw RuntimeException("Parse error on line $index: too many parts\n$line") 93 | } 94 | } else if (line.contains(" ")) { 95 | currentClass?.let { classes.add(it) } 96 | val parts = line.split(" ") 97 | when (parts.size) { 98 | 2 -> { 99 | // class 100 | val obf = parts[0] 101 | val deobf = parts[1] 102 | currentClass = Clazz(obf, deobf) 103 | } 104 | else -> throw RuntimeException("Parse error on line $index: class definition has too many parts\n$line") 105 | } 106 | } 107 | } 108 | currentClass?.let { 109 | if (!classes.contains(it)) classes.add(it) 110 | } 111 | return classes 112 | } 113 | 114 | fun toSrg(classes: List, srgFile: File) { 115 | val classNames = classes.map { it.obf to it.deobf }.toMap() 116 | val output = StringBuilder() 117 | // write the classes out in SRG format 118 | classes.forEach { clazz -> 119 | if (clazz.obf != clazz.deobf) { 120 | output.append("CL: ${clazz.obf} ${clazz.deobf}\n") 121 | } 122 | 123 | clazz.fields.forEach { field -> 124 | output.append("FD: ${clazz.obf}/${field.obf} ${clazz.deobf}/${field.deobf}\n") 125 | } 126 | 127 | clazz.methods.forEach { method -> 128 | val deobfSig = method.getDeobfSig(classNames) 129 | output.append( 130 | "MD: ${clazz.obf}/${method.obf} ${method.obfSig} " + 131 | "${clazz.deobf}/${method.deobf} $deobfSig\n" 132 | ) 133 | } 134 | } 135 | srgFile.writeText(output.toString().split("\n").sorted().filter { it.isNotEmpty() }.joinToString("\n")) 136 | } 137 | 138 | fun toSrg(tsrgFile: File, srgFile: File): List { 139 | // checks 140 | if (!(srgFile.exists())) srgFile.createNewFile() 141 | if (srgFile.exists() && !srgFile.isFile) throw RuntimeException("srg path is not a file: $srgFile") 142 | if (!tsrgFile.exists() || !tsrgFile.isFile) throw RuntimeException("tsrg file not found: $tsrgFile") 143 | 144 | val classes = parseTSrg(tsrgFile.readLines()) 145 | 146 | toSrg(classes, srgFile) 147 | return classes 148 | } 149 | 150 | fun fromSrg(srgFile: File, tsrgFile: File) { 151 | // checks 152 | if (!(tsrgFile.exists())) tsrgFile.createNewFile() 153 | if (tsrgFile.exists() && !tsrgFile.isFile) throw RuntimeException("tsrg path is not a file: $tsrgFile") 154 | if (!srgFile.exists() || !srgFile.isFile) throw RuntimeException("srg file not found: $srgFile") 155 | 156 | val lines = srgFile.readLines() 157 | val classes = mutableListOf() 158 | 159 | lines.forEach { line -> 160 | when (true) { 161 | line.startsWith("CL: ") -> { 162 | val l = line.substring(4, line.length) 163 | val parts = l.split(" ") 164 | val obf = parts[0] 165 | val deobf = parts[1] 166 | if (!classes.map { it.obf }.contains(obf)) { 167 | classes.add(Clazz(obf, deobf)) 168 | } 169 | } 170 | line.startsWith("FD: ") -> { 171 | val l = line.substring(4, line.length) 172 | val parts = l.split(" ") 173 | 174 | // obf part 175 | val p0 = parts[0] 176 | val p0s = p0.lastIndexOf('/') 177 | val obfClass = p0.substring(0, p0s) 178 | val obf = p0.substring(p0s + 1, p0.length) 179 | 180 | // deobf part 181 | val p1 = parts[1] 182 | val p1s = p1.lastIndexOf('/') 183 | val deobfClass = p1.substring(0, p1s) 184 | val deobf = p1.substring(p1s + 1, p1.length) 185 | 186 | val eligibleClasses = classes.filter { it.obf == obfClass && it.deobf == deobfClass } 187 | if (eligibleClasses.isNotEmpty()) { 188 | eligibleClasses.last().fields.add(Field(obf, deobf)) 189 | } else { 190 | // this *shouldn't* happen but just in case the ordering of the mappings is weird we will 191 | // add the class to the map 192 | val newClass = Clazz(obfClass, deobfClass, mutableListOf(Field(obf, deobf))) 193 | classes.add(newClass) 194 | } 195 | } 196 | line.startsWith("MD: ") -> { 197 | val l = line.substring(4, line.length) 198 | val parts = l.split(" ") 199 | 200 | // obf part 201 | val p0 = parts[0] 202 | val p0s = p0.lastIndexOf('/') 203 | val obfClass = p0.substring(0, p0s) 204 | val obf = p0.substring(p0s + 1, p0.length) 205 | 206 | val obfSig = parts[1] 207 | 208 | // deobf part 209 | val p2 = parts[2] 210 | val p2s = p2.lastIndexOf('/') 211 | val deobfClass = p2.substring(0, p2s) 212 | val deobf = p2.substring(p2s + 1, p2.length) 213 | 214 | val eligibleClasses = classes.filter { it.obf == obfClass && it.deobf == deobfClass } 215 | if (eligibleClasses.isNotEmpty()) { 216 | eligibleClasses.last().methods.add(Method(obf, obfSig, deobf)) 217 | } else { 218 | val newClass = 219 | Clazz(obfClass, deobfClass, mutableListOf(), mutableListOf(Method(obf, obfSig, deobf))) 220 | classes.add(newClass) 221 | } 222 | } 223 | } 224 | } 225 | 226 | val output = StringBuilder() 227 | classes.forEach { clazz -> 228 | output.append("$clazz\n") 229 | clazz.fields.forEach { 230 | output.append("\t$it\n") 231 | } 232 | clazz.methods.forEach { 233 | output.append("\t$it\n") 234 | } 235 | } 236 | tsrgFile.writeText(output.toString()) 237 | } 238 | 239 | } 240 | 241 | object MappingsGenerator { 242 | 243 | private data class ClassMapping(val deobf: String, var clientObf: String? = null, var serverObf: String? = null) { 244 | override fun toString(): String = "$deobf (client: $clientObf) (server: $serverObf)" 245 | } 246 | 247 | private data class FieldMapping( 248 | val deobfField: String, 249 | val deobfClass: String, 250 | var clientObfTotal: String? = null, 251 | var serverObfTotal: String? = null 252 | ) { 253 | override fun toString(): String = "$deobfClass.$deobfField (client: $clientObfTotal) (server: $serverObfTotal)" 254 | } 255 | 256 | private inline fun unquote(s: String): String = s.substring(1, s.length - 1) 257 | 258 | /** 259 | * @param classFile CSV file containing MCP v4.3 mappings 260 | * @return Pair(serverOnlyClassesObf, map from serverObf to clientObf) 261 | */ 262 | fun generateClassMappings(classFile: File): Pair, Map> { 263 | val classNames = classFile.readLines().toMutableList() 264 | classNames.removeAt(0) // remove column definition 265 | val classes = mutableListOf() 266 | 267 | classNames.forEach { 268 | val parts = it.split(",") 269 | val deobf = unquote(parts[0]) 270 | val obf = unquote(parts[1]) 271 | val isClient = unquote(parts[4]).toInt() == 0 272 | 273 | var foundClass = false 274 | classes.forEach { 275 | if (it.deobf == deobf && (it.clientObf == null || it.serverObf == null)) { 276 | foundClass = true 277 | if (isClient) it.clientObf = obf else it.serverObf = obf 278 | } 279 | } 280 | 281 | if (!foundClass) { 282 | if (isClient) { 283 | classes.add(ClassMapping(deobf, obf, null)) 284 | } else { 285 | classes.add(ClassMapping(deobf, null, obf)) 286 | } 287 | } 288 | 289 | } 290 | 291 | return Pair( 292 | classes.filter { it.clientObf == null && it.serverObf != null }.map { it.serverObf!! }, 293 | classes.filter { it.clientObf != null && it.serverObf != null }.map { it.serverObf!! to it.clientObf!! }.toMap() 294 | ) 295 | } 296 | 297 | /** 298 | * @param fieldFile CSV file containing MCP v4.3 mappings 299 | * @return TODO 300 | */ 301 | fun generateFieldMappings(fieldFile: File): Map { 302 | val fieldNames = fieldFile.readLines().toMutableList() 303 | fieldNames.removeAt(0) // remove column definition 304 | val fields = mutableListOf() 305 | 306 | fieldNames.forEach { 307 | val parts = it.split(",") 308 | val deobfField = unquote(parts[1]) 309 | val deobfClass = unquote(parts[5]) 310 | val obfField = unquote(parts[2]) 311 | val obfClass = unquote(parts[6]) 312 | val obf = "$obfClass.$obfField" 313 | val isClient = unquote(parts[8]).toInt() == 0 314 | 315 | var foundField = false 316 | fields.forEach { 317 | if (it.deobfClass == deobfClass && it.deobfField == deobfField) { 318 | foundField = true 319 | if (isClient) it.clientObfTotal = obf else it.serverObfTotal = obf 320 | } 321 | } 322 | 323 | if (!foundField) { 324 | if (isClient) { 325 | fields.add(FieldMapping(deobfField, deobfClass, obf, null)) 326 | } else { 327 | fields.add(FieldMapping(deobfField, deobfClass, null, obf)) 328 | } 329 | } 330 | } 331 | 332 | return fields.filter { 333 | it.clientObfTotal != null && it.serverObfTotal != null 334 | && it.clientObfTotal!!.split(".")[1] != 335 | it.serverObfTotal!!.split(".")[1] 336 | }.map { 337 | it.serverObfTotal!! to it.clientObfTotal!! 338 | }.toMap().toSortedMap() 339 | } 340 | 341 | fun generateMethodMappings(methodFile: File): Map { 342 | val methodNames = methodFile.readLines().toMutableList() 343 | methodNames.removeAt(0) // remove column definition 344 | val methods = mutableListOf() 345 | 346 | methodNames.forEach { 347 | val parts = it.split(",") 348 | val deobfName = unquote(parts[1]) 349 | val deobfClass = unquote(parts[5]) 350 | val obfName = unquote(parts[2]) 351 | val sig = unquote(parts[4]) 352 | val obfClass = unquote(parts[6]) 353 | val obf = "$obfClass.$obfName.$sig" 354 | val isClient = unquote(parts[8]).toInt() == 0 355 | 356 | var foundField = false 357 | methods.forEach { 358 | if (it.deobfClass == deobfClass && it.deobfField == deobfName) { 359 | foundField = true 360 | if (isClient) it.clientObfTotal = obf else it.serverObfTotal = obf 361 | } 362 | } 363 | 364 | if (!foundField) { 365 | if (isClient) { 366 | methods.add(FieldMapping(deobfName, deobfClass, obf, null)) 367 | } else { 368 | methods.add(FieldMapping(deobfName, deobfClass, null, obf)) 369 | } 370 | } 371 | } 372 | 373 | return methods.filter { 374 | it.clientObfTotal != null && it.serverObfTotal != null 375 | && it.clientObfTotal!!.split(".")[1] != 376 | it.serverObfTotal!!.split(".")[1] 377 | }.map { 378 | it.serverObfTotal!! to it.clientObfTotal!! 379 | }.toMap().toSortedMap() 380 | } 381 | 382 | } 383 | -------------------------------------------------------------------------------- /src/main/kotlin/version.kt: -------------------------------------------------------------------------------- 1 | import com.google.common.collect.ImmutableList 2 | import com.google.common.collect.MultimapBuilder 3 | import com.google.gson.Gson 4 | import com.google.gson.JsonArray 5 | import com.google.gson.JsonObject 6 | import net.techcable.srglib.FieldData 7 | import net.techcable.srglib.JavaType 8 | import net.techcable.srglib.MethodData 9 | import net.techcable.srglib.format.MappingsFormat 10 | import net.techcable.srglib.mappings.Mappings 11 | import provider.* 12 | import java.io.File 13 | 14 | enum class MinecraftVersion( 15 | val mcVersion: String, 16 | val mcpVersion: String? = null, 17 | val mcpConfig: Boolean = false, 18 | val spigot: Boolean = false, 19 | val yarn: Boolean = false, 20 | val mojang: Boolean = false 21 | ) { 22 | V1_15_2("1.15.2", "snapshot_20200515", true, true, true, true), 23 | V1_15_1("1.15.1", "snapshot_20191217", true, true, true, true), 24 | V1_15("1.15", "snapshot_nodoc_20191212", true, true, true, true), 25 | V1_14_4("1.14.4", "snapshot_nodoc_20190729", true, true, true, true), 26 | V1_14_3("1.14.3", "snapshot_nodoc_20190729", true, true, true), 27 | V1_14_2("1.14.2", "stable_nodoc_53", false, true, true), 28 | V1_14_1("1.14.1", "stable_nodoc_51", false, true, true), 29 | V1_14("1.14", "stable_nodoc_49", false, true, true), 30 | V1_13_2("1.13.2", "stable_nodoc_47", false, true, false), 31 | V1_13_1("1.13.1", "stable_nodoc_45", false, true, false), 32 | V1_13("1.13", "stable_nodoc_43", false, true, false), 33 | V1_12_2("1.12.2", "snapshot_nodoc_20180129", false, true, false), 34 | V1_12("1.12", "snapshot_nodoc_20180814", false, true, false), 35 | V1_11("1.11", "snapshot_nodoc_20170612", false, true, false), 36 | V1_10_2("1.10.2", "snapshot_nodoc_20160703", false, true, false), 37 | V1_9_4("1.9.4", "snapshot_nodoc_20160604", false, true, false), 38 | V1_9("1.9", "snapshot_nodoc_20160516", false, true, false), 39 | V1_8_9("1.8.9", "snapshot_nodoc_20160301", false, false, false), 40 | V1_8_8("1.8.8", "snapshot_nodoc_20151216", false, true, false), 41 | V1_8("1.8", "snapshot_nodoc_20141130", false, true, false), 42 | V1_7_10("1.7.10", "snapshot_nodoc_20140925", false, false, false); 43 | 44 | fun generateMappings(): List> { 45 | // Mappings, fromObf 46 | val mappings = mutableListOf>() 47 | 48 | if (mcpVersion != null) { 49 | val obf2srgMappings = if (mcpConfig) { 50 | getMCPConfigMappings(mcVersion) 51 | } else { 52 | downloadSrgMappings(mcVersion) 53 | } 54 | val srg2mcpMappings = downloadMcpMappings(obf2srgMappings, mcpVersion) 55 | val obf2mcp = Mappings.chain(ImmutableList.of(obf2srgMappings, srg2mcpMappings)) 56 | mappings.add(Pair(obf2srgMappings, "srg")) 57 | mappings.add(Pair(obf2mcp, "mcp")) 58 | } 59 | if (spigot) { 60 | val buildDataCommit = getBuildDataCommit(mcVersion) 61 | val obf2spigotMappings = downloadSpigotMappings(buildDataCommit) 62 | mappings.add(Pair(obf2spigotMappings, "spigot")) 63 | } 64 | if (yarn) { 65 | val obf2yarnMappingsSet = getYarnMappings(mcVersion) 66 | obf2yarnMappingsSet.forEach { id, m -> mappings.add(Pair(m, id)) } 67 | } 68 | if (mojang) { 69 | val obf2mojangMappingSet = MojangMappings.getMappings(mcVersion) 70 | mappings.add(Pair(obf2mojangMappingSet, "mojang")) 71 | } 72 | 73 | val completeMappings = mutableListOf>() 74 | for (a in mappings) { 75 | val obf2aMappings = a.first 76 | val a2obfMappings = obf2aMappings.inverted() 77 | 78 | completeMappings.add(Pair("obf2${a.second}", obf2aMappings)) 79 | completeMappings.add(Pair("${a.second}2obf", a2obfMappings)) 80 | for (b in mappings) { 81 | if (a != b) { 82 | val a2bMappings = Mappings.chain(a2obfMappings, b.first) 83 | completeMappings.add(Pair("${a.second}2${b.second}", a2bMappings)) 84 | } 85 | } 86 | } 87 | return completeMappings 88 | } 89 | 90 | fun write(mappingsFolder: File) { 91 | val outputFolder = File(mappingsFolder, mcVersion) 92 | outputFolder.mkdirs() 93 | 94 | fun Mappings.writeTo(fileName: String) { 95 | println("$mcVersion: writing mappings to $fileName.srg") 96 | val strippedMappings = stripDuplicates(this) 97 | val srgLines = MappingsFormat.SEARGE_FORMAT.toLines(strippedMappings) 98 | srgLines.sort() 99 | val file = File(outputFolder, "$fileName.srg") 100 | file.bufferedWriter().use { 101 | for (line in srgLines) { 102 | it.write(line) 103 | it.write("\n") 104 | } 105 | } 106 | 107 | println("$mcVersion: writing mappings to $fileName.csrg") 108 | val csrgLines = MappingsFormat.COMPACT_SEARGE_FORMAT.toLines(strippedMappings) 109 | csrgLines.sort() 110 | File(outputFolder, "$fileName.csrg").bufferedWriter().use { 111 | for (line in csrgLines) { 112 | it.write(line) 113 | it.write("\n") 114 | } 115 | } 116 | 117 | println("$mcVersion: writing mappings to $fileName.tsrg") 118 | TSrgUtil.fromSrg(file, File(outputFolder, "$fileName.tsrg")) 119 | } 120 | 121 | // srg & tsrg 122 | val generatedMappings = generateMappings() 123 | generatedMappings.forEach { pair -> 124 | val fileName = pair.first 125 | val mappings = pair.second 126 | mappings.writeTo(fileName) 127 | } 128 | 129 | // tiny 130 | println("$mcVersion: writing tiny mappings to $mcVersion.tiny") 131 | val tinyMappings = tiny.Mappings() 132 | generatedMappings.filter { it.first.startsWith("obf2") }.forEach { pair -> 133 | val name = pair.first.split("2")[1] 134 | 135 | tinyMappings.addMappings(name, pair.second) 136 | } 137 | File(outputFolder, "$mcVersion.tiny").bufferedWriter().use { 138 | for (line in tinyMappings.toStrings()) { 139 | it.write(line) 140 | it.write("\n") 141 | } 142 | } 143 | 144 | // json 145 | val classMappings = 146 | MultimapBuilder.hashKeys(1000).arrayListValues().build>() 147 | val fieldMappings = 148 | MultimapBuilder.hashKeys(1000).arrayListValues().build>() 149 | val methodMappings = 150 | MultimapBuilder.hashKeys(1000).arrayListValues().build>() 151 | generatedMappings.filter { it.first.startsWith("obf2") }.forEach { pair -> 152 | val name = pair.first.split("2")[1] 153 | val mappings = pair.second 154 | mappings.forEachClass { obf, mapped -> classMappings.put(obf, Pair(name, mapped)) } 155 | mappings.forEachField { obf, mapped -> fieldMappings.put(obf, Pair(name, mapped)) } 156 | mappings.forEachMethod { obf, mapped -> methodMappings.put(obf, Pair(name, mapped)) } 157 | println("$mcVersion: generating json for $name") 158 | } 159 | 160 | fun String.lp(): String = split(".").last() 161 | 162 | val classArray = JsonArray() 163 | val fieldArray = JsonArray() 164 | val methodArray = JsonArray() 165 | for (obf in classMappings.keySet()) { 166 | val mappedObj = JsonObject() 167 | mappedObj.addProperty("obf", obf.name.lp()) 168 | classMappings.get(obf).forEach { 169 | mappedObj.addProperty(it.first, it.second.name.lp()) 170 | } 171 | classArray.add(mappedObj) 172 | } 173 | for (obf in fieldMappings.keySet()) { 174 | val mappedObj = JsonObject() 175 | mappedObj.addProperty("obf", obf.declaringType.name.lp() + "." + obf.name.lp()) 176 | fieldMappings.get(obf).forEach { 177 | mappedObj.addProperty(it.first, it.second.declaringType.name.lp() + "." + it.second.name) 178 | } 179 | fieldArray.add(mappedObj) 180 | } 181 | for (obf in methodMappings.keySet()) { 182 | val mappedObj = JsonObject() 183 | mappedObj.addProperty("obf", obf.declaringType.name.lp() + "." + obf.name.lp()) 184 | methodMappings.get(obf).forEach { 185 | mappedObj.addProperty(it.first, it.second.declaringType.name.lp() + "." + it.second.name) 186 | } 187 | methodArray.add(mappedObj) 188 | } 189 | 190 | val bigJson = JsonObject() 191 | bigJson.addProperty("minecraftVersion", mcVersion) 192 | bigJson.add("classes", classArray) 193 | bigJson.add("fields", fieldArray) 194 | bigJson.add("methods", methodArray) 195 | File(outputFolder, "$mcVersion.json").writeText(Gson().toJson(bigJson)) 196 | } 197 | } 198 | -------------------------------------------------------------------------------- /viewer/.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | /dist 4 | 5 | # local env files 6 | .env.local 7 | .env.*.local 8 | 9 | # Log files 10 | npm-debug.log* 11 | yarn-debug.log* 12 | yarn-error.log* 13 | 14 | # Editor directories and files 15 | .idea 16 | .vscode 17 | *.suo 18 | *.ntvs* 19 | *.njsproj 20 | *.sln 21 | *.sw? 22 | -------------------------------------------------------------------------------- /viewer/README.md: -------------------------------------------------------------------------------- 1 | # viewer 2 | 3 | ## Project setup 4 | ``` 5 | yarn install 6 | ``` 7 | 8 | ### Compiles and hot-reloads for development 9 | ``` 10 | yarn run serve 11 | ``` 12 | 13 | ### Compiles and minifies for production 14 | ``` 15 | yarn run build 16 | ``` 17 | 18 | ### Run your tests 19 | ``` 20 | yarn run test 21 | ``` 22 | 23 | ### Lints and fixes files 24 | ``` 25 | yarn run lint 26 | ``` 27 | 28 | ### Customize configuration 29 | See [Configuration Reference](https://cli.vuejs.org/config/). 30 | -------------------------------------------------------------------------------- /viewer/babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: [ 3 | '@vue/app' 4 | ] 5 | } 6 | -------------------------------------------------------------------------------- /viewer/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "viewer", 3 | "version": "0.1.0", 4 | "private": true, 5 | "scripts": { 6 | "serve": "vue-cli-service serve", 7 | "build": "vue-cli-service build" 8 | }, 9 | "dependencies": { 10 | "core-js": "^2.6.5", 11 | "fuzzaldrin-plus": "^0.6.0", 12 | "vue": "^2.6.10", 13 | "vue-router": "^3.0.3" 14 | }, 15 | "devDependencies": { 16 | "@vue/cli-plugin-babel": "^3.7.0", 17 | "@vue/cli-service": "^3.7.0", 18 | "vue-template-compiler": "^2.5.21" 19 | }, 20 | "postcss": { 21 | "plugins": { 22 | "autoprefixer": {} 23 | } 24 | }, 25 | "browserslist": [ 26 | "> 1%", 27 | "last 2 versions" 28 | ], 29 | "gitHooks": { 30 | "pre-commit": "lint-staged" 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /viewer/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/phase/MinecraftMappings/411df6765ac0902fd05659398e6986e72ace3161/viewer/public/favicon.ico -------------------------------------------------------------------------------- /viewer/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | viewer 9 | 10 | 11 | 14 |
15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /viewer/src/App.vue: -------------------------------------------------------------------------------- 1 | 24 | 82 | 114 | -------------------------------------------------------------------------------- /viewer/src/main.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import App from './App.vue' 3 | import router from './router' 4 | 5 | Vue.config.productionTip = false 6 | 7 | new Vue({ 8 | router, 9 | render: h => h(App) 10 | }).$mount('#app') 11 | -------------------------------------------------------------------------------- /viewer/src/router.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import Router from 'vue-router' 3 | 4 | Vue.use(Router) 5 | 6 | export default new Router({ 7 | mode: 'history', 8 | base: process.env.BASE_URL, 9 | routes: [] 10 | }) 11 | --------------------------------------------------------------------------------