package app.create.rpg.file; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStream; import java.io.PrintWriter; import java.util.ArrayList; import java.util.List; import java.util.zip.DeflaterOutputStream; import java.util.zip.InflaterInputStream; import android.os.Parcel; import android.os.Parcelable; import com.jinoh.ruby.marshal.Marshaler; import com.jinoh.ruby.marshal.Unmarshaler; /** * Ruby Script Collection (RSC) by jinoh67 * * @author jinoh67 * */ public class RubyScriptCollection implements Parcelable, Cloneable { protected List<ScriptEntry> mList; protected List<Integer> mIDs; // index is oldest first, value is the right order in mKeys and mDatas protected File mFile; protected int mNextId; public static class ScriptEntry implements Parcelable, Cloneable { public String name; public byte[] scriptData; public ScriptEntry () { } public ScriptEntry (Parcel source) { this.name = source.readString(); this.scriptData = source.createByteArray(); } public ScriptEntry (ScriptEntry source) { this(source.name, source.scriptData); } public ScriptEntry (String name) { this.name = name; } public ScriptEntry (byte[] data) { this.scriptData = data; } public ScriptEntry (String name, byte[] data) { this.name = name; this.scriptData = data; } public void fillIn (ScriptEntry other) { this.name = other.name; this.scriptData = other.scriptData; } public ScriptEntry clone () { return new ScriptEntry (this); } public String toString () { return name; } public synchronized String inflate () { if (scriptData == null) return null; InputStreamReader reader = new InputStreamReader(new InflaterInputStream(new ByteArrayInputStream(scriptData))); char[] buf = new char[256]; StringBuffer sb = new StringBuffer(32); int read; try { while ((read = reader.read(buf)) > 0) { sb.append(buf, 0, read); } return sb.toString(); } catch (IOException e) { } finally { try { System.gc(); reader.close(); } catch (IOException e) { } } return null; } public synchronized void deflate (String script) { ByteArrayOutputStream baos = new ByteArrayOutputStream(); PrintWriter pw = new PrintWriter(new DeflaterOutputStream(baos)); pw.write(script); pw.close(); this.scriptData = baos.toByteArray(); } @Override public int describeContents() { return 0; } @Override public void writeToParcel(Parcel dest, int flags) { dest.writeString(name); dest.writeByteArray(scriptData); } public static final ScriptEntry EMPTY = new ScriptEntry(); public static final Creator<ScriptEntry> CREATOR = new Creator<ScriptEntry>() { public ScriptEntry createFromParcel(Parcel source) { return new ScriptEntry(source); } public ScriptEntry[] newArray(int size) { return new ScriptEntry[size]; } }; } public RubyScriptCollection(File file) { mIDs = null; mList = null; mFile = file; mNextId = 0; } public RubyScriptCollection(Parcel source, ClassLoader loader) { source.readList(mIDs = new ArrayList<Integer>(), loader); source.readList(mList = new ArrayList<ScriptEntry>(), loader); mFile = new File(source.readString()); mNextId = source.readInt(); } public synchronized List<ScriptEntry> getKeys () { return mList; } public synchronized void loadList () throws IOException { boolean success = false; InputStream is = new FileInputStream(mFile); try { // Use custom parser Unmarshaler um = new Unmarshaler(is); int major, minor, sz, t, sz2; major = is.read(); minor = is.read(); // Skip 2 bytes final String s_eof = "End of stream or array"; if (major != 4 || minor > 8 || minor < 0) throw new IOException (minor == -1 ? s_eof : "Incompatible version"); if (is.read() != '[') { throw new IOException ("Not an array"); } sz = um.readInt(); mNextId = 0; mIDs = new ArrayList<Integer>(); mList = new ArrayList<ScriptEntry>(); for (int i = 0; i < sz; i++) { t = is.read(); if (t == -1) throw new IOException (s_eof); if (t != '0') { if (t != '[') throw new IOException ("An item found that is not array"); sz2 = um.readInt(); if (sz2 < 3) throw new IOException (s_eof); while ((t = is.read()) == '0'); if (t == -1) throw new IOException (s_eof); mIDs.add(mNextId++); ScriptEntry se = new ScriptEntry(); if ((t = is.read()) != '"') throw new IOException ("Not a string"); se.name = new String(um.readBytesAsString()); if ((t = is.read()) != '"') throw new IOException ("Not a string"); se.scriptData = um.readBytesAsString(); } } success = true; } finally { if (!success) { mNextId = 0; mIDs = null; mList = null; } System.gc(); is.close(); } } public synchronized void saveList () throws IOException { mFile.renameTo(new File(mFile.getAbsolutePath() + ".bak")); OutputStream os = new FileOutputStream(mFile); Marshaler m = new Marshaler(os); os.write(4); os.write(8); os.write('['); m.writeInt(mList.size() + 1); m.marshal(); for (ScriptEntry se : mList) { os.write('['); m.writeInt(3); m.marshal(); os.write('"'); m.writeBytesAsString(se.name.getBytes()); os.write('"'); m.writeBytesAsString(se.scriptData); } System.gc(); os.close(); } public synchronized ScriptEntry findScriptById (int id) { int idx = mIDs.indexOf(id); if (idx < 0 || idx >= mList.size()) return null; return mList.get(idx); } public synchronized ScriptEntry get (int index) { return mList.get(index); } public synchronized int insertScript (int index, ScriptEntry se) { mIDs.add(index, mNextId); mList.add(index, se); return mNextId++; } public synchronized boolean deleteScript (ScriptEntry se) { int idx = mList.indexOf(se); if (idx == -1) return false; mIDs.remove(idx); mList.remove(idx); return true; } public synchronized boolean deleteScript (int id) { int idx = mIDs.indexOf(id); if (idx == -1) return false; mIDs.remove(idx); mList.remove(idx); return true; } public synchronized void deleteScriptAt (int idx) throws IndexOutOfBoundsException { mIDs.remove(idx); mList.remove(idx); } @Override public int describeContents() { return 0; } @Override public void writeToParcel(Parcel dest, int flags) { dest.writeList(mIDs); dest.writeList(mList); dest.writeString(mFile.getAbsolutePath()); dest.writeInt(mNextId); } public static final Creator<RubyScriptCollection> CREATOR = new Creator<RubyScriptCollection>() { public RubyScriptCollection[] newArray(int size) { return new RubyScriptCollection[size]; } public RubyScriptCollection createFromParcel(Parcel source) { return new RubyScriptCollection(source, source.getClass().getClassLoader()); } }; }