Asm mixins that "work"

This commit is contained in:
ThatGamerBlue
2019-05-16 14:36:19 +01:00
parent ada157ae9d
commit c9d7fc4c23
80 changed files with 39711 additions and 1759 deletions

View File

@@ -0,0 +1,65 @@
/*
* Copyright (c) 2019, ThatGamerBlue <thatgamerblue@gmail.com>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package us.runelitepl.mixinprocessor;
public class MethodGarbageValue
{
private final String type;
private final int value;
public MethodGarbageValue(int value)
{
if(value <= Byte.MAX_VALUE && value >= Byte.MIN_VALUE)
{
type = "B";
}
else if(value <= Short.MAX_VALUE && value >= Short.MIN_VALUE)
{
type = "S";
}
else
{
type = "I";
}
this.value = value;
}
public int getValue()
{
return value;
}
public String getType()
{
return type;
}
public String toString()
{
return "MethodGarbageValue[type="+getType()+",value="+getValue()+"]";
}
}

View File

@@ -0,0 +1,281 @@
/*
* Copyright (c) 2019, ThatGamerBlue <thatgamerblue@gmail.com>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package us.runelitepl.mixinprocessor;
import com.google.common.collect.ImmutableSet;
import com.google.common.io.ByteStreams;
import com.google.common.reflect.ClassPath;
import io.sigpipe.jbsdiff.Patch;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.LoaderClassPath;
import org.objectweb.asm.AnnotationVisitor;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.FieldVisitor;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
import us.runelitepl.mixinprocessor.generators.AnnotationProcessor;
import us.runelitepl.mixinprocessor.generators.PatchGenerator;
import us.runelitepl.mixinprocessor.generators.StaticGenerator;
import us.runelitepl.mixinprocessor.generators.StaticStageTwoGenerator;
import us.runelitepl.mixinprocessor.parsers.GamepackDownloader;
import us.runelitepl.mixinprocessor.parsers.HooksParser;
import us.runelitepl.mixinprocessor.transformers.AsmMethodGarbageTransformer;
import us.runelitepl.mixinprocessor.transformers.AsmMethodSignatureTransformer;
import us.runelitepl.mixinprocessor.util.JavassistUtils;
import us.runelitepl.mixinprocessor.util.RefUtils;
import org.apache.maven.plugin.AbstractMojo;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugin.MojoFailureException;
import org.apache.maven.plugins.annotations.LifecyclePhase;
import org.apache.maven.plugins.annotations.Mojo;
import org.apache.maven.plugins.annotations.Parameter;
import org.apache.maven.plugins.annotations.ResolutionScope;
import org.apache.maven.project.MavenProject;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.net.URLClassLoader;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* Goal which touches a timestamp file.
*/
@Mojo(name = "process-mixins", defaultPhase = LifecyclePhase.PROCESS_CLASSES, requiresDependencyResolution =
ResolutionScope.COMPILE)
public class MixinProcessorMojo
extends AbstractMojo implements Opcodes
{
private static final String PATCHES_PACKAGE = "us.runelitepl.mixins";
/**
* Location of the file.
*/
@Parameter(defaultValue = "${project.build.directory}", property = "outputDir", required = true)
private File outputDirectory;
@Parameter(defaultValue = "${project}", required = true, readonly = true)
private MavenProject project;
@Parameter(property = "process-mixins.hooks", defaultValue = "hooks.json", required = true)
private String hooks;
@Parameter(property = "process-mixins.ops", defaultValue = "gamepack.deob.jar.op.json", required = true)
private String ops;
private static MixinProcessorMojo INST;
public static ClassPool classPool;
public static HashMap<String, Long> fieldDecoders = new HashMap<>();
public static HashMap<String, String> classNames = new HashMap<>();
public static HashMap<String, String> methodNames = new HashMap<>();
public static HashMap<String, String> fieldNames = new HashMap<>();
public static HashMap<String, Boolean> isFieldTagged = new HashMap<>();
public static HashMap<String, Boolean> isMethodTagged = new HashMap<>();
public static HashMap<String, MethodGarbageValue> methodGarbageValues = new HashMap<>();
public static HashMap<String, byte[]> gamepack = new HashMap<>();
public static final int BUFFER_SIZE = 1024 * 1024 * 4;
public void execute()
throws MojoExecutionException
{
INST = this;
try
{
List runtimeClasspathElements = project.getRuntimeClasspathElements();
URL[] runtimeUrls = new URL[runtimeClasspathElements.size()];
for (int i = 0; i < runtimeClasspathElements.size(); i++)
{
String element = (String) runtimeClasspathElements.get(i);
runtimeUrls[i] = new File(element).toURI().toURL();
}
URLClassLoader classLoader = new URLClassLoader(runtimeUrls,
Thread.currentThread().getContextClassLoader());
File outputFolder = new File(project.getBuild().getOutputDirectory());
File projectDir = new File(System.getProperty("user.dir"));
File hooksFile = new File(projectDir, hooks);
File opsFile = new File(projectDir, ops);
outputFolder.mkdirs();
GamepackDownloader.downloadGamepack(gamepack);
ByteArrayOutputStream patchOutputStream = new ByteArrayOutputStream(BUFFER_SIZE); // 4mb
HooksParser.run(hooksFile, opsFile);
classPool = new ClassPool();
classPool.appendClassPath((new LoaderClassPath(classLoader)));
for (Map.Entry<String, byte[]> entry : gamepack.entrySet())
{
byte[] b1_;
try (InputStream is = getClass().getResourceAsStream("/patch/" + entry.getKey() + ".class.bs"))
{
if (is == null)
{
stderr("IS null for %s", entry.getKey());
continue;
}
b1_ = ByteStreams.toByteArray(is);
}
patchOutputStream.reset();
Patch.patch(entry.getValue(), b1_, patchOutputStream);
entry.setValue(patchOutputStream.toByteArray());
classPool.makeClass(new ByteArrayInputStream(entry.getValue()));
}
HashMap<String, byte[]> finalClasses = new HashMap<>();
ImmutableSet<ClassPath.ClassInfo> classes =
ClassPath.from(classLoader).getTopLevelClassesRecursive(PATCHES_PACKAGE);
for (ClassPath.ClassInfo clazz : classes)
{
stderr("");
stderr("Annotations: %s", clazz.getSimpleName());
CtClass ctClass = classPool.get(clazz.getName());
String className = ctClass.getSimpleName();
className = RefUtils.getObbedClassName(className);
byte[] finalCode = JavassistUtils.getClassBytecode(ctClass);
finalCode = new AnnotationProcessor(className, finalCode).run();
finalClasses.put(className, finalCode);
}
for (Map.Entry<String, byte[]> entry : finalClasses.entrySet())
{
String className = entry.getKey();
byte[] finalCode = entry.getValue();
stderr("");
stderr("Pass 1: %s", className);
finalCode = new AsmMethodSignatureTransformer(className, finalCode).transform();
finalCode = new AsmMethodGarbageTransformer(className, finalCode, finalClasses).transform();
finalClasses.put(className, finalCode);
}
stderr("");
stderr("Pass Statics");
new StaticGenerator().run(finalClasses.get(RefUtils.STATICS_STRING));
new StaticStageTwoGenerator(finalClasses).run();
for (Map.Entry<String, byte[]> entry : finalClasses.entrySet())
{
String className = entry.getKey();
if (className == null)
{
stderr("Class name null? %s", entry.getValue().length);
continue;
}
if (className.endsWith(RefUtils.STATICS_STRING))
{
continue;
}
stderr("");
stderr("Pass 2: %s", className);
byte[] finalCode = entry.getValue();
finalCode = new PatchGenerator(className, finalCode).run();
entry.setValue(finalCode);
}
deleteDir(new File(outputFolder, "us/runelitepl/mixins"));
outputFolder = new File(outputFolder, "extended-mixins");
outputFolder.mkdirs();
for (Map.Entry<String, byte[]> entry : finalClasses.entrySet())
{
if (entry.getKey().contains(RefUtils.STATICS_STRING))
{
continue;
}
Files.write(new File(outputFolder, entry.getKey() + ".class").toPath(),
entry.getValue());
}
}
catch (Exception ex)
{
ex.printStackTrace();
throw new MojoExecutionException(ex.getMessage());
}
}
public void stderr(String s, Object... format)
{
getLog().info(String.format(s, format));
}
public static void log(String s, Object... format)
{
INST.stderr(s, format);
}
static void deleteDir(File file) throws IOException
{
if (!file.exists())
{
return;
}
Files.walk(file.toPath())
.sorted(Comparator.reverseOrder())
.map(Path::toFile)
.forEach(File::delete);
}
}

View File

@@ -0,0 +1,34 @@
/*
* Copyright (c) 2019, ThatGamerBlue <thatgamerblue@gmail.com>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package us.runelitepl.mixinprocessor.annotations;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@Retention(RetentionPolicy.RUNTIME)
public @interface Append
{
}

View File

@@ -0,0 +1,34 @@
/*
* Copyright (c) 2019, ThatGamerBlue <thatgamerblue@gmail.com>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package us.runelitepl.mixinprocessor.annotations;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@Retention(RetentionPolicy.RUNTIME)
public @interface Inject
{
}

View File

@@ -0,0 +1,34 @@
/*
* Copyright (c) 2019, ThatGamerBlue <thatgamerblue@gmail.com>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package us.runelitepl.mixinprocessor.annotations;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@Retention(RetentionPolicy.RUNTIME)
public @interface Overwrite
{
}

View File

@@ -0,0 +1,34 @@
/*
* Copyright (c) 2019, ThatGamerBlue <thatgamerblue@gmail.com>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package us.runelitepl.mixinprocessor.annotations;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@Retention(RetentionPolicy.RUNTIME)
public @interface Prepend
{
}

View File

@@ -0,0 +1,34 @@
/*
* Copyright (c) 2019, ThatGamerBlue <thatgamerblue@gmail.com>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package us.runelitepl.mixinprocessor.annotations;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@Retention(RetentionPolicy.RUNTIME)
public @interface Provided
{
}

View File

@@ -0,0 +1,34 @@
/*
* Copyright (c) 2019, ThatGamerBlue <thatgamerblue@gmail.com>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package us.runelitepl.mixinprocessor.annotations;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@Retention(RetentionPolicy.RUNTIME)
public @interface Reobfuscate
{
}

View File

@@ -0,0 +1,35 @@
/*
* Copyright (c) 2019, ThatGamerBlue <thatgamerblue@gmail.com>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package us.runelitepl.mixinprocessor.enums;
public enum InjectionType
{
INJECT,
APPEND,
OVERWRITE,
PREPEND,
PROVIDED;
}

View File

@@ -0,0 +1,62 @@
/*
* Copyright (c) 2019, ThatGamerBlue <thatgamerblue@gmail.com>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package us.runelitepl.mixinprocessor.generators;
import javassist.CannotCompileException;
import javassist.CtClass;
import us.runelitepl.mixinprocessor.MixinProcessorMojo;
import us.runelitepl.mixinprocessor.parsers.FieldAnnotationParser;
import us.runelitepl.mixinprocessor.parsers.MethodAnnotationParser;
import us.runelitepl.mixinprocessor.util.JavassistUtils;
import java.io.ByteArrayInputStream;
import java.io.IOException;
public class AnnotationProcessor
{
private final String className;
private final byte[] finalCode;
public AnnotationProcessor(String className, byte[] finalCode)
{
this.className = className;
this.finalCode = finalCode;
}
public byte[] run() throws IOException, CannotCompileException, ClassNotFoundException
{
byte[] newCode = finalCode;
CtClass clazz = MixinProcessorMojo.classPool.makeClass(new ByteArrayInputStream(newCode));
new FieldAnnotationParser(clazz).run();
new MethodAnnotationParser(clazz).run();
newCode = JavassistUtils.getClassBytecode(clazz);
return newCode;
}
}

View File

@@ -0,0 +1,66 @@
/*
* Copyright (c) 2019, ThatGamerBlue <thatgamerblue@gmail.com>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package us.runelitepl.mixinprocessor.generators;
import us.runelitepl.mixinprocessor.transformers.AnnotationRemoverTransformer;
import us.runelitepl.mixinprocessor.transformers.AsmMethodGarbageTransformer;
import us.runelitepl.mixinprocessor.transformers.AsmMethodSignatureTransformer;
import us.runelitepl.mixinprocessor.transformers.AsmNameTransformer;
import us.runelitepl.mixinprocessor.transformers.AsmStaticUsageTransformer;
import us.runelitepl.mixinprocessor.transformers.DoNothingTransformer;
import us.runelitepl.mixinprocessor.transformers.GetFieldDecoderTransformer;
import us.runelitepl.mixinprocessor.transformers.ProvidedRemoverTransformer;
public class PatchGenerator
{
private final String className;
private final byte[] bytecode;
public PatchGenerator(String className, byte[] bytecode)
{
this.className = className;
this.bytecode = bytecode;
}
public byte[] run()
{
byte[] newCode = bytecode;
newCode = new GetFieldDecoderTransformer(className, newCode).transform();
// https://asm.ow2.io/javadoc/org/objectweb/asm/commons/Remapper.html
// https://asm.ow2.io/javadoc/org/objectweb/asm/commons/ClassRemapper.html
newCode = new AsmStaticUsageTransformer(className, newCode).transform();
newCode = new AsmNameTransformer(className, newCode).transform();
newCode = new ProvidedRemoverTransformer(className, newCode).transform();
newCode = new AnnotationRemoverTransformer(className, newCode).transform();
newCode = new DoNothingTransformer(className, newCode).transform();
return newCode;
}
}

View File

@@ -0,0 +1,110 @@
/*
* Copyright (c) 2019, ThatGamerBlue <thatgamerblue@gmail.com>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package us.runelitepl.mixinprocessor.generators;
import javassist.CannotCompileException;
import javassist.CtClass;
import javassist.NotFoundException;
import us.runelitepl.mixinprocessor.MixinProcessorMojo;
import us.runelitepl.mixinprocessor.util.JavassistUtils;
import us.runelitepl.mixinprocessor.util.RefUtils;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.tree.ClassNode;
import org.objectweb.asm.tree.FieldNode;
import org.objectweb.asm.tree.MethodNode;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Set;
public class StaticGenerator
{
public static HashMap<String, ArrayList<MethodNode>> staticMethods = new HashMap<>();
public static HashMap<String, ArrayList<FieldNode>> staticFields = new HashMap<>();
public static Set<String> modifiedClasses = new HashSet<>();
public void run(byte[] bytecode) throws NotFoundException, IOException, CannotCompileException
{
ClassReader cr = new ClassReader(bytecode);
ClassNode node = new ClassNode();
cr.accept(node, 0);
for (Object aaa : node.methods)
{
MethodNode method = (MethodNode) aaa;
String methodName = method.name;
method.desc = RefUtils.reobMethodDescriptor(method.desc);
int access = method.access;
if ((access & 8) != 8)
{
continue;
}
String reobbed = RefUtils.reobMethodName(RefUtils.STATICS_STRING, methodName, method.desc);
if(reobbed == null)
{
MixinProcessorMojo.log("Failed to reob static method: %s %s", methodName, method.desc);
throw new RuntimeException();
}
String[] split = reobbed.split(" ");
method.name = split[1];
ArrayList<MethodNode> list = staticMethods.getOrDefault(split[0], new ArrayList<>());
list.add(method);
staticMethods.put(split[0], list);
modifiedClasses.add(split[0]);
}
for (Object aaa : node.fields)
{
FieldNode field = (FieldNode) aaa;
String fieldName = field.name;
field.desc = RefUtils.reobDescriptor(field.desc);
int access = field.access;
if ((access & 8) != 8)
{
continue;
}
String reobbed = RefUtils.reobFieldName(RefUtils.STATICS_STRING, fieldName, field.desc);
if(reobbed == null)
{
MixinProcessorMojo.log("Failed to reob static field: %s %s", fieldName, field.desc);
throw new RuntimeException();
}
String[] split = reobbed.split(" ");
field.name = split[1];
ArrayList<FieldNode> list = staticFields.getOrDefault(split[0], new ArrayList<>());
list.add(field);
staticFields.put(split[0], list);
modifiedClasses.add(split[0]);
}
}
}

View File

@@ -0,0 +1,261 @@
/*
* Copyright (c) 2019, ThatGamerBlue <thatgamerblue@gmail.com>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package us.runelitepl.mixinprocessor.generators;
import org.objectweb.asm.Attribute;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.FieldVisitor;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.tree.AnnotationNode;
import org.objectweb.asm.tree.FieldNode;
import org.objectweb.asm.tree.MethodNode;
import org.objectweb.asm.tree.TypeAnnotationNode;
import us.runelitepl.mixinprocessor.MixinProcessorMojo;
import us.runelitepl.mixinprocessor.parsers.MethodReflector;
import java.util.HashMap;
import static us.runelitepl.mixinprocessor.generators.StaticGenerator.modifiedClasses;
import static us.runelitepl.mixinprocessor.generators.StaticGenerator.staticFields;
import static us.runelitepl.mixinprocessor.generators.StaticGenerator.staticMethods;
public class StaticStageTwoGenerator implements Opcodes
{
private HashMap<String, byte[]> classes;
public StaticStageTwoGenerator(HashMap<String, byte[]> classes)
{
this.classes = classes;
}
public void run()
{
for (String className : modifiedClasses)
{
byte[] targetBytecode = classes.getOrDefault(className, null);
if (targetBytecode == null)
{
//create new class
ClassWriter cw = new ClassWriter(0);
FieldVisitor fv;
MethodVisitor mv;
cw.visit(V1_6, ACC_PUBLIC, className, null, "java/lang/Object", null);
if (staticMethods.get(className) != null)
{
for (MethodNode method : staticMethods.get(className))
{
mv = cw.visitMethod(method.access, method.name, method.desc, method.signature,
(String[]) method.exceptions.toArray(new String[0]));
MethodReflector reflector = new MethodReflector(mv);
method.accept(reflector);
}
}
if (staticFields.get(className) != null)
{
for (FieldNode field : staticFields.get(className))
{
fv = cw.visitField(field.access, field.name, field.desc, field.signature, field.value);
int i;
int n;
AnnotationNode annotation;
if (field.visibleAnnotations != null)
{
i = 0;
for (n = field.visibleAnnotations.size(); i < n; ++i)
{
annotation = (AnnotationNode) field.visibleAnnotations.get(i);
annotation.accept(fv.visitAnnotation(annotation.desc, true));
}
}
if (field.invisibleAnnotations != null)
{
i = 0;
for (n = field.invisibleAnnotations.size(); i < n; ++i)
{
annotation = (AnnotationNode) field.invisibleAnnotations.get(i);
annotation.accept(fv.visitAnnotation(annotation.desc, false));
}
}
TypeAnnotationNode typeAnnotation;
if (field.visibleTypeAnnotations != null)
{
i = 0;
for (n = field.visibleTypeAnnotations.size(); i < n; ++i)
{
typeAnnotation = (TypeAnnotationNode) field.visibleTypeAnnotations.get(i);
typeAnnotation.accept(fv
.visitTypeAnnotation(typeAnnotation.typeRef, typeAnnotation.typePath,
typeAnnotation.desc, true));
}
}
if (field.invisibleTypeAnnotations != null)
{
i = 0;
for (n = field.invisibleTypeAnnotations.size(); i < n; ++i)
{
typeAnnotation = (TypeAnnotationNode) field.invisibleTypeAnnotations.get(i);
typeAnnotation.accept(fv
.visitTypeAnnotation(typeAnnotation.typeRef, typeAnnotation.typePath,
typeAnnotation.desc, false));
}
}
if (field.attrs != null)
{
i = 0;
for (n = field.attrs.size(); i < n; ++i)
{
fv.visitAttribute((Attribute) field.attrs.get(i));
}
}
fv.visitEnd();
}
}
cw.visitEnd();
targetBytecode = cw.toByteArray();
}
else
{
ClassReader cr = new ClassReader(targetBytecode);
ClassWriter cw = new ClassWriter(cr, 0);
ClassVisitor cv = new ClassVisitor(ASM6)
{
@Override
public void visit(int version, int access, String name, String signature, String superName, String[] interfaces)
{
super.visit(version, access, name, signature, superName, interfaces);
if (staticMethods.get(className) != null)
{
for (MethodNode method : staticMethods.get(className))
{
MethodVisitor mv = visitMethod(method.access, method.name, method.desc,
method.signature, (String[]) method.exceptions.toArray(new String[0]));
MethodReflector reflector = new MethodReflector(mv);
method.accept(reflector);
}
}
if (staticFields.get(className) != null)
{
for (FieldNode field : staticFields.get(className))
{
FieldVisitor fv = visitField(field.access, field.name, field.desc, field.signature,
field.value);
int i;
int n;
AnnotationNode annotation;
if (field.visibleAnnotations != null)
{
i = 0;
for (n = field.visibleAnnotations.size(); i < n; ++i)
{
annotation = (AnnotationNode) field.visibleAnnotations.get(i);
annotation.accept(fv.visitAnnotation(annotation.desc, true));
}
}
if (field.invisibleAnnotations != null)
{
i = 0;
for (n = field.invisibleAnnotations.size(); i < n; ++i)
{
annotation = (AnnotationNode) field.invisibleAnnotations.get(i);
annotation.accept(fv.visitAnnotation(annotation.desc, false));
}
}
TypeAnnotationNode typeAnnotation;
if (field.visibleTypeAnnotations != null)
{
i = 0;
for (n = field.visibleTypeAnnotations.size(); i < n; ++i)
{
typeAnnotation = (TypeAnnotationNode) field.visibleTypeAnnotations.get(i);
typeAnnotation.accept(fv
.visitTypeAnnotation(typeAnnotation.typeRef, typeAnnotation.typePath,
typeAnnotation.desc, true));
}
}
if (field.invisibleTypeAnnotations != null)
{
i = 0;
for (n = field.invisibleTypeAnnotations.size(); i < n; ++i)
{
typeAnnotation = (TypeAnnotationNode) field.invisibleTypeAnnotations.get(i);
typeAnnotation.accept(fv
.visitTypeAnnotation(typeAnnotation.typeRef, typeAnnotation.typePath,
typeAnnotation.desc, false));
}
}
if (field.attrs != null)
{
i = 0;
for (n = field.attrs.size(); i < n; ++i)
{
fv.visitAttribute((Attribute) field.attrs.get(i));
}
}
fv.visitEnd();
}
}
}
};
cr.accept(cv, 0);
targetBytecode = cw.toByteArray();
}
classes.put(className, targetBytecode);
}
}
}

View File

@@ -0,0 +1,110 @@
/*
* Copyright (c) 2019, ThatGamerBlue <thatgamerblue@gmail.com>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package us.runelitepl.mixinprocessor.parsers;
import javassist.CtClass;
import javassist.CtField;
import us.runelitepl.mixinprocessor.annotations.Append;
import us.runelitepl.mixinprocessor.annotations.Inject;
import us.runelitepl.mixinprocessor.annotations.Overwrite;
import us.runelitepl.mixinprocessor.annotations.Prepend;
import us.runelitepl.mixinprocessor.annotations.Provided;
import us.runelitepl.mixinprocessor.annotations.Reobfuscate;
import us.runelitepl.mixinprocessor.enums.InjectionType;
import us.runelitepl.mixinprocessor.util.RefUtils;
import static us.runelitepl.mixinprocessor.MixinProcessorMojo.isFieldTagged;
public class FieldAnnotationParser
{
private CtClass clazz;
public FieldAnnotationParser(CtClass clazz)
{
this.clazz = clazz;
}
public void run() throws ClassNotFoundException
{
for (CtField field : clazz.getDeclaredFields())
{
Object[] annotations = field.getAnnotations();
InjectionType type = null;
boolean reobfuscate = false;
for (Object obj : annotations)
{
reobfuscate = obj instanceof Reobfuscate || reobfuscate;
if (obj instanceof Inject)
{
type = InjectionType.INJECT;
}
else if (obj instanceof Append)
{
type = InjectionType.APPEND;
}
else if (obj instanceof Overwrite)
{
type = InjectionType.OVERWRITE;
}
else if (obj instanceof Prepend)
{
type = InjectionType.PREPEND;
}
else if (obj instanceof Provided)
{
type = InjectionType.PROVIDED;
}
}
String fieldOwner = field.getDeclaringClass().getSimpleName();
String fieldName = field.getName();
String fieldDescriptor = RefUtils.reobDescriptor(field.getSignature());
if (type == null)
{
throw new RuntimeException(
field.getDeclaringClass().getSimpleName() + "." + field.getName() + " is unannotated" +
"!");
}
if (type != InjectionType.PROVIDED && type != InjectionType.INJECT)
{
throw new RuntimeException(
field.getDeclaringClass().getSimpleName() + "." + field.getName() + " has an invalid " +
"annotation! @" + type);
}
if (reobfuscate)
{
isFieldTagged.put(fieldOwner + " " + fieldName + " " + fieldDescriptor, true);
}
}
}
}

View File

@@ -0,0 +1,91 @@
/*
* Copyright (c) 2019, ThatGamerBlue <thatgamerblue@gmail.com>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package us.runelitepl.mixinprocessor.parsers;
import us.runelitepl.mixinprocessor.util.WebUtils;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.HashMap;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
public class GamepackDownloader
{
private static final String CONFIG_URL = "http://oldschool.runescape.com/jav_config.ws";
private static String codebase;
private static String initial_jar;
public static void downloadGamepack(HashMap<String, byte[]> output) throws IOException
{
parseConfig();
byte[] gamepackJarAry = WebUtils.downloadFile(getGamepackUrl());
ZipInputStream zipInputStream = new ZipInputStream(new ByteArrayInputStream(gamepackJarAry));
byte[] buffer = new byte[2048];
ZipEntry entry;
ByteArrayOutputStream fileContent = new ByteArrayOutputStream(1024*1024*4);
while ((entry = zipInputStream.getNextEntry()) != null)
{
if (entry.getName().startsWith("META-INF"))
{
continue;
}
String key = entry.getName().replace(".class", "");
int len = 0;
while((len = zipInputStream.read(buffer)) > 0)
{
fileContent.write(buffer, 0, len);
}
output.put(key, fileContent.toByteArray());
fileContent.reset();
}
zipInputStream.close();
}
private static void parseConfig() throws IOException
{
String pageText = WebUtils.getUrlContent(CONFIG_URL);
for (String line : pageText.split("\n"))
{
if (line.startsWith("codebase="))
{
codebase = line.replace("codebase=", "");
}
else if (line.startsWith("initial_jar="))
{
initial_jar = line.replace("initial_jar=", "");
}
}
}
public static String getGamepackUrl()
{
return codebase + initial_jar;
}
}

View File

@@ -0,0 +1,144 @@
/*
* Copyright (c) 2019, ThatGamerBlue <thatgamerblue@gmail.com>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package us.runelitepl.mixinprocessor.parsers;
import us.runelitepl.mixinprocessor.MethodGarbageValue;
import us.runelitepl.mixinprocessor.MixinProcessorMojo;
import org.json.simple.JSONArray;
import org.json.simple.JSONObject;
import org.json.simple.parser.JSONParser;
import org.json.simple.parser.ParseException;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.util.Map;
public class HooksParser
{
public static void run(File hooksFile, File opsFile) throws IOException, ParseException
{
JSONParser parser = new JSONParser();
//region hooksFile
String jsonContent = String.join("\n", Files.readAllLines(hooksFile.toPath()));
JSONArray array = (JSONArray) parser.parse(jsonContent);
for (Object object : array)
{
JSONObject rootObject = (JSONObject) object;
String c_obbedName = (String) rootObject.get("name");
String c_deobbedName = (String) rootObject.get("class");
MixinProcessorMojo.classNames.put(c_deobbedName, c_obbedName);
}
int failedFields = 0;
int failedMethods = 0;
for (Object object : array)
{
JSONObject rootObject = (JSONObject) object;
JSONArray fieldArray = (JSONArray) rootObject.get("fields");
for (Object fieldObj : fieldArray)
{
JSONObject field = (JSONObject) fieldObj;
String f_deobbedName = (String) field.get("field");
String f_obbedName = (String) field.get("name");
String f_descriptor = (String) field.get("descriptor");
String f_owner = (String) field.get("owner");
long f_decoder = (long) field.getOrDefault("decoder", (long) 1);
String f_deobbedOwner = null;
if(f_deobbedName.startsWith("__"))
{
continue;
}
for (Map.Entry<String, String> entry : MixinProcessorMojo.classNames.entrySet())
{
if (entry.getValue().equals(f_owner))
{
f_deobbedOwner = entry.getKey();
break;
}
}
if (f_deobbedOwner == null)
{
failedFields++;
//stderr("Failed to find deobbed owner for field %s.%s %s", f_owner, f_obbedName, f_descriptor);
continue;
}
MixinProcessorMojo.fieldNames.put(String.format("%s %s %s", f_deobbedOwner, f_deobbedName, f_descriptor),
String.format("%s %s", f_owner, f_obbedName));
MixinProcessorMojo.fieldDecoders.put(String.format("%s %s", f_deobbedOwner, f_deobbedName),
f_decoder);
}
JSONArray methodArray = (JSONArray) rootObject.get("methods");
for (Object methodObj : methodArray)
{
JSONObject method = (JSONObject) methodObj;
String m_deobbedName = (String) method.get("method");
String m_owner = (String) method.get("owner");
String m_deobbedOwner = null;
String m_obbedName = (String) method.get("name");
String m_descriptor = (String) method.get("descriptor");
if(m_deobbedName.startsWith("__"))
{
continue;
}
for (Map.Entry<String, String> entry : MixinProcessorMojo.classNames.entrySet())
{
if (entry.getValue().equals(m_owner))
{
m_deobbedOwner = entry.getKey();
break;
}
}
if (m_deobbedOwner == null)
{
failedMethods++;
//stderr("Failed to find deobbed owner for method %s.%s %s", m_owner, m_obbedName, m_descriptor);
continue;
}
MixinProcessorMojo.methodNames.put(String.format("%s %s %s", m_deobbedOwner, m_deobbedName, m_descriptor),
String.format("%s %s", m_owner, m_obbedName));
}
}
MixinProcessorMojo.log("%d unidentified fields", failedFields);
MixinProcessorMojo.log("%d unidentified methods", failedMethods);
//endregion
//region opsFile
String opsContent = String.join("\n", Files.readAllLines(opsFile.toPath()));
JSONObject opsRoot = (JSONObject) parser.parse(opsContent);
opsRoot.forEach((key, value) ->
{
String k = (String) key;
MixinProcessorMojo.methodGarbageValues.put(k, new MethodGarbageValue(Math.toIntExact((Long) value)));
});
//endregion
}
}

View File

@@ -0,0 +1,127 @@
/*
* Copyright (c) 2019, ThatGamerBlue <thatgamerblue@gmail.com>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package us.runelitepl.mixinprocessor.parsers;
import javassist.CtClass;
import javassist.CtMethod;
import us.runelitepl.mixinprocessor.MixinProcessorMojo;
import us.runelitepl.mixinprocessor.annotations.Append;
import us.runelitepl.mixinprocessor.annotations.Inject;
import us.runelitepl.mixinprocessor.annotations.Overwrite;
import us.runelitepl.mixinprocessor.annotations.Prepend;
import us.runelitepl.mixinprocessor.annotations.Provided;
import us.runelitepl.mixinprocessor.annotations.Reobfuscate;
import us.runelitepl.mixinprocessor.enums.InjectionType;
import us.runelitepl.mixinprocessor.util.RefUtils;
import static us.runelitepl.mixinprocessor.MixinProcessorMojo.isMethodTagged;
public class MethodAnnotationParser
{
private final CtClass patch;
public MethodAnnotationParser(CtClass patch)
{
this.patch = patch;
}
public void run() throws ClassNotFoundException
{
for(CtMethod method : patch.getDeclaredMethods())
{
Object[] annotations = method.getAnnotations();
boolean reobfuscate = false;
InjectionType type = null;
for (Object obj : annotations)
{
reobfuscate = obj instanceof Reobfuscate || reobfuscate;
if (obj instanceof Inject)
{
type = InjectionType.INJECT;
}
else if (obj instanceof Append)
{
type = InjectionType.APPEND;
}
else if (obj instanceof Overwrite)
{
type = InjectionType.OVERWRITE;
}
else if (obj instanceof Prepend)
{
type = InjectionType.PREPEND;
}
else if (obj instanceof Provided)
{
type = InjectionType.PROVIDED;
}
}
if (type == null)
{
throw new RuntimeException(
method.getDeclaringClass().getSimpleName() + "." + method.getName() + " is unannotated" +
"!");
}
String methodName = method.getName();
if (type == InjectionType.PREPEND)
{
if (!methodName.startsWith("prepend$"))
{
throw new RuntimeException(
method.getDeclaringClass().getSimpleName() + "." + method.getName() + " has a @Prepend " +
"annotation without beginning with \"prepend$\"!");
}
}
else if (type == InjectionType.APPEND)
{
if (!methodName.startsWith("append$"))
{
throw new RuntimeException(
method.getDeclaringClass().getSimpleName() + "." + method.getName() + " has a @Append " +
"annotation without beginning with \"append$\"!");
}
}
if(reobfuscate)
{
MixinProcessorMojo.log("Marking: %s %s %s", method.getDeclaringClass().getSimpleName(), methodName,
RefUtils.reobMethodDescriptor(method.getSignature()));
String r = String.format("%s %s %s", method.getDeclaringClass().getSimpleName(), methodName,
RefUtils.reobMethodDescriptor(method.getSignature()));
isMethodTagged.put(r, true);
}
}
}
}

View File

@@ -0,0 +1,329 @@
/*
* Copyright (c) 2019, ThatGamerBlue <thatgamerblue@gmail.com>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package us.runelitepl.mixinprocessor.parsers;
import org.objectweb.asm.AnnotationVisitor;
import org.objectweb.asm.Attribute;
import org.objectweb.asm.Handle;
import org.objectweb.asm.Label;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.TypePath;
public class MethodReflector extends MethodVisitor
{
private MethodVisitor target;
public MethodReflector(MethodVisitor target)
{
super(Opcodes.ASM6);
this.target = target;
}
public void visitCode()
{
if (target != null)
{
target.visitCode();
}
super.visitCode();
}
public void visitEnd()
{
if (target != null)
{
target.visitEnd();
}
super.visitEnd();
}
public void visitParameter(String var1, int var2)
{
if (target != null)
{
target.visitParameter(var1, var2);
}
super.visitParameter(var1, var2);
}
public AnnotationVisitor visitAnnotationDefault()
{
if (target != null)
{
target.visitAnnotationDefault();
}
return super.visitAnnotationDefault();
}
public AnnotationVisitor visitAnnotation(String var1, boolean var2)
{
if (target != null)
{
target.visitAnnotation(var1, var2);
}
return super.visitAnnotation(var1, var2);
}
public AnnotationVisitor visitTypeAnnotation(int var1, TypePath var2, String var3, boolean var4)
{
if (target != null)
{
target.visitTypeAnnotation(var1, var2, var3, var4);
}
return super.visitTypeAnnotation(var1, var2, var3, var4);
}
public AnnotationVisitor visitParameterAnnotation(int var1, String var2, boolean var3)
{
if (target != null)
{
target.visitParameterAnnotation(var1, var2, var3);
}
return super.visitParameterAnnotation(var1, var2, var3);
}
public void visitAttribute(Attribute var1)
{
if (target != null)
{
target.visitAttribute(var1);
}
super.visitAttribute(var1);
}
public void visitFrame(int var1, int var2, Object[] var3, int var4, Object[] var5)
{
if (target != null)
{
target.visitFrame(var1, var2, var3, var4, var5);
}
super.visitFrame(var1, var2, var3, var4, var5);
}
public void visitInsn(int var1)
{
if (target != null)
{
target.visitInsn(var1);
}
super.visitInsn(var1);
}
public void visitIntInsn(int var1, int var2)
{
if (target != null)
{
target.visitIntInsn(var1, var2);
}
super.visitIntInsn(var1, var2);
}
public void visitVarInsn(int var1, int var2)
{
if (target != null)
{
target.visitVarInsn(var1, var2);
}
super.visitVarInsn(var1, var2);
}
public void visitTypeInsn(int var1, String var2)
{
if (target != null)
{
target.visitTypeInsn(var1, var2);
}
super.visitTypeInsn(var1, var2);
}
public void visitFieldInsn(int var1, String var2, String var3, String var4)
{
if (target != null)
{
target.visitFieldInsn(var1, var2, var3, var4);
}
super.visitFieldInsn(var1, var2, var3, var4);
}
/**
* @deprecated
*/
public void visitMethodInsn(int var1, String var2, String var3, String var4)
{
if (target != null)
{
target.visitMethodInsn(var1, var2, var3, var4);
}
super.visitMethodInsn(var1, var2, var3, var4);
}
public void visitMethodInsn(int var1, String var2, String var3, String var4, boolean var5)
{
if (target != null)
{
target.visitMethodInsn(var1, var2, var3, var4, var5);
}
super.visitMethodInsn(var1, var2, var3, var4, var5);
}
public void visitInvokeDynamicInsn(String var1, String var2, Handle var3, Object... var4)
{
if (target != null)
{
target.visitInvokeDynamicInsn(var1, var2, var3, var4);
}
super.visitInvokeDynamicInsn(var1, var2, var3, var4);
}
public void visitJumpInsn(int var1, Label var2)
{
if (target != null)
{
target.visitJumpInsn(var1, var2);
}
super.visitJumpInsn(var1, var2);
}
public void visitLabel(Label var1)
{
if (target != null)
{
target.visitLabel(var1);
}
super.visitLabel(var1);
}
public void visitLdcInsn(Object var1)
{
if (target != null)
{
target.visitLdcInsn(var1);
}
super.visitLdcInsn(var1);
}
public void visitIincInsn(int var1, int var2)
{
if (target != null)
{
target.visitIincInsn(var1, var2);
}
super.visitIincInsn(var1, var2);
}
public void visitTableSwitchInsn(int var1, int var2, Label var3, Label... var4)
{
if (target != null)
{
target.visitTableSwitchInsn(var1, var2, var3, var4);
}
super.visitTableSwitchInsn(var1, var2, var3, var4);
}
public void visitLookupSwitchInsn(Label var1, int[] var2, Label[] var3)
{
if (target != null)
{
target.visitLookupSwitchInsn(var1, var2, var3);
}
super.visitLookupSwitchInsn(var1, var2, var3);
}
public void visitMultiANewArrayInsn(String var1, int var2)
{
if (target != null)
{
target.visitMultiANewArrayInsn(var1, var2);
}
super.visitMultiANewArrayInsn(var1, var2);
}
public AnnotationVisitor visitInsnAnnotation(int var1, TypePath var2, String var3, boolean var4)
{
if (target != null)
{
target.visitInsnAnnotation(var1, var2, var3, var4);
}
return super.visitInsnAnnotation(var1, var2, var3, var4);
}
public void visitTryCatchBlock(Label var1, Label var2, Label var3, String var4)
{
if (target != null)
{
target.visitTryCatchBlock(var1, var2, var3, var4);
}
super.visitTryCatchBlock(var1, var2, var3, var4);
}
public AnnotationVisitor visitTryCatchAnnotation(int var1, TypePath var2, String var3, boolean var4)
{
if (target != null)
{
target.visitTryCatchAnnotation(var1, var2, var3, var4);
}
return super.visitTryCatchAnnotation(var1, var2, var3, var4);
}
public void visitLocalVariable(String var1, String var2, String var3, Label var4, Label var5, int var6)
{
if (target != null)
{
target.visitLocalVariable(var1, var2, var3, var4, var5, var6);
}
super.visitLocalVariable(var1, var2, var3, var4, var5, var6);
}
public AnnotationVisitor visitLocalVariableAnnotation(int var1, TypePath var2, Label[] var3, Label[] var4, int[] var5, String var6, boolean var7)
{
if (target != null)
{
target.visitLocalVariableAnnotation(var1, var2, var3, var4, var5, var6, var7);
}
return super.visitLocalVariableAnnotation(var1, var2, var3, var4, var5, var6, var7);
}
public void visitLineNumber(int var1, Label var2)
{
if (target != null)
{
target.visitLineNumber(var1, var2);
}
super.visitLineNumber(var1, var2);
}
public void visitMaxs(int var1, int var2)
{
if (target != null)
{
target.visitMaxs(var1, var2);
}
super.visitMaxs(var1, var2);
}
}

View File

@@ -0,0 +1,103 @@
/*
* Copyright (c) 2019, ThatGamerBlue <thatgamerblue@gmail.com>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package us.runelitepl.mixinprocessor.transformers;
import org.objectweb.asm.AnnotationVisitor;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.FieldVisitor;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
public class AnnotationRemoverTransformer extends AsmBaseTransformer
{
private final String className;
private final byte[] bytecode;
public AnnotationRemoverTransformer(String className, byte[] bytecode)
{
this.className = className;
this.bytecode = bytecode;
}
@Override
public byte[] transform()
{
ClassReader cr = new ClassReader(bytecode);
ClassWriter cw = new ClassWriter(cr, 0);
cr.accept(new ClassVisitor(Opcodes.ASM6, cw)
{
@Override
public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions)
{
MethodVisitor visitor = super.visitMethod(access, name, desc, signature, exceptions);
MethodVisitor mv = new MethodVisitor(Opcodes.ASM6, visitor)
{
@Override
public AnnotationVisitor visitAnnotation(String descriptor, boolean hidden)
{
if (descriptor.equals(makeAnnotationDescriptor("Reobfuscate")))
{
return null;
}
return super.visitAnnotation(descriptor, hidden);
}
};
return mv;
}
@Override
public FieldVisitor visitField(int access, String name, String descriptor, String signature, Object value)
{
FieldVisitor visitor = super.visitField(access, name, descriptor, signature, value);
return new FieldVisitor(Opcodes.ASM6, visitor)
{
@Override
public AnnotationVisitor visitAnnotation(String descriptor, boolean hidden)
{
if (descriptor.equals(makeAnnotationDescriptor("Reobfuscate")))
{
return null;
}
if (descriptor.equals(makeAnnotationDescriptor("Provided")))
{
return null;
}
return super.visitAnnotation(descriptor, hidden);
}
};
}
}, 0);
return cw.toByteArray();
}
public static String makeAnnotationDescriptor(String s)
{
return "Lus/runelitepl/mixinprocessor/annotations/" + s + ";";
}
}

View File

@@ -0,0 +1,44 @@
/*
* Copyright (c) 2019, ThatGamerBlue <thatgamerblue@gmail.com>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package us.runelitepl.mixinprocessor.transformers;
import org.objectweb.asm.Opcodes;
import java.util.ArrayList;
public abstract class AsmBaseTransformer implements Opcodes
{
protected final ArrayList<String> validMethods = new ArrayList<>();
protected final ArrayList<String> validFields = new ArrayList<>();
protected void buildMethodList(){}
protected void buildFieldList(){}
public abstract byte[] transform();
}

View File

@@ -0,0 +1,109 @@
/*
* Copyright (c) 2019, ThatGamerBlue <thatgamerblue@gmail.com>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package us.runelitepl.mixinprocessor.transformers;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.MethodVisitor;
import us.runelitepl.mixinprocessor.MethodGarbageValue;
import us.runelitepl.mixinprocessor.MixinProcessorMojo;
import us.runelitepl.mixinprocessor.util.RefUtils;
import java.sql.Ref;
import java.util.HashMap;
public class AsmMethodGarbageTransformer extends AsmBaseTransformer
{
private final String className;
private final byte[] bytecode;
private HashMap<String, byte[]> classSet;
public AsmMethodGarbageTransformer(String className, byte[] bytecode, HashMap<String, byte[]> classSet)
{
this.className = className;
this.bytecode = bytecode;
this.classSet = classSet;
}
@Override
public byte[] transform()
{
ClassReader cr = new ClassReader(bytecode);
ClassWriter cw = new ClassWriter(cr, ClassWriter.COMPUTE_FRAMES);
cr.accept(new ClassVisitor(ASM6, cw)
{
@Override
public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions)
{
MethodVisitor mv = super.visitMethod(access, name, desc, signature, exceptions);
return new MethodVisitor(ASM6, mv)
{
@Override
public void visitMethodInsn(int opcode, String owner, String name, String desc, boolean itf)
{
// get method owner.string(desc)
// check if method is annotated with @Reobfuscate
// brute force actual descriptor (check with shouldReobMethod(owner, name desc) )
// mv.visitLdcInsn(BIPUSH, constant);
// call super method with new descriptor
if(RefUtils.shouldReobMethod(owner, name, desc))
{
String nc = RefUtils.reobMethodName(owner, name, desc).split(" ")[0];
String nn = RefUtils.reobMethodName(owner, name, desc).split(" ")[1];
String nd = RefUtils.reobMethodDescriptor(desc);
MethodGarbageValue value;
if((value = MixinProcessorMojo.methodGarbageValues.getOrDefault(String.format("%s.%s%s",
nc, nn, nd), null)) != null)
{
switch (value.getType())
{
case "I":
super.visitLdcInsn(new Integer(value.getValue()));
break;
case "S":
super.visitIntInsn(SIPUSH, (short) value.getValue());
break;
case "B":
super.visitIntInsn(BIPUSH, (byte) value.getValue());
break;
}
}
}
super.visitMethodInsn(opcode, owner, name, desc, itf);
}
};
}
}, 0);
return cw.toByteArray();
}
}

View File

@@ -0,0 +1,107 @@
/*
* Copyright (c) 2019, ThatGamerBlue <thatgamerblue@gmail.com>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package us.runelitepl.mixinprocessor.transformers;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.MethodVisitor;
import us.runelitepl.mixinprocessor.MixinProcessorMojo;
import us.runelitepl.mixinprocessor.util.RefUtils;
public class AsmMethodSignatureTransformer extends AsmBaseTransformer
{
private final String className;
private final byte[] bytecode;
public AsmMethodSignatureTransformer(String className, byte[] bytecode)
{
this.className = RefUtils.deobClassName(className);
this.bytecode = bytecode;
}
@Override
public byte[] transform()
{
ClassReader cr = new ClassReader(bytecode);
ClassWriter cw = new ClassWriter(cr, ClassWriter.COMPUTE_FRAMES);
cr.accept(new ClassVisitor(ASM6, cw)
{
@Override
public MethodVisitor visitMethod(int access, String name, String desc, String sig, String[] exceptions)
{
MethodVisitor mv = super.visitMethod(access, name, fixMethodDesc(className, name, desc), sig, exceptions);
return new MethodVisitor(ASM6, mv)
{
@Override
public void visitMethodInsn(int opcode, String owner, String name, String desc, boolean itf)
{
super.visitMethodInsn(opcode, owner, name, fixMethodDesc(owner, name, desc), itf);
}
};
}
}, 0);
return cw.toByteArray();
}
private String fixMethodDesc(String className, String name, String desc)
{
if(className.startsWith(RefUtils.TYPE_PREFIX))
{
className = className.substring(RefUtils.TYPE_PREFIX.length());
}
if (RefUtils.shouldReobMethod(className, name, desc))
{
if (RefUtils.reobMethodName(className, name, desc) == null)
{
// get correct descriptor
String realDesc = null;
for (String s : RefUtils.POSSIBLE_GARBAGE_TYPES)
{
String check = RefUtils.reobMethodName(className, name, RefUtils.appendArgument(desc, s));
if (check != null)
{
realDesc = RefUtils.appendArgument(desc, s);
break;
}
}
if (realDesc == null)
{
MixinProcessorMojo.log("Failed to find actual method descriptor for %s.%s%s", className,
name, desc);
throw new RuntimeException();
}
// fixed = realDesc
MixinProcessorMojo.isMethodTagged.put(String.format("%s %s %s", className, name, realDesc), true);
return realDesc;
}
}
return desc;
}
}

View File

@@ -0,0 +1,175 @@
/*
* Copyright (c) 2019, ThatGamerBlue <thatgamerblue@gmail.com>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package us.runelitepl.mixinprocessor.transformers;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.commons.ClassRemapper;
import org.objectweb.asm.commons.Remapper;
import us.runelitepl.mixinprocessor.MixinProcessorMojo;
import us.runelitepl.mixinprocessor.util.RefUtils;
public class AsmNameTransformer extends AsmBaseTransformer
{
private String className;
private byte[] bytecode;
public AsmNameTransformer(String className, byte[] bytecode)
{
this.className = className;
this.bytecode = bytecode;
}
@Override
public byte[] transform()
{
ClassReader cr = new ClassReader(bytecode);
ClassWriter cw = new ClassWriter(cr, 0);
ClassRemapper remapper = new ClassRemapper(cw, new Remapper()
{
final String TYPE_PREFIX = "us/runelitepl/mixins/";
@Override
public String map(String internalName)
{
return super.map(internalName);
}
@Override
public String mapDesc(String descriptor)
{
return RefUtils.reobDescriptor(descriptor);
}
@Override
public String mapFieldName(String owner, String name, String descriptor)
{
String oldName = name;
String noPackage = owner.replace(TYPE_PREFIX, "");
descriptor = mapDesc(descriptor);
owner = RefUtils.reobClassName(owner);
if (RefUtils.shouldReobField(noPackage, name, descriptor))
{
name = RefUtils.reobFieldName(noPackage, name, descriptor);
if (name == null)
{
MixinProcessorMojo.log("Failed to reobfuscate field name %s.%s", noPackage, oldName);
throw new RuntimeException();
}
}
return super.mapFieldName(owner, name, descriptor);
}
@Override
public String mapInvokeDynamicMethodName(String name, String descriptor)
{
throw new UnsupportedOperationException("mapInvokeDynamicMethodName: Not implemented yet," +
"\nAsmNameTransformer#mapInvokeDynamicMethodName(" + name + ", " + descriptor + ")");
}
@Override
public String mapMethodDesc(String descriptor)
{
return RefUtils.reobMethodDescriptor(descriptor);
}
@Override
public String mapMethodName(String owner, String name, String descriptor)
{
String originalClass = owner;
if (originalClass.startsWith(TYPE_PREFIX))
{
originalClass = originalClass.substring(TYPE_PREFIX.length());
}
owner = RefUtils.reobClassName(owner);
if (name.startsWith("protect$"))
{
name = "1" + name;
}
// descriptor for reob checking is obfuscated
descriptor = mapMethodDesc(descriptor);
boolean reob = RefUtils.shouldReobMethod(originalClass, name, descriptor);
if (reob)
{
String originalName = name;
name = RefUtils.reobMethodName(originalClass, name, descriptor);
if (name == null)
{
MixinProcessorMojo.log("Failed to reobfuscate method: %s.%s%s", originalClass, originalName,
descriptor);
throw new RuntimeException();
}
}
//stderr("mapMethodName %s %s %s", owner, name, descriptor);
return super.mapMethodName(owner, name, descriptor);
}
@Override
public String mapPackageName(String name)
{
throw new UnsupportedOperationException("mapPackageName: Not implemented yet," +
"\nAsmNameTransformer#mapPackageName(" + name + ")");
}
@Override
public String mapSignature(String signature, boolean isTypeSig)
{
return super.mapSignature(signature, isTypeSig);
}
@Override
public String mapType(String internalName)
{
internalName = RefUtils.reobClassName(internalName);
return super.mapType(internalName);
}
@Override
public String[] mapTypes(String[] internalNames)
{
return super.mapTypes(internalNames);
}
@Override
public Object mapValue(Object value)
{
return super.mapValue(value);
}
});
cr.accept(remapper, 0);
return cw.toByteArray();
}
}

View File

@@ -0,0 +1,119 @@
/*
* Copyright (c) 2019, ThatGamerBlue <thatgamerblue@gmail.com>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package us.runelitepl.mixinprocessor.transformers;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.MethodVisitor;
import us.runelitepl.mixinprocessor.MixinProcessorMojo;
import us.runelitepl.mixinprocessor.util.RefUtils;
import static org.objectweb.asm.Opcodes.ASM6;
import static org.objectweb.asm.Opcodes.GETSTATIC;
import static org.objectweb.asm.Opcodes.INVOKESTATIC;
import static org.objectweb.asm.Opcodes.PUTSTATIC;
public class AsmStaticUsageTransformer extends AsmBaseTransformer
{
private String className;
private byte[] bytecode;
private final String TYPE_PREFIX = "us/runelitepl/mixins/";
public AsmStaticUsageTransformer(String className, byte[] bytecode)
{
this.className = className;
this.bytecode = bytecode;
}
@Override
public byte[] transform()
{
ClassReader cr = new ClassReader(bytecode);
ClassWriter cw = new ClassWriter(cr, 0);
ClassVisitor cv = new ClassVisitor(ASM6, cw)
{
@Override
public MethodVisitor visitMethod(int access, String name, String descriptor, String signature, String[] exceptions)
{
MethodVisitor visitor = super.visitMethod(access, name, descriptor, signature, exceptions);
return new MethodVisitor(ASM6, visitor)
{
@Override
public void visitFieldInsn(int opcode, String owner, String name, String descriptor)
{
if ((opcode == GETSTATIC || opcode == PUTSTATIC) && owner.endsWith(RefUtils.STATICS_STRING))
{
String oldName = name;
String originalOwner = owner.replace(TYPE_PREFIX, "");
String temp = RefUtils.reobFieldNameDangerous(name, RefUtils.reobDescriptor(descriptor));
if (temp == null)
{
MixinProcessorMojo.log("Failed to reobfuscate class name for field %s %s %s", owner,
name, descriptor);
throw new RuntimeException();
}
owner = temp.split(" ")[0];
if (RefUtils.shouldReobField(originalOwner, name, RefUtils.reobDescriptor(descriptor)))
{
name = temp.split(" ")[1];
}
}
super.visitFieldInsn(opcode, owner, name, descriptor);
}
@Override
public void visitMethodInsn(int opcode, String owner, String name, String descriptor, boolean isInterface)
{
if(opcode == INVOKESTATIC && owner.endsWith(RefUtils.STATICS_STRING))
{
String originalOwner = owner.replace(TYPE_PREFIX, "");
String temp = RefUtils.reobMethodName(RefUtils.STATICS_STRING, name, descriptor);
if (temp == null)
{
MixinProcessorMojo.log("Failed to reobfuscate class name for method %s %s %s", owner,
name, descriptor);
throw new RuntimeException();
}
owner = temp.split(" ")[0];
if (RefUtils.shouldReobMethod(originalOwner, name, descriptor))
{
name = temp.split(" ")[1];
}
}
super.visitMethodInsn(opcode, owner, name, descriptor, isInterface);
}
};
}
};
cr.accept(cv, 0);
return cw.toByteArray();
}
}

View File

@@ -0,0 +1,56 @@
/*
* Copyright (c) 2019, ThatGamerBlue <thatgamerblue@gmail.com>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package us.runelitepl.mixinprocessor.transformers;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.ClassWriter;
public class DoNothingTransformer extends AsmBaseTransformer
{
private final String className;
private final byte[] bytecode;
public DoNothingTransformer(String className, byte[] bytecode)
{
this.className = className;
this.bytecode = bytecode;
}
@Override
public byte[] transform()
{
ClassReader cr = new ClassReader(bytecode);
ClassWriter cw = new ClassWriter(cr, ClassWriter.COMPUTE_FRAMES);
cr.accept(new ClassVisitor(ASM6, cw)
{}, 0);
return cw.toByteArray();
}
}

View File

@@ -0,0 +1,196 @@
/*
* Copyright (c) 2019, ThatGamerBlue <thatgamerblue@gmail.com>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package us.runelitepl.mixinprocessor.transformers;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
import us.runelitepl.mixinprocessor.MixinProcessorMojo;
import us.runelitepl.mixinprocessor.util.RefUtils;
public class GetFieldDecoderTransformer extends AsmBaseTransformer
{
private final String className;
private final byte[] classFileBytecode;
public GetFieldDecoderTransformer(String className, byte[] bytes)
{
this.className = className;
this.classFileBytecode = bytes;
}
@Override
public void buildMethodList()
{
ClassReader cr = new ClassReader(classFileBytecode);
ClassWriter cw = new ClassWriter(cr, 0);
cr.accept(new ClassVisitor(Opcodes.ASM6, cw)
{
@Override
public MethodVisitor visitMethod(int access, String name, String desc, String signature,
String[] exceptions)
{
final boolean[] valid = {true};
MethodVisitor visitor = super.visitMethod(access, name, desc, signature, exceptions);
return new MethodVisitor(Opcodes.ASM6, visitor)
{
int opcodeCount = 0;
@Override
public void visitEnd()
{
if (valid[0])
{
MixinProcessorMojo.log("Valid method: %s %s %s %s", access, name, desc, signature);
validMethods.add(access + " " + name + " " + desc + " " + signature);
}
}
@Override
public void visitVarInsn(int opcode, int var)
{
if (opcode != Opcodes.ALOAD || var != 0 || opcodeCount != 0)
{
valid[0] = false;
}
opcodeCount++;
super.visitVarInsn(opcode, var);
}
@Override
public void visitFieldInsn(int opcode, String owner, String fieldName, String signature)
{
if (opcode != Opcodes.GETFIELD || opcodeCount != 1)
{
valid[0] = false;
}
opcodeCount++;
super.visitFieldInsn(opcode, owner, fieldName, signature);
}
@Override
public void visitLdcInsn(Object o)
{
if (!(o instanceof Integer) && !(o instanceof Long))
{
valid[0] = false;
}
if (opcodeCount != 2)
{
valid[0] = false;
}
opcodeCount++;
super.visitLdcInsn(o);
}
@Override
public void visitInsn(int opcode)
{
switch (opcode)
{
case Opcodes.IMUL:
if (opcodeCount != 3)
{
valid[0] = false;
}
break;
case Opcodes.IRETURN:
if (opcodeCount != 4)
{
valid[0] = false;
}
break;
default:
valid[0] = false;
break;
}
opcodeCount++;
super.visitInsn(opcode);
}
};
}
}, 0);
}
public byte[] transform()
{
buildMethodList();
ClassReader cr = new ClassReader(classFileBytecode);
ClassWriter cw = new ClassWriter(cr, 0);
cr.accept(new ClassVisitor(Opcodes.ASM6, cw)
{
@Override
public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions)
{
MethodVisitor visitor = super.visitMethod(access, name, desc, signature, exceptions);
if (!validMethods.contains(access + " " + name + " " + desc + " " + signature))
{
return visitor;
}
return new MethodVisitor(Opcodes.ASM6, visitor)
{
String target = null;
@Override
public void visitFieldInsn(int opcode, String owner, String fieldName, String signature)
{
target = fieldName;
super.visitFieldInsn(opcode, owner, fieldName, signature);
}
@Override
public void visitLdcInsn(Object o)
{
if (target != null)
{
if (o instanceof Long)
{
o = MixinProcessorMojo.fieldDecoders.getOrDefault(
RefUtils.deobClassName(className) + " " + target, 1L);
}
else
{
o = Math.toIntExact(MixinProcessorMojo.fieldDecoders.getOrDefault(RefUtils.deobClassName(className) +
" " + target,
1L));
}
}
MixinProcessorMojo.log("\tGetFieldDecoderTransformer: %s %s %s %s %s",
RefUtils.deobClassName(className), name, target, desc, o);
super.visitLdcInsn(o);
}
};
}
}, 0);
return cw.toByteArray();
}
}

View File

@@ -0,0 +1,101 @@
/*
* Copyright (c) 2019, ThatGamerBlue <thatgamerblue@gmail.com>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package us.runelitepl.mixinprocessor.transformers;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.tree.AnnotationNode;
import org.objectweb.asm.tree.ClassNode;
import org.objectweb.asm.tree.MethodNode;
import us.runelitepl.mixinprocessor.MixinProcessorMojo;
public class ProvidedRemoverTransformer extends AsmBaseTransformer
{
private final String className;
private final byte[] bytecode;
public ProvidedRemoverTransformer(String className, byte[] bytecode)
{
this.className = className;
this.bytecode = bytecode;
}
public void buildMethodList()
{
ClassReader cr = new ClassReader(bytecode);
ClassNode node = new ClassNode();
cr.accept(node, 0);
for(Object obj : node.methods)
{
MethodNode method = (MethodNode) obj;
if(method == null)
{
MixinProcessorMojo.log("ProvidedRemoverTransformer: Method null?");
continue;
}
if(method.visibleAnnotations == null)
{
continue;
}
for(Object obj2 : method.visibleAnnotations)
{
AnnotationNode annot = (AnnotationNode) obj2;
if(annot.desc.equals(AnnotationRemoverTransformer.makeAnnotationDescriptor("Provided")))
{
validMethods.add(method.access + " " + method.desc + " " + method.name);
}
}
}
}
@Override
public byte[] transform()
{
buildMethodList();
ClassReader cr = new ClassReader(bytecode);
ClassWriter cw = new ClassWriter(cr, 0);
cr.accept(new ClassVisitor(Opcodes.ASM6, cw)
{
@Override
public MethodVisitor visitMethod(int access, String name, String desc, String signature,
String[] exceptions)
{
if(validMethods.contains(access + " " + desc + " " + name))
{
return null;
}
return super.visitMethod(access, name, desc, signature, exceptions);
}
}, 0);
return cw.toByteArray();
}
}

View File

@@ -0,0 +1,44 @@
/*
* Copyright (c) 2019, ThatGamerBlue <thatgamerblue@gmail.com>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package us.runelitepl.mixinprocessor.util;
import javassist.CannotCompileException;
import javassist.CtClass;
import java.io.IOException;
public class JavassistUtils
{
public static byte[] getClassBytecode(CtClass clazz) throws IOException, CannotCompileException
{
clazz.stopPruning(true);
byte[] retVal = clazz.toBytecode();
clazz.defrost();
return retVal;
}
}

View File

@@ -0,0 +1,347 @@
/*
* Copyright (c) 2019, ThatGamerBlue <thatgamerblue@gmail.com>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package us.runelitepl.mixinprocessor.util;
import us.runelitepl.mixinprocessor.MixinProcessorMojo;
import java.util.Map;
public class RefUtils
{
public static final String TYPE_PREFIX = "us/runelitepl/mixins/";
public static final String STATICS_STRING = "_Statics_";
public static final String[] POSSIBLE_GARBAGE_TYPES = {"I", "B", "S"};
public static String deobClassName(String obbed)
{
for (Map.Entry<String, String> entry : MixinProcessorMojo.classNames.entrySet())
{
if (entry.getValue().equals(obbed))
{
return entry.getKey();
}
}
return obbed;
}
public static String getObbedClassName(String deob)
{
return MixinProcessorMojo.classNames.getOrDefault(deob, deob);
}
public static String reobClassName(String deobbed)
{
String old = deobbed;
if (deobbed.contains("/"))
{
deobbed = deobbed.replaceAll(TYPE_PREFIX, "");
deobbed = getObbedClassName(deobbed);
if (deobbed == null)
{
return old;
}
}
return deobbed;
}
@Deprecated
public static String reobFieldNameDangerous(String deob, String desc)
{
int i = 0;
Map.Entry<String, String> e1 = null;
for (Map.Entry<String, String> entry : MixinProcessorMojo.fieldNames.entrySet())
{
if (entry.getKey().endsWith(deob + " " + desc))
{
i++;
e1 = entry;
}
}
if (i == 1)
{
return e1.getValue();
}
return null;
}
@Deprecated
public static String reobMethodNameDangerous(String deob, String signature)
{
int i = 0;
Map.Entry<String, String> e1 = null;
for (Map.Entry<String, String> entry : MixinProcessorMojo.methodNames.entrySet())
{
if (entry.getKey().endsWith(" " + deob + " " + signature))
{
i++;
e1 = entry;
}
}
if (i == 1)
{
return e1.getValue();
}
return null;
}
public static String reobFieldName(String owner, String deob, String desc)
{
if (owner.equals(RefUtils.STATICS_STRING))
{
return reobFieldNameDangerous(deob, desc);
}
String asd = MixinProcessorMojo.fieldNames.getOrDefault(owner + " " + deob + " " + desc, null);
if (asd == null)
{
return null;
}
return asd.split(" ")[1];
}
public static String reobMethodName(String owner, String deob, String signature)
{
String prefix = "";
if(owner.startsWith(RefUtils.TYPE_PREFIX))
{
owner = owner.substring(RefUtils.TYPE_PREFIX.length());
}
if (deob.startsWith("prepend$"))
{
prefix = "prepend$";
deob = deob.substring("prepend$".length());
}
else if (deob.startsWith("append$"))
{
prefix = "append$";
deob = deob.substring("append$".length());
}
if (owner.equals(RefUtils.STATICS_STRING))
{
String retVal = reobMethodNameDangerous(deob, signature);
if (retVal == null)
{
return null;
}
String[] split = retVal.split(" ");
split[1] = prefix + split[1];
return String.join(" ", split);
}
String asd = MixinProcessorMojo.methodNames.getOrDefault(owner + " " + deob + " " + signature, null);
if (asd == null)
{
return null;
}
return prefix + asd.split(" ")[1];
}
@Deprecated
public static boolean shouldReobFieldDangerous(String deob, String desc)
{
int i = 0;
for (Map.Entry<String, Boolean> entry : MixinProcessorMojo.isFieldTagged.entrySet())
{
if (entry.getKey().endsWith(deob + " " + desc))
{
i++;
}
}
if (i == 1)
{
return true;
}
return false;
}
@Deprecated
public static boolean shouldReobMethodDangerous(String deob, String desc)
{
int i = 0;
for (Map.Entry<String, Boolean> entry : MixinProcessorMojo.isMethodTagged.entrySet())
{
if (entry.getKey().endsWith(" " + deob + " " + desc))
{
i++;
}
}
if (i == 1)
{
return true;
}
return false;
}
public static boolean shouldReobField(String owner, String deob, String desc)
{
if (owner.equals(RefUtils.STATICS_STRING))
{
return shouldReobFieldDangerous(deob, desc);
}
return MixinProcessorMojo.isFieldTagged.getOrDefault(owner + " " + deob + " " + desc, false);
}
public static boolean shouldReobMethod(String owner, String deob, String desc)
{
if(owner.startsWith(RefUtils.TYPE_PREFIX))
{
owner = owner.substring(RefUtils.TYPE_PREFIX.length());
}
if (owner.equals(RefUtils.STATICS_STRING))
{
return shouldReobMethodDangerous(deob, desc);
}
return MixinProcessorMojo.isMethodTagged.getOrDefault(owner + " " + deob + " " + desc, false);
}
public static String reobDescriptor(String descriptor)
{
if (!descriptor.startsWith("L"))
{
return descriptor;
}
if (!descriptor.contains("us/runelitepl/mixins/"))
{
return descriptor;
}
String orig = descriptor;
descriptor = descriptor.replace("us/runelitepl/mixins/", "");
descriptor = descriptor.substring(1, descriptor.length() - 1);
descriptor = getObbedClassName(descriptor);
if (descriptor == null)
{
return orig;
}
return "L" + descriptor + ";";
}
public static String reobMethodDescriptor(String descriptor)
{
int strIndex = 0;
if (descriptor.charAt(0) != '(')
{
throw new IllegalArgumentException("sig is not a method signature: " + descriptor);
}
StringBuilder deobbed = new StringBuilder(1024 * 1024);
while (strIndex < descriptor.length())
{
switch (descriptor.charAt(strIndex))
{
case '(':
case ')':
case 'I':
case 'B':
case 'J':
case 'S':
case 'D':
case 'F':
case 'C':
case 'Z':
case 'V':
case '[':
deobbed.append(descriptor.charAt(strIndex));
strIndex++;
break;
case 'L':
try
{
String sigPart = descriptor.substring(strIndex, descriptor.indexOf(";", strIndex) + 1);
String className = sigPart.substring(1, sigPart.length() - 1);
className = className.replace(TYPE_PREFIX, "");
String obbedName = MixinProcessorMojo.classNames.getOrDefault(className, null);
if (obbedName == null)
{
obbedName = className;
}
deobbed.append("L" + obbedName + ";");
strIndex += sigPart.length();
}
catch (StringIndexOutOfBoundsException ex)
{
System.err.println(
"Method signature %s is probably missing a semi-colon".replace("%s", descriptor));
throw ex;
}
break;
default:
throw new IllegalArgumentException("signature is invalid: " + descriptor);
}
}
return deobbed.toString();
}
public static String appendArgument(String desc, String append)
{
int strIndex = 0;
if (desc.charAt(0) != '(')
{
throw new IllegalArgumentException("sig is not a method signature: " + desc);
}
StringBuilder deobbed = new StringBuilder(1024 * 1024);
while (strIndex < desc.length())
{
switch (desc.charAt(strIndex))
{
case ')':
deobbed.append(append);
deobbed.append(")");
strIndex++;
break;
case '(':
case 'I':
case 'B':
case 'J':
case 'S':
case 'D':
case 'F':
case 'C':
case 'Z':
case 'V':
case '[':
deobbed.append(desc.charAt(strIndex));
strIndex++;
break;
case 'L':
try
{
String sigPart = desc.substring(strIndex, desc.indexOf(";", strIndex) + 1);
deobbed.append(sigPart);
strIndex += sigPart.length();
}
catch (StringIndexOutOfBoundsException ex)
{
System.err.println(
"Method signature %s is probably missing a semi-colon".replace("%s", desc));
throw ex;
}
break;
default:
throw new IllegalArgumentException("signature is invalid: " + desc);
}
}
return deobbed.toString();
}
}

View File

@@ -0,0 +1,68 @@
/*
* Copyright (c) 2019, ThatGamerBlue <thatgamerblue@gmail.com>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package us.runelitepl.mixinprocessor.util;
import java.io.BufferedReader;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.URL;
import java.net.URLConnection;
import java.nio.charset.StandardCharsets;
import java.util.stream.Collectors;
public class WebUtils
{
public static String getUrlContent(String url) throws IOException
{
String pageText;
URLConnection conn = new URL(url).openConnection();
try (BufferedReader reader = new BufferedReader(
new InputStreamReader(conn.getInputStream(), StandardCharsets.UTF_8)))
{
pageText = reader.lines().collect(Collectors.joining("\n"));
}
return pageText;
}
public static byte[] downloadFile(String urlText) throws IOException {
URL url = new URL(urlText);
ByteArrayOutputStream output = new ByteArrayOutputStream();
try (InputStream inputStream = url.openStream()) {
int n = 0;
byte [] buffer = new byte[ 1024 ];
while (-1 != (n = inputStream.read(buffer))) {
output.write(buffer, 0, n);
}
}
return output.toByteArray();
}
}