diff --git a/manual-bwapi/Mirror.java b/manual-bwapi/Mirror.java index 887068a..78296b1 100644 --- a/manual-bwapi/Mirror.java +++ b/manual-bwapi/Mirror.java @@ -8,6 +8,7 @@ import java.io.File; import java.lang.Exception; import java.lang.UnsupportedOperationException; import java.util.*; +import java.util.regex.Pattern; import java.util.zip.*; /** @@ -56,55 +57,79 @@ public class Mirror { private static final boolean EXTRACT_JAR = true; - private static final String VERSION = "2_5"; + private static void extractResourceFile(String resourceFilename, String outputFilename) throws Exception { + InputStream in = Thread.currentThread().getContextClassLoader().getResourceAsStream(resourceFilename); + if (in == null) + throw new FileNotFoundException("Resource file not found: " + resourceFilename); + FileOutputStream out; + try { + out = new FileOutputStream(outputFilename); + } catch (Exception e) { + throw new FileNotFoundException("Could not open output file: " + outputFilename); + } + byte[] buffer = new byte[4096]; + int bytesRead; + while ((bytesRead = in.read(buffer, 0, buffer.length)) != -1) { + out.write(buffer, 0, bytesRead); + } + out.close(); + in.close(); + } + + private static boolean extractAndLoadNativeLibraries() { + try { + System.out.println("Extracting bwapi_bridge.dll"); + extractResourceFile("bwapi_bridge.dll", "./bwapi_bridge.dll"); + + System.out.println("Extracting libgmp-10.dll"); + extractResourceFile("libgmp-10.dll", "./libgmp-10.dll"); + + System.out.println("Extracting libmpfr-4.dll"); + extractResourceFile("libmpfr-4.dll", "./libmpfr-4.dll"); + + System.out.println("Loading native library bwapi_bridge.dll"); + System.load(new File("./bwapi_bridge.dll").getAbsolutePath()); + + } catch (Exception e) { + e.printStackTrace(); + return false; + } + + return true; + } + + private static boolean extractBwtaDataFiles() { + try { + Collection bwtaFilenames = ResourceList.getResources(Pattern.compile("bwapi\\-data/BWTA2/[a-zA-Z0-9]+\\.bwta")); + + System.out.println("Creating ./bwapi-data/BWTA2 directory"); + new File("./bwapi-data/BWTA2").mkdirs(); + + System.out.println("Extracting " + bwtaFilenames.size() + " BWTA2 files:"); + for (String filename : bwtaFilenames) { + System.out.println(filename); + String outputFilename = "./" + filename; + extractResourceFile(filename, outputFilename); + } + + } catch (Exception e) { + e.printStackTrace(); + return false; + } + + return true; + } static { String arch = System.getProperty("os.arch"); - String dllNames[] = {"bwapi_bridge" + VERSION, "libgmp-10", "libmpfr-4"}; if(!arch.equals("x86")){ throw new UnsupportedOperationException("BWMirror API supports only x86 architecture."); } - try { - if (EXTRACT_JAR) { - System.setProperty("java.library.path", "."); - java.lang.reflect.Field fieldSysPath = ClassLoader.class.getDeclaredField("sys_paths"); - fieldSysPath.setAccessible(true); - fieldSysPath.set(null, null); - String path = Mirror.class.getProtectionDomain().getCodeSource().getLocation().getPath(); - String decodedPath = java.net.URLDecoder.decode(path, "UTF-8"); - - JarResources jar = null; - for (String dllName : dllNames) { - String dllNameExt = dllName + ".dll"; - if (!new File(dllNameExt).exists()) { - if (null == jar) { - jar = new JarResources(decodedPath); - } - byte[] correctDllData = jar.getResource(dllNameExt); - // prevents the creation of zero byte files - if (null != correctDllData) { - FileOutputStream funnyStream = new FileOutputStream(dllNameExt); - funnyStream.write(correctDllData); - funnyStream.close(); - } - } - } - } - } catch (Exception e) { - System.err.println("Failed to extract native libraries.\n" + e); - } - - System.loadLibrary(dllNames[0]); - - File dataDir = new File("bwapi-data/BWTA"); - if(!dataDir.exists()){ - try { - dataDir.mkdirs(); - } catch (Exception e) { - System.err.println("Unable to create /bwapi-data/BWTA folder, BWTA analysis will not be saved."); - } - } + if (!extractAndLoadNativeLibraries()) + System.exit(1); + if (!extractBwtaDataFiles()) + System.exit(1); } public Game getGame() { @@ -138,131 +163,4 @@ public class Mirror { /*public*/ private interface FrameCallback { public void update(); } - - @SuppressWarnings({"unchecked"}) - private static class JarResources { - - // external debug flag - public boolean debugOn = false; - - // jar resource mapping tables - private Hashtable htSizes = new Hashtable(); - private Hashtable htJarContents = new Hashtable(); - - // a jar file - private String jarFileName; - - /** - * creates a javabot.JarResources. It extracts all resources from a Jar - * into an internal hashtable, keyed by resource names. - * - * @param jarFileName a jar or zip file - */ - public JarResources(String jarFileName) { - this.jarFileName = jarFileName; - init(); - } - - /** - * Extracts a jar resource as a blob. - * - * @param name a resource name. - */ - public byte[] getResource(String name) { - return (byte[]) htJarContents.get(name); - } - - /** - * initializes internal hash tables with Jar file resources. - */ - private void init() { - try { - // extracts just sizes only. - ZipFile zf = new ZipFile(jarFileName); - Enumeration e = zf.entries(); - while (e.hasMoreElements()) { - ZipEntry ze = (ZipEntry) e.nextElement(); - if (debugOn) { - System.out.println(dumpZipEntry(ze)); - } - htSizes.put(ze.getName(), new Integer((int) ze.getSize())); - } - zf.close(); - - // extract resources and put them into the hashtable. - FileInputStream fis = new FileInputStream(jarFileName); - BufferedInputStream bis = new BufferedInputStream(fis); - ZipInputStream zis = new ZipInputStream(bis); - ZipEntry ze = null; - while ((ze = zis.getNextEntry()) != null) { - if (ze.isDirectory()) { - continue; - } - if (debugOn) { - System.out.println( - "ze.getName()=" + ze.getName() + "," + "getSize()=" + ze.getSize() - ); - } - int size = (int) ze.getSize(); - // -1 means unknown size. - if (size == -1) { - size = ((Integer) htSizes.get(ze.getName())).intValue(); - } - byte[] b = new byte[(int) size]; - int rb = 0; - int chunk = 0; - while (((int) size - rb) > 0) { - chunk = zis.read(b, rb, (int) size - rb); - if (chunk == -1) { - break; - } - rb += chunk; - } - // add to internal resource hashtable - htJarContents.put(ze.getName(), b); - if (debugOn) { - System.out.println( - ze.getName() + " rb=" + rb + - ",size=" + size + - ",csize=" + ze.getCompressedSize() - ); - } - } - } catch (NullPointerException e) { - System.out.println("done."); - } catch (FileNotFoundException e) { - e.printStackTrace(); - } catch (IOException e) { - e.printStackTrace(); - } - } - - /** - * Dumps a zip entry into a string. - * - * @param ze a ZipEntry - */ - private String dumpZipEntry(ZipEntry ze) { - StringBuffer sb = new StringBuffer(); - if (ze.isDirectory()) { - sb.append("d "); - } else { - sb.append("f "); - } - if (ze.getMethod() == ZipEntry.STORED) { - sb.append("stored "); - } else { - sb.append("defalted "); - } - sb.append(ze.getName()); - sb.append("\t"); - sb.append("" + ze.getSize()); - if (ze.getMethod() == ZipEntry.DEFLATED) { - sb.append("/" + ze.getCompressedSize()); - } - return (sb.toString()); - } - - - } } \ No newline at end of file diff --git a/manual-bwapi/ResourceList.java b/manual-bwapi/ResourceList.java new file mode 100644 index 0000000..80b1052 --- /dev/null +++ b/manual-bwapi/ResourceList.java @@ -0,0 +1,143 @@ +package bwapi; + +import java.io.File; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Enumeration; +import java.util.regex.Pattern; +import java.util.zip.ZipEntry; +import java.util.zip.ZipException; +import java.util.zip.ZipFile; + +/** + * Get a list of all resources that exist on the classpath that match + * a given java.util.regex.Pattern. + * + * All elements listed in the system property "java.class.path" will be searched + * and whether it is a JAR file or directory will be handled automatically. + * + * Resources that are listed with this class can be loaded using a ClassLoader. + * e.g. Thread.currentThread().getContextClassLoader().getResourceAsStream(...) + */ +public class ResourceList { + + /** + * Returns a list of all resources within the classpath matching the given pattern. + * + * NOTE: If your pattern includes paths, you should use forward slashes between + * directory and filenames instead of backslashes, even when running on Windows. + * Internally, paths will be converted to forward slashes before the pattern is + * matched for consistency reasons. + * + * @param pattern the pattern to match + * @return the resources in the order they are found + */ + public static Collection getResources(final Pattern pattern) { + final ArrayList retval = new ArrayList<>(); + final String classPath = System.getProperty("java.class.path", "."); + final String[] classPathElements = classPath.split(File.pathSeparator); + for (final String element : classPathElements) { + retval.addAll(getResources(element, pattern)); + } + return retval; + } + + private static Collection getResources(final String element, + final Pattern pattern) { + final ArrayList retval = new ArrayList<>(); + final File file = new File(element); + if (file.isDirectory()) { + String baseDirectory; + try { + baseDirectory = file.getCanonicalPath() + File.separator; + } catch (final IOException e) { + throw new java.lang.Error(e); + } + retval.addAll(getResourcesFromDirectory(file, baseDirectory, pattern)); + } else { + retval.addAll(getResourcesFromJarFile(file, pattern)); + } + return retval; + } + + private static Collection getResourcesFromJarFile(final File file, final Pattern pattern) { + final ArrayList retval = new ArrayList<>(); + ZipFile zf; + try { + zf = new ZipFile(file); + } catch (final ZipException e) { + throw new java.lang.Error(e); + } catch (final IOException e) { + throw new java.lang.Error(e); + } + final Enumeration e = zf.entries(); + while (e.hasMoreElements()) { + final ZipEntry ze = (ZipEntry) e.nextElement(); + final String fileName = ze.getName(); + final boolean accept = pattern.matcher(fileName).matches(); + if (accept) { + retval.add(fileName); + } + } + try { + zf.close(); + } catch (final IOException e1) { + throw new java.lang.Error(e1); + } + return retval; + } + + private static Collection getResourcesFromDirectory(final File directory, final String baseDirectory, final Pattern pattern) { + final ArrayList retval = new ArrayList<>(); + final File[] fileList = directory.listFiles(); + for (final File file : fileList) { + if (file.isDirectory()) { + retval.addAll(getResourcesFromDirectory(file, baseDirectory, pattern)); + } else { + try { + // the below call to file.getCanonicalPath will return an absolute path. + // since JAR file paths will all be relative, we trim off the absolute path's + // base directory prefix before doing the regex match. + // + // also convert the remaining sub-path to use forward slashes. this is a convenience thing + // as the _ONLY_ time a path being matched in this entire class will have backslashes + // is in this method (when checking local directories only) and ONLY on Windows. + // checking JAR file paths (getResourcesFromJarFile) will have forward slashes, even + // on Windows... + // so let's just force forward slashes everywhere to be consistent, shall we? + final String fileName = file.getCanonicalPath().substring(baseDirectory.length()).replaceAll("\\\\", "/"); + final boolean accept = pattern.matcher(fileName).matches(); + if (accept) { + // also note that we are adding the slash-converted sub-path to the list of files, NOT + // the absolute path initially obtained! this is so that this path can be passed + // directly into a call to getResourceAsStream() or similar + retval.add(fileName); + } + } catch (final IOException e) { + throw new java.lang.Error(e); + } + } + } + return retval; + } + + /** + * list the resources that match args[0] + * + * @param args args[0] is the pattern to match, or list all resources if + * there are no args + */ + public static void main(final String[] args) { + Pattern pattern; + if (args.length < 1) { + pattern = Pattern.compile(".*"); + } else { + pattern = Pattern.compile(args[0]); + } + final Collection list = ResourceList.getResources(pattern); + for (final String name : list) { + System.out.println(name); + } + } +}