diff --git a/cache/pom.xml b/cache/pom.xml
new file mode 100644
index 0000000000..9b4eefa2d0
--- /dev/null
+++ b/cache/pom.xml
@@ -0,0 +1,121 @@
+
+
+
+ 4.0.0
+
+
+ net.runelite
+ runelite-parent
+ 1.1.0-SNAPSHOT
+
+
+ net.runelite
+ cache
+ 1.1.0-SNAPSHOT
+ Cache
+
+
+
+ net.runelite.rs
+ api
+ 1.0.1-SNAPSHOT
+
+
+
+ com.google.guava
+ guava
+ 18.0
+
+
+ org.slf4j
+ slf4j-api
+ 1.7.12
+
+
+ org.slf4j
+ slf4j-simple
+ 1.7.12
+
+
+ org.apache.commons
+ commons-compress
+ 1.10
+
+
+ com.google.code.gson
+ gson
+ 2.4
+
+
+ org.gnu
+ gnu-crypto
+ 2.0.1
+
+
+
+ junit
+ junit
+ 4.12
+ test
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-surefire-plugin
+ 2.16
+
+ true
+ -Xmx1024m
+
+
+
+ maven-assembly-plugin
+
+
+ jar-with-dependencies
+
+
+
+
+ make-assembly
+ package
+
+ single
+
+
+
+
+
+
+
diff --git a/cache/src/main/java/net/runelite/cache/ConfigType.java b/cache/src/main/java/net/runelite/cache/ConfigType.java
new file mode 100644
index 0000000000..1b9099230d
--- /dev/null
+++ b/cache/src/main/java/net/runelite/cache/ConfigType.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright (c) 2016, Adam
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Adam
+ * 4. Neither the name of the Adam nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Adam ''AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL Adam BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package net.runelite.cache;
+
+public enum ConfigType
+{
+ // types from https://github.com/im-frizzy/OpenRS/blob/master/source/net/openrs/cache/type/ConfigArchive.java
+ UNDERLAY(1),
+ IDENTKIT(3),
+ OVERLAY(4),
+ INV(5),
+ OBJECT(6),
+ ENUM(8),
+ NPC(9),
+ ITEM(10),
+ SEQUENCE(12),
+ SPOTANIM(13),
+ VARBIT(14),
+ VARCLIENT(19),
+ VARCLIENTSTRING(15),
+ VARPLAYER(16);
+
+ private final int id;
+
+ ConfigType(int id)
+ {
+ this.id = id;
+ }
+
+ public int getId()
+ {
+ return id;
+ }
+}
diff --git a/cache/src/main/java/net/runelite/cache/IndexType.java b/cache/src/main/java/net/runelite/cache/IndexType.java
new file mode 100644
index 0000000000..e1f381b0d9
--- /dev/null
+++ b/cache/src/main/java/net/runelite/cache/IndexType.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright (c) 2016, Adam
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Adam
+ * 4. Neither the name of the Adam nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Adam ''AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL Adam BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package net.runelite.cache;
+
+public enum IndexType
+{
+ // names from https://github.com/im-frizzy/OpenRS/blob/master/source/net/openrs/cache/type/CacheIndex.java
+ SKINS(1),
+ CONFIGS(2),
+ INTERFACES(3),
+ SOUNDEFFECTS(4),
+ LANDSCAPES(5),
+ TRACK1(6),
+ MODELS(7),
+ SPRITES(8),
+ TEXTURES(9),
+ BINARY(10),
+ TRACK2(11),
+ CLIENTSCRIPT(12),
+ FONTS(13),
+ VORBIS(14),
+ INSTRUMENTS(15);
+
+ private int id;
+
+ IndexType(int id)
+ {
+ this.id = id;
+ }
+
+ public int getNumber()
+ {
+ return id;
+ }
+}
\ No newline at end of file
diff --git a/cache/src/main/java/net/runelite/cache/ItemDumper.java b/cache/src/main/java/net/runelite/cache/ItemDumper.java
new file mode 100644
index 0000000000..7a4c84986f
--- /dev/null
+++ b/cache/src/main/java/net/runelite/cache/ItemDumper.java
@@ -0,0 +1,162 @@
+/*
+ * Copyright (c) 2016, Adam
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Adam
+ * 4. Neither the name of the Adam nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Adam ''AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL Adam BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package net.runelite.cache;
+
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.HashSet;
+import java.util.Set;
+import net.runelite.cache.definitions.ItemDefinition;
+import net.runelite.cache.definitions.loaders.ItemLoader;
+import net.runelite.cache.fs.Archive;
+import net.runelite.cache.fs.Index;
+import net.runelite.cache.fs.Store;
+import net.runelite.cache.io.InputStream;
+
+public class ItemDumper
+{
+ private final File cache, out, java;
+ private final Gson gson;
+ private ItemLoader loader;
+
+ public ItemDumper(File cache, File out, File java)
+ {
+ this.cache = cache;
+ this.out = out;
+ this.java = java;
+
+ GsonBuilder builder = new GsonBuilder()
+ .setPrettyPrinting();
+ gson = builder.create();
+ }
+
+ public static void main(String[] args) throws IOException
+ {
+ if (args.length < 3)
+ System.exit(1);
+
+ File cache = new File(args[0]);
+ File out = new File(args[1]);
+ File java = new File(args[2]);
+
+ ItemDumper dumper = new ItemDumper(cache, out, java);
+ dumper.load();
+ dumper.dump();
+ dumper.java();
+ }
+
+ public void load() throws IOException
+ {
+ loader = new ItemLoader();
+
+ try (Store store = new Store(cache))
+ {
+ store.load();
+
+ Index index = store.getIndex(IndexType.CONFIGS);
+ Archive archive = index.getArchive(ConfigType.ITEM.getId());
+
+ for (net.runelite.cache.fs.File f : archive.getFiles())
+ {
+ loader.load(f.getFileId(), new InputStream(f.getContents()));
+ }
+ }
+ }
+
+ public void dump() throws IOException
+ {
+ for (ItemDefinition def : loader.getItems())
+ {
+ out.mkdirs();
+ java.io.File targ = new java.io.File(out, def.id + ".json");
+ try (FileWriter fw = new FileWriter(targ))
+ {
+ fw.write(gson.toJson(def));
+ }
+ }
+ }
+
+ public void java() throws IOException
+ {
+ java.mkdirs();
+ java.io.File targ = new java.io.File(java, "ItemID.java");
+ try (PrintWriter fw = new PrintWriter(targ))
+ {
+ Set used = new HashSet<>();
+
+ fw.println("/* This file is automatically generated. Do not edit. */");
+ fw.println("package net.runelite.api;");
+ fw.println("");
+ fw.println("public final class ItemID {");
+ for (ItemDefinition def : loader.getItems())
+ {
+ if (def.name.equalsIgnoreCase("NULL"))
+ continue;
+
+ String name = name(def.name);
+ if (name == null)
+ continue;
+
+ String suffix = "";
+ while (used.contains(name + suffix))
+ {
+ if (suffix.isEmpty())
+ suffix = "_2";
+ else
+ suffix = "_" + (Integer.parseInt(suffix.substring(1)) + 1);
+ }
+ name += suffix;
+
+ used.add(name);
+
+ fw.println(" public static final int " + name + " = " + def.id + ";");
+ }
+ fw.println("}");
+ }
+ }
+
+ private static String name(String in)
+ {
+ String s = in.toUpperCase()
+ .replace(' ', '_')
+ .replaceAll("[^a-zA-Z0-9_]", "");
+ if (s.isEmpty())
+ return null;
+ if (Character.isDigit(s.charAt(0)))
+ return "_" + s;
+ else
+ return s;
+ }
+}
diff --git a/cache/src/main/java/net/runelite/cache/NpcDumper.java b/cache/src/main/java/net/runelite/cache/NpcDumper.java
new file mode 100644
index 0000000000..c18ec8e556
--- /dev/null
+++ b/cache/src/main/java/net/runelite/cache/NpcDumper.java
@@ -0,0 +1,162 @@
+/*
+ * Copyright (c) 2016, Adam
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Adam
+ * 4. Neither the name of the Adam nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Adam ''AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL Adam BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package net.runelite.cache;
+
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.HashSet;
+import java.util.Set;
+import net.runelite.cache.definitions.NpcDefinition;
+import net.runelite.cache.definitions.loaders.NpcLoader;
+import net.runelite.cache.fs.Archive;
+import net.runelite.cache.fs.Index;
+import net.runelite.cache.fs.Store;
+import net.runelite.cache.io.InputStream;
+
+public class NpcDumper
+{
+ private final File cache, out, java;
+ private final Gson gson;
+ private NpcLoader loader;
+
+ public NpcDumper(File cache, File out, File java)
+ {
+ this.cache = cache;
+ this.out = out;
+ this.java = java;
+
+ GsonBuilder builder = new GsonBuilder()
+ .setPrettyPrinting();
+ gson = builder.create();
+ }
+
+ public static void main(String[] args) throws IOException
+ {
+ if (args.length < 3)
+ System.exit(1);
+
+ File cache = new File(args[0]);
+ File out = new File(args[1]);
+ File java = new File(args[2]);
+
+ ItemDumper dumper = new ItemDumper(cache, out, java);
+ dumper.load();
+ dumper.dump();
+ dumper.java();
+ }
+
+ public void load() throws IOException
+ {
+ loader = new NpcLoader();
+
+ try (Store store = new Store(cache))
+ {
+ store.load();
+
+ Index index = store.getIndex(IndexType.CONFIGS);
+ Archive archive = index.getArchive(ConfigType.NPC.getId());
+
+ for (net.runelite.cache.fs.File f : archive.getFiles())
+ {
+ loader.load(f.getFileId(), new InputStream(f.getContents()));
+ }
+ }
+ }
+
+ public void dump() throws IOException
+ {
+ for (NpcDefinition def : loader.getNpcs())
+ {
+ out.mkdirs();
+ java.io.File targ = new java.io.File(out, def.id + ".json");
+ try (FileWriter fw = new FileWriter(targ))
+ {
+ fw.write(gson.toJson(def));
+ }
+ }
+ }
+
+ public void java() throws IOException
+ {
+ java.mkdirs();
+ java.io.File targ = new java.io.File(java, "NpcID.java");
+ try (PrintWriter fw = new PrintWriter(targ))
+ {
+ Set used = new HashSet<>();
+
+ fw.println("/* This file is automatically generated. Do not edit. */");
+ fw.println("package net.runelite.api;");
+ fw.println("");
+ fw.println("public final class NpcID {");
+ for (NpcDefinition def : loader.getNpcs())
+ {
+ if (def.name.equalsIgnoreCase("NULL"))
+ continue;
+
+ String name = name(def.name);
+ if (name == null)
+ continue;
+
+ String suffix = "";
+ while (used.contains(name + suffix))
+ {
+ if (suffix.isEmpty())
+ suffix = "_2";
+ else
+ suffix = "_" + (Integer.parseInt(suffix.substring(1)) + 1);
+ }
+ name += suffix;
+
+ used.add(name);
+
+ fw.println(" public static final int " + name + " = " + def.id + ";");
+ }
+ fw.println("}");
+ }
+ }
+
+ private static String name(String in)
+ {
+ String s = in.toUpperCase()
+ .replace(' ', '_')
+ .replaceAll("[^a-zA-Z0-9_]", "");
+ if (s.isEmpty())
+ return null;
+ if (Character.isDigit(s.charAt(0)))
+ return "_" + s;
+ else
+ return s;
+ }
+}
diff --git a/cache/src/main/java/net/runelite/cache/ObjectDumper.java b/cache/src/main/java/net/runelite/cache/ObjectDumper.java
new file mode 100644
index 0000000000..14397c0a03
--- /dev/null
+++ b/cache/src/main/java/net/runelite/cache/ObjectDumper.java
@@ -0,0 +1,165 @@
+/*
+ * Copyright (c) 2016, Adam
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Adam
+ * 4. Neither the name of the Adam nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Adam ''AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL Adam BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package net.runelite.cache;
+
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import net.runelite.cache.definitions.ObjectDefinition;
+import net.runelite.cache.definitions.loaders.ObjectLoader;
+import net.runelite.cache.fs.Archive;
+import net.runelite.cache.fs.Index;
+import net.runelite.cache.fs.Store;
+
+public class ObjectDumper
+{
+ private final File cache, out, java;
+ private final Gson gson;
+ private ObjectLoader loader;
+ private final List objects = new ArrayList<>();
+
+ public ObjectDumper(File cache, File out, File java)
+ {
+ this.cache = cache;
+ this.out = out;
+ this.java = java;
+
+ GsonBuilder builder = new GsonBuilder()
+ .setPrettyPrinting();
+ gson = builder.create();
+ }
+
+ public static void main(String[] args) throws IOException
+ {
+ if (args.length < 3)
+ System.exit(1);
+
+ File cache = new File(args[0]);
+ File out = new File(args[1]);
+ File java = new File(args[2]);
+
+ ObjectDumper dumper = new ObjectDumper(cache, out, java);
+ dumper.load();
+ dumper.dump();
+ dumper.java();
+ }
+
+ public void load() throws IOException
+ {
+ loader = new ObjectLoader();
+
+ try (Store store = new Store(cache))
+ {
+ store.load();
+
+ Index index = store.getIndex(IndexType.CONFIGS);
+ Archive archive = index.getArchive(ConfigType.OBJECT.getId());
+
+ for (net.runelite.cache.fs.File f : archive.getFiles())
+ {
+ ObjectDefinition def = loader.load(f.getFileId(), f.getContents());
+ objects.add(def);
+ }
+ }
+ }
+
+ public void dump() throws IOException
+ {
+ for (ObjectDefinition def : objects)
+ {
+ out.mkdirs();
+ java.io.File targ = new java.io.File(out, def.getId() + ".json");
+ try (FileWriter fw = new FileWriter(targ))
+ {
+ fw.write(gson.toJson(def));
+ }
+ }
+ }
+
+ public void java() throws IOException
+ {
+ java.mkdirs();
+ java.io.File targ = new java.io.File(java, "ObjectID.java");
+ try (PrintWriter fw = new PrintWriter(targ))
+ {
+ Set used = new HashSet<>();
+
+ fw.println("/* This file is automatically generated. Do not edit. */");
+ fw.println("package net.runelite.api;");
+ fw.println("");
+ fw.println("public final class ObjectID {");
+ for (ObjectDefinition def : objects)
+ {
+ if (def.getName().equalsIgnoreCase("NULL"))
+ continue;
+
+ String name = name(def.getName());
+ if (name == null)
+ continue;
+
+ String suffix = "";
+ while (used.contains(name + suffix))
+ {
+ if (suffix.isEmpty())
+ suffix = "_2";
+ else
+ suffix = "_" + (Integer.parseInt(suffix.substring(1)) + 1);
+ }
+ name += suffix;
+
+ used.add(name);
+
+ fw.println(" public static final int " + name + " = " + def.getId() + ";");
+ }
+ fw.println("}");
+ }
+ }
+
+ private static String name(String in)
+ {
+ String s = in.toUpperCase()
+ .replace(' ', '_')
+ .replaceAll("[^a-zA-Z0-9_]", "");
+ if (s.isEmpty())
+ return null;
+ if (Character.isDigit(s.charAt(0)))
+ return "_" + s;
+ else
+ return s;
+ }
+}
diff --git a/cache/src/main/java/net/runelite/cache/definitions/EnumDefinition.java b/cache/src/main/java/net/runelite/cache/definitions/EnumDefinition.java
new file mode 100644
index 0000000000..10cf714bec
--- /dev/null
+++ b/cache/src/main/java/net/runelite/cache/definitions/EnumDefinition.java
@@ -0,0 +1,134 @@
+/*
+ * Copyright (c) 2016, Adam
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Adam
+ * 4. Neither the name of the Adam nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Adam ''AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL Adam BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package net.runelite.cache.definitions;
+
+public class EnumDefinition
+{
+ private int id;
+ private int[] intVals;
+ private char keyType;
+ private char valType;
+ private String defaultString = "null";
+ private int defaultInt;
+ private int size;
+ private int[] keys;
+ private String[] stringVals;
+
+ public int getId()
+ {
+ return id;
+ }
+
+ public void setId(int id)
+ {
+ this.id = id;
+ }
+
+ public int[] getIntVals()
+ {
+ return intVals;
+ }
+
+ public void setIntVals(int[] intVals)
+ {
+ this.intVals = intVals;
+ }
+
+ public char getKeyType()
+ {
+ return keyType;
+ }
+
+ public void setKeyType(char keyType)
+ {
+ this.keyType = keyType;
+ }
+
+ public char getValType()
+ {
+ return valType;
+ }
+
+ public void setValType(char valType)
+ {
+ this.valType = valType;
+ }
+
+ public String getDefaultString()
+ {
+ return defaultString;
+ }
+
+ public void setDefaultString(String defaultString)
+ {
+ this.defaultString = defaultString;
+ }
+
+ public int getDefaultInt()
+ {
+ return defaultInt;
+ }
+
+ public void setDefaultInt(int defaultInt)
+ {
+ this.defaultInt = defaultInt;
+ }
+
+ public int getSize()
+ {
+ return size;
+ }
+
+ public void setSize(int size)
+ {
+ this.size = size;
+ }
+
+ public int[] getKeys()
+ {
+ return keys;
+ }
+
+ public void setKeys(int[] keys)
+ {
+ this.keys = keys;
+ }
+
+ public String[] getStringVals()
+ {
+ return stringVals;
+ }
+
+ public void setStringVals(String[] stringVals)
+ {
+ this.stringVals = stringVals;
+ }
+}
diff --git a/cache/src/main/java/net/runelite/cache/definitions/ItemDefinition.java b/cache/src/main/java/net/runelite/cache/definitions/ItemDefinition.java
new file mode 100644
index 0000000000..1b039e26b2
--- /dev/null
+++ b/cache/src/main/java/net/runelite/cache/definitions/ItemDefinition.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright (c) 2016, Adam
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Adam
+ * 4. Neither the name of the Adam nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Adam ''AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL Adam BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package net.runelite.cache.definitions;
+
+public class ItemDefinition
+{
+ public int id;
+ public int resizeY;
+ public int xan2d = 0;
+ public int cost = 1;
+ public int inventoryModel;
+ public int resizeZ;
+ public short[] colorFind;
+ public short[] colorReplace;
+ public short[] textureFind;
+ public String name = "null";
+ public int zoom2d = 200000;
+ public int yan2d = 0;
+ public int zan2d = 0;
+ public int maleOffset;
+ public int yOffset2d = 0;
+ public int stackable = 0;
+ public int[] countCo;
+ public boolean members = false;
+ public String[] options;
+ public String[] interfaceOptions;
+ public int maleModel0;
+ public int maleModel1;
+ public short[] textureReplace;
+ public int femaleModel1;
+ public int femaleOffset;
+ public int maleModel2;
+ public int xOffset2d = 0;
+ public int maleHeadModel;
+ public int maleHeadModel2;
+ public int femaleHeadModel;
+ public int femaleHeadModel2;
+ public int[] countObj;
+ public int femaleModel2;
+ public int notedID;
+ public int femaleModel0;
+ public int resizeX;
+ public int notedTemplate;
+ public int ambient;
+ public int contrast;
+ public int team;
+
+ public ItemDefinition(int definitionID)
+ {
+ this.id = definitionID;
+ this.options = new String[]
+ {
+ null, null, "Take", null, null
+ };
+ this.interfaceOptions = new String[]
+ {
+ null, null, null, null, "Drop"
+ };
+ this.maleModel0 = -1;
+ this.maleModel1 = -1;
+ this.maleOffset = 0;
+ this.femaleModel0 = -1;
+ this.femaleModel1 = -1;
+ this.femaleOffset = 0;
+ this.maleModel2 = -1;
+ this.femaleModel2 = -1;
+ this.maleHeadModel = -1;
+ this.maleHeadModel2 = -1;
+ this.femaleHeadModel = -1;
+ this.femaleHeadModel2 = -1;
+ this.notedID = -1;
+ this.notedTemplate = -1;
+ this.resizeX = 0;
+ this.resizeY = 0;
+ this.resizeZ = 0;
+ this.ambient = 0;
+ this.contrast = 0;
+ this.team = 0;
+ }
+}
diff --git a/cache/src/main/java/net/runelite/cache/definitions/ModelDefinition.java b/cache/src/main/java/net/runelite/cache/definitions/ModelDefinition.java
new file mode 100644
index 0000000000..eff96f563b
--- /dev/null
+++ b/cache/src/main/java/net/runelite/cache/definitions/ModelDefinition.java
@@ -0,0 +1,45 @@
+package net.runelite.cache.definitions;
+
+public class ModelDefinition
+{
+ public short[] texTriangleX;
+ public int[] vertexX;
+ public byte[] faceRenderPriorities;
+ public int[] vertexY;
+ public int triangleFaceCount;
+ public int[] trianglePointsX;
+ public int[] vertexSkins;
+ public int[] trianglePointsZ;
+ public int anInt2562;
+ public int[] trianglePointsY;
+ public byte[] faceAlphas;
+ public short aShort2565;
+ public byte[] faceRenderType;
+ public short[] faceTextures;
+ public byte priority;
+ public int anInt2569;
+ public byte[] textureRenderTypes;
+ public short[] texTriangleY;
+ public short[] texTriangleZ;
+ public short[] aShortArray2574;
+ public short[] aShortArray2575;
+ public short[] aShortArray2577;
+ public short[] aShortArray2578;
+ boolean aBool2579;
+ public byte[] aByteArray2580;
+ public byte[] textureCoords;
+ public int[] triangleSkinValues;
+ public int[][] anIntArrayArray2583;
+ public int[][] anIntArrayArray2584;
+ public short[] aShortArray2586;
+ public short aShort2589;
+ public short[] faceColor;
+ public int shadowIntensity;
+ public int anInt2592;
+ public int anInt2593;
+ public int[] vertexZ;
+ public int anInt2595;
+ public int vertexCount = 0;
+ public short[] texturePrimaryColor;
+
+}
diff --git a/cache/src/main/java/net/runelite/cache/definitions/NpcDefinition.java b/cache/src/main/java/net/runelite/cache/definitions/NpcDefinition.java
new file mode 100644
index 0000000000..f942cc6f1e
--- /dev/null
+++ b/cache/src/main/java/net/runelite/cache/definitions/NpcDefinition.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright (c) 2016, Adam
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Adam
+ * 4. Neither the name of the Adam nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Adam ''AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL Adam BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package net.runelite.cache.definitions;
+
+public class NpcDefinition
+{
+
+ public int id;
+ public short[] recolorToFind;
+ public int anInt2156 = 32;
+ public String name = "null";
+ public short[] recolorToReplace;
+ public int[] models;
+ public int[] models_2;
+ public int stanceAnimation = -1;
+ public int anInt2165 = -1;
+ public int tileSpacesOccupied = 1;
+ public int walkAnimation = -1;
+ public short[] retextureToReplace;
+ public int rotate90RightAnimation = -1;
+ public boolean aBool2170 = true;
+ public int resizeX = 128;
+ public int contrast = 0;
+ public int rotate180Animation = -1;
+ public int anInt2174 = -1;
+ public String[] options = new String[5];
+ public boolean renderOnMinimap = true;
+ public int combatLevel = -1;
+ public int rotate90LeftAnimation = -1;
+ public int resizeY = 128;
+ public boolean hasRenderPriority = false;
+ public int ambient = 0;
+ public int headIcon = -1;
+ public int anInt2184 = 30;
+ public int[] anIntArray2185;
+ public short[] retextureToFind;
+ public int anInt2187 = -1;
+ public boolean isClickable = true;
+ public int anInt2189 = -1;
+ public boolean aBool2190 = false;
+
+ public NpcDefinition(int definitionID)
+ {
+ this.id = definitionID;
+ }
+}
diff --git a/cache/src/main/java/net/runelite/cache/definitions/ObjectDefinition.java b/cache/src/main/java/net/runelite/cache/definitions/ObjectDefinition.java
new file mode 100644
index 0000000000..3cb0407fed
--- /dev/null
+++ b/cache/src/main/java/net/runelite/cache/definitions/ObjectDefinition.java
@@ -0,0 +1,508 @@
+/*
+ * Copyright (c) 2016, Adam
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Adam
+ * 4. Neither the name of the Adam nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Adam ''AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL Adam BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package net.runelite.cache.definitions;
+
+public class ObjectDefinition
+{
+ private int id;
+ private short[] retextureToFind;
+ private int anInt2069 = 16;
+ private boolean isSolid = false;
+ private String name = "null";
+ private int[] objectModels;
+ private int[] objectTypes;
+ private short[] recolorToFind;
+ private int mapIconID = -1;
+ private short[] textureToReplace;
+ private int sizeX = 1;
+ private int sizeY = 1;
+ private int anInt2083 = 0;
+ private int[] anIntArray2084;
+ private int offsetX = 0;
+ private boolean nonFlatShading = false;
+ private int anInt2088 = -1;
+ private int animationID = -1;
+ private int varpID = -1;
+ private int ambient = 0;
+ private int contrast = 0;
+ private String[] actions = new String[5];
+ private int anInt2094 = 2;
+ private int mapSceneID = -1;
+ private short[] recolorToReplace;
+ private boolean aBool2097 = true;
+ private int modelSizeX = 128;
+ private int modelSizeHeight = 128;
+ private int modelSizeY = 128;
+ private int objectID;
+ private int offsetHeight = 0;
+ private int offsetY = 0;
+ private boolean aBool2104 = false;
+ private int anInt2105 = -1;
+ private int anInt2106 = -1;
+ private int[] configChangeDest;
+ private boolean aBool2108 = false;
+ private int configId = -1;
+ private int anInt2110 = -1;
+ private boolean aBool2111 = false;
+ private int anInt2112 = 0;
+ private int anInt2113 = 0;
+ private boolean aBool2114 = true;
+
+ public int getId()
+ {
+ return id;
+ }
+
+ public void setId(int id)
+ {
+ this.id = id;
+ }
+
+ public short[] getRetextureToFind()
+ {
+ return retextureToFind;
+ }
+
+ public void setRetextureToFind(short[] retextureToFind)
+ {
+ this.retextureToFind = retextureToFind;
+ }
+
+ public int getAnInt2069()
+ {
+ return anInt2069;
+ }
+
+ public void setAnInt2069(int anInt2069)
+ {
+ this.anInt2069 = anInt2069;
+ }
+
+ public boolean isIsSolid()
+ {
+ return isSolid;
+ }
+
+ public void setIsSolid(boolean isSolid)
+ {
+ this.isSolid = isSolid;
+ }
+
+ public String getName()
+ {
+ return name;
+ }
+
+ public void setName(String name)
+ {
+ this.name = name;
+ }
+
+ public int[] getObjectModels()
+ {
+ return objectModels;
+ }
+
+ public void setObjectModels(int[] objectModels)
+ {
+ this.objectModels = objectModels;
+ }
+
+ public int[] getObjectTypes()
+ {
+ return objectTypes;
+ }
+
+ public void setObjectTypes(int[] objectTypes)
+ {
+ this.objectTypes = objectTypes;
+ }
+
+ public short[] getRecolorToFind()
+ {
+ return recolorToFind;
+ }
+
+ public void setRecolorToFind(short[] recolorToFind)
+ {
+ this.recolorToFind = recolorToFind;
+ }
+
+ public int getMapIconID()
+ {
+ return mapIconID;
+ }
+
+ public void setMapIconID(int mapIconID)
+ {
+ this.mapIconID = mapIconID;
+ }
+
+ public short[] getTextureToReplace()
+ {
+ return textureToReplace;
+ }
+
+ public void setTextureToReplace(short[] textureToReplace)
+ {
+ this.textureToReplace = textureToReplace;
+ }
+
+ public int getSizeX()
+ {
+ return sizeX;
+ }
+
+ public void setSizeX(int sizeX)
+ {
+ this.sizeX = sizeX;
+ }
+
+ public int getSizeY()
+ {
+ return sizeY;
+ }
+
+ public void setSizeY(int sizeY)
+ {
+ this.sizeY = sizeY;
+ }
+
+ public int getAnInt2083()
+ {
+ return anInt2083;
+ }
+
+ public void setAnInt2083(int anInt2083)
+ {
+ this.anInt2083 = anInt2083;
+ }
+
+ public int[] getAnIntArray2084()
+ {
+ return anIntArray2084;
+ }
+
+ public void setAnIntArray2084(int[] anIntArray2084)
+ {
+ this.anIntArray2084 = anIntArray2084;
+ }
+
+ public int getOffsetX()
+ {
+ return offsetX;
+ }
+
+ public void setOffsetX(int offsetX)
+ {
+ this.offsetX = offsetX;
+ }
+
+ public boolean isNonFlatShading()
+ {
+ return nonFlatShading;
+ }
+
+ public void setNonFlatShading(boolean nonFlatShading)
+ {
+ this.nonFlatShading = nonFlatShading;
+ }
+
+ public int getAnInt2088()
+ {
+ return anInt2088;
+ }
+
+ public void setAnInt2088(int anInt2088)
+ {
+ this.anInt2088 = anInt2088;
+ }
+
+ public int getAnimationID()
+ {
+ return animationID;
+ }
+
+ public void setAnimationID(int animationID)
+ {
+ this.animationID = animationID;
+ }
+
+ public int getVarpID()
+ {
+ return varpID;
+ }
+
+ public void setVarpID(int varpID)
+ {
+ this.varpID = varpID;
+ }
+
+ public int getAmbient()
+ {
+ return ambient;
+ }
+
+ public void setAmbient(int ambient)
+ {
+ this.ambient = ambient;
+ }
+
+ public int getContrast()
+ {
+ return contrast;
+ }
+
+ public void setContrast(int contrast)
+ {
+ this.contrast = contrast;
+ }
+
+ public String[] getActions()
+ {
+ return actions;
+ }
+
+ public void setActions(String[] actions)
+ {
+ this.actions = actions;
+ }
+
+ public int getAnInt2094()
+ {
+ return anInt2094;
+ }
+
+ public void setAnInt2094(int anInt2094)
+ {
+ this.anInt2094 = anInt2094;
+ }
+
+ public int getMapSceneID()
+ {
+ return mapSceneID;
+ }
+
+ public void setMapSceneID(int mapSceneID)
+ {
+ this.mapSceneID = mapSceneID;
+ }
+
+ public short[] getRecolorToReplace()
+ {
+ return recolorToReplace;
+ }
+
+ public void setRecolorToReplace(short[] recolorToReplace)
+ {
+ this.recolorToReplace = recolorToReplace;
+ }
+
+ public boolean isaBool2097()
+ {
+ return aBool2097;
+ }
+
+ public void setaBool2097(boolean aBool2097)
+ {
+ this.aBool2097 = aBool2097;
+ }
+
+ public int getModelSizeX()
+ {
+ return modelSizeX;
+ }
+
+ public void setModelSizeX(int modelSizeX)
+ {
+ this.modelSizeX = modelSizeX;
+ }
+
+ public int getModelSizeHeight()
+ {
+ return modelSizeHeight;
+ }
+
+ public void setModelSizeHeight(int modelSizeHeight)
+ {
+ this.modelSizeHeight = modelSizeHeight;
+ }
+
+ public int getModelSizeY()
+ {
+ return modelSizeY;
+ }
+
+ public void setModelSizeY(int modelSizeY)
+ {
+ this.modelSizeY = modelSizeY;
+ }
+
+ public int getObjectID()
+ {
+ return objectID;
+ }
+
+ public void setObjectID(int objectID)
+ {
+ this.objectID = objectID;
+ }
+
+ public int getOffsetHeight()
+ {
+ return offsetHeight;
+ }
+
+ public void setOffsetHeight(int offsetHeight)
+ {
+ this.offsetHeight = offsetHeight;
+ }
+
+ public int getOffsetY()
+ {
+ return offsetY;
+ }
+
+ public void setOffsetY(int offsetY)
+ {
+ this.offsetY = offsetY;
+ }
+
+ public boolean isaBool2104()
+ {
+ return aBool2104;
+ }
+
+ public void setaBool2104(boolean aBool2104)
+ {
+ this.aBool2104 = aBool2104;
+ }
+
+ public int getAnInt2105()
+ {
+ return anInt2105;
+ }
+
+ public void setAnInt2105(int anInt2105)
+ {
+ this.anInt2105 = anInt2105;
+ }
+
+ public int getAnInt2106()
+ {
+ return anInt2106;
+ }
+
+ public void setAnInt2106(int anInt2106)
+ {
+ this.anInt2106 = anInt2106;
+ }
+
+ public int[] getConfigChangeDest()
+ {
+ return configChangeDest;
+ }
+
+ public void setConfigChangeDest(int[] configChangeDest)
+ {
+ this.configChangeDest = configChangeDest;
+ }
+
+ public boolean isaBool2108()
+ {
+ return aBool2108;
+ }
+
+ public void setaBool2108(boolean aBool2108)
+ {
+ this.aBool2108 = aBool2108;
+ }
+
+ public int getConfigId()
+ {
+ return configId;
+ }
+
+ public void setConfigId(int configId)
+ {
+ this.configId = configId;
+ }
+
+ public int getAnInt2110()
+ {
+ return anInt2110;
+ }
+
+ public void setAnInt2110(int anInt2110)
+ {
+ this.anInt2110 = anInt2110;
+ }
+
+ public boolean isaBool2111()
+ {
+ return aBool2111;
+ }
+
+ public void setaBool2111(boolean aBool2111)
+ {
+ this.aBool2111 = aBool2111;
+ }
+
+ public int getAnInt2112()
+ {
+ return anInt2112;
+ }
+
+ public void setAnInt2112(int anInt2112)
+ {
+ this.anInt2112 = anInt2112;
+ }
+
+ public int getAnInt2113()
+ {
+ return anInt2113;
+ }
+
+ public void setAnInt2113(int anInt2113)
+ {
+ this.anInt2113 = anInt2113;
+ }
+
+ public boolean isaBool2114()
+ {
+ return aBool2114;
+ }
+
+ public void setaBool2114(boolean aBool2114)
+ {
+ this.aBool2114 = aBool2114;
+ }
+}
diff --git a/cache/src/main/java/net/runelite/cache/definitions/ScriptDefinition.java b/cache/src/main/java/net/runelite/cache/definitions/ScriptDefinition.java
new file mode 100644
index 0000000000..bc75a9efe4
--- /dev/null
+++ b/cache/src/main/java/net/runelite/cache/definitions/ScriptDefinition.java
@@ -0,0 +1,123 @@
+/*
+ * Copyright (c) 2016, Adam
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Adam
+ * 4. Neither the name of the Adam nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Adam ''AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL Adam BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package net.runelite.cache.definitions;
+
+public class ScriptDefinition
+{
+ private int id;
+ private int anInt2269;
+ private int[] instructions;
+ private int[] intOperands;
+ private String[] aStringArray2272;
+ private int localStringCount;
+ private int anInt2276;
+ private int localIntCount;
+
+ public int getId()
+ {
+ return id;
+ }
+
+ public void setId(int id)
+ {
+ this.id = id;
+ }
+
+ public int getAnInt2269()
+ {
+ return anInt2269;
+ }
+
+ public void setAnInt2269(int anInt2269)
+ {
+ this.anInt2269 = anInt2269;
+ }
+
+ public int[] getInstructions()
+ {
+ return instructions;
+ }
+
+ public void setInstructions(int[] instructions)
+ {
+ this.instructions = instructions;
+ }
+
+ public int[] getIntOperands()
+ {
+ return intOperands;
+ }
+
+ public void setIntOperands(int[] intOperands)
+ {
+ this.intOperands = intOperands;
+ }
+
+ public String[] getaStringArray2272()
+ {
+ return aStringArray2272;
+ }
+
+ public void setaStringArray2272(String[] aStringArray2272)
+ {
+ this.aStringArray2272 = aStringArray2272;
+ }
+
+ public int getLocalStringCount()
+ {
+ return localStringCount;
+ }
+
+ public void setLocalStringCount(int localStringCount)
+ {
+ this.localStringCount = localStringCount;
+ }
+
+ public int getAnInt2276()
+ {
+ return anInt2276;
+ }
+
+ public void setAnInt2276(int anInt2276)
+ {
+ this.anInt2276 = anInt2276;
+ }
+
+ public int getLocalIntCount()
+ {
+ return localIntCount;
+ }
+
+ public void setLocalIntCount(int localIntCount)
+ {
+ this.localIntCount = localIntCount;
+ }
+}
diff --git a/cache/src/main/java/net/runelite/cache/definitions/SpriteDefinition.java b/cache/src/main/java/net/runelite/cache/definitions/SpriteDefinition.java
new file mode 100644
index 0000000000..4f0163ff5c
--- /dev/null
+++ b/cache/src/main/java/net/runelite/cache/definitions/SpriteDefinition.java
@@ -0,0 +1,132 @@
+/*
+ * Copyright (c) 2016, Adam
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Adam
+ * 4. Neither the name of the Adam nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Adam ''AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL Adam BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package net.runelite.cache.definitions;
+
+import net.runelite.cache.definitions.loaders.SpriteLoader;
+
+public class SpriteDefinition
+{
+ private SpriteLoader loader;
+ private int offsetX;
+ private int offsetY;
+ private int width;
+ private int height;
+ private byte[] pixels;
+ private int maxWidth;
+ private int maxHeight;
+
+ public SpriteDefinition(SpriteLoader loader)
+ {
+ this.loader = loader;
+ }
+
+ public SpriteLoader getLoader()
+ {
+ return loader;
+ }
+
+ public void setLoader(SpriteLoader loader)
+ {
+ this.loader = loader;
+ }
+
+ public int getOffsetX()
+ {
+ return offsetX;
+ }
+
+ public void setOffsetX(int offsetX)
+ {
+ this.offsetX = offsetX;
+ }
+
+ public int getOffsetY()
+ {
+ return offsetY;
+ }
+
+ public void setOffsetY(int offsetY)
+ {
+ this.offsetY = offsetY;
+ }
+
+ public int getWidth()
+ {
+ return width;
+ }
+
+ public void setWidth(int width)
+ {
+ this.width = width;
+ }
+
+ public int getHeight()
+ {
+ return height;
+ }
+
+ public void setHeight(int height)
+ {
+ this.height = height;
+ }
+
+ public byte[] getPixels()
+ {
+ return pixels;
+ }
+
+ public void setPixels(byte[] pixels)
+ {
+ this.pixels = pixels;
+ }
+
+ public int getMaxWidth()
+ {
+ return maxWidth;
+ }
+
+ public void setMaxWidth(int maxWidth)
+ {
+ this.maxWidth = maxWidth;
+ }
+
+ public int getMaxHeight()
+ {
+ return maxHeight;
+ }
+
+ public void setMaxHeight(int maxHeight)
+ {
+ this.maxHeight = maxHeight;
+ }
+
+
+}
diff --git a/cache/src/main/java/net/runelite/cache/definitions/loaders/EnumLoader.java b/cache/src/main/java/net/runelite/cache/definitions/loaders/EnumLoader.java
new file mode 100644
index 0000000000..b8f5e155c1
--- /dev/null
+++ b/cache/src/main/java/net/runelite/cache/definitions/loaders/EnumLoader.java
@@ -0,0 +1,111 @@
+/*
+ * Copyright (c) 2016, Adam
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Adam
+ * 4. Neither the name of the Adam nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Adam ''AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL Adam BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package net.runelite.cache.definitions.loaders;
+
+import net.runelite.cache.definitions.EnumDefinition;
+import net.runelite.cache.io.InputStream;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class EnumLoader
+{
+ private static final Logger logger = LoggerFactory.getLogger(EnumLoader.class);
+
+ public EnumDefinition load(int id, byte[] b)
+ {
+ EnumDefinition def = new EnumDefinition();
+ InputStream is = new InputStream(b);
+
+ def.setId(id);
+
+ for (;;)
+ {
+ int opcode = is.readUnsignedByte();
+ if (opcode == 0)
+ {
+ break;
+ }
+
+ processOp(opcode, def, is);
+ }
+
+ return def;
+ }
+
+ private void processOp(int opcode, EnumDefinition def, InputStream is)
+ {
+ switch (opcode)
+ {
+ case 1:
+ def.setKeyType((char) is.readUnsignedByte());
+ break;
+ case 2:
+ def.setValType((char) is.readUnsignedByte());
+ break;
+ case 3:
+ def.setDefaultString(is.readString());
+ break;
+ case 4:
+ def.setDefaultInt(is.readInt());
+ break;
+ case 5:
+ {
+ int size = is.readUnsignedShort();
+ int[] keys = new int[size];
+ String[] stringVals = new String[size];
+ for (int index = 0; index < size; ++index)
+ {
+ keys[index] = is.readInt();
+ stringVals[index] = is.readString();
+ }
+ def.setKeys(keys);
+ def.setStringVals(stringVals);
+ break;
+ }
+ case 6:
+ {
+ int size = is.readUnsignedShort();
+ int[] keys = new int[size];
+ int[] intVals = new int[size];
+ for (int index = 0; index < size; ++index)
+ {
+ keys[index] = is.readInt();
+ intVals[index] = is.readInt();
+ }
+ def.setKeys(keys);
+ def.setIntVals(intVals);
+ break;
+ }
+ default:
+ logger.warn("Unrecognized opcode {}", opcode);
+ break;
+ }
+ }
+}
diff --git a/cache/src/main/java/net/runelite/cache/definitions/loaders/ItemLoader.java b/cache/src/main/java/net/runelite/cache/definitions/loaders/ItemLoader.java
new file mode 100644
index 0000000000..7b8f6aca36
--- /dev/null
+++ b/cache/src/main/java/net/runelite/cache/definitions/loaders/ItemLoader.java
@@ -0,0 +1,266 @@
+/*
+ * Copyright (c) 2016, Adam
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Adam
+ * 4. Neither the name of the Adam nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Adam ''AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL Adam BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package net.runelite.cache.definitions.loaders;
+
+import java.util.ArrayList;
+import java.util.List;
+import net.runelite.cache.definitions.ItemDefinition;
+import net.runelite.cache.io.InputStream;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class ItemLoader
+{
+ private static final Logger logger = LoggerFactory.getLogger(ItemLoader.class);
+ private final List items = new ArrayList<>();
+
+ public List getItems()
+ {
+ return items;
+ }
+
+ public void load(int id, InputStream stream)
+ {
+ ItemDefinition def = new ItemDefinition(id);
+
+ while (true)
+ {
+ int opcode = stream.readUnsignedByte();
+ if (opcode == 0)
+ {
+ break;
+ }
+
+ this.decodeValues(opcode, def, stream);
+ }
+
+ items.add(def);
+ }
+
+ private void decodeValues(int opcode, ItemDefinition def, InputStream stream)
+ {
+ if (opcode == 1)
+ {
+ def.inventoryModel = stream.readUnsignedShort();
+ }
+ else if (opcode == 2)
+ {
+ def.name = stream.readString();
+ }
+ else if (opcode == 4)
+ {
+ def.zoom2d = stream.readUnsignedShort();
+ }
+ else if (opcode == 5)
+ {
+ def.xan2d = stream.readUnsignedShort();
+ }
+ else if (opcode == 6)
+ {
+ def.yan2d = stream.readUnsignedShort();
+ }
+ else if (7 == opcode)
+ {
+ def.xOffset2d = stream.readUnsignedShort();
+ if (def.xOffset2d > 32767)
+ {
+ def.xOffset2d -= 65536;
+ }
+ }
+ else if (8 == opcode)
+ {
+ def.yOffset2d = stream.readUnsignedShort();
+ if (def.yOffset2d > 32767)
+ {
+ def.yOffset2d -= 65536;
+ }
+ }
+ else if (11 == opcode)
+ {
+ def.stackable = 1;
+ }
+ else if (opcode == 12)
+ {
+ def.cost = stream.readInt();
+ }
+ else if (16 == opcode)
+ {
+ def.members = true;
+ }
+ else if (opcode == 23)
+ {
+ def.maleModel0 = stream.readUnsignedShort();
+ def.maleOffset = stream.readUnsignedByte();
+ }
+ else if (opcode == 24)
+ {
+ def.maleModel1 = stream.readUnsignedShort();
+ }
+ else if (25 == opcode)
+ {
+ def.femaleModel0 = stream.readUnsignedShort();
+ def.femaleOffset = stream.readUnsignedByte();
+ }
+ else if (26 == opcode)
+ {
+ def.femaleModel1 = stream.readUnsignedShort();
+ }
+ else if (opcode >= 30 && opcode < 35)
+ {
+ def.options[opcode - 30] = stream.readString();
+ if (def.options[opcode - 30].equalsIgnoreCase("Hidden"))
+ {
+ def.options[opcode - 30] = null;
+ }
+ }
+ else if (opcode >= 35 && opcode < 40)
+ {
+ def.interfaceOptions[opcode - 35] = stream.readString();
+ }
+ else if (opcode == 40)
+ {
+ int var5 = stream.readUnsignedByte();
+ def.colorFind = new short[var5];
+ def.colorReplace = new short[var5];
+
+ for (int var4 = 0; var4 < var5; ++var4)
+ {
+ def.colorFind[var4] = (short) stream.readUnsignedShort();
+ def.colorReplace[var4] = (short) stream.readUnsignedShort();
+ }
+
+ }
+ else if (opcode == 78)
+ {
+ def.maleModel2 = stream.readUnsignedShort();
+ }
+ else if (opcode == 79)
+ {
+ def.femaleModel2 = stream.readUnsignedShort();
+ }
+ else if (90 == opcode)
+ {
+ def.maleHeadModel = stream.readUnsignedShort();
+ }
+ else if (91 == opcode)
+ {
+ def.femaleHeadModel = stream.readUnsignedShort();
+ }
+ else if (92 == opcode)
+ {
+ def.maleHeadModel2 = stream.readUnsignedShort();
+ }
+ else if (opcode == 93)
+ {
+ def.femaleHeadModel2 = stream.readUnsignedShort();
+ }
+ else if (opcode == 95)
+ {
+ def.zan2d = stream.readUnsignedShort();
+ }
+ else if (97 == opcode)
+ {
+ def.notedID = stream.readUnsignedShort();
+ }
+ else if (98 == opcode)
+ {
+ def.notedTemplate = stream.readUnsignedShort();
+ }
+ else if (opcode >= 100 && opcode < 110)
+ {
+ if (def.countObj == null)
+ {
+ def.countObj = new int[10];
+ def.countCo = new int[10];
+ }
+
+ def.countObj[opcode - 100] = stream.readUnsignedShort();
+ def.countCo[opcode - 100] = stream.readUnsignedShort();
+ }
+ else if (110 == opcode)
+ {
+ def.resizeX = stream.readUnsignedShort();
+ }
+ else if (opcode == 111)
+ {
+ def.resizeY = stream.readUnsignedShort();
+ }
+ else if (opcode == 112)
+ {
+ def.resizeZ = stream.readUnsignedShort();
+ }
+ else if (opcode == 113)
+ {
+ def.ambient = stream.readByte();
+ }
+ else if (114 == opcode)
+ {
+ def.contrast = stream.readByte();
+ }
+ else if (115 == opcode)
+ {
+ def.team = stream.readUnsignedByte();
+ }
+ else if (opcode == 41)
+ {
+ int var5 = stream.readUnsignedByte();
+ def.textureFind = new short[var5];
+ def.textureReplace = new short[var5];
+
+ for (int var4 = 0; var4 < var5; ++var4)
+ {
+ def.textureFind[var4] = (short) stream.readUnsignedShort();
+ def.textureReplace[var4] = (short) stream.readUnsignedShort();
+ }
+
+ }
+ else if (opcode == 148)
+ {
+ stream.readUnsignedShort();
+ }
+ else if (opcode == 65)
+ {
+
+ }
+ else if (opcode == 139)
+ {
+ stream.readUnsignedShort();
+ }
+ else if (opcode == 140)
+ {
+
+ }
+ else
+ {
+ logger.warn("Unrecognized opcode {}", opcode);
+ }
+ }
+}
diff --git a/cache/src/main/java/net/runelite/cache/definitions/loaders/ModelLoader.java b/cache/src/main/java/net/runelite/cache/definitions/loaders/ModelLoader.java
new file mode 100644
index 0000000000..b613a8837b
--- /dev/null
+++ b/cache/src/main/java/net/runelite/cache/definitions/loaders/ModelLoader.java
@@ -0,0 +1,741 @@
+package net.runelite.cache.definitions.loaders;
+
+import net.runelite.cache.definitions.ModelDefinition;
+import net.runelite.cache.io.InputStream;
+
+public class ModelLoader extends ModelDefinition
+{
+ public void load(byte[] var1)
+ {
+ if (var1[var1.length - 1] == -1 && var1[var1.length - 2] == -1)
+ {
+ this.load1(var1);
+ }
+ else
+ {
+ this.load2(var1);
+ }
+ }
+
+ private void load1(byte[] var1)
+ {
+ InputStream var2 = new InputStream(var1);
+ InputStream var24 = new InputStream(var1);
+ InputStream var3 = new InputStream(var1);
+ InputStream var28 = new InputStream(var1);
+ InputStream var6 = new InputStream(var1);
+ InputStream var55 = new InputStream(var1);
+ InputStream var51 = new InputStream(var1);
+ var2.setOffset(var1.length - 23);
+ int verticeCount = var2.readUnsignedShort();
+ int triangleCount = var2.readUnsignedShort();
+ int textureTriangleCount = var2.readUnsignedByte();
+ int var13 = var2.readUnsignedByte();
+ int modelPriority = var2.readUnsignedByte();
+ int var50 = var2.readUnsignedByte();
+ int var17 = var2.readUnsignedByte();
+ int modelTexture = var2.readUnsignedByte();
+ int modelVertexSkins = var2.readUnsignedByte();
+ int var20 = var2.readUnsignedShort();
+ int var21 = var2.readUnsignedShort();
+ int var42 = var2.readUnsignedShort();
+ int var22 = var2.readUnsignedShort();
+ int var38 = var2.readUnsignedShort();
+ int textureAmount = 0;
+ int var7 = 0;
+ int var29 = 0;
+ int position;
+ if (textureTriangleCount > 0)
+ {
+ this.textureRenderTypes = new byte[textureTriangleCount];
+ var2.setOffset(0);
+
+ for (position = 0; position < textureTriangleCount; ++position)
+ {
+ byte renderType = this.textureRenderTypes[position] = var2.readByte();
+ if (renderType == 0)
+ {
+ ++textureAmount;
+ }
+
+ if (renderType >= 1 && renderType <= 3)
+ {
+ ++var7;
+ }
+
+ if (renderType == 2)
+ {
+ ++var29;
+ }
+ }
+ }
+
+ position = textureTriangleCount + verticeCount;
+ int renderTypePos = position;
+ if (var13 == 1)
+ {
+ position += triangleCount;
+ }
+
+ int var49 = position;
+ position += triangleCount;
+ int priorityPos = position;
+ if (modelPriority == 255)
+ {
+ position += triangleCount;
+ }
+
+ int triangleSkinPos = position;
+ if (var17 == 1)
+ {
+ position += triangleCount;
+ }
+
+ int var35 = position;
+ if (modelVertexSkins == 1)
+ {
+ position += verticeCount;
+ }
+
+ int alphaPos = position;
+ if (var50 == 1)
+ {
+ position += triangleCount;
+ }
+
+ int var11 = position;
+ position += var22;
+ int texturePos = position;
+ if (modelTexture == 1)
+ {
+ position += triangleCount * 2;
+ }
+
+ int textureCoordPos = position;
+ position += var38;
+ int colorPos = position;
+ position += triangleCount * 2;
+ int var40 = position;
+ position += var20;
+ int var41 = position;
+ position += var21;
+ int var8 = position;
+ position += var42;
+ int var43 = position;
+ position += textureAmount * 6;
+ int var37 = position;
+ position += var7 * 6;
+ int var48 = position;
+ position += var7 * 6;
+ int var56 = position;
+ position += var7 * 2;
+ int var45 = position;
+ position += var7;
+ int var46 = position;
+ position += var7 * 2 + var29 * 2;
+ this.vertexCount = verticeCount;
+ this.triangleFaceCount = triangleCount;
+ this.anInt2569 = textureTriangleCount;
+ this.vertexX = new int[verticeCount];
+ this.vertexY = new int[verticeCount];
+ this.vertexZ = new int[verticeCount];
+ this.trianglePointsX = new int[triangleCount];
+ this.trianglePointsY = new int[triangleCount];
+ this.trianglePointsZ = new int[triangleCount];
+ if (modelVertexSkins == 1)
+ {
+ this.vertexSkins = new int[verticeCount];
+ }
+
+ if (var13 == 1)
+ {
+ this.faceRenderType = new byte[triangleCount];
+ }
+
+ if (modelPriority == 255)
+ {
+ this.faceRenderPriorities = new byte[triangleCount];
+ }
+ else
+ {
+ this.priority = (byte) modelPriority;
+ }
+
+ if (var50 == 1)
+ {
+ this.faceAlphas = new byte[triangleCount];
+ }
+
+ if (var17 == 1)
+ {
+ this.triangleSkinValues = new int[triangleCount];
+ }
+
+ if (modelTexture == 1)
+ {
+ this.faceTextures = new short[triangleCount];
+ }
+
+ if (modelTexture == 1 && textureTriangleCount > 0)
+ {
+ this.textureCoords = new byte[triangleCount];
+ }
+
+ this.faceColor = new short[triangleCount];
+ if (textureTriangleCount > 0)
+ {
+ this.texTriangleX = new short[textureTriangleCount];
+ this.texTriangleY = new short[textureTriangleCount];
+ this.texTriangleZ = new short[textureTriangleCount];
+ if (var7 > 0)
+ {
+ this.aShortArray2574 = new short[var7];
+ this.aShortArray2575 = new short[var7];
+ this.aShortArray2586 = new short[var7];
+ this.aShortArray2577 = new short[var7];
+ this.aByteArray2580 = new byte[var7];
+ this.aShortArray2578 = new short[var7];
+ }
+
+ if (var29 > 0)
+ {
+ this.texturePrimaryColor = new short[var29];
+ }
+ }
+
+ var2.setOffset(textureTriangleCount);
+ var24.setOffset(var40);
+ var3.setOffset(var41);
+ var28.setOffset(var8);
+ var6.setOffset(var35);
+ int vX = 0;
+ int vY = 0;
+ int vZ = 0;
+
+ int vertexZOffset;
+ int var10;
+ int vertexYOffset;
+ int var15;
+ int point;
+ for (point = 0; point < verticeCount; ++point)
+ {
+ int vertexFlags = var2.readUnsignedByte();
+ int vertexXOffset = 0;
+ if ((vertexFlags & 1) != 0)
+ {
+ vertexXOffset = var24.readShortSmart();
+ }
+
+ vertexYOffset = 0;
+ if ((vertexFlags & 2) != 0)
+ {
+ vertexYOffset = var3.readShortSmart();
+ }
+
+ vertexZOffset = 0;
+ if ((vertexFlags & 4) != 0)
+ {
+ vertexZOffset = var28.readShortSmart();
+ }
+
+ this.vertexX[point] = vX + vertexXOffset;
+ this.vertexY[point] = vY + vertexYOffset;
+ this.vertexZ[point] = vZ + vertexZOffset;
+ vX = this.vertexX[point];
+ vY = this.vertexY[point];
+ vZ = this.vertexZ[point];
+ if (modelVertexSkins == 1)
+ {
+ this.vertexSkins[point] = var6.readUnsignedByte();
+ }
+ }
+
+ var2.setOffset(colorPos);
+ var24.setOffset(renderTypePos);
+ var3.setOffset(priorityPos);
+ var28.setOffset(alphaPos);
+ var6.setOffset(triangleSkinPos);
+ var55.setOffset(texturePos);
+ var51.setOffset(textureCoordPos);
+
+ for (point = 0; point < triangleCount; ++point)
+ {
+ this.faceColor[point] = (short) var2.readUnsignedShort();
+ if (var13 == 1)
+ {
+ this.faceRenderType[point] = var24.readByte();
+ }
+
+ if (modelPriority == 255)
+ {
+ this.faceRenderPriorities[point] = var3.readByte();
+ }
+
+ if (var50 == 1)
+ {
+ this.faceAlphas[point] = var28.readByte();
+ }
+
+ if (var17 == 1)
+ {
+ this.triangleSkinValues[point] = var6.readUnsignedByte();
+ }
+
+ if (modelTexture == 1)
+ {
+ this.faceTextures[point] = (short) (var55.readUnsignedShort() - 1);
+ }
+
+ if (this.textureCoords != null && this.faceTextures[point] != -1)
+ {
+ this.textureCoords[point] = (byte) (var51.readUnsignedByte() - 1);
+ }
+ }
+
+ var2.setOffset(var11);
+ var24.setOffset(var49);
+ int trianglePointX = 0;
+ int trianglePointY = 0;
+ int trianglePointZ = 0;
+ vertexYOffset = 0;
+
+ int var16;
+ for (vertexZOffset = 0; vertexZOffset < triangleCount; ++vertexZOffset)
+ {
+ int numFaces = var24.readUnsignedByte();
+ if (numFaces == 1)
+ {
+ trianglePointX = var2.readShortSmart() + vertexYOffset;
+ trianglePointY = var2.readShortSmart() + trianglePointX;
+ trianglePointZ = var2.readShortSmart() + trianglePointY;
+ vertexYOffset = trianglePointZ;
+ this.trianglePointsX[vertexZOffset] = trianglePointX;
+ this.trianglePointsY[vertexZOffset] = trianglePointY;
+ this.trianglePointsZ[vertexZOffset] = trianglePointZ;
+ }
+
+ if (numFaces == 2)
+ {
+ trianglePointY = trianglePointZ;
+ trianglePointZ = var2.readShortSmart() + vertexYOffset;
+ vertexYOffset = trianglePointZ;
+ this.trianglePointsX[vertexZOffset] = trianglePointX;
+ this.trianglePointsY[vertexZOffset] = trianglePointY;
+ this.trianglePointsZ[vertexZOffset] = trianglePointZ;
+ }
+
+ if (numFaces == 3)
+ {
+ trianglePointX = trianglePointZ;
+ trianglePointZ = var2.readShortSmart() + vertexYOffset;
+ vertexYOffset = trianglePointZ;
+ this.trianglePointsX[vertexZOffset] = trianglePointX;
+ this.trianglePointsY[vertexZOffset] = trianglePointY;
+ this.trianglePointsZ[vertexZOffset] = trianglePointZ;
+ }
+
+ if (numFaces == 4)
+ {
+ int var57 = trianglePointX;
+ trianglePointX = trianglePointY;
+ trianglePointY = var57;
+ trianglePointZ = var2.readShortSmart() + vertexYOffset;
+ vertexYOffset = trianglePointZ;
+ this.trianglePointsX[vertexZOffset] = trianglePointX;
+ this.trianglePointsY[vertexZOffset] = var57;
+ this.trianglePointsZ[vertexZOffset] = trianglePointZ;
+ }
+ }
+
+ var2.setOffset(var43);
+ var24.setOffset(var37);
+ var3.setOffset(var48);
+ var28.setOffset(var56);
+ var6.setOffset(var45);
+ var55.setOffset(var46);
+
+ for (int texIndex = 0; texIndex < textureTriangleCount; ++texIndex)
+ {
+ int type = this.textureRenderTypes[texIndex] & 255;
+ if (type == 0)
+ {
+ this.texTriangleX[texIndex] = (short) var2.readUnsignedShort();
+ this.texTriangleY[texIndex] = (short) var2.readUnsignedShort();
+ this.texTriangleZ[texIndex] = (short) var2.readUnsignedShort();
+ }
+
+ if (type == 1)
+ {
+ this.texTriangleX[texIndex] = (short) var24.readUnsignedShort();
+ this.texTriangleY[texIndex] = (short) var24.readUnsignedShort();
+ this.texTriangleZ[texIndex] = (short) var24.readUnsignedShort();
+ this.aShortArray2574[texIndex] = (short) var3.readUnsignedShort();
+ this.aShortArray2575[texIndex] = (short) var3.readUnsignedShort();
+ this.aShortArray2586[texIndex] = (short) var3.readUnsignedShort();
+ this.aShortArray2577[texIndex] = (short) var28.readUnsignedShort();
+ this.aByteArray2580[texIndex] = var6.readByte();
+ this.aShortArray2578[texIndex] = (short) var55.readUnsignedShort();
+ }
+
+ if (type == 2)
+ {
+ this.texTriangleX[texIndex] = (short) var24.readUnsignedShort();
+ this.texTriangleY[texIndex] = (short) var24.readUnsignedShort();
+ this.texTriangleZ[texIndex] = (short) var24.readUnsignedShort();
+ this.aShortArray2574[texIndex] = (short) var3.readUnsignedShort();
+ this.aShortArray2575[texIndex] = (short) var3.readUnsignedShort();
+ this.aShortArray2586[texIndex] = (short) var3.readUnsignedShort();
+ this.aShortArray2577[texIndex] = (short) var28.readUnsignedShort();
+ this.aByteArray2580[texIndex] = var6.readByte();
+ this.aShortArray2578[texIndex] = (short) var55.readUnsignedShort();
+ this.texturePrimaryColor[texIndex] = (short) var55.readUnsignedShort();
+ }
+
+ if (type == 3)
+ {
+ this.texTriangleX[texIndex] = (short) var24.readUnsignedShort();
+ this.texTriangleY[texIndex] = (short) var24.readUnsignedShort();
+ this.texTriangleZ[texIndex] = (short) var24.readUnsignedShort();
+ this.aShortArray2574[texIndex] = (short) var3.readUnsignedShort();
+ this.aShortArray2575[texIndex] = (short) var3.readUnsignedShort();
+ this.aShortArray2586[texIndex] = (short) var3.readUnsignedShort();
+ this.aShortArray2577[texIndex] = (short) var28.readUnsignedShort();
+ this.aByteArray2580[texIndex] = var6.readByte();
+ this.aShortArray2578[texIndex] = (short) var55.readUnsignedShort();
+ }
+ }
+
+ var2.setOffset(position);
+ vertexZOffset = var2.readUnsignedByte();
+ if (vertexZOffset != 0)
+ {
+ //new Class41();
+ var2.readUnsignedShort();
+ var2.readUnsignedShort();
+ var2.readUnsignedShort();
+ var2.readInt();
+ }
+ }
+
+ private void load2(byte[] var1)
+ {
+ boolean var2 = false;
+ boolean var43 = false;
+ InputStream var5 = new InputStream(var1);
+ InputStream var39 = new InputStream(var1);
+ InputStream var26 = new InputStream(var1);
+ InputStream var9 = new InputStream(var1);
+ InputStream var3 = new InputStream(var1);
+ var5.setOffset(var1.length - 18);
+ int var10 = var5.readUnsignedShort();
+ int var11 = var5.readUnsignedShort();
+ int var12 = var5.readUnsignedByte();
+ int var13 = var5.readUnsignedByte();
+ int var14 = var5.readUnsignedByte();
+ int var30 = var5.readUnsignedByte();
+ int var15 = var5.readUnsignedByte();
+ int var28 = var5.readUnsignedByte();
+ int var27 = var5.readUnsignedShort();
+ int var20 = var5.readUnsignedShort();
+ int var36 = var5.readUnsignedShort();
+ int var23 = var5.readUnsignedShort();
+ byte var16 = 0;
+ int var46 = var16 + var10;
+ int var24 = var46;
+ var46 += var11;
+ int var25 = var46;
+ if (var14 == 255)
+ {
+ var46 += var11;
+ }
+
+ int var4 = var46;
+ if (var15 == 1)
+ {
+ var46 += var11;
+ }
+
+ int var42 = var46;
+ if (var13 == 1)
+ {
+ var46 += var11;
+ }
+
+ int var37 = var46;
+ if (var28 == 1)
+ {
+ var46 += var10;
+ }
+
+ int var29 = var46;
+ if (var30 == 1)
+ {
+ var46 += var11;
+ }
+
+ int var44 = var46;
+ var46 += var23;
+ int var17 = var46;
+ var46 += var11 * 2;
+ int var32 = var46;
+ var46 += var12 * 6;
+ int var34 = var46;
+ var46 += var27;
+ int var35 = var46;
+ var46 += var20;
+ int var10000 = var46 + var36;
+ this.vertexCount = var10;
+ this.triangleFaceCount = var11;
+ this.anInt2569 = var12;
+ this.vertexX = new int[var10];
+ this.vertexY = new int[var10];
+ this.vertexZ = new int[var10];
+ this.trianglePointsX = new int[var11];
+ this.trianglePointsY = new int[var11];
+ this.trianglePointsZ = new int[var11];
+ if (var12 > 0)
+ {
+ this.textureRenderTypes = new byte[var12];
+ this.texTriangleX = new short[var12];
+ this.texTriangleY = new short[var12];
+ this.texTriangleZ = new short[var12];
+ }
+
+ if (var28 == 1)
+ {
+ this.vertexSkins = new int[var10];
+ }
+
+ if (var13 == 1)
+ {
+ this.faceRenderType = new byte[var11];
+ this.textureCoords = new byte[var11];
+ this.faceTextures = new short[var11];
+ }
+
+ if (var14 == 255)
+ {
+ this.faceRenderPriorities = new byte[var11];
+ }
+ else
+ {
+ this.priority = (byte) var14;
+ }
+
+ if (var30 == 1)
+ {
+ this.faceAlphas = new byte[var11];
+ }
+
+ if (var15 == 1)
+ {
+ this.triangleSkinValues = new int[var11];
+ }
+
+ this.faceColor = new short[var11];
+ var5.setOffset(var16);
+ var39.setOffset(var34);
+ var26.setOffset(var35);
+ var9.setOffset(var46);
+ var3.setOffset(var37);
+ int var41 = 0;
+ int var33 = 0;
+ int var19 = 0;
+
+ int var6;
+ int var7;
+ int var8;
+ int var18;
+ int var31;
+ for (var18 = 0; var18 < var10; ++var18)
+ {
+ var8 = var5.readUnsignedByte();
+ var31 = 0;
+ if ((var8 & 1) != 0)
+ {
+ var31 = var39.readShortSmart();
+ }
+
+ var6 = 0;
+ if ((var8 & 2) != 0)
+ {
+ var6 = var26.readShortSmart();
+ }
+
+ var7 = 0;
+ if ((var8 & 4) != 0)
+ {
+ var7 = var9.readShortSmart();
+ }
+
+ this.vertexX[var18] = var41 + var31;
+ this.vertexY[var18] = var33 + var6;
+ this.vertexZ[var18] = var19 + var7;
+ var41 = this.vertexX[var18];
+ var33 = this.vertexY[var18];
+ var19 = this.vertexZ[var18];
+ if (var28 == 1)
+ {
+ this.vertexSkins[var18] = var3.readUnsignedByte();
+ }
+ }
+
+ var5.setOffset(var17);
+ var39.setOffset(var42);
+ var26.setOffset(var25);
+ var9.setOffset(var29);
+ var3.setOffset(var4);
+
+ for (var18 = 0; var18 < var11; ++var18)
+ {
+ this.faceColor[var18] = (short) var5.readUnsignedShort();
+ if (var13 == 1)
+ {
+ var8 = var39.readUnsignedByte();
+ if ((var8 & 1) == 1)
+ {
+ this.faceRenderType[var18] = 1;
+ var2 = true;
+ }
+ else
+ {
+ this.faceRenderType[var18] = 0;
+ }
+
+ if ((var8 & 2) == 2)
+ {
+ this.textureCoords[var18] = (byte) (var8 >> 2);
+ this.faceTextures[var18] = this.faceColor[var18];
+ this.faceColor[var18] = 127;
+ if (this.faceTextures[var18] != -1)
+ {
+ var43 = true;
+ }
+ }
+ else
+ {
+ this.textureCoords[var18] = -1;
+ this.faceTextures[var18] = -1;
+ }
+ }
+
+ if (var14 == 255)
+ {
+ this.faceRenderPriorities[var18] = var26.readByte();
+ }
+
+ if (var30 == 1)
+ {
+ this.faceAlphas[var18] = var9.readByte();
+ }
+
+ if (var15 == 1)
+ {
+ this.triangleSkinValues[var18] = var3.readUnsignedByte();
+ }
+ }
+
+ var5.setOffset(var44);
+ var39.setOffset(var24);
+ var18 = 0;
+ var8 = 0;
+ var31 = 0;
+ var6 = 0;
+
+ int var21;
+ int var22;
+ for (var7 = 0; var7 < var11; ++var7)
+ {
+ var22 = var39.readUnsignedByte();
+ if (var22 == 1)
+ {
+ var18 = var5.readShortSmart() + var6;
+ var8 = var5.readShortSmart() + var18;
+ var31 = var5.readShortSmart() + var8;
+ var6 = var31;
+ this.trianglePointsX[var7] = var18;
+ this.trianglePointsY[var7] = var8;
+ this.trianglePointsZ[var7] = var31;
+ }
+
+ if (var22 == 2)
+ {
+ var8 = var31;
+ var31 = var5.readShortSmart() + var6;
+ var6 = var31;
+ this.trianglePointsX[var7] = var18;
+ this.trianglePointsY[var7] = var8;
+ this.trianglePointsZ[var7] = var31;
+ }
+
+ if (var22 == 3)
+ {
+ var18 = var31;
+ var31 = var5.readShortSmart() + var6;
+ var6 = var31;
+ this.trianglePointsX[var7] = var18;
+ this.trianglePointsY[var7] = var8;
+ this.trianglePointsZ[var7] = var31;
+ }
+
+ if (var22 == 4)
+ {
+ var21 = var18;
+ var18 = var8;
+ var8 = var21;
+ var31 = var5.readShortSmart() + var6;
+ var6 = var31;
+ this.trianglePointsX[var7] = var18;
+ this.trianglePointsY[var7] = var21;
+ this.trianglePointsZ[var7] = var31;
+ }
+ }
+
+ var5.setOffset(var32);
+
+ for (var7 = 0; var7 < var12; ++var7)
+ {
+ this.textureRenderTypes[var7] = 0;
+ this.texTriangleX[var7] = (short) var5.readUnsignedShort();
+ this.texTriangleY[var7] = (short) var5.readUnsignedShort();
+ this.texTriangleZ[var7] = (short) var5.readUnsignedShort();
+ }
+
+ if (this.textureCoords != null)
+ {
+ boolean var45 = false;
+
+ for (var22 = 0; var22 < var11; ++var22)
+ {
+ var21 = this.textureCoords[var22] & 255;
+ if (var21 != 255)
+ {
+ if ((this.texTriangleX[var21] & '\uffff') == this.trianglePointsX[var22] && (this.texTriangleY[var21] & '\uffff') == this.trianglePointsY[var22] && (this.texTriangleZ[var21] & '\uffff') == this.trianglePointsZ[var22])
+ {
+ this.textureCoords[var22] = -1;
+ }
+ else
+ {
+ var45 = true;
+ }
+ }
+ }
+
+ if (!var45)
+ {
+ this.textureCoords = null;
+ }
+ }
+
+ if (!var43)
+ {
+ this.faceTextures = null;
+ }
+
+ if (!var2)
+ {
+ this.faceRenderType = null;
+ }
+ }
+
+}
diff --git a/cache/src/main/java/net/runelite/cache/definitions/loaders/NpcLoader.java b/cache/src/main/java/net/runelite/cache/definitions/loaders/NpcLoader.java
new file mode 100644
index 0000000000..69aa79ac69
--- /dev/null
+++ b/cache/src/main/java/net/runelite/cache/definitions/loaders/NpcLoader.java
@@ -0,0 +1,241 @@
+/*
+ * Copyright (c) 2016, Adam
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Adam
+ * 4. Neither the name of the Adam nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Adam ''AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL Adam BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package net.runelite.cache.definitions.loaders;
+
+import java.util.ArrayList;
+import java.util.List;
+import net.runelite.cache.definitions.NpcDefinition;
+import net.runelite.cache.io.InputStream;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class NpcLoader
+{
+ private static final Logger logger = LoggerFactory.getLogger(NpcLoader.class);
+ private final List npcs = new ArrayList<>();
+
+ public List getNpcs()
+ {
+ return npcs;
+ }
+
+ public void load(int id, InputStream stream)
+ {
+ NpcDefinition def = new NpcDefinition(id);
+ while (true)
+ {
+ int opcode = stream.readUnsignedByte();
+ if (opcode == 0)
+ {
+ break;
+ }
+
+ this.decodeValues(opcode, def, stream);
+ }
+ npcs.add(def);
+ }
+
+ void decodeValues(int opcode, NpcDefinition def, InputStream stream)
+ {
+ int length;
+ int index;
+ if (1 == opcode)
+ {
+ length = stream.readUnsignedByte();
+ def.models = new int[length];
+
+ for (index = 0; index < length; ++index)
+ {
+ def.models[index] = stream.readUnsignedShort();
+ }
+
+ }
+ else if (2 == opcode)
+ {
+ def.name = stream.readString();
+ }
+ else if (12 == opcode)
+ {
+ def.tileSpacesOccupied = stream.readUnsignedByte();
+ }
+ else if (opcode == 13)
+ {
+ def.stanceAnimation = stream.readUnsignedShort();
+ }
+ else if (opcode == 14)
+ {
+ def.walkAnimation = stream.readUnsignedShort();
+ }
+ else if (15 == opcode)
+ {
+ def.anInt2165 = stream.readUnsignedShort();
+ }
+ else if (opcode == 16)
+ {
+ def.anInt2189 = stream.readUnsignedShort();
+ }
+ else if (17 == opcode)
+ {
+ def.walkAnimation = stream.readUnsignedShort();
+ def.rotate180Animation = stream.readUnsignedShort();
+ def.rotate90RightAnimation = stream.readUnsignedShort();
+ def.rotate90LeftAnimation = stream.readUnsignedShort();
+ }
+ else if (opcode >= 30 && opcode < 35)
+ {
+ def.options[opcode - 30] = stream.readString();
+ if (def.options[opcode - 30].equalsIgnoreCase("Hidden"))
+ {
+ def.options[opcode - 30] = null;
+ }
+ }
+ else if (opcode == 40)
+ {
+ length = stream.readUnsignedByte();
+ def.recolorToFind = new short[length];
+ def.recolorToReplace = new short[length];
+
+ for (index = 0; index < length; ++index)
+ {
+ def.recolorToFind[index] = (short) stream.readUnsignedShort();
+ def.recolorToReplace[index] = (short) stream.readUnsignedShort();
+ }
+
+ }
+ else if (opcode == 41)
+ {
+ length = stream.readUnsignedByte();
+ def.retextureToFind = new short[length];
+ def.retextureToReplace = new short[length];
+
+ for (index = 0; index < length; ++index)
+ {
+ def.retextureToFind[index] = (short) stream.readUnsignedShort();
+ def.retextureToReplace[index] = (short) stream.readUnsignedShort();
+ }
+
+ }
+ else if (60 == opcode)
+ {
+ length = stream.readUnsignedByte();
+ def.models_2 = new int[length];
+
+ for (index = 0; index < length; ++index)
+ {
+ def.models_2[index] = stream.readUnsignedShort();
+ }
+
+ }
+ else if (opcode == 93)
+ {
+ def.renderOnMinimap = false;
+ }
+ else if (95 == opcode)
+ {
+ def.combatLevel = stream.readUnsignedShort();
+ }
+ else if (97 == opcode)
+ {
+ def.resizeX = stream.readUnsignedShort();
+ }
+ else if (98 == opcode)
+ {
+ def.resizeY = stream.readUnsignedShort();
+ }
+ else if (opcode == 99)
+ {
+ def.hasRenderPriority = true;
+ }
+ else if (100 == opcode)
+ {
+ def.ambient = stream.readByte();
+ }
+ else if (101 == opcode)
+ {
+ def.contrast = stream.readByte();
+ }
+ else if (opcode == 102)
+ {
+ def.headIcon = stream.readUnsignedShort();
+ }
+ else if (103 == opcode)
+ {
+ def.anInt2156 = stream.readUnsignedShort();
+ }
+ else if (opcode == 106)
+ {
+ def.anInt2174 = stream.readUnsignedShort();
+ if ('\uffff' == def.anInt2174)
+ {
+ def.anInt2174 = -1;
+ }
+
+ def.anInt2187 = stream.readUnsignedShort();
+ if ('\uffff' == def.anInt2187)
+ {
+ def.anInt2187 = -40212193;
+ }
+
+ length = stream.readUnsignedByte();
+ def.anIntArray2185 = new int[length + 1];
+
+ for (index = 0; index <= length; ++index)
+ {
+ def.anIntArray2185[index] = stream.readUnsignedShort();
+ if (def.anIntArray2185[index] == '\uffff')
+ {
+ def.anIntArray2185[index] = -1;
+ }
+ }
+
+ }
+ else if (107 == opcode)
+ {
+ def.isClickable = false;
+ }
+ else if (opcode == 109)
+ {
+ def.aBool2170 = false;
+ }
+ else if (opcode == 111)
+ {
+ def.aBool2190 = true;
+ }
+ else if (opcode == 112)
+ {
+ def.anInt2184 = stream.readUnsignedByte();
+ }
+ else
+ {
+ logger.warn("Unrecognized opcode {}", opcode);
+ }
+ }
+}
diff --git a/cache/src/main/java/net/runelite/cache/definitions/loaders/ObjectLoader.java b/cache/src/main/java/net/runelite/cache/definitions/loaders/ObjectLoader.java
new file mode 100644
index 0000000000..22023175d8
--- /dev/null
+++ b/cache/src/main/java/net/runelite/cache/definitions/loaders/ObjectLoader.java
@@ -0,0 +1,313 @@
+/*
+ * Copyright (c) 2016, Adam
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Adam
+ * 4. Neither the name of the Adam nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Adam ''AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL Adam BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package net.runelite.cache.definitions.loaders;
+
+import net.runelite.cache.definitions.ObjectDefinition;
+import net.runelite.cache.io.InputStream;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class ObjectLoader
+{
+ private static final Logger logger = LoggerFactory.getLogger(ObjectLoader.class);
+
+ public ObjectDefinition load(int id, byte[] b)
+ {
+ ObjectDefinition def = new ObjectDefinition();
+ InputStream is = new InputStream(b);
+
+ def.setId(id);
+
+ for (;;)
+ {
+ int opcode = is.readUnsignedByte();
+ if (opcode == 0)
+ {
+ break;
+ }
+
+ processOp(opcode, def, is);
+ }
+
+ return def;
+ }
+
+ private void processOp(int opcode, ObjectDefinition def, InputStream is)
+ {
+ if (1 == opcode)
+ {
+ int length = is.readUnsignedByte();
+ if (length > 0)
+ {
+ int[] objectTypes = new int[length];
+ int[] objectModels = new int[length];
+
+ for (int index = 0; index < length; ++index)
+ {
+ objectModels[index] = is.readUnsignedShort();
+ objectTypes[index] = is.readUnsignedByte();
+ }
+
+ def.setObjectTypes(objectTypes);
+ def.setObjectModels(objectModels);
+ }
+ }
+ else if (opcode == 2)
+ {
+ def.setName(is.readString());
+ }
+ else if (opcode == 5)
+ {
+ int length = is.readUnsignedByte();
+ if (length > 0)
+ {
+ def.setObjectTypes(null);
+ int[] objectModels = new int[length];
+
+ for (int index = 0; index < length; ++index)
+ {
+ objectModels[index] = is.readUnsignedShort();
+ }
+
+ def.setObjectModels(objectModels);
+ }
+ }
+ else if (opcode == 14)
+ {
+ def.setSizeX(is.readUnsignedByte());
+ }
+ else if (15 == opcode)
+ {
+ def.setSizeY(is.readUnsignedByte());
+ }
+ else if (opcode == 17)
+ {
+ def.setAnInt2094(0);
+ def.setaBool2114(false);
+ }
+ else if (opcode == 18)
+ {
+ def.setaBool2114(false);
+ }
+ else if (opcode == 19)
+ {
+ def.setAnInt2088(is.readUnsignedByte());
+ }
+ else if (opcode == 21)
+ {
+ def.setAnInt2105(0);
+ }
+ else if (22 == opcode)
+ {
+ def.setNonFlatShading(false);
+ }
+ else if (opcode == 23)
+ {
+ def.setaBool2111(true);
+ }
+ else if (24 == opcode)
+ {
+ def.setAnimationID(is.readUnsignedShort());
+ if (def.getAnimationID() == 0xFFFF)
+ {
+ def.setAnimationID(-1);
+ }
+ }
+ else if (opcode == 27)
+ {
+ def.setAnInt2094(1);
+ }
+ else if (opcode == 28)
+ {
+ def.setAnInt2069(is.readUnsignedByte());
+ }
+ else if (opcode == 29)
+ {
+ def.setAmbient(is.readByte());
+ }
+ else if (opcode == 39)
+ {
+ def.setContrast(is.readByte());
+ }
+ else if (opcode >= 30 && opcode < 35)
+ {
+ String[] actions = def.getActions();
+ actions[opcode - 30] = is.readString();
+ if (actions[opcode - 30].equalsIgnoreCase("Hidden"))
+ {
+ actions[opcode - 30] = null;
+ }
+ }
+ else if (opcode == 40)
+ {
+ int length = is.readUnsignedByte();
+ short[] recolorToFind = new short[length];
+ short[] recolorToReplace = new short[length];
+
+ for (int index = 0; index < length; ++index)
+ {
+ recolorToFind[index] = is.readShort();
+ recolorToReplace[index] = is.readShort();
+ }
+
+ def.setRecolorToFind(recolorToFind);
+ def.setRecolorToReplace(recolorToReplace);
+ }
+ else if (opcode == 41)
+ {
+ int length = is.readUnsignedByte();
+ short[] retextureToFind = new short[length];
+ short[] textureToReplace = new short[length];
+
+ for (int index = 0; index < length; ++index)
+ {
+ retextureToFind[index] = is.readShort();
+ textureToReplace[index] = is.readShort();
+ }
+
+ def.setRetextureToFind(retextureToFind);
+ def.setTextureToReplace(textureToReplace);
+ }
+ else if (opcode == 60)
+ {
+ def.setMapIconID(is.readUnsignedShort());
+ }
+ else if (62 == opcode)
+ {
+ def.setaBool2108(true);
+ }
+ else if (opcode == 64)
+ {
+ def.setaBool2097(false);
+ }
+ else if (opcode == 65)
+ {
+ def.setModelSizeX(is.readUnsignedShort());
+ }
+ else if (opcode == 66)
+ {
+ def.setModelSizeHeight(is.readUnsignedShort());
+ }
+ else if (67 == opcode)
+ {
+ def.setModelSizeY(is.readUnsignedShort());
+ }
+ else if (opcode == 68)
+ {
+ def.setMapSceneID(is.readUnsignedShort());
+ }
+ else if (opcode == 69)
+ {
+ is.readByte();
+ }
+ else if (70 == opcode)
+ {
+ def.setOffsetX(is.readUnsignedShort());
+ }
+ else if (opcode == 71)
+ {
+ def.setOffsetHeight(is.readUnsignedShort());
+ }
+ else if (opcode == 72)
+ {
+ def.setOffsetY(is.readUnsignedShort());
+ }
+ else if (73 == opcode)
+ {
+ def.setaBool2104(true);
+ }
+ else if (74 == opcode)
+ {
+ def.setIsSolid(true);
+ }
+ else if (opcode == 75)
+ {
+ def.setAnInt2106(is.readUnsignedByte());
+ }
+ else if (opcode == 77)
+ {
+ int varpID = is.readUnsignedShort();
+ if (varpID == 0xFFFF)
+ {
+ varpID = -1;
+ }
+ def.setVarpID(varpID);
+
+ int configId = is.readUnsignedShort();
+ if (configId == 0xFFFF)
+ {
+ configId = -1;
+ }
+ def.setConfigId(configId);
+
+ int length = is.readUnsignedByte();
+ int[] configChangeDest = new int[length + 1];
+
+ for (int index = 0; index <= length; ++index)
+ {
+ configChangeDest[index] = is.readUnsignedShort();
+ if (0xFFFF == configChangeDest[index])
+ {
+ configChangeDest[index] = -1;
+ }
+ }
+
+ def.setConfigChangeDest(configChangeDest);
+ }
+ else if (opcode == 78)
+ {
+ def.setAnInt2110(is.readUnsignedShort());
+ def.setAnInt2083(is.readUnsignedByte());
+ }
+ else if (opcode == 79)
+ {
+ def.setAnInt2112(is.readUnsignedShort());
+ def.setAnInt2113(is.readUnsignedShort());
+ def.setAnInt2083(is.readUnsignedByte());
+ int length = is.readUnsignedByte();
+ int[] anIntArray2084 = new int[length];
+
+ for (int index = 0; index < length; ++index)
+ {
+ anIntArray2084[index] = is.readUnsignedShort();
+ }
+
+ def.setAnIntArray2084(anIntArray2084);
+ }
+ else if (opcode == 81)
+ {
+ def.setAnInt2105(is.readUnsignedByte());
+ }
+ else
+ {
+ logger.warn("Unrecognized opcode {}", opcode);
+ }
+ }
+}
diff --git a/cache/src/main/java/net/runelite/cache/definitions/loaders/ScriptLoader.java b/cache/src/main/java/net/runelite/cache/definitions/loaders/ScriptLoader.java
new file mode 100644
index 0000000000..419a550482
--- /dev/null
+++ b/cache/src/main/java/net/runelite/cache/definitions/loaders/ScriptLoader.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright (c) 2016, Adam
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Adam
+ * 4. Neither the name of the Adam nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Adam ''AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL Adam BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package net.runelite.cache.definitions.loaders;
+
+import net.runelite.cache.definitions.ScriptDefinition;
+import net.runelite.cache.io.InputStream;
+
+public class ScriptLoader
+{
+ public ScriptDefinition load(int id, byte[] b)
+ {
+ ScriptDefinition def = new ScriptDefinition();
+ InputStream in = new InputStream(b);
+
+ in.setOffset(in.getLength() - 12);
+ int paramCount = in.readInt();
+ int localIntCount = in.readUnsignedShort();
+ int localStringCount = in.readUnsignedShort();
+ int anInt2269 = in.readUnsignedShort();
+ int anInt2276 = in.readUnsignedShort();
+
+ def.setLocalIntCount(localIntCount);
+ def.setLocalStringCount(localStringCount);
+ def.setAnInt2269(anInt2269);
+ def.setAnInt2276(anInt2276);
+
+ in.setOffset(0);
+ in.readStringOrNull();
+
+ int[] instructions = new int[paramCount];
+ int[] intOperands = new int[paramCount];
+ String[] aStringArray2272 = new String[paramCount];
+
+ def.setInstructions(instructions);
+ def.setIntOperands(intOperands);
+ def.setaStringArray2272(aStringArray2272);
+
+ int var3;
+ for (int var6 = 0; in.getOffset() < in.getLength() - 12; instructions[var6++] = var3)
+ {
+ var3 = in.readUnsignedShort();
+ if (var3 == 3)
+ {
+ aStringArray2272[var6] = in.readString();
+ }
+ else if (var3 < 100 && 21 != var3 && 38 != var3 && 39 != var3)
+ {
+ intOperands[var6] = in.readInt();
+ }
+ else
+ {
+ intOperands[var6] = in.readUnsignedByte();
+ }
+ }
+
+ return def;
+ }
+}
diff --git a/cache/src/main/java/net/runelite/cache/definitions/loaders/SpriteLoader.java b/cache/src/main/java/net/runelite/cache/definitions/loaders/SpriteLoader.java
new file mode 100644
index 0000000000..00c2ca5339
--- /dev/null
+++ b/cache/src/main/java/net/runelite/cache/definitions/loaders/SpriteLoader.java
@@ -0,0 +1,132 @@
+/*
+ * Copyright (c) 2016, Adam
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Adam
+ * 4. Neither the name of the Adam nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Adam ''AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL Adam BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package net.runelite.cache.definitions.loaders;
+
+import net.runelite.cache.definitions.SpriteDefinition;
+import net.runelite.cache.io.InputStream;
+
+public class SpriteLoader
+{
+ private SpriteDefinition[] sprites;
+
+ private int[] loadedPalette;
+ private int loadedSpriteMaxWidth;
+ private int loadedSpriteMaxHeight;
+
+ public void load(InputStream stream)
+ {
+ stream.setOffset(stream.getLength() - 2);
+ int paletteChildCount = stream.readUnsignedShort();
+ sprites = new SpriteDefinition[paletteChildCount];
+ for (int i = 0; i < paletteChildCount; ++i)
+ {
+ sprites[i] = new SpriteDefinition(this);
+ }
+ stream.setOffset(stream.getLength() - 7 - paletteChildCount * 8);
+ loadedSpriteMaxWidth = stream.readUnsignedShort();
+ loadedSpriteMaxHeight = stream.readUnsignedShort();
+ int var3 = (stream.readUnsignedByte() & 255) + 1;
+
+ int spriteIndex;
+ for (spriteIndex = 0; spriteIndex < paletteChildCount; ++spriteIndex)
+ {
+ sprites[spriteIndex].setOffsetX(stream.readUnsignedShort());
+ }
+
+ for (spriteIndex = 0; spriteIndex < paletteChildCount; ++spriteIndex)
+ {
+ sprites[spriteIndex].setOffsetY(stream.readUnsignedShort());
+ }
+
+ for (spriteIndex = 0; spriteIndex < paletteChildCount; ++spriteIndex)
+ {
+ sprites[spriteIndex].setWidth(stream.readUnsignedShort());
+ }
+
+ for (spriteIndex = 0; spriteIndex < paletteChildCount; ++spriteIndex)
+ {
+ sprites[spriteIndex].setHeight(stream.readUnsignedShort());
+ }
+
+ stream.setOffset(stream.getLength() - 7 - paletteChildCount * 8 - (var3 - 1) * 3);
+ loadedPalette = new int[var3];
+
+ for (spriteIndex = 1; spriteIndex < var3; ++spriteIndex)
+ {
+ loadedPalette[spriteIndex] = stream.read24BitInt();
+ if (0 == loadedPalette[spriteIndex])
+ {
+ loadedPalette[spriteIndex] = 1;
+ }
+ }
+
+ stream.setOffset(0);
+
+ for (spriteIndex = 0; spriteIndex < paletteChildCount; ++spriteIndex)
+ {
+ SpriteDefinition def = sprites[spriteIndex];
+ int width = def.getWidth();
+ int height = def.getHeight();
+ int dimmension = width * height;
+ byte[] loadPixels = new byte[dimmension];
+ int var4 = stream.readUnsignedByte();
+ int var5;
+ if (var4 == 0)
+ {
+ for (var5 = 0; var5 < dimmension; ++var5)
+ {
+ loadPixels[var5] = (byte) stream.readByte();
+ }
+ }
+ else if (1 == var4)
+ {
+ for (var5 = 0; var5 < width; ++var5)
+ {
+ for (int var8 = 0; var8 < height; ++var8)
+ {
+ loadPixels[width * var8 + var5] = (byte) stream.readByte();
+ }
+ }
+ }
+ def.setPixels(loadPixels);
+ }
+ }
+
+ public SpriteDefinition[] getSprites()
+ {
+ return sprites;
+ }
+
+ public int[] getLoadedPalette()
+ {
+ return loadedPalette;
+ }
+}
diff --git a/cache/src/main/java/net/runelite/cache/fs/Archive.java b/cache/src/main/java/net/runelite/cache/fs/Archive.java
new file mode 100644
index 0000000000..e4c8ddbed1
--- /dev/null
+++ b/cache/src/main/java/net/runelite/cache/fs/Archive.java
@@ -0,0 +1,177 @@
+/*
+ * Copyright (c) 2016, Adam
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Adam
+ * 4. Neither the name of the Adam nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Adam ''AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL Adam BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package net.runelite.cache.fs;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Objects;
+import net.runelite.cache.io.InputStream;
+
+public class Archive
+{
+ private Index index; // member of this index
+ private int archiveId;
+ private int nameHash;
+ private byte[] whirlpool;
+ private int crc;
+ private int revision;
+ private List files = new ArrayList<>();
+
+ public Archive(Index index, int id)
+ {
+ this.index = index;
+ this.archiveId = id;
+ }
+
+ @Override
+ public int hashCode()
+ {
+ int hash = 7;
+ hash = 47 * hash + this.archiveId;
+ hash = 47 * hash + this.nameHash;
+ hash = 47 * hash + this.revision;
+ hash = 47 * hash + Objects.hashCode(this.files);
+ return hash;
+ }
+
+ @Override
+ public boolean equals(Object obj)
+ {
+ if (obj == null)
+ {
+ return false;
+ }
+ if (getClass() != obj.getClass())
+ {
+ return false;
+ }
+ final Archive other = (Archive) obj;
+ if (this.archiveId != other.archiveId)
+ {
+ return false;
+ }
+ if (this.nameHash != other.nameHash)
+ {
+ return false;
+ }
+ // crc is of the file data, we always rewrite in one loop, so iti is different
+ if (this.revision != other.revision)
+ {
+ return false;
+ }
+ if (!Objects.equals(this.files, other.files))
+ {
+ return false;
+ }
+ return true;
+ }
+
+ public File addFile(int id)
+ {
+ File file = new File(this, id);
+ this.files.add(file);
+ return file;
+ }
+
+ public void load(InputStream stream, int numberOfFiles, int protocol)
+ {
+ int archive = 0;
+
+ for (int i = 0; i < numberOfFiles; ++i)
+ {
+ int fileId = archive += protocol >= 7 ? stream.readBigSmart() : stream.readUnsignedShort();
+
+ File file = new File(this, fileId);
+ this.files.add(file);
+ }
+ }
+
+ public void loadNames(InputStream stream, int numberOfFiles)
+ {
+ for (int i = 0; i < numberOfFiles; ++i)
+ {
+ File file = this.files.get(i);
+ int name = stream.readInt();
+ file.setNameHash(name);
+ }
+ }
+
+ public int getArchiveId()
+ {
+ return archiveId;
+ }
+
+ public int getNameHash()
+ {
+ return nameHash;
+ }
+
+ public void setNameHash(int nameHash)
+ {
+ this.nameHash = nameHash;
+ }
+
+ public byte[] getWhirlpool()
+ {
+ return whirlpool;
+ }
+
+ public void setWhirlpool(byte[] whirlpool)
+ {
+ this.whirlpool = whirlpool;
+ }
+
+ public int getCrc()
+ {
+ return crc;
+ }
+
+ public void setCrc(int crc)
+ {
+ this.crc = crc;
+ }
+
+ public int getRevision()
+ {
+ return revision;
+ }
+
+ public void setRevision(int revision)
+ {
+ this.revision = revision;
+ }
+
+ public List getFiles()
+ {
+ return files;
+ }
+}
diff --git a/cache/src/main/java/net/runelite/cache/fs/CompressionType.java b/cache/src/main/java/net/runelite/cache/fs/CompressionType.java
new file mode 100644
index 0000000000..74ec84fa1a
--- /dev/null
+++ b/cache/src/main/java/net/runelite/cache/fs/CompressionType.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2016, Adam
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Adam
+ * 4. Neither the name of the Adam nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Adam ''AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL Adam BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package net.runelite.cache.fs;
+
+public class CompressionType
+{
+ public static final int NONE = 0;
+ public static final int BZ2 = 1;
+ public static final int GZ = 2;
+}
diff --git a/cache/src/main/java/net/runelite/cache/fs/DataFile.java b/cache/src/main/java/net/runelite/cache/fs/DataFile.java
new file mode 100644
index 0000000000..a292a34049
--- /dev/null
+++ b/cache/src/main/java/net/runelite/cache/fs/DataFile.java
@@ -0,0 +1,373 @@
+/*
+ * Copyright (c) 2016, Adam
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Adam
+ * 4. Neither the name of the Adam nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Adam ''AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL Adam BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package net.runelite.cache.fs;
+
+import java.io.Closeable;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.RandomAccessFile;
+import java.nio.ByteBuffer;
+import net.runelite.cache.fs.util.BZip2;
+import net.runelite.cache.io.InputStream;
+import net.runelite.cache.io.OutputStream;
+import net.runelite.cache.fs.util.CRC32HGenerator;
+import net.runelite.cache.fs.util.GZip;
+import net.runelite.cache.fs.util.Whirlpool;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class DataFile implements Closeable
+{
+ private static final Logger logger = LoggerFactory.getLogger(DataFile.class);
+
+ private static final int SECTOR_SIZE = 520;
+
+ private final Store store;
+ private final File file;
+ private final RandomAccessFile dat;
+ private final byte[] readCachedBuffer = new byte[SECTOR_SIZE];
+
+ public DataFile(Store store, File file) throws FileNotFoundException
+ {
+ this.file = file;
+ this.store = store;
+ dat = new RandomAccessFile(file, "rw");
+ }
+
+ @Override
+ public void close() throws IOException
+ {
+ dat.close();
+ }
+
+ /**
+ *
+ * @param indexId
+ * @param archiveId
+ * @param sector sector to start reading at
+ * @param size expected size of file
+ * @return
+ * @throws IOException
+ */
+ public synchronized DataFileReadResult read(int indexId, int archiveId, int sector, int size) throws IOException
+ {
+ if (sector <= 0L || dat.length() / 520L < (long) sector)
+ {
+ logger.warn("bad read, dat length {}, requested sector {}", dat.length(), sector);
+ return null;
+ }
+
+ ByteBuffer buffer = ByteBuffer.allocate(size);
+
+ for (int part = 0, readBytesCount = 0, nextSector;
+ size > readBytesCount;
+ sector = nextSector)
+ {
+ if (sector == 0)
+ {
+ logger.warn("sector == 0");
+ return null;
+ }
+
+ dat.seek(SECTOR_SIZE * sector);
+
+ int dataBlockSize = size - readBytesCount;
+ byte headerSize;
+ int currentIndex;
+ int currentPart;
+ int currentArchive;
+ if (0xFFFF < archiveId)
+ {
+ headerSize = 10;
+ if (dataBlockSize > SECTOR_SIZE - headerSize)
+ {
+ dataBlockSize = SECTOR_SIZE - headerSize;
+ }
+
+ int i = dat.read(this.readCachedBuffer, 0, headerSize + dataBlockSize);
+ if (i != headerSize + dataBlockSize)
+ {
+ logger.warn("short read");
+ return null;
+ }
+
+ currentArchive = ((this.readCachedBuffer[1] & 255) << 16) + ((this.readCachedBuffer[0] & 255) << 24) + (('\uff00' & this.readCachedBuffer[2] << 8) - -(this.readCachedBuffer[3] & 255));
+ currentPart = ((this.readCachedBuffer[4] & 255) << 8) + (255 & this.readCachedBuffer[5]);
+ nextSector = (this.readCachedBuffer[8] & 255) + ('\uff00' & this.readCachedBuffer[7] << 8) + ((255 & this.readCachedBuffer[6]) << 16);
+ currentIndex = this.readCachedBuffer[9] & 255;
+ }
+ else
+ {
+ headerSize = 8;
+ if (dataBlockSize > SECTOR_SIZE - headerSize)
+ {
+ dataBlockSize = SECTOR_SIZE - headerSize;
+ }
+
+ int i = dat.read(this.readCachedBuffer, 0, headerSize + dataBlockSize);
+ if (i != headerSize + dataBlockSize)
+ {
+ logger.warn("short read");
+ return null;
+ }
+
+ currentArchive = (255 & this.readCachedBuffer[1]) + ('\uff00' & this.readCachedBuffer[0] << 8);
+ currentPart = ((this.readCachedBuffer[2] & 255) << 8) + (255 & this.readCachedBuffer[3]);
+ nextSector = (this.readCachedBuffer[6] & 255) + ('\uff00' & this.readCachedBuffer[5] << 8) + ((255 & this.readCachedBuffer[4]) << 16);
+ currentIndex = this.readCachedBuffer[7] & 255;
+ }
+
+ if (archiveId != currentArchive || currentPart != part || indexId != currentIndex)
+ {
+ logger.warn("data mismatch {} != {}, {} != {}, {} != {}",
+ archiveId, currentArchive,
+ part, currentPart,
+ indexId, currentIndex);
+ return null;
+ }
+
+ if (nextSector < 0 || dat.length() / SECTOR_SIZE < (long) nextSector)
+ {
+ logger.warn("Invalid next sector");
+ return null;
+ }
+
+ buffer.put(readCachedBuffer, headerSize, dataBlockSize);
+ readBytesCount += dataBlockSize;
+
+ ++part;
+ }
+
+ buffer.flip();
+
+ //XTEA decrypt here?
+
+ return this.decompress(buffer.array());
+ }
+
+ public synchronized DataFileWriteResult write(int indexId, int archiveId, ByteBuffer data, int compression, int revision) throws IOException
+ {
+ int sector;
+ int startSector;
+
+ byte[] compressedData = this.compress(data.array(), compression, revision);
+ data = ByteBuffer.wrap(compressedData);
+
+ //XTEA encrypt here?
+
+ sector = (int) ((dat.length() + (long) (SECTOR_SIZE - 1)) / (long) SECTOR_SIZE);
+ if (sector == 0)
+ {
+ sector = 1;
+ }
+ startSector = sector;
+
+ for (int part = 0; data.hasRemaining(); ++part)
+ {
+ int nextSector = 0;
+ int dataToWrite;
+
+ if (nextSector == 0)
+ {
+ nextSector = (int) ((dat.length() + (long) (SECTOR_SIZE - 1)) / (long) SECTOR_SIZE);
+ if (nextSector == 0)
+ {
+ ++nextSector;
+ }
+
+ if (nextSector == sector)
+ {
+ ++nextSector;
+ }
+ }
+
+
+ if (0xFFFF < archiveId)
+ {
+ if (data.remaining() <= 510)
+ {
+ nextSector = 0;
+ }
+
+ this.readCachedBuffer[0] = (byte) (archiveId >> 24);
+ this.readCachedBuffer[1] = (byte) (archiveId >> 16);
+ this.readCachedBuffer[2] = (byte) (archiveId >> 8);
+ this.readCachedBuffer[3] = (byte) archiveId;
+ this.readCachedBuffer[4] = (byte) (part >> 8);
+ this.readCachedBuffer[5] = (byte) part;
+ this.readCachedBuffer[6] = (byte) (nextSector >> 16);
+ this.readCachedBuffer[7] = (byte) (nextSector >> 8);
+ this.readCachedBuffer[8] = (byte) nextSector;
+ this.readCachedBuffer[9] = (byte) indexId;
+ dat.seek(SECTOR_SIZE * sector);
+ dat.write(this.readCachedBuffer, 0, 10);
+
+ dataToWrite = data.remaining();
+ if (dataToWrite > 510)
+ {
+ dataToWrite = 510;
+ }
+ }
+ else
+ {
+ if (data.remaining() <= 512)
+ {
+ nextSector = 0;
+ }
+
+ this.readCachedBuffer[0] = (byte) (archiveId >> 8);
+ this.readCachedBuffer[1] = (byte) archiveId;
+ this.readCachedBuffer[2] = (byte) (part >> 8);
+ this.readCachedBuffer[3] = (byte) part;
+ this.readCachedBuffer[4] = (byte) (nextSector >> 16);
+ this.readCachedBuffer[5] = (byte) (nextSector >> 8);
+ this.readCachedBuffer[6] = (byte) nextSector;
+ this.readCachedBuffer[7] = (byte) indexId;
+ dat.seek(SECTOR_SIZE * sector);
+ dat.write(this.readCachedBuffer, 0, 8);
+
+ dataToWrite = data.remaining();
+ if (dataToWrite > 512)
+ {
+ dataToWrite = 512;
+ }
+ }
+
+ data.get(readCachedBuffer, 0, dataToWrite);
+ dat.write(readCachedBuffer, 0, dataToWrite);
+ sector = nextSector;
+ }
+
+ DataFileWriteResult res = new DataFileWriteResult();
+ res.sector = startSector;
+ res.compressedLength = compressedData.length;
+ res.crc = CRC32HGenerator.getHash(compressedData, compressedData.length - 2);
+ res.whirlpool = Whirlpool.getHash(compressedData, compressedData.length - 2);
+ return res;
+ }
+
+ private DataFileReadResult decompress(byte[] b)
+ {
+ InputStream stream = new InputStream(b);
+
+ int compression = stream.readUnsignedByte();
+ int compressedLength = stream.readInt();
+ if (compressedLength < 0 || compressedLength > 1000000)
+ throw new RuntimeException("Invalid data");
+
+ byte[] data;
+ int revision;
+ switch (compression)
+ {
+ case CompressionType.NONE:
+ data = new byte[compressedLength];
+ revision = this.checkRevision(stream, compressedLength);
+ stream.readBytes(data, 0, compressedLength);
+ break;
+ case CompressionType.BZ2:
+ {
+ int length = stream.readInt();
+ revision = this.checkRevision(stream, compressedLength);
+ data = BZip2.decompress(stream.getRemaining());
+ assert data.length == length;
+ break;
+ }
+ case CompressionType.GZ:
+ {
+ int length = stream.readInt();
+ revision = this.checkRevision(stream, compressedLength);
+ data = GZip.decompress(stream.getRemaining());
+ assert data.length == length;
+ break;
+ }
+ default:
+ throw new RuntimeException("Unknown decompression type");
+ }
+
+ DataFileReadResult res = new DataFileReadResult();
+ res.data = data;
+ res.revision = revision;
+ res.crc = CRC32HGenerator.getHash(b, b.length - 2);
+ res.whirlpool = Whirlpool.getHash(b, b.length - 2);
+ return res;
+ }
+
+ private byte[] compress(byte[] data, int compression, int revision) throws IOException
+ {
+ OutputStream stream = new OutputStream();
+ stream.writeByte(compression);
+ byte[] compressedData;
+ switch (compression)
+ {
+ case CompressionType.NONE:
+ compressedData = data;
+ stream.writeInt(data.length);
+ break;
+ case CompressionType.BZ2:
+ compressedData = BZip2.compress(data);
+
+ stream.writeInt(compressedData.length);
+ stream.writeInt(data.length);
+ break;
+ case CompressionType.GZ:
+ compressedData = GZip.compress(data);
+
+ stream.writeInt(compressedData.length);
+ stream.writeInt(data.length);
+ break;
+ default:
+ throw new RuntimeException("Unknown compression type");
+ }
+
+ stream.writeBytes(compressedData);
+ stream.writeShort(revision);
+
+ return stream.flip();
+ }
+
+ private int checkRevision(InputStream stream, int compressedLength)
+ {
+ int offset = stream.getOffset();
+ int revision;
+ if (stream.getLength() - (compressedLength + stream.getOffset()) >= 2)
+ {
+ stream.setOffset(stream.getLength() - 2);
+ revision = stream.readUnsignedShort();
+ stream.setOffset(offset);
+ }
+ else
+ {
+ revision = -1;
+ }
+ return revision;
+ }
+}
diff --git a/cache/src/main/java/net/runelite/cache/fs/DataFileReadResult.java b/cache/src/main/java/net/runelite/cache/fs/DataFileReadResult.java
new file mode 100644
index 0000000000..0a2d7e47b3
--- /dev/null
+++ b/cache/src/main/java/net/runelite/cache/fs/DataFileReadResult.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2016, Adam
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Adam
+ * 4. Neither the name of the Adam nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Adam ''AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL Adam BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package net.runelite.cache.fs;
+
+public class DataFileReadResult
+{
+ public byte[] data;
+ public int revision;
+ public int crc; // crc of compressed data
+ public byte[] whirlpool;
+}
diff --git a/cache/src/main/java/net/runelite/cache/fs/DataFileWriteResult.java b/cache/src/main/java/net/runelite/cache/fs/DataFileWriteResult.java
new file mode 100644
index 0000000000..b45b4fc23b
--- /dev/null
+++ b/cache/src/main/java/net/runelite/cache/fs/DataFileWriteResult.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 2016, Adam
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Adam
+ * 4. Neither the name of the Adam nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Adam ''AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL Adam BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package net.runelite.cache.fs;
+
+public class DataFileWriteResult
+{
+ public int sector, compressedLength;
+ public int crc; // crc of compressed data
+ public byte[] whirlpool;
+}
diff --git a/cache/src/main/java/net/runelite/cache/fs/File.java b/cache/src/main/java/net/runelite/cache/fs/File.java
new file mode 100644
index 0000000000..b4ee24c550
--- /dev/null
+++ b/cache/src/main/java/net/runelite/cache/fs/File.java
@@ -0,0 +1,119 @@
+/*
+ * Copyright (c) 2016, Adam
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Adam
+ * 4. Neither the name of the Adam nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Adam ''AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL Adam BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package net.runelite.cache.fs;
+
+import java.util.Arrays;
+
+public class File
+{
+ private Archive archive;
+ private int fileId;
+ private int nameHash;
+ private byte[] contents;
+
+ public File(Archive archive, int fileId)
+ {
+ this.archive = archive;
+ this.fileId = fileId;
+ }
+
+ @Override
+ public int hashCode()
+ {
+ int hash = 7;
+ hash = 97 * hash + this.fileId;
+ hash = 97 * hash + this.nameHash;
+ hash = 97 * hash + Arrays.hashCode(this.contents);
+ return hash;
+ }
+
+ @Override
+ public boolean equals(Object obj)
+ {
+ if (obj == null)
+ {
+ return false;
+ }
+ if (getClass() != obj.getClass())
+ {
+ return false;
+ }
+ final File other = (File) obj;
+ if (this.fileId != other.fileId)
+ {
+ return false;
+ }
+ if (this.nameHash != other.nameHash)
+ {
+ return false;
+ }
+ if (!Arrays.equals(this.contents, other.contents))
+ {
+ return false;
+ }
+ return true;
+ }
+
+ public Archive getArchive()
+ {
+ return archive;
+ }
+
+ public int getFileId()
+ {
+ return fileId;
+ }
+
+ public int getNameHash()
+ {
+ return nameHash;
+ }
+
+ public void setNameHash(int nameHash)
+ {
+ this.nameHash = nameHash;
+ }
+
+ public byte[] getContents()
+ {
+ return contents;
+ }
+
+ public void setContents(byte[] contents)
+ {
+ this.contents = contents;
+ }
+
+ public int getSize()
+ {
+ return contents.length;
+ }
+}
diff --git a/cache/src/main/java/net/runelite/cache/fs/Index.java b/cache/src/main/java/net/runelite/cache/fs/Index.java
new file mode 100644
index 0000000000..fbe6948736
--- /dev/null
+++ b/cache/src/main/java/net/runelite/cache/fs/Index.java
@@ -0,0 +1,533 @@
+/*
+ * Copyright (c) 2016, Adam
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Adam
+ * 4. Neither the name of the Adam nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Adam ''AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL Adam BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package net.runelite.cache.fs;
+
+import java.io.Closeable;
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Objects;
+import net.runelite.cache.fs.util.Djb2;
+import net.runelite.cache.io.InputStream;
+import net.runelite.cache.io.OutputStream;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class Index implements Closeable
+{
+ private static final Logger logger = LoggerFactory.getLogger(Index.class);
+
+ private final Store store;
+ private final IndexFile index;
+ private final int id;
+ private int revision;
+ private final List archives = new ArrayList<>();
+
+ public Index(Store store, IndexFile index, int id)
+ {
+ this.store = store;
+ this.index = index;
+ this.id = id;
+ }
+
+ @Override
+ public void close() throws IOException
+ {
+ index.close();
+ }
+
+ @Override
+ public int hashCode()
+ {
+ int hash = 3;
+ hash = 97 * hash + this.id;
+ hash = 97 * hash + this.revision;
+ hash = 97 * hash + Objects.hashCode(this.archives);
+ return hash;
+ }
+
+ @Override
+ public boolean equals(Object obj)
+ {
+ if (obj == null)
+ {
+ return false;
+ }
+ if (getClass() != obj.getClass())
+ {
+ return false;
+ }
+ final Index other = (Index) obj;
+ if (this.id != other.id)
+ {
+ return false;
+ }
+ if (this.revision != other.revision)
+ {
+ return false;
+ }
+ if (!Objects.equals(this.archives, other.archives))
+ {
+ return false;
+ }
+ return true;
+ }
+
+ public int getId()
+ {
+ return id;
+ }
+
+ public IndexFile getIndex()
+ {
+ return index;
+ }
+
+ public List getArchives()
+ {
+ return archives;
+ }
+
+ public Archive addArchive(int id)
+ {
+ Archive archive = new Archive(this, id);
+ this.archives.add(archive);
+ return archive;
+ }
+
+ public Archive getArchive(int id)
+ {
+ for (Archive a : archives)
+ if (a.getArchiveId() == id)
+ return a;
+ return null;
+ }
+
+ public Archive findArchiveByName(String name)
+ {
+ int hash = Djb2.hash(name);
+ for (Archive a : archives)
+ if (a.getNameHash() == hash)
+ return a;
+ return null;
+ }
+
+ public void load() throws IOException
+ {
+ DataFile dataFile = store.getData();
+ IndexFile index255 = store.getIndex255();
+
+ IndexEntry entry = index255.read(id);
+ DataFileReadResult res = dataFile.read(index255.getIndexFileId(), entry.getId(), entry.getSector(), entry.getLength());
+ byte[] data = res.data;
+
+ archives.clear();
+
+ readIndexData(data);
+
+ this.loadFiles();
+ }
+
+ public void save() throws IOException
+ {
+ saveFiles();
+
+ byte[] data = this.writeIndexData();
+
+ DataFile dataFile = store.getData();
+ IndexFile index255 = store.getIndex255();
+
+ DataFileWriteResult res = dataFile.write(index255.getIndexFileId(), this.id, ByteBuffer.wrap(data), 0, this.revision);
+ index255.write(new IndexEntry(index255, id, res.sector, res.compressedLength));
+ }
+
+ private void readIndexData(byte[] data)
+ {
+ InputStream stream = new InputStream(data);
+ int protocol = stream.readUnsignedByte();
+ if (protocol >= 5 && protocol <= 7)
+ {
+ if (protocol >= 6)
+ {
+ this.revision = stream.readInt();
+ }
+
+ int hash = stream.readUnsignedByte();
+ boolean named = (1 & hash) != 0;
+ boolean usesWhirpool = (2 & hash) != 0;
+ int validArchivesCount = protocol >= 7 ? stream.readBigSmart() : stream.readUnsignedShort();
+ int lastArchiveId = 0;
+
+ int index;
+ int archive;
+ for (index = 0; index < validArchivesCount; ++index)
+ {
+ archive = lastArchiveId += protocol >= 7 ? stream.readBigSmart() : stream.readUnsignedShort();
+ Archive a = new Archive(this, archive);
+ this.archives.add(a);
+ }
+
+ if (named)
+ {
+ for (index = 0; index < validArchivesCount; ++index)
+ {
+ int nameHash = stream.readInt();
+ Archive a = this.archives.get(index);
+ a.setNameHash(nameHash);
+ }
+ }
+
+ if (usesWhirpool)
+ {
+ for (index = 0; index < validArchivesCount; ++index)
+ {
+ byte[] var13 = new byte[64];
+ stream.readBytes(var13);
+
+ Archive a = this.archives.get(index);
+ a.setWhirlpool(var13);
+ }
+ }
+
+ for (index = 0; index < validArchivesCount; ++index)
+ {
+ int crc = stream.readInt();
+
+ Archive a = this.archives.get(index);
+ a.setCrc(crc);
+ }
+
+ for (index = 0; index < validArchivesCount; ++index)
+ {
+ int revision = stream.readInt();
+
+ Archive a = this.archives.get(index);
+ a.setRevision(revision);
+ }
+
+ int[] numberOfFiles = new int[validArchivesCount];
+ for (index = 0; index < validArchivesCount; ++index)
+ {
+ int num = protocol >= 7 ? stream.readBigSmart() : stream.readUnsignedShort();
+ numberOfFiles[index] = num;
+ }
+
+ for (index = 0; index < validArchivesCount; ++index)
+ {
+ archive = 0;
+
+ Archive a = this.archives.get(index);
+ a.load(stream, numberOfFiles[index], protocol);
+ }
+
+ if (named)
+ {
+ for (index = 0; index < validArchivesCount; ++index)
+ {
+ Archive a = this.archives.get(index);
+ a.loadNames(stream, numberOfFiles[index]);
+ }
+ }
+ }
+ }
+
+ private void loadFiles() throws IOException
+ {
+ // get data from index file
+ for (Archive a : archives)
+ {
+ IndexEntry entry = this.index.read(a.getArchiveId());
+ if (entry == null)
+ {
+ logger.debug("can't read archive " + a.getArchiveId() + " from index " + this.id);
+ continue;
+ }
+
+ assert this.index.getIndexFileId() == this.id;
+ assert entry.getId() == a.getArchiveId();
+ DataFileReadResult res = store.getData().read(this.id, entry.getId(), entry.getSector(), entry.getLength());
+ byte[] data = res.data;
+
+ if (a.getCrc() != res.crc)
+ {
+ logger.warn("crc mismatch for archive {}", a);
+ }
+
+ if (a.getWhirlpool() != null && !Arrays.equals(a.getWhirlpool(), res.whirlpool))
+ {
+ logger.warn("whirlpool mismatch for archive {}", a);
+ }
+
+ if (a.getFiles().size() == 1)
+ {
+ a.getFiles().get(0).setContents(data);
+ continue;
+ }
+
+ final int filesCount = a.getFiles().size();
+
+ int readPosition = data.length;
+ --readPosition;
+ int amtOfLoops = data[readPosition] & 255;
+ readPosition -= amtOfLoops * filesCount * 4;
+ InputStream stream = new InputStream(data);
+ stream.setOffset(readPosition);
+ int[] filesSize = new int[filesCount];
+
+ int sourceOffset;
+ int count;
+ for (int filesData = 0; filesData < amtOfLoops; ++filesData)
+ {
+ sourceOffset = 0;
+
+ for (count = 0; count < filesCount; ++count)
+ {
+ filesSize[count] += sourceOffset += stream.readInt();
+ }
+ }
+
+ byte[][] var18 = new byte[filesCount][];
+
+ for (sourceOffset = 0; sourceOffset < filesCount; ++sourceOffset)
+ {
+ var18[sourceOffset] = new byte[filesSize[sourceOffset]];
+ filesSize[sourceOffset] = 0;
+ }
+
+ stream.setOffset(readPosition);
+ sourceOffset = 0;
+
+ int fileId;
+ int i;
+ for (count = 0; count < amtOfLoops; ++count)
+ {
+ fileId = 0;
+
+ for (i = 0; i < filesCount; ++i)
+ {
+ fileId += stream.readInt();
+ System.arraycopy(data, sourceOffset, var18[i], filesSize[i], fileId);
+ sourceOffset += fileId;
+ filesSize[i] += fileId;
+ }
+ }
+
+ for (i = 0; i < filesCount; ++i)
+ {
+ File f = a.getFiles().get(i);
+ f.setContents(var18[i]);
+ }
+ }
+ }
+
+ public void saveFiles() throws IOException
+ {
+ for (Archive a : archives)
+ {
+ OutputStream stream = new OutputStream();
+
+ int sourceOffset = 0;
+ final int filesCount = a.getFiles().size();
+
+ if (filesCount == 1)
+ {
+ File file = a.getFiles().get(0);
+ stream.writeBytes(file.getContents());
+ }
+ else
+ {
+ for (int i = 0; i < filesCount; ++i)
+ {
+ File file = a.getFiles().get(i);
+ stream.writeBytes(file.getContents());
+ }
+
+ for (int count = 0; count < filesCount; ++count)
+ {
+ File file = a.getFiles().get(count);
+
+ int sz = file.getSize() - sourceOffset;
+ sourceOffset = file.getSize();
+ stream.writeInt(sz);
+ }
+
+ stream.writeByte(1); // number of loops
+ }
+
+ byte[] fileData = stream.flip();
+
+ assert this.index.getIndexFileId() == this.id;
+ DataFile data = store.getData();
+
+ // XXX old data is just left there in the file?
+ DataFileWriteResult res = data.write(this.id, a.getArchiveId(), ByteBuffer.wrap(fileData), 0, this.revision);
+ this.index.write(new IndexEntry(this.index, a.getArchiveId(), res.sector, res.compressedLength));
+
+ a.setCrc(res.crc);
+ a.setWhirlpool(res.whirlpool);
+ }
+ }
+
+ public byte[] writeIndexData()
+ {
+ OutputStream stream = new OutputStream();
+ int protocol = 7;//this.getProtocol();
+ stream.writeByte(protocol);
+ if (protocol >= 6)
+ {
+ stream.writeInt(this.revision);
+ }
+
+ boolean named = true, usesWhirpool = false;
+ stream.writeByte((named ? 1 : 0) | (usesWhirpool ? 2 : 0));
+ if (protocol >= 7)
+ {
+ stream.writeBigSmart(this.archives.size());
+ }
+ else
+ {
+ stream.writeShort(this.archives.size());
+ }
+
+ int data;
+ for (data = 0; data < this.archives.size(); ++data)
+ {
+ Archive a = this.archives.get(data);
+ int archive = a.getArchiveId();
+
+ if (data != 0)
+ {
+ Archive prev = this.archives.get(data - 1);
+ archive -= prev.getArchiveId();
+ }
+
+ if (protocol >= 7)
+ {
+ stream.writeBigSmart(archive);
+ }
+ else
+ {
+ stream.writeShort(archive);
+ }
+ }
+
+ if (named)
+ {
+ for (data = 0; data < this.archives.size(); ++data)
+ {
+ Archive a = this.archives.get(data);
+ stream.writeInt(a.getNameHash());
+ }
+ }
+
+ if (usesWhirpool)
+ {
+ for (data = 0; data < this.archives.size(); ++data)
+ {
+ Archive a = this.archives.get(data);
+ stream.writeBytes(a.getWhirlpool());
+ }
+ }
+
+ for (data = 0; data < this.archives.size(); ++data)
+ {
+ Archive a = this.archives.get(data);
+ stream.writeInt(a.getCrc());
+ }
+
+ for (data = 0; data < this.archives.size(); ++data)
+ {
+ Archive a = this.archives.get(data);
+ stream.writeInt(a.getRevision());
+ }
+
+ for (data = 0; data < this.archives.size(); ++data)
+ {
+ Archive a = this.archives.get(data);
+
+ int len = a.getFiles().size();
+
+ if (protocol >= 7)
+ {
+ stream.writeBigSmart(len);
+ }
+ else
+ {
+ stream.writeShort(len);
+ }
+ }
+
+ int index2;
+ for (data = 0; data < this.archives.size(); ++data)
+ {
+ Archive a = this.archives.get(data);
+
+ for (index2 = 0; index2 < a.getFiles().size(); ++index2)
+ {
+ File file = a.getFiles().get(index2);
+ int offset = file.getFileId();
+
+ if (index2 != 0)
+ {
+ File prev = a.getFiles().get(index2 - 1);
+ offset -= prev.getFileId();
+ }
+
+ if (protocol >= 7)
+ {
+ stream.writeBigSmart(offset);
+ }
+ else
+ {
+ stream.writeShort(offset);
+ }
+ }
+ }
+
+ if (named)
+ {
+ for (data = 0; data < this.archives.size(); ++data)
+ {
+ Archive a = this.archives.get(data);
+
+ for (index2 = 0; index2 < a.getFiles().size(); ++index2)
+ {
+ File file = a.getFiles().get(index2);
+ stream.writeInt(file.getNameHash());
+ }
+ }
+ }
+
+ return stream.flip();
+ }
+}
diff --git a/cache/src/main/java/net/runelite/cache/fs/IndexEntry.java b/cache/src/main/java/net/runelite/cache/fs/IndexEntry.java
new file mode 100644
index 0000000000..a103c11fbf
--- /dev/null
+++ b/cache/src/main/java/net/runelite/cache/fs/IndexEntry.java
@@ -0,0 +1,109 @@
+/*
+ * Copyright (c) 2016, Adam
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Adam
+ * 4. Neither the name of the Adam nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Adam ''AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL Adam BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package net.runelite.cache.fs;
+
+import java.util.Objects;
+
+public class IndexEntry
+{
+ private final IndexFile indexFile;
+ private final int id, sector, length;
+
+ public IndexEntry(IndexFile indexFile, int id, int sector, int length)
+ {
+ this.indexFile = indexFile;
+ this.id = id;
+ this.sector = sector;
+ this.length = length;
+ }
+
+ public IndexFile getIndexFile()
+ {
+ return indexFile;
+ }
+
+ public int getId()
+ {
+ return id;
+ }
+
+ public int getSector()
+ {
+ return sector;
+ }
+
+ public int getLength()
+ {
+ return length;
+ }
+
+ @Override
+ public int hashCode()
+ {
+ int hash = 7;
+ hash = 19 * hash + Objects.hashCode(this.indexFile);
+ hash = 19 * hash + this.id;
+ hash = 19 * hash + this.sector;
+ hash = 19 * hash + this.length;
+ return hash;
+ }
+
+ @Override
+ public boolean equals(Object obj)
+ {
+ if (obj == null)
+ {
+ return false;
+ }
+ if (getClass() != obj.getClass())
+ {
+ return false;
+ }
+ final IndexEntry other = (IndexEntry) obj;
+ if (!Objects.equals(this.indexFile, other.indexFile))
+ {
+ return false;
+ }
+ if (this.id != other.id)
+ {
+ return false;
+ }
+ if (this.sector != other.sector)
+ {
+ return false;
+ }
+ if (this.length != other.length)
+ {
+ return false;
+ }
+ return true;
+ }
+}
diff --git a/cache/src/main/java/net/runelite/cache/fs/IndexFile.java b/cache/src/main/java/net/runelite/cache/fs/IndexFile.java
new file mode 100644
index 0000000000..2b0a43a75d
--- /dev/null
+++ b/cache/src/main/java/net/runelite/cache/fs/IndexFile.java
@@ -0,0 +1,145 @@
+/*
+ * Copyright (c) 2016, Adam
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Adam
+ * 4. Neither the name of the Adam nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Adam ''AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL Adam BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package net.runelite.cache.fs;
+
+import java.io.Closeable;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.RandomAccessFile;
+import java.util.Objects;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class IndexFile implements Closeable
+{
+ private static final Logger logger = LoggerFactory.getLogger(IndexFile.class);
+ private static final int INDEX_ENTRY_LEN = 6;
+
+ private final Store store;
+ private final int indexFileId;
+ private final File file;
+ private final RandomAccessFile idx;
+ private final byte[] buffer = new byte[INDEX_ENTRY_LEN];
+
+ public IndexFile(Store store, int indexFileId, File file) throws FileNotFoundException
+ {
+ this.store = store;
+ this.indexFileId = indexFileId;
+ this.file = file;
+ this.idx = new RandomAccessFile(file, "rw");
+ }
+
+ @Override
+ public void close() throws IOException
+ {
+ idx.close();
+ }
+
+ @Override
+ public int hashCode()
+ {
+ int hash = 3;
+ hash = 41 * hash + Objects.hashCode(this.file);
+ return hash;
+ }
+
+ @Override
+ public boolean equals(Object obj)
+ {
+ if (obj == null)
+ {
+ return false;
+ }
+ if (getClass() != obj.getClass())
+ {
+ return false;
+ }
+ final IndexFile other = (IndexFile) obj;
+ if (!Objects.equals(this.file, other.file))
+ {
+ return false;
+ }
+ return true;
+ }
+
+ public Store getStore()
+ {
+ return store;
+ }
+
+ public int getIndexFileId()
+ {
+ return indexFileId;
+ }
+
+ public synchronized void write(IndexEntry entry) throws IOException
+ {
+ idx.seek(entry.getId() * INDEX_ENTRY_LEN);
+
+ buffer[0] = (byte) (entry.getLength() >> 16);
+ buffer[1] = (byte) (entry.getLength() >> 8);
+ buffer[2] = (byte) entry.getLength();
+
+ buffer[3] = (byte) (entry.getSector() >> 16);
+ buffer[4] = (byte) (entry.getSector() >> 8);
+ buffer[5] = (byte) entry.getSector();
+
+ idx.write(buffer);
+ }
+
+ public synchronized IndexEntry read(int id) throws IOException
+ {
+ idx.seek(id * INDEX_ENTRY_LEN);
+ int i = idx.read(buffer);
+ if (i != INDEX_ENTRY_LEN)
+ {
+ logger.warn("short read for id {} on index {}: {}", id, indexFileId, i);
+ return null;
+ }
+
+ int length = ((buffer[0] & 0xFF) << 16) | ((buffer[1] & 0xFF) << 8) | (buffer[2] & 0xFF);
+ int sector = ((buffer[3] & 0xFF) << 16) | ((buffer[4] & 0xFF) << 8) | (buffer[5] & 0xFF);
+
+ if (length <= 0 || sector <= 0)
+ {
+ logger.debug("invalid length or sector {}/{}", length, sector);
+ return null;
+ }
+
+ return new IndexEntry(this, id, sector, length);
+ }
+
+ public synchronized int getIndexCount() throws IOException
+ {
+ return (int)(idx.length() / INDEX_ENTRY_LEN);
+ }
+}
diff --git a/cache/src/main/java/net/runelite/cache/fs/Store.java b/cache/src/main/java/net/runelite/cache/fs/Store.java
new file mode 100644
index 0000000000..399a600986
--- /dev/null
+++ b/cache/src/main/java/net/runelite/cache/fs/Store.java
@@ -0,0 +1,153 @@
+/*
+ * Copyright (c) 2016, Adam
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Adam
+ * 4. Neither the name of the Adam nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Adam ''AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL Adam BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package net.runelite.cache.fs;
+
+import java.io.Closeable;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+import net.runelite.cache.IndexType;
+
+public class Store implements Closeable
+{
+ private static final String MAIN_FILE_CACHE_DAT = "main_file_cache.dat2";
+ private static final String MAIN_FILE_CACHE_IDX = "main_file_cache.idx";
+
+ private final File folder;
+ private final DataFile data;
+ private final IndexFile index255;
+ private final List indexes = new ArrayList<>();
+
+ public Store(File folder) throws IOException
+ {
+ this.folder = folder;
+
+ data = new DataFile(this, new File(folder, MAIN_FILE_CACHE_DAT));
+ index255 = new IndexFile(this, 255, new File(folder, MAIN_FILE_CACHE_IDX + "255"));
+
+ for (int i = 0; i < index255.getIndexCount(); ++i)
+ {
+ this.addIndex(i);
+ }
+ }
+
+ @Override
+ public void close() throws IOException
+ {
+ data.close();
+ index255.close();
+ for (Index i : indexes)
+ i.close();
+ }
+
+ @Override
+ public int hashCode()
+ {
+ int hash = 5;
+ hash = 79 * hash + Objects.hashCode(this.indexes);
+ return hash;
+ }
+
+ @Override
+ public boolean equals(Object obj)
+ {
+ if (obj == null)
+ {
+ return false;
+ }
+ if (getClass() != obj.getClass())
+ {
+ return false;
+ }
+ final Store other = (Store) obj;
+ if (!Objects.equals(this.indexes, other.indexes))
+ {
+ return false;
+ }
+ return true;
+ }
+
+ public final Index addIndex(int id) throws FileNotFoundException
+ {
+ for (Index i : indexes)
+ if (i.getIndex().getIndexFileId() == id)
+ throw new IllegalArgumentException("index " + id + " already exists");
+
+ IndexFile indexFile = new IndexFile(this, id, new File(folder, MAIN_FILE_CACHE_IDX + id));
+ Index index = new Index(this, indexFile, id);
+
+ this.indexes.add(index);
+
+ return index;
+ }
+
+ public void load() throws IOException
+ {
+ for (Index i : indexes)
+ {
+ int id = i.getIndex().getIndexFileId();
+ if (id == 5) // XXX maps, XTEA encrypted, can't decompress
+ continue;
+ if (id == 6 || id == 14)
+ continue; // XXX I get more Indexes than there is length of the index file for these
+ i.load();
+ }
+ }
+
+ public void save() throws IOException
+ {
+ for (Index i : indexes)
+ i.save();
+ }
+
+ public DataFile getData()
+ {
+ return data;
+ }
+
+ public IndexFile getIndex255()
+ {
+ return index255;
+ }
+
+ public List getIndexes()
+ {
+ return indexes;
+ }
+
+ public Index getIndex(IndexType type)
+ {
+ return indexes.get(type.getNumber());
+ }
+}
diff --git a/cache/src/main/java/net/runelite/cache/fs/util/BZip2.java b/cache/src/main/java/net/runelite/cache/fs/util/BZip2.java
new file mode 100644
index 0000000000..2cca0b6f56
--- /dev/null
+++ b/cache/src/main/java/net/runelite/cache/fs/util/BZip2.java
@@ -0,0 +1,101 @@
+/*
+ * Copyright (c) 2016, Adam
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Adam
+ * 4. Neither the name of the Adam nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Adam ''AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL Adam BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package net.runelite.cache.fs.util;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.Arrays;
+import org.apache.commons.compress.compressors.bzip2.BZip2CompressorInputStream;
+import org.apache.commons.compress.compressors.bzip2.BZip2CompressorOutputStream;
+import org.apache.commons.compress.utils.IOUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class BZip2
+{
+ private static final Logger logger = LoggerFactory.getLogger(BZip2.class);
+
+ private static final byte[] BZIP_HEADER = new byte[] {
+ 'B', 'Z', // magic
+ 'h', // 'h' for Bzip2 ('H'uffman coding)
+ '1' // block size
+ };
+
+ public static byte[] compress(byte[] bytes)
+ {
+ try
+ {
+ InputStream is = new ByteArrayInputStream(bytes);
+ ByteArrayOutputStream bout = new ByteArrayOutputStream();
+ try (OutputStream os = new BZip2CompressorOutputStream(bout))
+ {
+ IOUtils.copy(is, os);
+ }
+
+ byte[] out = bout.toByteArray();
+ return Arrays.copyOfRange(out, BZIP_HEADER.length, out.length); // remove header..
+ }
+ catch (IOException ex)
+ {
+ logger.warn(null, ex);
+ return null;
+ }
+ }
+
+ public static byte[] decompress(byte[] bytes)
+ {
+ try
+ {
+ byte[] data = new byte[bytes.length + BZIP_HEADER.length];
+
+ // add header
+ System.arraycopy(BZIP_HEADER, 0, data, 0, BZIP_HEADER.length);
+ System.arraycopy(bytes, 0, data, BZIP_HEADER.length, bytes.length);
+
+ ByteArrayOutputStream os = new ByteArrayOutputStream();
+
+ try (InputStream is = new BZip2CompressorInputStream(new ByteArrayInputStream(data)))
+ {
+ IOUtils.copy(is, os);
+ }
+
+ return os.toByteArray();
+ }
+ catch (IOException ex)
+ {
+ logger.warn(null, ex);
+ return null;
+ }
+ }
+}
diff --git a/cache/src/main/java/net/runelite/cache/fs/util/CRC32HGenerator.java b/cache/src/main/java/net/runelite/cache/fs/util/CRC32HGenerator.java
new file mode 100644
index 0000000000..130ff67055
--- /dev/null
+++ b/cache/src/main/java/net/runelite/cache/fs/util/CRC32HGenerator.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 2016, Adam
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Adam
+ * 4. Neither the name of the Adam nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Adam ''AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL Adam BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package net.runelite.cache.fs.util;
+
+import java.util.zip.CRC32;
+
+public final class CRC32HGenerator
+{
+ public static final CRC32 CRC32Instance = new CRC32();
+
+ public static synchronized int getHash(byte[] data, int len)
+ {
+ CRC32Instance.update(data, 0, len);
+ try
+ {
+ return (int) CRC32Instance.getValue();
+ }
+ finally
+ {
+ CRC32Instance.reset();
+ }
+ }
+}
diff --git a/cache/src/main/java/net/runelite/cache/fs/util/Djb2.java b/cache/src/main/java/net/runelite/cache/fs/util/Djb2.java
new file mode 100644
index 0000000000..4cd490423a
--- /dev/null
+++ b/cache/src/main/java/net/runelite/cache/fs/util/Djb2.java
@@ -0,0 +1,28 @@
+package net.runelite.cache.fs.util;
+
+/**
+ * An implementation of the {@code djb2} hash function.
+ *
+ * @author Graham
+ * @author `Discardedx2
+ */
+public final class Djb2
+{
+ /**
+ * An implementation of Dan Bernstein's {@code djb2} hash function which
+ * is slightly modified. Instead of the initial hash being 5381, it is
+ * zero.
+ *
+ * @param str The string to hash.
+ * @return The hash code.
+ */
+ public static int hash(String str)
+ {
+ int hash = 0;
+ for (int i = 0; i < str.length(); i++)
+ {
+ hash = str.charAt(i) + ((hash << 5) - hash);
+ }
+ return hash;
+ }
+}
diff --git a/cache/src/main/java/net/runelite/cache/fs/util/GZip.java b/cache/src/main/java/net/runelite/cache/fs/util/GZip.java
new file mode 100644
index 0000000000..9b6adbb76c
--- /dev/null
+++ b/cache/src/main/java/net/runelite/cache/fs/util/GZip.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright (c) 2016, Adam
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Adam
+ * 4. Neither the name of the Adam nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Adam ''AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL Adam BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package net.runelite.cache.fs.util;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.zip.GZIPInputStream;
+import java.util.zip.GZIPOutputStream;
+import org.apache.commons.compress.utils.IOUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class GZip
+{
+ private static final Logger logger = LoggerFactory.getLogger(GZip.class);
+
+ public static byte[] compress(byte[] bytes)
+ {
+ InputStream is = new ByteArrayInputStream(bytes);
+ ByteArrayOutputStream bout = new ByteArrayOutputStream();
+
+ try (OutputStream os = new GZIPOutputStream(bout))
+ {
+ IOUtils.copy(is, os);
+ }
+ catch (IOException ex)
+ {
+ logger.warn(null, ex);
+ return null;
+ }
+
+ return bout.toByteArray();
+ }
+
+ public static byte[] decompress(byte[] bytes)
+ {
+ ByteArrayOutputStream os = new ByteArrayOutputStream();
+
+ try (InputStream is = new GZIPInputStream(new ByteArrayInputStream(bytes)))
+ {
+ IOUtils.copy(is, os);
+ }
+ catch (IOException ex)
+ {
+ logger.warn(null, ex);
+ return null;
+ }
+
+ return os.toByteArray();
+ }
+}
diff --git a/cache/src/main/java/net/runelite/cache/fs/util/Whirlpool.java b/cache/src/main/java/net/runelite/cache/fs/util/Whirlpool.java
new file mode 100644
index 0000000000..a380819276
--- /dev/null
+++ b/cache/src/main/java/net/runelite/cache/fs/util/Whirlpool.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2016, Adam
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Adam
+ * 4. Neither the name of the Adam nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Adam ''AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL Adam BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package net.runelite.cache.fs.util;
+
+public class Whirlpool {
+ private static gnu.crypto.hash.Whirlpool whirlpool = new gnu.crypto.hash.Whirlpool();
+
+ public static synchronized byte[] getHash(byte[] data, int len)
+ {
+ whirlpool.update(data, 0, len);
+ try
+ {
+ return whirlpool.digest();
+ }
+ finally
+ {
+ whirlpool.reset();
+ }
+ }
+}
diff --git a/cache/src/main/java/net/runelite/cache/io/InputStream.java b/cache/src/main/java/net/runelite/cache/io/InputStream.java
new file mode 100644
index 0000000000..af76b73859
--- /dev/null
+++ b/cache/src/main/java/net/runelite/cache/io/InputStream.java
@@ -0,0 +1,193 @@
+/*
+ * Copyright (c) 2016, Adam
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Adam
+ * 4. Neither the name of the Adam nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Adam ''AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL Adam BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package net.runelite.cache.io;
+
+import java.io.IOException;
+import java.nio.ByteBuffer;
+
+public class InputStream extends java.io.InputStream
+{
+ private static final char[] CHARACTERS = new char[]
+ {
+ '\u20ac', '\u0000', '\u201a', '\u0192', '\u201e', '\u2026',
+ '\u2020', '\u2021', '\u02c6', '\u2030', '\u0160', '\u2039',
+ '\u0152', '\u0000', '\u017d', '\u0000', '\u0000', '\u2018',
+ '\u2019', '\u201c', '\u201d', '\u2022', '\u2013', '\u2014',
+ '\u02dc', '\u2122', '\u0161', '\u203a', '\u0153', '\u0000',
+ '\u017e', '\u0178'
+ };
+
+ private final ByteBuffer buffer;
+
+ public InputStream(byte[] buffer)
+ {
+ this.buffer = ByteBuffer.wrap(buffer);
+ }
+
+ public int read24BitInt()
+ {
+ return (this.readUnsignedByte() << 16) + (this.readUnsignedByte() << 8) + this.readUnsignedByte();
+ }
+
+ public void skip(int length)
+ {
+ int pos = buffer.position();
+ pos += length;
+ buffer.position(pos);
+ }
+
+ public void setOffset(int offset)
+ {
+ buffer.position(offset);
+ }
+
+ public int getOffset()
+ {
+ return buffer.position();
+ }
+
+ public int getLength()
+ {
+ return buffer.limit();
+ }
+
+ public byte readByte()
+ {
+ return buffer.get();
+ }
+
+ public void readBytes(byte[] buffer, int off, int len)
+ {
+ this.buffer.get(buffer, off, len);
+ }
+
+ public void readBytes(byte[] buffer)
+ {
+ this.buffer.get(buffer);
+ }
+
+ public int readUnsignedByte()
+ {
+ return this.readByte() & 0xFF;
+ }
+
+ public int readUnsignedShort()
+ {
+ return buffer.getShort() & 0xFFFF;
+ }
+
+ public short readShort()
+ {
+ return buffer.getShort();
+ }
+
+ public int readInt()
+ {
+ return buffer.getInt();
+ }
+
+ public byte peek()
+ {
+ int position = buffer.position();
+ try
+ {
+ return buffer.get();
+ }
+ finally
+ {
+ buffer.position(position);
+ }
+ }
+
+ public int readBigSmart()
+ {
+ return peek() >= 0 ? this.readUnsignedShort() : Integer.MAX_VALUE & this.readInt();
+ }
+
+ public int readShortSmart()
+ {
+ int var2 = this.peek() & 0xFF;
+ return var2 < 128 ? this.readUnsignedByte() - 64 : this.readUnsignedShort() - 0xc000;
+ }
+
+ public String readString()
+ {
+ StringBuilder sb = new StringBuilder();
+
+ for (;;)
+ {
+ int ch = this.readByte();
+
+ if (ch == 0)
+ break;
+
+ if (ch >= 128 && ch < 160)
+ {
+ char var7 = CHARACTERS[ch - 128];
+ if (0 == var7)
+ {
+ var7 = 63;
+ }
+
+ ch = var7;
+ }
+
+ sb.append((char) ch);
+ }
+ return sb.toString();
+ }
+
+
+ public String readStringOrNull()
+ {
+ if (this.peek() != 0)
+ {
+ return readString();
+ }
+ else
+ {
+ this.readByte(); // discard
+ return null;
+ }
+ }
+
+ public byte[] getRemaining()
+ {
+ byte[] b = new byte[buffer.remaining()];
+ buffer.get(b);
+ return b;
+ }
+
+ @Override
+ public int read() throws IOException
+ {
+ return this.readUnsignedByte();
+ }
+}
diff --git a/cache/src/main/java/net/runelite/cache/io/OutputStream.java b/cache/src/main/java/net/runelite/cache/io/OutputStream.java
new file mode 100644
index 0000000000..ee6e41a2ea
--- /dev/null
+++ b/cache/src/main/java/net/runelite/cache/io/OutputStream.java
@@ -0,0 +1,129 @@
+/*
+ * Copyright (c) 2016, Adam
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Adam
+ * 4. Neither the name of the Adam nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Adam ''AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL Adam BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package net.runelite.cache.io;
+
+import java.io.IOException;
+import java.nio.ByteBuffer;
+
+public final class OutputStream extends java.io.OutputStream
+{
+ private ByteBuffer buffer;
+
+ public OutputStream(int capacity)
+ {
+ buffer = ByteBuffer.allocate(capacity);
+ }
+
+ public OutputStream()
+ {
+ this(16);
+ }
+
+ private void ensureRemaining(int remaining)
+ {
+ while (remaining > buffer.remaining())
+ {
+ int newCapacity = buffer.capacity() * 2;
+
+ ByteBuffer old = buffer;
+ old.flip();
+
+ buffer = ByteBuffer.allocate(newCapacity);
+
+ buffer.put(old);
+ }
+ }
+
+ public void skip(int length)
+ {
+ int pos = buffer.position();
+ pos += length;
+ buffer.position(pos);
+ }
+
+ public void setOffset(int offset)
+ {
+ buffer.position(offset);
+ }
+
+ public void writeBytes(byte[] b)
+ {
+ ensureRemaining(b.length);
+ buffer.put(b);
+ }
+
+ public void writeByte(int i)
+ {
+ ensureRemaining(1);
+ buffer.put((byte) i);
+ }
+
+ public void writeBigSmart(int value)
+ {
+ if (value >= 65536)
+ {
+ ensureRemaining(5);
+ this.writeByte(-1);
+ this.writeInt(Integer.MAX_VALUE & value);
+ }
+ else
+ {
+ ensureRemaining(2);
+ this.writeShort(value);
+ }
+ }
+
+ public void writeShort(int i)
+ {
+ ensureRemaining(2);
+ buffer.putShort((short) i);
+ }
+
+ public void writeInt(int i)
+ {
+ ensureRemaining(4);
+ buffer.putInt(i);
+ }
+
+ public byte[] flip()
+ {
+ buffer.flip();
+ byte[] b = new byte[buffer.limit()];
+ buffer.get(b);
+ return b;
+ }
+
+ @Override
+ public void write(int b) throws IOException
+ {
+ buffer.put((byte) b);
+ }
+
+}
diff --git a/cache/src/main/java/net/runelite/cache/renderable/RGBSprite.java b/cache/src/main/java/net/runelite/cache/renderable/RGBSprite.java
new file mode 100644
index 0000000000..2c21d4bb3c
--- /dev/null
+++ b/cache/src/main/java/net/runelite/cache/renderable/RGBSprite.java
@@ -0,0 +1,152 @@
+/*
+ * Copyright (c) 2016, Adam
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Adam
+ * 4. Neither the name of the Adam nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Adam ''AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL Adam BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package net.runelite.cache.renderable;
+
+import java.awt.*;
+import java.awt.image.*;
+import net.runelite.cache.definitions.SpriteDefinition;
+
+public class RGBSprite
+{
+ private int offsetY;
+ private int spriteWidth;
+ private int spriteHeight;
+ private int offsetX;
+ private int maxHeight;
+ private int maxWidth;
+ private int[] pixels;
+
+ RGBSprite()
+ {
+ }
+
+ public int getOffsetY()
+ {
+ return offsetY;
+ }
+
+ public int getSpriteWidth()
+ {
+ return spriteWidth;
+ }
+
+ public int getSpriteHeight()
+ {
+ return spriteHeight;
+ }
+
+ public int getOffsetX()
+ {
+ return offsetX;
+ }
+
+ public int getMaxHeight()
+ {
+ return maxHeight;
+ }
+
+ public int getMaxWidth()
+ {
+ return maxWidth;
+ }
+
+ public int[] getPixels()
+ {
+ return pixels;
+ }
+
+ public static RGBSprite fromSpriteDefinition(SpriteDefinition def)
+ {
+ RGBSprite sprite = new RGBSprite();
+
+ sprite.maxWidth = def.getMaxWidth();
+ sprite.maxHeight = def.getMaxHeight();
+ sprite.offsetX = def.getOffsetX();
+ sprite.offsetY = def.getOffsetY();
+ sprite.spriteWidth = def.getWidth();
+ sprite.spriteHeight = def.getHeight();
+
+ int dimmension = sprite.spriteWidth * sprite.spriteHeight;
+ byte[] pixels = def.getPixels();
+ int[] palette = def.getLoader().getLoadedPalette();
+
+ sprite.pixels = new int[dimmension];
+
+ for (int pos = 0; pos < dimmension; ++pos)
+ {
+ sprite.pixels[pos] = palette[pixels[pos] & 255];
+ }
+
+ return sprite;
+ }
+
+ public BufferedImage getBufferedImage()
+ {
+ BufferedImage bi = new BufferedImage(spriteWidth, spriteHeight, BufferedImage.TYPE_INT_RGB);
+ bi.setRGB(0, 0, spriteWidth, spriteHeight, pixels, 0, spriteWidth);
+ Image img = makeColorTransparent(bi, new Color(0, 0, 0));
+ BufferedImage trans = imageToBufferedImage(img);
+ return trans;
+ }
+
+ private static Image makeColorTransparent(BufferedImage im, final Color color)
+ {
+ final int markerRGB = color.getRGB() | 0xFF000000;
+
+ RGBImageFilter filter = new RGBImageFilter()
+ {
+ @Override
+ public final int filterRGB(int x, int y, int rgb)
+ {
+ if ((rgb | 0xFF000000) == markerRGB)
+ {
+ return 0x00FFFFFF & rgb;
+ }
+ else
+ {
+ return rgb;
+ }
+ }
+ };
+
+ ImageProducer ip = new FilteredImageSource(im.getSource(), filter);
+ return Toolkit.getDefaultToolkit().createImage(ip);
+ }
+
+ private static BufferedImage imageToBufferedImage(Image image)
+ {
+ BufferedImage bufferedImage = new BufferedImage(image.getWidth(null), image.getHeight(null), BufferedImage.TYPE_INT_ARGB);
+ Graphics2D g2 = bufferedImage.createGraphics();
+ g2.drawImage(image, 0, 0, null);
+ g2.dispose();
+ return bufferedImage;
+ }
+}
diff --git a/cache/src/test/java/net/runelite/cache/EnumDumperTest.java b/cache/src/test/java/net/runelite/cache/EnumDumperTest.java
new file mode 100644
index 0000000000..9a31945fb2
--- /dev/null
+++ b/cache/src/test/java/net/runelite/cache/EnumDumperTest.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright (c) 2016, Adam
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Adam
+ * 4. Neither the name of the Adam nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Adam ''AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL Adam BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package net.runelite.cache;
+
+import com.google.common.io.Files;
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
+import java.io.File;
+import java.io.IOException;
+import java.nio.charset.Charset;
+import net.runelite.cache.definitions.EnumDefinition;
+import net.runelite.cache.definitions.loaders.EnumLoader;
+import net.runelite.cache.fs.Archive;
+import net.runelite.cache.fs.Index;
+import net.runelite.cache.fs.Store;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class EnumDumperTest
+{
+ private static final Logger logger = LoggerFactory.getLogger(EnumDumperTest.class);
+
+ private Gson gson = new GsonBuilder().setPrettyPrinting().create();
+
+ @Rule
+ public TemporaryFolder folder = StoreLocation.getTemporaryFolder();
+
+ @Test
+ public void test() throws IOException
+ {
+ File dumpDir = folder.newFolder();
+ int count = 0;
+
+ try (Store store = new Store(StoreLocation.LOCATION))
+ {
+ store.load();
+
+ Index index = store.getIndex(IndexType.CONFIGS);
+ Archive archive = index.getArchive(ConfigType.ENUM.getId());
+
+ EnumLoader loader = new EnumLoader();
+
+ for (net.runelite.cache.fs.File file : archive.getFiles())
+ {
+ byte[] b = file.getContents();
+
+ EnumDefinition def = loader.load(file.getFileId(), b);
+
+ Files.write(gson.toJson(def), new File(dumpDir, file.getFileId() + ".json"), Charset.defaultCharset());
+ ++count;
+ }
+ }
+
+ logger.info("Dumped {} enums to {}", count, dumpDir);
+ }
+}
diff --git a/cache/src/test/java/net/runelite/cache/ItemDumperTest.java b/cache/src/test/java/net/runelite/cache/ItemDumperTest.java
new file mode 100644
index 0000000000..989c8ef7b1
--- /dev/null
+++ b/cache/src/test/java/net/runelite/cache/ItemDumperTest.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (c) 2016, Adam
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Adam
+ * 4. Neither the name of the Adam nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Adam ''AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL Adam BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package net.runelite.cache;
+
+import java.io.File;
+import java.io.IOException;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class ItemDumperTest
+{
+ private static final Logger logger = LoggerFactory.getLogger(ItemDumperTest.class);
+
+ @Rule
+ public TemporaryFolder folder = StoreLocation.getTemporaryFolder();
+
+ @Test
+ public void test() throws IOException
+ {
+ File dumpDir = folder.newFolder(),
+ javaDir = folder.newFolder();
+
+ ItemDumper dumper = new ItemDumper(
+ StoreLocation.LOCATION,
+ dumpDir,
+ javaDir
+ );
+ dumper.load();
+ dumper.dump();
+ dumper.java();
+
+ logger.info("Dumped to {}, java {}", dumpDir, javaDir);
+ }
+
+}
diff --git a/cache/src/test/java/net/runelite/cache/ModelDumperTest.java b/cache/src/test/java/net/runelite/cache/ModelDumperTest.java
new file mode 100644
index 0000000000..717c14984c
--- /dev/null
+++ b/cache/src/test/java/net/runelite/cache/ModelDumperTest.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright (c) 2016, Adam
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Adam
+ * 4. Neither the name of the Adam nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Adam ''AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL Adam BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package net.runelite.cache;
+
+
+import com.google.common.io.Files;
+import com.google.gson.Gson;
+import java.io.IOException;
+import net.runelite.cache.definitions.loaders.ModelLoader;
+import net.runelite.cache.fs.Archive;
+import net.runelite.cache.fs.File;
+import net.runelite.cache.fs.Index;
+import net.runelite.cache.fs.Store;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class ModelDumperTest
+{
+ private static final Logger logger = LoggerFactory.getLogger(ModelDumperTest.class);
+
+ private Gson gson = new Gson();
+
+ @Rule
+ public TemporaryFolder folder = StoreLocation.getTemporaryFolder();
+
+ @Test
+ public void test() throws IOException
+ {
+ java.io.File modelDir = folder.newFolder("models");
+ int count = 0;
+
+ try (Store store = new Store(StoreLocation.LOCATION))
+ {
+ store.load();
+
+ Index index = store.getIndex(IndexType.MODELS);
+
+ for (Archive archive : index.getArchives())
+ {
+ assert archive.getFiles().size() == 1;
+
+ File file = archive.getFiles().get(0);
+ byte[] contents = file.getContents();
+
+ ModelLoader loader = new ModelLoader();
+ loader.load(contents);
+
+ Files.write(contents, new java.io.File(modelDir, archive.getArchiveId() + ".model"));
+ //Files.write(gson.toJson(loader), new java.io.File(modelDir, archive.getArchiveId() + ".json"), Charset.defaultCharset());
+ ++count;
+ }
+ }
+
+ logger.info("Dumped {} models to {}", count, modelDir);
+ }
+}
diff --git a/cache/src/test/java/net/runelite/cache/NpcDumperTest.java b/cache/src/test/java/net/runelite/cache/NpcDumperTest.java
new file mode 100644
index 0000000000..99fa877ed3
--- /dev/null
+++ b/cache/src/test/java/net/runelite/cache/NpcDumperTest.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (c) 2016, Adam
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Adam
+ * 4. Neither the name of the Adam nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Adam ''AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL Adam BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package net.runelite.cache;
+
+import java.io.File;
+import java.io.IOException;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class NpcDumperTest
+{
+ private static final Logger logger = LoggerFactory.getLogger(NpcDumperTest.class);
+
+ @Rule
+ public TemporaryFolder folder = StoreLocation.getTemporaryFolder();
+
+ @Test
+ public void test() throws IOException
+ {
+ File dumpDir = folder.newFolder(),
+ javaDir = folder.newFolder();
+
+ NpcDumper dumper = new NpcDumper(
+ StoreLocation.LOCATION,
+ dumpDir,
+ javaDir
+ );
+ dumper.load();
+ dumper.dump();
+ dumper.java();
+
+ logger.info("Dumped to {}, java {}", dumpDir, javaDir);
+ }
+
+}
diff --git a/cache/src/test/java/net/runelite/cache/ObjectDumperTest.java b/cache/src/test/java/net/runelite/cache/ObjectDumperTest.java
new file mode 100644
index 0000000000..cce14cc3ec
--- /dev/null
+++ b/cache/src/test/java/net/runelite/cache/ObjectDumperTest.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright (c) 2016, Adam
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Adam
+ * 4. Neither the name of the Adam nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Adam ''AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL Adam BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package net.runelite.cache;
+
+import java.io.File;
+import java.io.IOException;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class ObjectDumperTest
+{
+ private static final Logger logger = LoggerFactory.getLogger(ObjectDumperTest.class);
+
+ @Rule
+ public TemporaryFolder folder = StoreLocation.getTemporaryFolder();
+
+ @Test
+ public void test() throws IOException
+ {
+ File dumpDir = folder.newFolder(),
+ javaDir = folder.newFolder();
+
+ ObjectDumper dumper = new ObjectDumper(
+ StoreLocation.LOCATION,
+ dumpDir,
+ javaDir
+ );
+ dumper.load();
+ dumper.dump();
+ dumper.java();
+
+ logger.info("Dumped to {}, java {}", dumpDir, javaDir);
+ }
+
+}
diff --git a/cache/src/test/java/net/runelite/cache/ScriptDumperTest.java b/cache/src/test/java/net/runelite/cache/ScriptDumperTest.java
new file mode 100644
index 0000000000..403b9d8496
--- /dev/null
+++ b/cache/src/test/java/net/runelite/cache/ScriptDumperTest.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright (c) 2016, Adam
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Adam
+ * 4. Neither the name of the Adam nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Adam ''AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL Adam BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package net.runelite.cache;
+
+import com.google.common.io.Files;
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
+import java.io.IOException;
+import net.runelite.cache.definitions.ScriptDefinition;
+import net.runelite.cache.definitions.loaders.ScriptLoader;
+import net.runelite.cache.fs.Archive;
+import net.runelite.cache.fs.File;
+import net.runelite.cache.fs.Index;
+import net.runelite.cache.fs.Store;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class ScriptDumperTest
+{
+ private static final Logger logger = LoggerFactory.getLogger(ScriptDumperTest.class);
+
+ private Gson gson = new GsonBuilder().setPrettyPrinting().create();
+
+ @Rule
+ public TemporaryFolder folder = StoreLocation.getTemporaryFolder();
+
+ @Test
+ public void test() throws IOException
+ {
+ java.io.File outDir = folder.newFolder();
+ int count = 0;
+
+ try (Store store = new Store(StoreLocation.LOCATION))
+ {
+ store.load();
+
+ Index index = store.getIndex(IndexType.CLIENTSCRIPT);
+ ScriptLoader loader = new ScriptLoader();
+
+ for (Archive archive : index.getArchives())
+ {
+ assert archive.getFiles().size() == 1;
+
+ File file = archive.getFiles().get(0);
+ byte[] contents = file.getContents();
+
+ ScriptDefinition script = loader.load(file.getFileId(), contents);
+
+ Files.write(contents, new java.io.File(outDir, archive.getArchiveId() + ".script"));
+ ++count;
+ }
+ }
+
+ logger.info("Dumped {} scripts to {}", count, outDir);
+ }
+}
diff --git a/cache/src/test/java/net/runelite/cache/StoreLocation.java b/cache/src/test/java/net/runelite/cache/StoreLocation.java
new file mode 100644
index 0000000000..caf9bf2832
--- /dev/null
+++ b/cache/src/test/java/net/runelite/cache/StoreLocation.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright (c) 2016, Adam
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Adam
+ * 4. Neither the name of the Adam nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Adam ''AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL Adam BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package net.runelite.cache;
+
+import java.io.File;
+import java.net.URISyntaxException;
+import org.junit.rules.TemporaryFolder;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class StoreLocation
+{
+ private static final Logger logger = LoggerFactory.getLogger(StoreLocation.class);
+
+ public static File LOCATION;
+
+ static
+ {
+ try
+ {
+ LOCATION = new File(StoreLocation.class.getResource("/cache").toURI());
+ }
+ catch (URISyntaxException ex)
+ {
+ logger.error(null, ex);
+ }
+
+ File tmp = new File("d:/temp");
+ if (tmp.exists() || tmp.mkdir())
+ System.setProperty("java.io.tmpdir", "d:/temp");
+ }
+
+ public static TemporaryFolder getTemporaryFolder()
+ {
+ return new TemporaryFolder();
+ }
+}
\ No newline at end of file
diff --git a/cache/src/test/java/net/runelite/cache/fs/DataFileTest.java b/cache/src/test/java/net/runelite/cache/fs/DataFileTest.java
new file mode 100644
index 0000000000..ea5ac06d91
--- /dev/null
+++ b/cache/src/test/java/net/runelite/cache/fs/DataFileTest.java
@@ -0,0 +1,105 @@
+/*
+ * Copyright (c) 2016, Adam
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Adam
+ * 4. Neither the name of the Adam nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Adam ''AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL Adam BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package net.runelite.cache.fs;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import net.runelite.cache.StoreLocation;
+import org.junit.Assert;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+
+public class DataFileTest
+{
+ @Rule
+ public TemporaryFolder folder = StoreLocation.getTemporaryFolder();
+
+ @Test
+ public void test1() throws IOException
+ {
+ File file = folder.newFile();
+ Store store = new Store(folder.getRoot());
+ DataFile df = new DataFile(store, file);
+ DataFileWriteResult res = df.write(42, 3, ByteBuffer.wrap("test".getBytes()), CompressionType.NONE, 0);
+ DataFileReadResult res2 = df.read(42, 3, res.sector, res.compressedLength);
+ byte[] buf = res2.data;
+ String str = new String(buf);
+ Assert.assertEquals("test", str);
+ file.delete();
+ }
+
+ @Test
+ public void test2() throws IOException
+ {
+ byte[] b = new byte[1024];
+ for (int i = 0; i < 1024; ++i)
+ b[i] = (byte) i;
+
+ File file = folder.newFile();
+ Store store = new Store(folder.getRoot());
+ DataFile df = new DataFile(store, file);
+ DataFileWriteResult res = df.write(42, 0x1FFFF, ByteBuffer.wrap(b), CompressionType.NONE, 0);
+ DataFileReadResult res2 = df.read(42, 0x1FFFF, res.sector, res.compressedLength);
+ byte[] buf = res2.data;
+ Assert.assertArrayEquals(b, buf);
+ file.delete();
+ }
+
+ @Test
+ public void testGZipCompression() throws IOException
+ {
+ try (Store store = new Store(folder.getRoot()))
+ {
+ DataFile df = new DataFile(store, folder.newFile());
+ DataFileWriteResult res = df.write(41, 4, ByteBuffer.wrap("test".getBytes()), CompressionType.GZ, 0);
+ DataFileReadResult res2 = df.read(41, 4, res.sector, res.compressedLength);
+ byte[] buf = res2.data;
+ String str = new String(buf);
+ Assert.assertEquals("test", str);
+ }
+ }
+
+ @Test
+ public void testBZip2Compression() throws IOException
+ {
+ try (Store store = new Store(folder.getRoot()))
+ {
+ DataFile df = new DataFile(store, folder.newFile());
+ DataFileWriteResult res = df.write(41, 4, ByteBuffer.wrap("test".getBytes()), CompressionType.BZ2, 0);
+ DataFileReadResult res2 = df.read(41, 4, res.sector, res.compressedLength);
+ byte[] buf = res2.data;
+ String str = new String(buf);
+ Assert.assertEquals("test", str);
+ }
+ }
+}
diff --git a/cache/src/test/java/net/runelite/cache/fs/IndexFileTest.java b/cache/src/test/java/net/runelite/cache/fs/IndexFileTest.java
new file mode 100644
index 0000000000..fa9a17b603
--- /dev/null
+++ b/cache/src/test/java/net/runelite/cache/fs/IndexFileTest.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright (c) 2016, Adam
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Adam
+ * 4. Neither the name of the Adam nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Adam ''AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL Adam BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package net.runelite.cache.fs;
+
+import java.io.File;
+import java.io.IOException;
+import net.runelite.cache.StoreLocation;
+import org.junit.Assert;
+import org.junit.BeforeClass;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+
+public class IndexFileTest
+{
+ @Rule
+ public TemporaryFolder folder = StoreLocation.getTemporaryFolder();
+
+ @Test
+ public void test1() throws IOException
+ {
+ File file = folder.newFile();
+ Store store = new Store(folder.getRoot());
+ IndexFile index = new IndexFile(store, 5, file);
+ IndexEntry entry = new IndexEntry(index, 7, 8, 9);
+ index.write(entry);
+ IndexEntry entry2 = index.read(7);
+ Assert.assertEquals(entry, entry2);
+ }
+}
diff --git a/cache/src/test/java/net/runelite/cache/fs/StoreLoadTest.java b/cache/src/test/java/net/runelite/cache/fs/StoreLoadTest.java
new file mode 100644
index 0000000000..0e56fe384a
--- /dev/null
+++ b/cache/src/test/java/net/runelite/cache/fs/StoreLoadTest.java
@@ -0,0 +1,109 @@
+/*
+ * Copyright (c) 2016, Adam
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Adam
+ * 4. Neither the name of the Adam nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Adam ''AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL Adam BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package net.runelite.cache.fs;
+
+import com.google.common.io.Files;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import net.runelite.cache.StoreLocation;
+import org.junit.Assert;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+
+public class StoreLoadTest
+{
+ @Rule
+ public TemporaryFolder folder = StoreLocation.getTemporaryFolder();
+
+ @Test
+ public void testLoad() throws IOException
+ {
+ Store store = new Store(StoreLocation.LOCATION);
+ store.load();
+ System.out.println(store);
+ }
+
+ @Test
+ public void testSave() throws IOException
+ {
+ Store store = new Store(StoreLocation.LOCATION);
+ store.load();
+
+ java.io.File testStoreFile = folder.newFolder();
+ for (java.io.File f : StoreLocation.LOCATION.listFiles())
+ Files.copy(f, new java.io.File(testStoreFile, f.getName()));
+
+ Store testStore = new Store(testStoreFile);
+ testStore.load();
+
+ Assert.assertTrue(store.equals(testStore));
+
+ testStore.save();
+ testStore.load();
+
+ Assert.assertTrue(store.equals(testStore));
+ }
+
+ //@Test
+ public void unpackStore() throws IOException
+ {
+ java.io.File base = StoreLocation.LOCATION;
+ try (Store store = new Store(base))
+ {
+ store.load();
+
+ for (Index i : store.getIndexes())
+ {
+ java.io.File ifile = new java.io.File(folder.newFolder(), "" + i.getId());
+ ifile.mkdir();
+
+ for (Archive a : i.getArchives())
+ {
+ java.io.File afile = new java.io.File(ifile, "" + a.getArchiveId());
+ afile.mkdir();
+
+ for (File f : a.getFiles())
+ {
+ java.io.File ffile = new java.io.File(afile, "" + f.getFileId());
+ try (FileOutputStream fout = new FileOutputStream(ffile))
+ {
+ if (f.getContents() != null)
+ {
+ fout.write(f.getContents());
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/cache/src/test/java/net/runelite/cache/fs/StoreTest.java b/cache/src/test/java/net/runelite/cache/fs/StoreTest.java
new file mode 100644
index 0000000000..03a4f6ed2a
--- /dev/null
+++ b/cache/src/test/java/net/runelite/cache/fs/StoreTest.java
@@ -0,0 +1,155 @@
+/*
+ * Copyright (c) 2016, Adam
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Adam
+ * 4. Neither the name of the Adam nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Adam ''AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL Adam BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package net.runelite.cache.fs;
+
+import java.io.IOException;
+import java.util.Random;
+import net.runelite.cache.StoreLocation;
+import org.junit.Assert;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+
+public class StoreTest
+{
+ @Rule
+ public TemporaryFolder folder = StoreLocation.getTemporaryFolder();
+
+ @Test
+ public void testOneFile() throws IOException
+ {
+ try (Store store = new Store(folder.getRoot()))
+ {
+ Index index = store.addIndex(0);
+ Archive archive = index.addArchive(0);
+ File file = archive.addFile(0);
+ file.setNameHash(7);
+ file.setContents("test".getBytes());
+
+ store.save();
+
+ try (Store store2 = new Store(folder.getRoot()))
+ {
+ store2.load();
+
+ Assert.assertEquals(store, store2);
+ }
+ }
+ }
+
+ private static final int NUMBER_OF_FILES = 1024;
+
+ @Test
+ public void testManyFiles() throws IOException
+ {
+ Random random = new Random(42L);
+
+ try (Store store = new Store(folder.getRoot()))
+ {
+ Index index = store.addIndex(0);
+ Archive archive = index.addArchive(0);
+ archive.setNameHash(random.nextInt());
+
+ for (int i = 0; i < NUMBER_OF_FILES; ++i)
+ {
+ File file = archive.addFile(i);
+ file.setNameHash(random.nextInt());
+ byte[] data = new byte[random.nextInt(1024)];
+ random.nextBytes(data);
+ file.setContents(data);
+ }
+
+ store.save();
+
+ try (Store store2 = new Store(folder.getRoot()))
+ {
+ store2.load();
+
+ Assert.assertEquals(store, store2);
+ }
+ }
+ }
+
+ @Test
+ public void testMultipleArchives() throws IOException
+ {
+ Random random = new Random(43L);
+
+ try (Store store = new Store(folder.getRoot()))
+ {
+ Index index = store.addIndex(0);
+ Index index2 = store.addIndex(1);
+
+ Archive archive = index.addArchive(0);
+ archive.setNameHash(random.nextInt());
+
+ Archive archive2 = index.addArchive(1);
+
+ Archive archive3 = index2.addArchive(0);
+
+ for (int i = 0; i < NUMBER_OF_FILES; ++i)
+ {
+ File file = archive.addFile(i);
+ file.setNameHash(random.nextInt());
+ byte[] data = new byte[random.nextInt(1024)];
+ random.nextBytes(data);
+ file.setContents(data);
+ }
+
+ for (int i = 0; i < NUMBER_OF_FILES; ++i)
+ {
+ File file = archive2.addFile(i);
+ file.setNameHash(random.nextInt());
+ byte[] data = new byte[random.nextInt(1024)];
+ random.nextBytes(data);
+ file.setContents(data);
+ }
+
+ for (int i = 0; i < NUMBER_OF_FILES; ++i)
+ {
+ File file = archive3.addFile(i);
+ file.setNameHash(random.nextInt());
+ byte[] data = new byte[random.nextInt(1024)];
+ random.nextBytes(data);
+ file.setContents(data);
+ }
+
+ store.save();
+
+ try (Store store2 = new Store(folder.getRoot()))
+ {
+ store2.load();
+
+ Assert.assertEquals(store, store2);
+ }
+ }
+ }
+}
diff --git a/cache/src/test/java/net/runelite/cache/loaders/SpriteLoaderTest.java b/cache/src/test/java/net/runelite/cache/loaders/SpriteLoaderTest.java
new file mode 100644
index 0000000000..b8180359c2
--- /dev/null
+++ b/cache/src/test/java/net/runelite/cache/loaders/SpriteLoaderTest.java
@@ -0,0 +1,105 @@
+/*
+ * Copyright (c) 2016, Adam
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Adam
+ * 4. Neither the name of the Adam nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Adam ''AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL Adam BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package net.runelite.cache.loaders;
+
+import java.awt.image.BufferedImage;
+import java.io.IOException;
+import java.util.List;
+import javax.imageio.ImageIO;
+import net.runelite.cache.IndexType;
+import net.runelite.cache.StoreLocation;
+import net.runelite.cache.definitions.SpriteDefinition;
+import net.runelite.cache.definitions.loaders.SpriteLoader;
+import net.runelite.cache.fs.Archive;
+import net.runelite.cache.fs.File;
+import net.runelite.cache.fs.Index;
+import net.runelite.cache.fs.Store;
+import net.runelite.cache.io.InputStream;
+import net.runelite.cache.renderable.RGBSprite;
+import org.junit.Assert;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class SpriteLoaderTest
+{
+ private static final Logger logger = LoggerFactory.getLogger(SpriteLoaderTest.class);
+
+ @Rule
+ public TemporaryFolder folder = StoreLocation.getTemporaryFolder();
+
+ @Test
+ public void extract() throws IOException
+ {
+ java.io.File base = StoreLocation.LOCATION,
+ outDir = folder.newFolder();
+
+ try (Store store = new Store(base))
+ {
+ store.load();
+
+ Index index = store.getIndex(IndexType.SPRITES);
+
+ for (Archive a : index.getArchives())
+ {
+ List files = a.getFiles();
+
+ Assert.assertEquals(1, files.size());
+
+ File file = files.get(0);
+ byte[] contents = file.getContents();
+
+ SpriteLoader loader = new SpriteLoader();
+ loader.load(new InputStream(contents));
+
+ SpriteDefinition[] defs = loader.getSprites();
+
+ for (int i = 0; i < defs.length; ++i)
+ {
+ RGBSprite sp = RGBSprite.fromSpriteDefinition(defs[i]);
+
+ // I don't know why this happens
+ if (sp.getSpriteHeight() <= 0 || sp.getSpriteWidth() <= 0)
+ continue;
+
+ BufferedImage image = sp.getBufferedImage();
+ java.io.File targ = new java.io.File(outDir, a.getArchiveId() + "-" + i + ".png");
+ targ.mkdirs();
+ ImageIO.write(image, "png", targ);
+ }
+ }
+ }
+
+ logger.info("Dumped to {}", outDir);
+ }
+}
diff --git a/cache/src/test/java/net/runelite/cache/loaders/TitleDumper.java b/cache/src/test/java/net/runelite/cache/loaders/TitleDumper.java
new file mode 100644
index 0000000000..3ca8ca00d5
--- /dev/null
+++ b/cache/src/test/java/net/runelite/cache/loaders/TitleDumper.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright (c) 2016, Adam
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Adam
+ * 4. Neither the name of the Adam nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Adam ''AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL Adam BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package net.runelite.cache.loaders;
+
+import java.io.IOException;
+import java.nio.file.Files;
+import net.runelite.cache.IndexType;
+import net.runelite.cache.StoreLocation;
+import net.runelite.cache.fs.Archive;
+import net.runelite.cache.fs.File;
+import net.runelite.cache.fs.Index;
+import net.runelite.cache.fs.Store;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class TitleDumper
+{
+ private static final Logger logger = LoggerFactory.getLogger(TitleDumper.class);
+
+ @Rule
+ public TemporaryFolder folder = StoreLocation.getTemporaryFolder();
+
+ @Test
+ public void extract() throws IOException
+ {
+ java.io.File base = StoreLocation.LOCATION,
+ outFile = folder.newFile();
+
+ try (Store store = new Store(base))
+ {
+ store.load();
+
+ Index index = store.getIndex(IndexType.BINARY);
+ Archive a = index.findArchiveByName("title.jpg");
+ File file = a.getFiles().get(0);
+
+ Files.write(outFile.toPath(), file.getContents());
+ }
+
+ logger.info("Dumped to {}", outFile);
+ }
+}
diff --git a/model-viewer/pom.xml b/model-viewer/pom.xml
index a52ce360d7..4e7c36d330 100644
--- a/model-viewer/pom.xml
+++ b/model-viewer/pom.xml
@@ -45,7 +45,7 @@
net.runelite
- deob
+ cache
1.1.0-SNAPSHOT
diff --git a/pom.xml b/pom.xml
index 3551ac75d6..fe8008d5b7 100644
--- a/pom.xml
+++ b/pom.xml
@@ -71,6 +71,7 @@
runelite-client
runelite-api
model-viewer
+ cache