Added command line tool frontend.

This commit is contained in:
Chicken-Bomes committed 2014-02-03 11:02:42 +10:00
1 parent 7450370d99
commit ae652f51c2
9 files changed
+396 -23

No files matched your search

+2
View File
@@ -0,0 +1,2 @@
Manifest-Version: 1.0
Main-Class: codechicken.lib.tool.Main
+14 -23
View File
@@ -239,7 +239,6 @@ public class QBImporter
}
}
private static final int[][] vertOrder = new int[][] {//clockwise because MC is left handed
{3, 0},
{1, 0},
@@ -577,8 +576,6 @@ public class QBImporter
public QBMatrix[] matrices;
public boolean rightHanded;
public RasterisedModel toRasterisedModel(int flags) {
List<QBImage> qbImages = new ArrayList<QBImage>();
List<List<QBQuad>> modelQuads = new ArrayList<List<QBQuad>>();
@@ -727,29 +724,23 @@ public class QBImporter
}
public static QBModel loadQB(ResourceLocation res) {
try
{
try {
return loadQB(Minecraft.getMinecraft().getResourceManager().getResource(res).getInputStream());
}
catch(Exception e)
{
} catch(Exception e) {
throw new RuntimeException("failed to load model: "+res, e);
}
}
/**
* try {
File dump = new File("packed.png");
if(!dump.exists()) dump.createNewFile();
ImageIO.write(img, "PNG", dump);
File dump2 = new File("packed.obj");
if(!dump2.exists()) dump2.createNewFile();
PrintWriter p = new PrintWriter(dump2);
CCModel.exportObj(Collections.singletonMap(name, m), p);
p.close();
} catch(IOException e) {
e.printStackTrace();
}
*/
public static QBModel loadQB(File file) {
try {
FileInputStream fin = new FileInputStream(file);
try {
return loadQB(fin);
} finally {
fin.close();
}
} catch(Exception e) {
throw new RuntimeException("failed to load model: "+file.getPath(), e);
}
}
}
+110
View File
@@ -0,0 +1,110 @@
package codechicken.lib.tool;
import java.io.*;
import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLClassLoader;
import java.net.URLConnection;
import java.nio.ByteBuffer;
import java.util.LinkedList;
import java.util.List;
public class LibDownloader
{
private static String[] libs = new String[] {
"org/ow2/asm/asm-debug-all/4.1/asm-debug-all-4.1.jar",
"com/google/guava/guava/14.0/guava-14.0.jar",
"net/sf/jopt-simple/jopt-simple/4.5/jopt-simple-4.5.jar"};
private static File libDir = new File("lib");
private static ByteBuffer downloadBuffer = ByteBuffer.allocateDirect(1 << 23);
public static void load() {
if(!libDir.exists())
libDir.mkdir();
if(!libDir.isDirectory())
throw new RuntimeException("/lib is not a directory");
List<String> missing = checkExists();
for(String lib : missing)
download(lib);
addPaths(libs);
}
private static void addPaths(String[] libs) {
try {
URLClassLoader cl = (URLClassLoader) ClassLoader.getSystemClassLoader();
Method m_addURL = URLClassLoader.class.getDeclaredMethod("addURL", new Class[]{URL.class});
m_addURL.setAccessible(true);
for(String lib : libs)
m_addURL.invoke(cl, new File(libDir, fileName(lib)).toURI().toURL());
}
catch (Exception e) {
throw new RuntimeException("Failed to add libraries to classpath", e);
}
}
private static void download(String lib) {
File libFile = new File(libDir, fileName(lib));
try
{
URL libDownload = new URL("http://repo1.maven.org/maven2/"+lib);
URLConnection connection = libDownload.openConnection();
connection.setConnectTimeout(5000);
connection.setReadTimeout(5000);
connection.setRequestProperty("User-Agent", "CodeChickenLib Downloader");
int sizeGuess = connection.getContentLength();
download(connection.getInputStream(), sizeGuess, libFile);
}
catch (Exception e)
{
libFile.delete();
throw new RuntimeException("A download error occured", e);
}
}
private static void download(InputStream is, int sizeGuess, File target) throws Exception
{
String name = target.getName();
if (sizeGuess > downloadBuffer.capacity())
throw new Exception(String.format("The file %s is too large to be downloaded", name));
downloadBuffer.clear();
int bytesRead, fullLength = 0;
System.out.format("Downloading lib %s", name);
byte[] smallBuffer = new byte[1024];
while ((bytesRead = is.read(smallBuffer)) >= 0) {
downloadBuffer.put(smallBuffer, 0, bytesRead);
fullLength += bytesRead;
System.out.format("\rDownloading lib %s %d%%", name, (int) (fullLength * 100 / sizeGuess));
}
System.out.format("\rDownloaded lib %s \n", name);
is.close();
downloadBuffer.limit(fullLength);
if(!target.exists())
target.createNewFile();
downloadBuffer.position(0);
FileOutputStream fos = new FileOutputStream(target);
fos.getChannel().write(downloadBuffer);
fos.close();
}
private static String fileName(String lib) {
return lib.replaceAll(".+/", "");
}
private static List<String> checkExists() {
LinkedList<String> list = new LinkedList<String>();
for(String lib : libs) {
File file = new File(libDir, fileName(lib));
if(!file.exists())
list.add(lib);
}
return list;
}
}
@@ -0,0 +1,44 @@
package codechicken.lib.tool;
import codechicken.lib.asm.ASMHelper;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.commons.Remapper;
import org.objectweb.asm.commons.RemappingMethodAdapter;
import org.objectweb.asm.tree.ClassNode;
import org.objectweb.asm.tree.MethodNode;
import java.util.Iterator;
public class MCStripTransformer {
public static class ReferenceDetector extends Remapper {
boolean found = false;
@Override
public String map(String typeName) {
if(typeName.startsWith("net/minecraft"))
found = true;
return typeName;
}
}
public static byte[] transform(byte[] bytes) {
ClassNode cnode = ASMHelper.createClassNode(bytes, ClassReader.EXPAND_FRAMES);
boolean changed = false;
Iterator<MethodNode> it = cnode.methods.iterator();
while(it.hasNext()) {
MethodNode mnode = it.next();
ReferenceDetector r = new ReferenceDetector();
mnode.accept(new RemappingMethodAdapter(mnode.access, mnode.desc, new MethodVisitor(Opcodes.ASM4) {}, r));
if(r.found) {
it.remove();
changed = true;
}
}
if(changed)
bytes = ASMHelper.createBytes(cnode, 0);
return bytes;
}
}
+14
View File
@@ -0,0 +1,14 @@
package codechicken.lib.tool;
public class Main
{
public static void main(String[] args) {
LibDownloader.load();
try {
Class<?> c_toolMain = new StripClassLoader().loadClass("codechicken.lib.tool.ToolMain");
c_toolMain.getDeclaredMethod("main", String[].class).invoke(null, new Object[]{args});
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
@@ -0,0 +1,50 @@
package codechicken.lib.tool;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.net.URLClassLoader;
public class StripClassLoader extends URLClassLoader
{
public StripClassLoader() {
super(new URL[0], StripClassLoader.class.getClassLoader());
}
@Override
public Class<?> loadClass(String name) throws ClassNotFoundException {
if(!name.startsWith("codechicken.lib"))
return super.loadClass(name);
try {
String resName = name.replace('.', '/')+".class";
InputStream res = getResourceAsStream(resName);
if(res == null)
throw new ClassNotFoundException("Could not find resource: "+resName);
byte[] bytes = readFully(res);
bytes = transform(bytes);
return defineClass(name, bytes, 0, bytes.length);
} catch(IOException e) {
throw new ClassNotFoundException(name, e);
}
}
public static byte[] readFully(InputStream is) throws IOException {
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
int read;
byte[] data = new byte[16384];
while ((read = is.read(data, 0, data.length)) > 0)
buffer.write(data, 0, read);
return buffer.toByteArray();
}
private byte[] transform(byte[] bytes) {
bytes = MCStripTransformer.transform(bytes);
return bytes;
}
}
+44
View File
@@ -0,0 +1,44 @@
package codechicken.lib.tool;
import codechicken.lib.tool.module.ModuleQBConverter;
public class ToolMain
{
public static interface Module {
public void main(String[] args);
public String name();
public void printHelp();
}
public static Module[] modules = new Module[]{
new ModuleQBConverter()
};
private static void printHelp() {
System.out.println("Usage: [module] [args]");
System.out.println(" Modules: ");
for(Module m : modules)
System.out.println(" - "+m.name());
System.out.println("-h [module] for module help");
}
public static void main(String[] args) {
if(args.length > 0) {
for(Module m : modules)
if(args[0].equals(m.name())) {
String[] args2 = new String[args.length-1];
System.arraycopy(args, 1, args2, 0, args2.length);
m.main(args2);
return;
}
if(args[0].equals("-h") && args.length >= 2) {
for(Module m : modules)
if(args[1].equals(m.name())) {
m.printHelp();
return;
}
}
}
printHelp();
}
}
@@ -0,0 +1,48 @@
package codechicken.lib.tool.module;
import codechicken.lib.tool.ToolMain;
import joptsimple.OptionException;
import joptsimple.OptionParser;
import joptsimple.OptionSet;
import java.io.IOException;
public abstract class JOptModule implements ToolMain.Module
{
OptionParser parser = new OptionParser();
@Override
public void main(String[] args) {
OptionSet options;
try
{
options = parser.parse(args);
}
catch(OptionException ex)
{
System.err.println(ex.getLocalizedMessage());
System.exit(-1);
return;
}
try
{
main(parser, options);
}
catch(Exception e)
{
throw new RuntimeException(e);
}
}
protected abstract void main(OptionParser parser, OptionSet options);
@Override
public void printHelp() {
try {
parser.printHelpOn(System.out);
} catch (IOException e) {
e.printStackTrace();
}
}
}
@@ -0,0 +1,70 @@
package codechicken.lib.tool.module;
import codechicken.lib.render.QBImporter;
import joptsimple.OptionParser;
import joptsimple.OptionSet;
import java.io.File;
import static java.util.Arrays.asList;
public class ModuleQBConverter extends JOptModule
{
public ModuleQBConverter() {
parser.acceptsAll(asList("?", "h", "help"), "Show the help");
parser.acceptsAll(asList("i", "input"), "comma separated list of paths to models (.qb or directories)")
.withRequiredArg().ofType(File.class).withValuesSeparatedBy(',').required();
parser.acceptsAll(asList("o", "out"), "Output Directory")
.withRequiredArg().ofType(File.class);
parser.acceptsAll(asList("o2", "textureplanes"), "2nd level optimisation. Merges coplanar polygons. Increases texture size");
parser.acceptsAll(asList("s", "squaretextures"), "Produce square textures");
parser.acceptsAll(asList("t", "mergetextures"), "Use the same texture for all models");
parser.acceptsAll(asList("r", "scalemc"), "Resize model to mc standard (shrink by factor of 16)");
}
protected void main(OptionParser parser, OptionSet options) {
int flags = 0;
if(options.has("o2")) flags |= QBImporter.TEXTUREPLANES;
if(options.has("s")) flags |= QBImporter.SQUARETEXTURE;
if(options.has("t")) flags |= QBImporter.MERGETEXTURES;
if(options.has("r")) flags |= QBImporter.SCALEMC;
File[] input = options.valuesOf("input").toArray(new File[0]);
File[] outDir = new File[input.length];
if(options.has("out")) {
File output = (File)options.valueOf("out");
if(output.isFile())
throw new RuntimeException("Output Path is not a directory");
if(!output.exists())
output.mkdirs();
for(int i = 0; i < input.length; i++)
outDir[i] = output;
} else {
for(int i = 0; i < input.length; i++)
outDir[i] = input[i].isDirectory() ? input[i] : input[i].getParentFile();
}
for(int i = 0; i < input.length; i++) {
File file = input[i];
if(file.isDirectory()) {
for(File file2 : file.listFiles())
if(file2.getName().endsWith(".qb"))
convert(file2, outDir[i], flags);
}
else
convert(file, outDir[i], flags);
}
}
private void convert(File in, File outDir, int flags) {
System.out.println("Converting: "+in.getName());
QBImporter.RasterisedModel m = QBImporter.loadQB(in).toRasterisedModel(flags);
m.export(new File(outDir, in.getName().replace(".qb", ".obj")), outDir);
}
@Override
public String name() {
return "QBConverter";
}
}