package net.fybertech.dynamicmappings;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import net.fybertech.dynamicmappings.InheritanceMap.FieldHolder;
import net.fybertech.dynamicmappings.InheritanceMap.MethodHolder;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.Type;
import org.objectweb.asm.tree.ClassNode;
import org.objectweb.asm.tree.FieldNode;
import org.objectweb.asm.tree.MethodNode;
public class ModMappings {
/*public static List<String> walkTreeForClasses(File initialDir, File dir)
{
List<String> files = new ArrayList<>();
for (File f : dir.listFiles()) {
if (f.isDirectory()) files.addAll(walkTreeForClasses(initialDir, f));
else if (f.getName().endsWith(".class")) {
String className = initialDir.toURI().relativize(f.toURI()).getPath();
className = className.substring(0, className.length() - 6);
files.add(className);
}
}
return files;
}*/
static class Holder
{
public int type;
public String owner;
public String name;
public String desc;
public Holder(int t, String o, String n, String d) {
type = t;
owner = o;
name = n;
desc = d;
}
@Override
public int hashCode() {
int result = 17;
result = 31 * result + type;
result = 31 * result + owner.hashCode();
result = 31 * result + name.hashCode();
result = 31 * result + desc.hashCode();
return result;
}
@Override
public boolean equals(Object obj) {
return obj.hashCode() == this.hashCode();
}
}
static String[] exclusions = new String[] {
"org/objectweb/",
"java/",
"net/minecraft/launchwrapper/",
"net/fybertech/dynamicmappings/",
"org/apache/",
"net/fybertech/meddle/",
"net/fybertech/meddleapi/"
};
public static boolean isExcluded(String owner)
{
for (String exclusion : exclusions) if (owner.startsWith(exclusion)) return true;
return false;
}
public static List<File> walkTreeForClasses(File dir)
{
List<File> files = new ArrayList<>();
for (File f : dir.listFiles()) {
if (f.isDirectory()) files.addAll(walkTreeForClasses(f));
else if (f.getName().endsWith(".class")) files.add(f);
}
return files;
}
public static Set<String> searchConstantPoolForClasses(File classFile)
{
Set<String> classes = new HashSet<>();
InputStream stream;
ClassReader reader;
try {
stream = new FileInputStream(classFile);
reader = new ClassReader(stream);
} catch (Exception e1) { return classes; }
int itemCount = reader.getItemCount();
char[] buffer = new char[reader.getMaxStringLength()];
for (int n = 1; n < itemCount; n++) {
int pos = reader.getItem(n);
if (pos == 0 || reader.b[pos - 1] != 7) continue;
Arrays.fill(buffer, (char)0);
reader.readUTF8(pos, buffer);
String string = (new String(buffer)).trim();
if (string.length() < 1) continue;
while (string != null && string.startsWith("[")) string = getArrayType(string);
if (string == null) continue;
if (!isExcluded(string)) classes.add(string);
}
return classes;
}
public static Set<Holder> searchConstantPoolForFields(File classFile)
{
Set<Holder> fields = new HashSet<>();
InputStream stream;
ClassReader reader;
try {
stream = new FileInputStream(classFile);
reader = new ClassReader(stream);
} catch (Exception e1) { return fields; }
int itemCount = reader.getItemCount();
char[] buffer = new char[reader.getMaxStringLength()];
for (int n = 1; n < itemCount; n++) {
int pos = reader.getItem(n);
if (pos == 0) continue;
int cpType = reader.b[pos - 1];
if (cpType != 9) continue;
String owner = reader.readClass(pos, buffer);
int cpIndex = reader.getItem(reader.readUnsignedShort(pos + 2));
String name = reader.readUTF8(cpIndex, buffer);
String desc = reader.readUTF8(cpIndex + 2, buffer);
if (!isExcluded(owner)) fields.add(new Holder(cpType, owner, name, desc));
}
return fields;
}
public static Set<Holder> searchConstantPoolForMethods(File classFile)
{
Set<Holder> fields = new HashSet<>();
InputStream stream;
ClassReader reader;
try {
stream = new FileInputStream(classFile);
reader = new ClassReader(stream);
} catch (Exception e1) { return fields; }
int itemCount = reader.getItemCount();
char[] buffer = new char[reader.getMaxStringLength()];
for (int n = 1; n < itemCount; n++) {
int pos = reader.getItem(n);
if (pos == 0) continue;
int cpType = reader.b[pos - 1];
if (cpType != 10 && cpType != 11) continue;
String owner = reader.readClass(pos, buffer);
int cpIndex = reader.getItem(reader.readUnsignedShort(pos + 2));
String name = reader.readUTF8(cpIndex, buffer);
String desc = reader.readUTF8(cpIndex + 2, buffer);
if (!isExcluded(owner)) fields.add(new Holder(cpType, owner, name, desc));
}
return fields;
}
public static Set<Holder> getClassFields(File classFile)
{
Set<Holder> fields = new HashSet<>();
InputStream stream;
ClassReader reader;
ClassNode cn = new ClassNode();
try {
stream = new FileInputStream(classFile);
reader = new ClassReader(stream);
} catch (Exception e1) { return fields; }
reader.accept(cn, 0);
for (FieldNode field : cn.fields) {
fields.add(new Holder(9, cn.name, field.name, field.desc));
}
return fields;
}
public static Set<Holder> getClassMethods(File classFile)
{
Set<Holder> methods = new HashSet<>();
InputStream stream;
ClassReader reader;
ClassNode cn = new ClassNode();
try {
stream = new FileInputStream(classFile);
reader = new ClassReader(stream);
} catch (Exception e1) { return methods; }
reader.accept(cn, 0);
for (MethodNode method : cn.methods) {
methods.add(new Holder(10, cn.name, method.name, method.desc));
}
return methods;
}
public static String getArrayType(String array)
{
Type t = Type.getType(array);
if (t.getSort() != Type.ARRAY) return array;
t = t.getElementType();
if (t.getSort() != Type.OBJECT) return null;
return t.getClassName().replace(".", "/");
}
public static int getMappingSide(String mapping)
{
if (DynamicMappings.clientMappingsSet.contains(mapping)) return 1;
if (DynamicMappings.serverMappingsSet.contains(mapping)) return 2;
return 3;
}
public static void main(String[] args)
{
//System.out.println(System.getProperty("java.class.path"));
if (args.length < 1) {
System.out.println("No input directory specified!");
return;
}
File classDir = new File(args[0]);
if (!classDir.exists() || !classDir.isDirectory()) {
System.out.println("Doesn't exist or isn't directory: " + args[0]);
return;
}
File outputFile = null;
if (args.length < 2) {
System.out.println("No output file specified, printing to console.");
}
else outputFile = new File(args[1]);
// Put DynamicMappings in simulated mode so that we can get the full list of mappings they
// might provide, while allowing this application to use a remapped jar for its inheritance
// maps.
DynamicMappings.simulatedMappings = true;
DynamicMappings.generateClassMappings();
//System.out.println(classDir.getAbsolutePath());
Set<String> classes = new HashSet<>();
Set<Holder> fields = new HashSet<>();
Set<Holder> methods = new HashSet<>();
List<File> classFiles = walkTreeForClasses(classDir);
for (File f : classFiles) {
classes.addAll(searchConstantPoolForClasses(f));
fields.addAll(searchConstantPoolForFields(f));
fields.addAll(getClassFields(f));
methods.addAll(searchConstantPoolForMethods(f));
methods.addAll(getClassMethods(f));
}
InheritanceMap inheritanceMap = new InheritanceMap();
List<String> output = new ArrayList<>();
//System.out.println("Classes:");
for (String s : classes) {
boolean hasMapping = DynamicMappings.classMappings.containsKey(s);
//System.out.println(" " + (hasMapping ? "TRUE " :"") + s);
if (hasMapping) output.add("C " + getMappingSide(s) + " " + s);
}
//System.out.println("Fields:");
for (Holder holder : fields) {
//System.out.println(" " + holder.owner + " " + holder.name + " " + holder.desc);
InheritanceMap im = null;
try {
im = inheritanceMap.buildMap(holder.owner);
} catch (IOException e) {
e.printStackTrace();
}
if (im == null) { System.out.println("COULDN'T BUILD MAP: " + holder.owner); continue; }
HashSet<FieldHolder> fh = im.fields.get(holder.name + " " + holder.desc);
for (FieldHolder f : fh) {
String mapping = f.cn.name + " " + f.fn.name + " " + f.fn.desc;
boolean hasMapping = DynamicMappings.fieldMappings.containsKey(mapping);
//System.out.println(" " + (hasMapping ? "TRUE " :"") + f.cn.name + " " + f.fn.name);
if (hasMapping) output.add("F " + getMappingSide(mapping) + " " + mapping);
}
}
//System.out.println("Methods:");
for (Holder holder : methods) {
//System.out.println(" " + holder.owner + " " + holder.name + " " + holder.desc);
InheritanceMap im = null;
try {
im = inheritanceMap.buildMap(holder.owner);
} catch (IOException e) {
e.printStackTrace();
}
if (im == null) { System.out.println("COULDN'T BUILD MAP: " + holder.owner); continue; }
HashSet<MethodHolder> mh = im.methods.get(holder.name + " " + holder.desc);
if (mh == null) continue; // Shouldn't be possible in normal circumstances, but we'll do for safety
for (MethodHolder m : mh) {
String mapping = m.cn.name + " " + m.mn.name + " " + m.mn.desc;
boolean hasMapping = DynamicMappings.methodMappings.containsKey(mapping);
//System.out.println(" " + (hasMapping ? "TRUE " :"") + m.cn.name + " " + m.mn.name);
if (hasMapping) output.add("M " + getMappingSide(mapping) + " " + mapping);
}
}
if (outputFile != null) {
outputFile.toPath().getParent().toFile().mkdirs();
PrintWriter pw;
try {
pw = new PrintWriter(new FileOutputStream(outputFile));
for (String line : output) pw.println(line);
pw.close();
} catch (IOException e) {
e.printStackTrace();
}
}
else
for (String line : output) System.out.println(line);
}
}