Initial commit.

This commit is contained in:
md_5 2012-11-27 23:02:36 +11:00
commit e5af484c54
12 changed files with 903 additions and 0 deletions

42
.gitignore vendored Normal file
View File

@ -0,0 +1,42 @@
# Eclipse stuff
/.classpath
/.project
/.settings
# netbeans
/nbproject
/nbactions.xml
/nb-configuration.xml
# we use maven!
/build.xml
# maven
/target
/dependency-reduced-pom.xml
# vim
.*.sw[a-p]
# various other potential build files
/build
/bin
/dist
/manifest.mf
# Mac filesystem dust
/.DS_Store
# intellij
*.iml
*.ipr
*.iws
.idea/
# other files
*.csv
*.jar
*.log
*.rg
*.srg
*.txt

24
LICENSE Normal file
View File

@ -0,0 +1,24 @@
Copyright (c) 2012, md_5
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* 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.
* Neither the name of the <organization> nor the
names of its contributors may be used to endorse or promote products
derived from this software without specific prior written permission.
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 <COPYRIGHT HOLDER> 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.

4
README.md Normal file
View File

@ -0,0 +1,4 @@
Special Source
==============
Automatic generator and renamer of jar obfuscation mappings.

126
pom.xml Normal file
View File

@ -0,0 +1,126 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.sonatype.oss</groupId>
<artifactId>oss-parent</artifactId>
<version>7</version>
</parent>
<groupId>net.md-5</groupId>
<artifactId>SpecialSource</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>
<name>SpecialSource</name>
<description>A jar compare and renaming engine designed for comparing and remapping 2 jars of differnent obfuscation mappings. Can also be useful for reobfuscation.</description>
<url>https://github.com/md-5/SpecialSource</url>
<inceptionYear>2012</inceptionYear>
<licenses>
<license>
<name>The BSD 3-Clause License</name>
<url>http://opensource.org/licenses/BSD-3-Clause</url>
<distribution>repo</distribution>
</license>
</licenses>
<developers>
<developer>
<id>md_5</id>
<name>Michael Dardis</name>
<email>md_5@live.com.au</email>
<timezone>+10</timezone>
</developer>
</developers>
<scm>
<connection>scm:git:git@github.com:md-5/SpecialSource.git</connection>
<developerConnection>scm:git:git@github.com:md-5/SpecialSource.git</developerConnection>
<url>git@github.com:md-5/SpecialSource.git</url>
</scm>
<issueManagement>
<system>GitHub</system>
<url>https://github.com/md-5/SpecialSource/issues</url>
</issueManagement>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>org.ow2.asm</groupId>
<artifactId>asm-all</artifactId>
<version>4.1</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>0.11.6</version>
<scope>provided</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>com.mycila.maven-license-plugin</groupId>
<artifactId>maven-license-plugin</artifactId>
<version>1.10.b1</version>
<executions>
<execution>
<phase>clean</phase>
<goals>
<goal>format</goal>
</goals>
</execution>
</executions>
<configuration>
<strictCheck>true</strictCheck>
<header>${basedir}/LICENSE</header>
<includes>
<include>src/**</include>
</includes>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.0</version>
<configuration>
<source>1.7</source>
<target>1.7</target>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<version>2.4</version>
<configuration>
<archive>
<manifestEntries>
<Main-Class>${main.class}</Main-Class>
</manifestEntries>
</archive>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>2.0</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
</execution>
</executions>
<configuration>
<minimizeJar>true</minimizeJar>
</configuration>
</plugin>
</plugins>
</build>
</project>

View File

@ -0,0 +1,104 @@
/**
* Copyright (c) 2012, md_5
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * 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.
* * Neither the name of the <organization> nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* 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 <COPYRIGHT HOLDER> 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 net.md_5.specialsource;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.jar.JarFile;
import java.util.zip.ZipEntry;
import lombok.AccessLevel;
import lombok.EqualsAndHashCode;
import lombok.RequiredArgsConstructor;
import lombok.ToString;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.tree.ClassNode;
/**
* This class wraps a {@link JarFile} enabling quick access to the jar's main
* class, as well as the ability to get the {@link InputStream} of a class file,
* and speedy lookups to see if the jar contains the specified class.
*/
@ToString
@EqualsAndHashCode
@RequiredArgsConstructor(access = AccessLevel.PRIVATE)
public class Jar {
public final JarFile file;
public final String main;
private final Set<String> contains = new HashSet<>();
private final Map<String, ClassNode> classes = new HashMap<>();
public boolean containsClass(String clazz) {
return contains.contains(clazz) ? true : getClass(clazz) != null;
}
public InputStream getClass(String clazz) {
try {
ZipEntry e = file.getEntry(clazz + ".class");
if (e != null) {
contains.add(clazz);
}
return e == null ? null : file.getInputStream(e);
} catch (IOException ex) {
throw new RuntimeException(ex);
}
}
public ClassNode getNode(String clazz) {
try {
ClassNode cache = classes.get(clazz);
if (cache != null) {
return cache;
}
InputStream is = getClass(clazz);
if (is != null) {
ClassReader cr = new ClassReader(getClass(clazz));
ClassNode node = new ClassNode();
cr.accept(node, 0);
classes.put(clazz, node);
return node;
} else {
return null;
}
} catch (IOException ex) {
System.out.println(clazz);
throw new RuntimeException(ex);
}
}
public static Jar init(String jar) throws IOException {
File file = new File(jar);
JarFile jarFile = new JarFile(file);
String main = jarFile.getManifest().getMainAttributes().getValue("Main-Class").replace('.', '/');
return new Jar(jarFile, main);
}
}

View File

@ -0,0 +1,126 @@
/**
* Copyright (c) 2012, md_5
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * 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.
* * Neither the name of the <organization> nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* 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 <COPYRIGHT HOLDER> 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 net.md_5.specialsource;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.FieldVisitor;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type;
public class JarComparer extends ClassVisitor {
private final MethodReferenceFinder methodVisitor = new MethodReferenceFinder();
public final Jar jar;
private String myName;
public int iterDepth;
public NoDupeList<String> classes = new NoDupeList<>();
public NoDupeList<Ownable> fields = new NoDupeList<>();
public NoDupeList<Ownable> methods = new NoDupeList<>();
private void visitType(Type type) {
// FIXME: Scan arrays too!
if (type.getSort() == Type.OBJECT) {
String name = type.getInternalName();
if (jar.containsClass(name)) {
classes.add(name);
}
}
}
public JarComparer(Jar jar) {
super(Opcodes.ASM4);
this.jar = jar;
}
@Override
public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
myName = name;
// FIXME: Scan the super class too!
for (String implement : interfaces) {
if (jar.containsClass(implement)) {
classes.add(implement);
}
}
}
@Override
public FieldVisitor visitField(int access, String name, String desc, String signature, Object value) {
Ownable field = new Ownable(NodeType.FIELD, myName, name, desc);
fields.add(field);
return null; // No need to get more info about a field!
}
@Override
public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
// Check for initializers
if (!name.equals("<init>") && !name.equals("<clinit>")) {
Ownable method = new Ownable(NodeType.METHOD, myName, name, desc);
methods.add(method);
}
// FIXME: Scan return types too!
for (Type t : Type.getArgumentTypes(desc)) {
visitType(t);
}
return methodVisitor;
}
private class MethodReferenceFinder extends MethodVisitor {
public MethodReferenceFinder() {
super(Opcodes.ASM4);
}
@Override
public void visitFieldInsn(int opcode, String owner, String name, String desc) {
if (jar.containsClass(owner)) {
classes.add(owner);
}
}
@Override
public void visitLdcInsn(Object cst) {
if (cst instanceof Type) {
visitType((Type) cst);
}
}
@Override
public void visitMethodInsn(int opcode, String owner, String name, String desc) {
if (jar.containsClass(owner)) {
classes.add(owner);
}
}
@Override
public void visitTypeInsn(int opcode, String type) {
if (jar.containsClass(type)) {
classes.add(type);
}
}
}
}

View File

@ -0,0 +1,188 @@
/**
* Copyright (c) 2012, md_5
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * 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.
* * Neither the name of the <organization> nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* 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 <COPYRIGHT HOLDER> 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 net.md_5.specialsource;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.jar.JarEntry;
import java.util.jar.JarOutputStream;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.commons.Remapper;
import org.objectweb.asm.commons.RemappingClassAdapter;
import org.objectweb.asm.tree.ClassNode;
import org.objectweb.asm.tree.FieldNode;
import org.objectweb.asm.tree.MethodNode;
public class JarRemapper extends Remapper {
private static final int CLASS_LEN = ".class".length();
private static final String HEADER = ""
+ "# THESE ARE AUTOMATICALLY GENERATED MAPPINGS BETWEEN {0} and {1}\n"
+ "# THEY WERE GENERATED ON {2} USING Special Source (c) md_5 2012.\n"
+ "# PLEASE DO NOT REMOVE THIS HEADER OR DISTRIBUTE THIS FILE WITHOUT PERMISSION!\n";
private final JarComparer oldJar;
private final JarComparer newJar;
private final Jar self;
private final Map<String, String> classes = new HashMap<>();
private final Map<String, String> fields = new HashMap<>();
private final Map<String, String> methods = new HashMap<>();
private JarRemapper(JarComparer oldJar, JarComparer newJar, Jar self, File logfile) throws IOException {
SpecialSource.validate(oldJar, newJar);
this.oldJar = oldJar;
this.newJar = newJar;
this.self = self;
List<String> searge = new ArrayList<>();
for (int i = 0; i < oldJar.classes.size(); i++) {
String oldClass = oldJar.classes.get(i);
String newClass = newJar.classes.get(i);
classes.put(oldClass, newClass);
if (!Objects.equals(oldClass, newClass)) {
searge.add("CL: " + oldClass + " " + newClass);
}
}
for (int i = 0; i < oldJar.fields.size(); i++) {
Ownable oldField = oldJar.fields.get(i);
Ownable newField = newJar.fields.get(i);
fields.put(oldField.owner + "/" + oldField.name, newField.name);
if (!Objects.equals(oldField, newField)) {
searge.add("FD: " + oldField.owner + "/" + oldField.name + " " + newField.owner + "/" + newField.name);
}
}
for (int i = 0; i < oldJar.methods.size(); i++) {
Ownable oldMethod = oldJar.methods.get(i);
Ownable newMethod = newJar.methods.get(i);
methods.put(oldMethod.owner + "/" + oldMethod.name + " " + oldMethod.descriptor, newMethod.name);
if (!Objects.equals(oldMethod, newMethod)) {
searge.add("MD: " + oldMethod.owner + "/" + oldMethod.name + " " + oldMethod.descriptor + " " + newMethod.owner + "/" + newMethod.name + " " + newMethod.descriptor);
}
}
Collections.sort(searge);
try (PrintWriter out = new PrintWriter(logfile)) {
out.println(MessageFormat.format(HEADER, oldJar.jar.file.getName(), newJar.jar.file.getName(), new Date()));
for (String s : searge) {
out.println(s);
}
}
}
@Override
public String map(String typeName) {
int index = typeName.indexOf('$');
String key = (index == -1) ? typeName : typeName.substring(0, index);
String mapped = classes.get(key);
return mapped != null ? mapped + (index == -1 ? "" : typeName.substring(index, typeName.length())) : typeName;
}
@Override
public String mapFieldName(String owner, String name, String desc) {
String mapped = tryClimb(fields, NodeType.FIELD, owner, name);
return mapped == null ? name : mapped;
}
private String tryClimb(Map<String, String> map, NodeType type, String owner, String name) {
String key = owner + "/" + name;
if (key.contains("findNearestMapFeature")) {
System.out.println();
}
String mapped = map.get(key);
if (mapped == null) {
ClassNode node = self.getNode(owner);
if (node != null) {
for (String iface : (List<String>) node.interfaces) {
mapped = tryClimb(map, type, iface, name);
if (mapped != null) {
return mapped;
}
}
return tryClimb(map, type, node.superName, name);
}
}
return mapped;
}
@Override
public String mapMethodName(String owner, String name, String desc) {
String mapped = tryClimb(methods, NodeType.METHOD, owner, name + " " + desc);
return mapped == null ? name : mapped;
}
public static void renameJar(Jar jar, File target, JarComparer oldNames, JarComparer newNames) throws IOException {
try (JarOutputStream out = new JarOutputStream(new FileOutputStream(target))) {
JarRemapper self = new JarRemapper(oldNames, newNames, jar, new File(target.getPath() + ".srg"));
for (Enumeration<JarEntry> entr = jar.file.entries(); entr.hasMoreElements();) {
JarEntry entry = entr.nextElement();
try (InputStream is = jar.file.getInputStream(entry)) {
String name = entry.getName();
byte[] data;
if (name.endsWith(".class")) {
name = name.substring(0, name.length() - CLASS_LEN);
ClassReader reader = new ClassReader(is);
ClassWriter wr = new ClassWriter(0);
RemappingClassAdapter mapper = new RemappingClassAdapter(wr, self);
reader.accept(mapper, ClassReader.EXPAND_FRAMES);
data = wr.toByteArray();
String newName = self.map(name);
entry = new JarEntry(newName == null ? name : newName + ".class");
} else {
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
int n;
byte[] b = new byte[1 << 15]; // Max class file size, arbritrary number
while ((n = is.read(b, 0, b.length)) != -1) {
buffer.write(b, 0, n);
}
buffer.flush();
data = buffer.toByteArray();
}
entry.setTime(0);
out.putNextEntry(entry);
out.write(data);
}
}
}
}
}

View File

@ -0,0 +1,69 @@
/**
* Copyright (c) 2012, md_5
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * 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.
* * Neither the name of the <organization> nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* 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 <COPYRIGHT HOLDER> 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 net.md_5.specialsource;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
/**
* A pseudo class implementing the {@link List} like, but does not allow
* duplicates. Is backed by an {@link ArrayList} and {@link HashSet} for speedy
* contains lookups.
*
* @param <E> the type of elements contained in this 'list'
*/
public class NoDupeList<E> implements Iterable<E> {
private final Set<E> set = new HashSet<>();
private final List<E> backing = new ArrayList<>();
public boolean add(E e) {
if (set.contains(e)) {
return false;
} else {
set.add(e);
backing.add(e);
return true;
}
}
public E get(int index) {
return backing.get(index);
}
public int size() {
return backing.size();
}
@Override
public Iterator<E> iterator() {
return backing.iterator();
}
}

View File

@ -0,0 +1,32 @@
/**
* Copyright (c) 2012, md_5
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * 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.
* * Neither the name of the <organization> nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* 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 <COPYRIGHT HOLDER> 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 net.md_5.specialsource;
public enum NodeType {
FIELD, METHOD;
}

View File

@ -0,0 +1,42 @@
/**
* Copyright (c) 2012, md_5
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * 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.
* * Neither the name of the <organization> nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* 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 <COPYRIGHT HOLDER> 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 net.md_5.specialsource;
import lombok.Data;
/**
* A class which can be used to represent a field, method, or anything else
* which has an owner, a name and a descriptor.
*/
@Data
public class Ownable {
final NodeType type;
final String owner;
final String name;
final String descriptor;
}

View File

@ -0,0 +1,46 @@
/**
* Copyright (c) 2012, md_5
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * 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.
* * Neither the name of the <organization> nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* 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 <COPYRIGHT HOLDER> 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 net.md_5.specialsource;
import lombok.EqualsAndHashCode;
import lombok.RequiredArgsConstructor;
import lombok.ToString;
/**
* A class representing a set of 2 objects as defined by the type parameters.
*
* @param <T1> First type
* @param <T2> Second type
*/
@ToString
@EqualsAndHashCode
@RequiredArgsConstructor
public class Pair<T1, T2> {
public final T1 first;
public final T2 second;
}

View File

@ -0,0 +1,100 @@
/**
* Copyright (c) 2012, md_5
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * 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.
* * Neither the name of the <organization> nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* 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 <COPYRIGHT HOLDER> 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 net.md_5.specialsource;
import java.io.File;
import java.io.IOException;
import java.util.jar.JarFile;
import org.objectweb.asm.ClassReader;
public class SpecialSource {
public static void main(String[] args) throws Exception {
args = new String[]{"jars/1.4.5/bukkit.jar", "jars/1.4.5/vanilla.jar", "jars/1.4.5/craftbukkit-1.4.5-R0.3-SNAPSHOT.jar"};
if (args.length != 3) {
System.err.println("SpecialSource takes 3 arguments. It will take 2 jars to generate a difference between, and a 3rd jar based on the first jar to rename to the second jar.");
System.err.println("Usage: java -jar SpecialSource.jar <first jar> <second jar> <jar of first names>");
return;
}
System.out.println("Reading jars");
Jar jar1 = Jar.init(args[0]);
Jar jar2 = Jar.init(args[1]);
System.out.println("Creating jar compare");
JarComparer visitor1 = new JarComparer(jar1);
JarComparer visitor2 = new JarComparer(jar2);
visit(new Pair<>(jar1, jar2), new Pair<>(visitor1, visitor2), new Pair<>(jar1.main, jar2.main));
System.out.println("Checking vailidity");
if (visitor1.classes.size() != 1004 || visitor2.classes.size() != 1004) {
throw new IllegalStateException("classes");
}
if (visitor1.fields.size() != 3582 || visitor2.fields.size() != 3582) {
throw new IllegalStateException("fields");
}
if (visitor1.methods.size() != 6531 + 4 || visitor2.methods.size() != 6531 + 4) { // 3 broken enums (EnumEntitySize, EnumFacing, EnumGameType), and 1 main method
throw new IllegalStateException("methods");
}
System.out.println("Renaming final jar");
JarRemapper.renameJar(Jar.init(args[2]), new File("out.jar"), visitor1, visitor2);
}
private static void visit(Pair<Jar, Jar> jars, Pair<JarComparer, JarComparer> visitors, Pair<String, String> classes) throws IOException {
JarComparer visitor1 = visitors.first;
JarComparer visitor2 = visitors.second;
ClassReader clazz1 = new ClassReader(jars.first.getClass(classes.first));
ClassReader clazz2 = new ClassReader(jars.second.getClass(classes.second));
clazz1.accept(visitor1, 0);
clazz2.accept(visitor2, 0);
validate(visitor1, visitor2);
while (visitor1.iterDepth < visitor1.classes.size()) {
String className1 = visitor1.classes.get(visitor1.iterDepth);
String className2 = visitor2.classes.get(visitor1.iterDepth);
Pair<String, String> pair = new Pair<>(className1, className2);
visitor1.iterDepth++;
visit(jars, visitors, pair);
}
}
public static void validate(JarComparer visitor1, JarComparer visitor2) {
if (visitor1.classes.size() != visitor2.classes.size()) {
throw new IllegalStateException("classes");
}
if (visitor1.fields.size() != visitor2.fields.size()) {
throw new IllegalStateException("fields");
}
if (visitor1.methods.size() != visitor2.methods.size()) {
throw new IllegalStateException("methods");
}
}
}