package com.kreative.paint.material;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLConnection;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;
import java.util.zip.CRC32;
import java.util.zip.CheckedInputStream;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
import java.util.zip.ZipOutputStream;
public class MaterialPacker {
public static void main(String[] args) throws IOException {
Iterator<String> a = Arrays.asList(args).iterator();
if (a.hasNext()) {
String verb = a.next();
if (verb.equalsIgnoreCase("pack")) {
if (a.hasNext()) {
File out = new File(a.next());
List<File> in = new ArrayList<File>();
while (a.hasNext()) in.add(new File(a.next()));
try { zip(out, in); }
catch (IOException e) { e.printStackTrace(); }
return;
}
} else if (verb.equalsIgnoreCase("unpack")) {
if (a.hasNext()) {
File in = new File(a.next());
if (a.hasNext()) {
File out = new File(a.next());
if (!a.hasNext()) {
try { unzip(in, out); }
catch (IOException e) { e.printStackTrace(); }
return;
}
}
}
} else if (verb.equalsIgnoreCase("decorate")) {
if (a.hasNext()) {
String creator = a.next();
while (a.hasNext()) {
File file = new File(a.next());
decorate(file, creator);
}
return;
}
}
}
System.err.println("Usage:");
System.err.println(" pack <zipfile> <file> ...");
System.err.println(" unpack <zipfile> <directory>");
System.err.println(" decorate <creator> <file> ...");
}
public static void zip(File out, List<File> files) throws IOException {
FileOutputStream fos = new FileOutputStream(out);
BufferedOutputStream bos = new BufferedOutputStream(fos);
ZipOutputStream zos = new ZipOutputStream(bos);
for (File f : files) zipFile(zos, f, "");
zos.close();
bos.close();
fos.close();
}
public static void unzip(File file, File base) throws IOException {
FileInputStream in = new FileInputStream(file);
unzip(in, base);
in.close();
}
public static void unzip(URL url, File base) throws IOException {
URLConnection uc = url.openConnection();
InputStream in = uc.getInputStream();
unzip(in, base);
in.close();
}
public static void unzip(InputStream in, File base) throws IOException {
BufferedInputStream bis = new BufferedInputStream(in);
ZipInputStream zis = new ZipInputStream(bis);
while (unzipFile(zis, base));
zis.close();
bis.close();
}
public static void decorate(File file, String creator) {
int creatorInt = 0;
if (creator != null && creator.length() > 0) {
byte[] creatorBytes;
try { creatorBytes = creator.getBytes("MacRoman"); }
catch (IOException e) { creatorBytes = creator.getBytes(); }
int n = Math.min(creatorBytes.length, 4);
for (int i = 0, s = 24; i < n; i++, s -= 8)
creatorInt |= ((creatorBytes[i] & 0xFF) << s);
for (int i = n, s = 0; i < 4; i++, s += 8)
creatorInt |= (0x20 << s);
}
decorateFile(file, creatorInt);
}
public static void decorate(File file, int creator) {
decorateFile(file, creator);
}
private static void zipFile(ZipOutputStream zos, File file, String prefix) throws IOException {
String name = file.getName();
if (ignoreFileName(name)) return;
if (file.isDirectory()) {
String childPrefix = prefix + name + "/";
File[] children = file.listFiles();
Arrays.sort(children, new FileComparator());
for (File child : children) zipFile(zos, child, childPrefix);
} else {
ZipEntry z = new ZipEntry(prefix + name);
if (isIncompressible(name)) {
z.setMethod(ZipEntry.STORED);
z.setSize(file.length());
z.setCompressedSize(file.length());
z.setCrc(crc32(file));
}
zos.putNextEntry(z);
zipdata(zos, file);
}
}
private static boolean unzipFile(ZipInputStream zis, File base) throws IOException {
ZipEntry z = zis.getNextEntry();
if (z == null) return false;
if (z.isDirectory()) return true;
File file = base;
String[] names = z.getName().split("/");
for (String name : names) {
if (!file.exists()) file.mkdir();
file = new File(file, name);
}
unzipdata(zis, file);
return true;
}
private static void decorateFile(File file, int creator) {
String name = file.getName();
if (ignoreFileName(name)) return;
if (file.isDirectory()) {
if (isBundle(name)) makeBundle(file);
File[] children = file.listFiles();
for (File child : children) decorateFile(child, creator);
} else {
int type = getMacType(name);
setMacTypeCreator(file, type, creator);
}
}
private static boolean ignoreFileName(String name) {
return name.startsWith(".")
|| name.contains("\r")
|| name.contains("\n")
|| name.contains("\uF00D")
|| name.contains("\uF00A")
|| name.equalsIgnoreCase("Thumbs.db")
|| name.equalsIgnoreCase("Desktop.ini")
|| name.equalsIgnoreCase("Desktop.ico");
}
private static class FileComparator implements Comparator<File> {
@Override
public int compare(File a, File b) {
return a.getName().compareTo(b.getName());
}
}
private static boolean isIncompressible(String name) {
name = name.toLowerCase();
return name.endsWith(".png")
|| name.endsWith(".zip")
|| name.endsWith(".jar")
|| name.endsWith(".pmz");
}
private static long crc32(File file) throws IOException {
CRC32 crc = new CRC32();
FileInputStream fis = new FileInputStream(file);
CheckedInputStream cis = new CheckedInputStream(fis, crc);
byte[] buf = new byte[65536];
while (cis.read(buf, 0, buf.length) >= 0);
cis.close();
fis.close();
return crc.getValue();
}
private static void zipdata(ZipOutputStream zos, File file) throws IOException {
FileInputStream fis = new FileInputStream(file);
BufferedInputStream bis = new BufferedInputStream(fis);
byte[] buf = new byte[65536];
int len = 0;
while ((len = bis.read(buf, 0, buf.length)) >= 0) {
zos.write(buf, 0, len);
}
bis.close();
fis.close();
}
private static void unzipdata(ZipInputStream zis, File file) throws IOException {
FileOutputStream fos = new FileOutputStream(file);
BufferedOutputStream bos = new BufferedOutputStream(fos);
byte[] buf = new byte[65536];
int len = 0;
while ((len = zis.read(buf, 0, buf.length)) >= 0) {
bos.write(buf, 0, len);
}
bos.flush();
fos.flush();
bos.close();
fos.close();
}
private static boolean isBundle(String name) {
name = name.toLowerCase();
return name.endsWith(".frnd")
|| name.endsWith(".spnd")
|| name.endsWith(".txrd");
}
private static void makeBundle(File file) {
try {
Runtime.getRuntime().exec(new String[]{
"/usr/bin/SetFile", "-a", "B",
file.getAbsolutePath()
}).waitFor();
} catch (Throwable e) {}
}
private static int getMacType(String name) {
name = name.toLowerCase();
if (name.endsWith(".png")) return 0x504E4720;
if (name.endsWith(".zip")) return 0x5A495020;
if (name.endsWith(".jar")) return 0x4A415220;
if (name.endsWith(".pmz")) return 0x504D5A20;
if (name.endsWith(".ttf")) return 0x73666E74;
if (name.endsWith(".alpx")) return 0x414C5058;
if (name.endsWith(".ditx")) return 0x44495458;
if (name.endsWith(".frnx")) return 0x46524E58;
if (name.endsWith(".grdx")) return 0x47524458;
if (name.endsWith(".lnsx")) return 0x4C4E5358;
if (name.endsWith(".patx")) return 0x50415458;
if (name.endsWith(".rcpx")) return 0x52435058;
if (name.endsWith(".rfpx")) return 0x52465058;
if (name.endsWith(".shpx")) return 0x53485058;
if (name.endsWith(".spnx")) return 0x53504E58;
if (name.endsWith(".txrx")) return 0x54585258;
return 0;
}
private static void setMacTypeCreator(File file, int type, int creator) {
try {
Class<?> fm = Class.forName("com.apple.eio.FileManager");
Method sft = fm.getDeclaredMethod("setFileType", String.class, int.class);
sft.invoke(null, file.getAbsolutePath(), type);
Method sfc = fm.getDeclaredMethod("setFileCreator", String.class, int.class);
sfc.invoke(null, file.getAbsolutePath(), creator);
} catch (Throwable e) {}
}
}