package net.minecraftforge.lex.fffixer;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
import java.util.zip.ZipOutputStream;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.tree.ClassNode;
import org.objectweb.asm.tree.MethodNode;
import org.objectweb.asm.Type;
import com.google.common.io.ByteStreams;
public class FFFixerImpl
{
final static Logger log = Logger.getLogger("FFFixer");
private final List<IClassProcessor> processors = new ArrayList<IClassProcessor>();
public FFFixerImpl()
{
processors.add(new InnerClassNPEFixer(this));
processors.add(new InnerClassOrderFixer(this));
processors.add(new VariableNumberFixer(this));
processors.add(new EnableStackTracesInLog(this));
}
public static void process(String inFile, String outFile, String logFile) throws IOException
{
FFFixerImpl inst = new FFFixerImpl();
inst.processJar(inFile, outFile);
log.fine("Processed " + inFile);
}
public void processJar(String inFile, String outFile) throws IOException
{
ZipInputStream inJar = null;
ZipOutputStream outJar = null;
try
{
try
{
inJar = new ZipInputStream(new BufferedInputStream(new FileInputStream(inFile)));
}
catch (FileNotFoundException e)
{
throw new FileNotFoundException("Could not open input file: " + e.getMessage());
}
try
{
OutputStream out = (outFile == null ? new ByteArrayOutputStream() : new FileOutputStream(outFile));
outJar = new ZipOutputStream(new BufferedOutputStream(out));
}
catch (FileNotFoundException e)
{
throw new FileNotFoundException("Could not open output file: " + e.getMessage());
}
while (true)
{
ZipEntry entry = inJar.getNextEntry();
if (entry == null)
{
break;
}
if (entry.isDirectory())
{
outJar.putNextEntry(entry);
continue;
}
byte[] data = new byte[4096];
ByteArrayOutputStream entryBuffer = new ByteArrayOutputStream();
int len;
do
{
len = inJar.read(data);
if (len > 0)
{
entryBuffer.write(data, 0, len);
}
} while (len != -1);
byte[] entryData = entryBuffer.toByteArray();
String entryName = entry.getName();
if (entryName.endsWith(".class"))
{
FFFixerImpl.log.log(Level.FINE, "Processing " + entryName);
entryData = this.processClass(entryData, outFile == null);
FFFixerImpl.log.log(Level.FINE, "Processed " + entryBuffer.size() + " -> " + entryData.length);
}
else
{
FFFixerImpl.log.log(Level.FINE, "Copying " + entryName);
}
ZipEntry newEntry = new ZipEntry(entryName);
outJar.putNextEntry(newEntry);
outJar.write(entryData);
}
// Add Out Util class:
String[] extras = {
Type.getInternalName(Util.class) + ".class",
Type.getInternalName(Util.class) + "$1.class",
Type.getInternalName(Util.Indexed.class) + ".class"
};
for (String name : extras)
{
ZipEntry newEntry = new ZipEntry(name);
outJar.putNextEntry(newEntry);
outJar.write(ByteStreams.toByteArray(FFFixerImpl.class.getClassLoader().getResourceAsStream(name)));
}
}
finally
{
if (outJar != null)
{
try
{
outJar.close();
}
catch (IOException e)
{
// ignore
}
}
if (inJar != null)
{
try
{
inJar.close();
}
catch (IOException e)
{
// ignore
}
}
}
}
private boolean workDone = false;
public void setWorkDone()
{
workDone = true;
}
public byte[] processClass(byte[] cls, boolean readOnly)
{
workDone = false;
ClassReader cr = new ClassReader(cls);
ClassNode cn = new ClassNode();
ClassVisitor ca = cn;
//ca = new LineInjectorAdaptor(ASM4, cn);
cr.accept(ca, 0);
for (IClassProcessor proc : processors)
proc.process(cn);
ClassWriter writer = new ClassWriter(ClassWriter.COMPUTE_MAXS);
cn.accept(writer);
return workDone ? writer.toByteArray() : cls;
}
public static MethodNode getMethod(ClassNode cls, String name, String desc)
{
for (MethodNode method : cls.methods)
{
if (method.name.equals(name) && method.desc.equals(desc))
return method;
}
return null;
}
}