package net.minecraftforkage.instsetup;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.jar.Attributes;
import java.util.jar.JarEntry;
import java.util.jar.JarInputStream;
import java.util.jar.Manifest;
class Installer {
private static boolean isSetupClasspathJar(Attributes manifestSection) {
String value = manifestSection.getValue("MCF-InstanceSetupClasspath");
if(value == null)
return false;
return value.equals("true")
|| value.startsWith("true; "); // future compatibility?
}
/**
* Returns all URLs
*
* Note: this may return nested JAR URLs such as <tt>jar:jar:file:somefile.jar!/somepath.jar!/somepath</tt>.
* Java is not capable of handling these natively.
*
* @param modsDir The main mods folder.
*/
static List<URL> findSetupClasspathJars(File modsDir) throws IOException {
List<URL> result = new ArrayList<>();
for(File modFileOrDir : modsDir.listFiles()) {
if(modFileOrDir.isFile())
findSetupClasspathJarsInJar(modFileOrDir, result);
else if(modFileOrDir.isDirectory())
findSetupClasspathJarsInDirectory(modFileOrDir, result);
}
return result;
}
private static void findSetupClasspathJarsInDirectory(File modDir, List<URL> result) throws IOException {
File manifestFile = new File(new File(modDir, "META-INF"), "MANIFEST.MF");
if(!manifestFile.exists())
return;
// Read manifest data from manifest file
Manifest manifest = new Manifest();
try (InputStream in = new FileInputStream(manifestFile)) {
manifest.read(in);
}
if(isSetupClasspathJar(manifest.getMainAttributes())) {
result.add(modDir.toURI().toURL());
}
for(Map.Entry<String, Attributes> entry : manifest.getEntries().entrySet()) {
if(isSetupClasspathJar(entry.getValue())) {
findSetupClasspathJarsInJar(new File(modDir, entry.getKey()), result);
}
}
}
private static void findSetupClasspathJarsInJar(File modJar, List<URL> result) throws IOException {
try (JarInputStream j_in = new JarInputStream(new FileInputStream(modJar))) {
findSetupClasspathJarsInJar(j_in, result, modJar.toURI().toURL());
}
}
private static void findSetupClasspathJarsInJar(JarInputStream jarIn, List<URL> result, URL jarURL) throws IOException {
Manifest manifest = jarIn.getManifest();
if(manifest == null)
return;
if(isSetupClasspathJar(manifest.getMainAttributes())) {
result.add(jarURL);
}
Set<String> subJarNames = new HashSet<>();
for(Map.Entry<String, Attributes> entry : manifest.getEntries().entrySet()) {
if(isSetupClasspathJar(entry.getValue())) {
subJarNames.add(entry.getKey());
}
}
if(subJarNames.size() == 0)
return;
JarEntry je;
while((je = jarIn.getNextJarEntry()) != null) {
if(subJarNames.contains(je.getName())) {
findSetupClasspathJarsInJar(new JarInputStream(jarIn), result, new URL("jar:" + jarURL.toString() + "!/" + je.getName()));
jarIn.closeEntry();
}
}
}
}