diff --git a/cache/pom.xml b/cache/pom.xml
index a7dcc973ff..6d590fc0bc 100644
--- a/cache/pom.xml
+++ b/cache/pom.xml
@@ -36,6 +36,10 @@
cache
Cache
+
+ 4.6
+
+
net.runelite
@@ -78,6 +82,11 @@
netty-all
4.1.0.Final
+
+ org.antlr
+ antlr4-runtime
+ ${antlr4.version}
+
junit
@@ -92,7 +101,6 @@
org.apache.maven.plugins
maven-surefire-plugin
- 2.16
true
-Xmx2048m
@@ -115,6 +123,19 @@
+
+ org.antlr
+ antlr4-maven-plugin
+ ${antlr4.version}
+
+
+ process-resources
+
+ antlr4
+
+
+
+
diff --git a/cache/src/main/antlr4/net/runelite/cache/script/assembler/rs2asm.g4 b/cache/src/main/antlr4/net/runelite/cache/script/assembler/rs2asm.g4
new file mode 100644
index 0000000000..fdd89213f6
--- /dev/null
+++ b/cache/src/main/antlr4/net/runelite/cache/script/assembler/rs2asm.g4
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2017, 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 THE COPYRIGHT OWNER OR CONTRIBUTORS 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.
+ */
+grammar rs2asm;
+
+prog: (line NEWLINE)+ ;
+line: instruction | label ;
+instruction: instruction_name instruction_operand ;
+label: 'LABEL' INT ':' ;
+
+instruction_name: name_string | name_opcode ;
+name_string: INSTRUCTION ;
+name_opcode: INT ;
+
+instruction_operand: operand_int | operand_qstring | operand_label | ;
+operand_int: INT ;
+operand_qstring: QSTRING ;
+operand_label: 'LABEL' INT ;
+
+NEWLINE: '\n'+ ;
+INT: '-'? [0-9]+ ;
+QSTRING: '"' (~('"' | '\\' | '\r' | '\n') | '\\' ('"' | '\\'))* '"' ;
+INSTRUCTION: [a-z0-9_]+ ;
+
+WS: (' ' | '\t')+ -> channel(HIDDEN) ;
\ No newline at end of file
diff --git a/cache/src/main/java/net/runelite/cache/script/Instructions.java b/cache/src/main/java/net/runelite/cache/script/Instructions.java
index 1bfb845e40..83acf358f6 100644
--- a/cache/src/main/java/net/runelite/cache/script/Instructions.java
+++ b/cache/src/main/java/net/runelite/cache/script/Instructions.java
@@ -30,6 +30,7 @@ import java.util.Map;
public class Instructions
{
private static final Map instructions = new HashMap<>();
+ private static final Map instructionsByName = new HashMap<>();
public static void init()
{
@@ -443,6 +444,12 @@ public class Instructions
assert instructions.containsKey(opcode) == false;
instructions.put(opcode, i);
+
+ if (name != null)
+ {
+ assert instructionsByName.containsKey(name) == false;
+ instructionsByName.put(name, i);
+ }
}
private static void add(int opcode, int ipops, int ipushes)
@@ -464,4 +471,9 @@ public class Instructions
{
return instructions.get(opcode);
}
+
+ public static Instruction find(String name)
+ {
+ return instructionsByName.get(name);
+ }
}
diff --git a/cache/src/main/java/net/runelite/cache/script/assembler/Assembler.java b/cache/src/main/java/net/runelite/cache/script/assembler/Assembler.java
new file mode 100644
index 0000000000..3cd20f43ba
--- /dev/null
+++ b/cache/src/main/java/net/runelite/cache/script/assembler/Assembler.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright (c) 2017, 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 THE COPYRIGHT OWNER OR CONTRIBUTORS 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.script.assembler;
+
+import java.io.IOException;
+import java.io.InputStream;
+import net.runelite.cache.definitions.ScriptDefinition;
+import net.runelite.cache.script.Instructions;
+import net.runelite.cache.script.assembler.rs2asmParser.ProgContext;
+import org.antlr.v4.runtime.ANTLRInputStream;
+import org.antlr.v4.runtime.CommonTokenStream;
+import org.antlr.v4.runtime.tree.ParseTreeWalker;
+
+public class Assembler
+{
+ public ScriptDefinition assemble(InputStream in) throws IOException
+ {
+ Instructions.init();
+
+ // Get our lexer
+ rs2asmLexer lexer = new rs2asmLexer(new ANTLRInputStream(in));
+
+ LexerErrorListener errorListener = new LexerErrorListener();
+ lexer.addErrorListener(errorListener);
+
+ // Get a list of matched tokens
+ CommonTokenStream tokens = new CommonTokenStream(lexer);
+
+ // Pass the tokens to the parser
+ rs2asmParser parser = new rs2asmParser(tokens);
+
+ // Specify our entry point
+ ProgContext progContext = parser.prog();
+
+ if (errorListener.getErrors() > 0)
+ {
+ throw new RuntimeException("syntax error");
+ }
+
+ // Walk it and attach our listener
+ ParseTreeWalker walker = new ParseTreeWalker();
+
+ // walk through first and resolve labels
+ LabelVisitor labelVisitor = new LabelVisitor();
+ walker.walk(labelVisitor, progContext);
+
+ ScriptWriter listener = new ScriptWriter(labelVisitor);
+ walker.walk(listener, progContext);
+
+ return listener.buildScript();
+ }
+}
diff --git a/cache/src/main/java/net/runelite/cache/script/assembler/LabelVisitor.java b/cache/src/main/java/net/runelite/cache/script/assembler/LabelVisitor.java
new file mode 100644
index 0000000000..b78cfd6523
--- /dev/null
+++ b/cache/src/main/java/net/runelite/cache/script/assembler/LabelVisitor.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright (c) 2017, 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 THE COPYRIGHT OWNER OR CONTRIBUTORS 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.script.assembler;
+
+import java.util.HashMap;
+import java.util.Map;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class LabelVisitor extends rs2asmBaseListener
+{
+ private static final Logger logger = LoggerFactory.getLogger(LabelVisitor.class);
+
+ private int pos;
+ private final Map map = new HashMap<>();
+
+ @Override
+ public void exitInstruction(rs2asmParser.InstructionContext ctx)
+ {
+ ++pos;
+ }
+
+ @Override
+ public void enterLabel(rs2asmParser.LabelContext ctx)
+ {
+ String text = ctx.getText();
+ text = text.substring(0, text.length() - 1); // remove trailing :
+
+ logger.debug("Label {} is on instruction {}", text, pos);
+
+ map.put(text, pos);
+ }
+
+ public Integer getInstructionForLabel(String label)
+ {
+ return map.get(label);
+ }
+}
diff --git a/cache/src/main/java/net/runelite/cache/script/assembler/LexerErrorListener.java b/cache/src/main/java/net/runelite/cache/script/assembler/LexerErrorListener.java
new file mode 100644
index 0000000000..bd3463a946
--- /dev/null
+++ b/cache/src/main/java/net/runelite/cache/script/assembler/LexerErrorListener.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2017, 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 THE COPYRIGHT OWNER OR CONTRIBUTORS 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.script.assembler;
+
+import org.antlr.v4.runtime.BaseErrorListener;
+import org.antlr.v4.runtime.RecognitionException;
+import org.antlr.v4.runtime.Recognizer;
+
+public class LexerErrorListener extends BaseErrorListener
+{
+ private int errors;
+
+ @Override
+ public void syntaxError(Recognizer, ?> recognizer, Object offendingSymbol, int line, int charPositionInLine, String msg, RecognitionException e)
+ {
+ ++errors;
+ }
+
+ public int getErrors()
+ {
+ return errors;
+ }
+
+}
diff --git a/cache/src/main/java/net/runelite/cache/script/assembler/ScriptWriter.java b/cache/src/main/java/net/runelite/cache/script/assembler/ScriptWriter.java
new file mode 100644
index 0000000000..119afe155a
--- /dev/null
+++ b/cache/src/main/java/net/runelite/cache/script/assembler/ScriptWriter.java
@@ -0,0 +1,132 @@
+/*
+ * Copyright (c) 2017, 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 THE COPYRIGHT OWNER OR CONTRIBUTORS 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.script.assembler;
+
+import java.util.ArrayList;
+import java.util.List;
+import net.runelite.cache.definitions.ScriptDefinition;
+import net.runelite.cache.script.Instruction;
+import net.runelite.cache.script.Instructions;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class ScriptWriter extends rs2asmBaseListener
+{
+ private static final Logger logger = LoggerFactory.getLogger(ScriptWriter.class);
+
+ private final LabelVisitor labelVisitor;
+
+ private int pos;
+ private List opcodes = new ArrayList<>();
+ private List iops = new ArrayList<>();
+ private List sops = new ArrayList<>();
+
+ public ScriptWriter(LabelVisitor labelVisitor)
+ {
+ this.labelVisitor = labelVisitor;
+ }
+
+ @Override
+ public void exitInstruction(rs2asmParser.InstructionContext ctx)
+ {
+ ++pos;
+ }
+
+ @Override
+ public void enterName_string(rs2asmParser.Name_stringContext ctx)
+ {
+ String text = ctx.getText();
+ Instruction i = Instructions.find(text);
+ if (i == null)
+ {
+ logger.warn("Unknown instruction {}", text);
+ throw new RuntimeException("Unknown instruction " + text);
+ }
+
+ int opcode = i.getOpcode();
+ addOpcode(opcode);
+ }
+
+ @Override
+ public void enterName_opcode(rs2asmParser.Name_opcodeContext ctx)
+ {
+ String text = ctx.getText();
+ int opcode = Integer.parseInt(text);
+ addOpcode(opcode);
+ }
+
+ private void addOpcode(int opcode)
+ {
+ assert opcodes.size() == pos;
+ assert iops.size() == pos;
+ assert sops.size() == pos;
+
+ opcodes.add(opcode);
+ iops.add(null);
+ sops.add(null);
+ }
+
+ @Override
+ public void enterOperand_int(rs2asmParser.Operand_intContext ctx)
+ {
+ String text = ctx.getText();
+ int value = Integer.parseInt(text);
+ iops.set(pos, value);
+ }
+
+ @Override
+ public void enterOperand_qstring(rs2asmParser.Operand_qstringContext ctx)
+ {
+ String text = ctx.getText();
+ text = text.substring(1, text.length() - 1);
+ sops.set(pos, text);
+ }
+
+ @Override
+ public void enterOperand_label(rs2asmParser.Operand_labelContext ctx)
+ {
+ String text = ctx.getText();
+ Integer instruction = labelVisitor.getInstructionForLabel(text);
+ if (instruction == null)
+ {
+ throw new RuntimeException("reference to unknown label " + text);
+ }
+
+ int target = instruction - pos - 1; // -1 to go to the instruction prior
+ iops.set(pos, target);
+ }
+
+ public ScriptDefinition buildScript()
+ {
+ ScriptDefinition script = new ScriptDefinition();
+ script.setInstructions(opcodes.stream().mapToInt(Integer::valueOf).toArray());
+ script.setIntOperands(iops.stream()
+ .map(i -> i == null ? 0 : i)
+ .mapToInt(Integer::valueOf)
+ .toArray());
+ script.setStringOperands(sops.toArray(new String[0]));
+ return script;
+ }
+}
diff --git a/cache/src/main/java/net/runelite/cache/script/disassembler/Disassembler.java b/cache/src/main/java/net/runelite/cache/script/disassembler/Disassembler.java
new file mode 100644
index 0000000000..fd8910a247
--- /dev/null
+++ b/cache/src/main/java/net/runelite/cache/script/disassembler/Disassembler.java
@@ -0,0 +1,147 @@
+/*
+ * Copyright (c) 2017, 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 THE COPYRIGHT OWNER OR CONTRIBUTORS 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.script.disassembler;
+
+import java.io.IOException;
+import net.runelite.cache.definitions.ScriptDefinition;
+import net.runelite.cache.script.Instruction;
+import net.runelite.cache.script.Instructions;
+import net.runelite.cache.script.Opcodes;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class Disassembler
+{
+ private static final Logger logger = LoggerFactory.getLogger(Disassembler.class);
+
+ private boolean isJump(int opcode)
+ {
+ switch (opcode)
+ {
+ case Opcodes.JUMP:
+ case Opcodes.IF_ICMPEQ:
+ case Opcodes.IF_ICMPGE:
+ case Opcodes.IF_ICMPGT:
+ case Opcodes.IF_ICMPLE:
+ case Opcodes.IF_ICMPLT:
+ case Opcodes.IF_ICMPNE:
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ private boolean[] needLabel(ScriptDefinition script)
+ {
+ int[] instructions = script.getInstructions();
+ int[] iop = script.getIntOperands();
+ boolean[] jumped = new boolean[instructions.length];
+
+ for (int i = 0; i < instructions.length; ++i)
+ {
+ int opcode = instructions[i];
+
+ if (!isJump(opcode))
+ {
+ continue;
+ }
+
+ // + 1 because the jumps go to the instructions prior to the
+ // one you really want, because the pc is incremented on the
+ // next loop
+ int to = i + iop[i] + 1;
+ assert to >= 0 && to < instructions.length;
+
+ jumped[to] = true;
+ }
+
+ return jumped;
+ }
+
+ public String disassemble(ScriptDefinition script) throws IOException
+ {
+ StringBuilder writer = new StringBuilder();
+
+ int[] instructions = script.getInstructions();
+ int[] iops = script.getIntOperands();
+ String[] sops = script.getStringOperands();
+
+ assert iops.length == instructions.length;
+ assert sops.length == instructions.length;
+
+ boolean[] jumps = needLabel(script);
+
+ for (int i = 0; i < instructions.length; ++i)
+ {
+ int opcode = instructions[i];
+ int iop = iops[i];
+ String sop = sops[i];
+
+ Instruction ins = Instructions.find(opcode);
+ if (ins == null)
+ {
+ logger.warn("Unknown instruction {} in script {}", opcode, script.getId());
+ }
+
+ if (jumps[i])
+ {
+ // something jumps here
+ writer.append("LABEL").append(i).append(":\n");
+ }
+
+ String name;
+ if (ins != null && ins.getName() != null)
+ {
+ name = ins.getName();
+ }
+ else
+ {
+ name = String.format("%03d", opcode);
+ }
+
+ writer.append(String.format(" %-22s", name));
+
+ if (iop != 0 || opcode == Opcodes.LOAD_INT)
+ {
+ if (isJump(opcode))
+ {
+ writer.append(" LABEL").append(i + iop + 1);
+ }
+ else
+ {
+ writer.append(" ").append(iop);
+ }
+ }
+
+ if (sop != null)
+ {
+ writer.append(" \"").append(sop).append("\"");
+ }
+ writer.append("\n");
+ }
+
+ return writer.toString();
+ }
+}
diff --git a/cache/src/test/java/net/runelite/cache/script/assembler/AssemblerTest.java b/cache/src/test/java/net/runelite/cache/script/assembler/AssemblerTest.java
new file mode 100644
index 0000000000..8623974bdc
--- /dev/null
+++ b/cache/src/test/java/net/runelite/cache/script/assembler/AssemblerTest.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright (c) 2017, 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 THE COPYRIGHT OWNER OR CONTRIBUTORS 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.script.assembler;
+
+import java.io.InputStream;
+import net.runelite.cache.definitions.ScriptDefinition;
+import net.runelite.cache.script.disassembler.Disassembler;
+import org.apache.commons.compress.utils.IOUtils;
+import org.junit.Assert;
+import org.junit.Test;
+import org.junit.Before;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.slf4j.impl.SimpleLogger;
+
+public class AssemblerTest
+{
+ private static final Logger logger = LoggerFactory.getLogger(AssemblerTest.class);
+
+ @Before
+ public void before()
+ {
+ System.setProperty(SimpleLogger.DEFAULT_LOG_LEVEL_KEY, "DEBUG");
+ }
+
+ @Test
+ public void testAssemble() throws Exception
+ {
+ InputStream in = AssemblerTest.class.getResourceAsStream("395.rs2asm");
+ Assert.assertNotNull(in);
+
+ Assembler assembler = new Assembler();
+ ScriptDefinition script = assembler.assemble(in);
+
+ // compare with disassembler
+ Disassembler disassembler = new Disassembler();
+ String out = disassembler.disassemble(script);
+
+ in = AssemblerTest.class.getResourceAsStream("395.rs2asm");
+ Assert.assertNotNull(in);
+
+ String original = new String(IOUtils.toByteArray(in));
+
+ logger.debug(original);
+ logger.debug("-----------------------");
+ logger.debug(out);
+
+ Assert.assertEquals(original, out);
+ }
+
+}
diff --git a/cache/src/test/java/net/runelite/cache/ScriptDumperTest.java b/cache/src/test/java/net/runelite/cache/script/disassembler/DisassemblerTest.java
similarity index 51%
rename from cache/src/test/java/net/runelite/cache/ScriptDumperTest.java
rename to cache/src/test/java/net/runelite/cache/script/disassembler/DisassemblerTest.java
index a829b0c5c9..692025c265 100644
--- a/cache/src/test/java/net/runelite/cache/ScriptDumperTest.java
+++ b/cache/src/test/java/net/runelite/cache/script/disassembler/DisassemblerTest.java
@@ -22,29 +22,28 @@
* (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;
+package net.runelite.cache.script.disassembler;
-import java.io.BufferedWriter;
-import java.io.FileWriter;
+import com.google.common.io.Files;
import java.io.IOException;
+import net.runelite.cache.IndexType;
+import net.runelite.cache.StoreLocation;
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 net.runelite.cache.script.Instruction;
import net.runelite.cache.script.Instructions;
-import net.runelite.cache.script.Opcodes;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-public class ScriptDumperTest
+public class DisassemblerTest
{
- private static final Logger logger = LoggerFactory.getLogger(ScriptDumperTest.class);
+ private static final Logger logger = LoggerFactory.getLogger(DisassemblerTest.class);
@Rule
public TemporaryFolder folder = StoreLocation.getTemporaryFolder();
@@ -74,7 +73,11 @@ public class ScriptDumperTest
ScriptDefinition script = loader.load(file.getFileId(), contents);
java.io.File outFile = new java.io.File(outDir, archive.getArchiveId() + ".rs2asm");
- writeScript(outFile, script);
+
+ Disassembler disassembler = new Disassembler();
+ String out = disassembler.disassemble(script);
+
+ Files.write(out.getBytes(), outFile);
++count;
}
@@ -82,112 +85,4 @@ public class ScriptDumperTest
logger.info("Dumped {} scripts to {}", count, outDir);
}
-
- private boolean isJump(int opcode)
- {
- switch (opcode)
- {
- case Opcodes.JUMP:
- case Opcodes.IF_ICMPEQ:
- case Opcodes.IF_ICMPGE:
- case Opcodes.IF_ICMPGT:
- case Opcodes.IF_ICMPLE:
- case Opcodes.IF_ICMPLT:
- case Opcodes.IF_ICMPNE:
- return true;
- default:
- return false;
- }
- }
-
- private boolean[] needLabel(ScriptDefinition script)
- {
- int[] instructions = script.getInstructions();
- int[] iop = script.getIntOperands();
- boolean[] jumped = new boolean[instructions.length];
-
- for (int i = 0; i < instructions.length; ++i)
- {
- int opcode = instructions[i];
-
- if (!isJump(opcode))
- {
- continue;
- }
-
- // + 1 because the jumps go to the instructions prior to the
- // one you really want, because the pc is incremented on the
- // next loop
- int to = i + iop[i] + 1;
- assert to >= 0 && to < instructions.length;
-
- jumped[to] = true;
- }
-
- return jumped;
- }
-
- private void writeScript(java.io.File file, ScriptDefinition script) throws IOException
- {
- try (BufferedWriter writer = new BufferedWriter(new FileWriter(file)))
- {
- int[] instructions = script.getInstructions();
- int[] iops = script.getIntOperands();
- String[] sops = script.getStringOperands();
-
- assert iops.length == instructions.length;
- assert sops.length == instructions.length;
-
- boolean[] jumps = needLabel(script);
-
- for (int i = 0; i < instructions.length; ++i)
- {
- int opcode = instructions[i];
- int iop = iops[i];
- String sop = sops[i];
-
- Instruction ins = Instructions.find(opcode);
- if (ins == null)
- {
- logger.warn("Unknown instruction {} in script {}", opcode, script.getId());
- }
-
- String name;
- if (ins != null && ins.getName() != null)
- {
- name = ins.getName();
- }
- else
- {
- name = String.format("%03d", opcode);
- }
-
- if (jumps[i])
- {
- // something jumps here
- writer.write("LABEL" + i + ":\n");
- }
-
- writer.write(String.format(" %-22s", name));
-
- if (iop != 0 || opcode == Opcodes.LOAD_INT)
- {
- if (isJump(opcode))
- {
- writer.write(" LABEL" + (i + iop + 1));
- }
- else
- {
- writer.write(" " + iop);
- }
- }
-
- if (sop != null)
- {
- writer.write(" \"" + sop + "\"");
- }
- writer.write("\n");
- }
- }
- }
}
diff --git a/cache/src/test/resources/net/runelite/cache/script/assembler/395.rs2asm b/cache/src/test/resources/net/runelite/cache/script/assembler/395.rs2asm
new file mode 100644
index 0000000000..c6f8b3b88a
--- /dev/null
+++ b/cache/src/test/resources/net/runelite/cache/script/assembler/395.rs2asm
@@ -0,0 +1,99 @@
+ 033
+ get_boostedskilllevels
+ int_to_string
+ 1112
+ 033
+ get_realskilllevels
+ 034 3
+ 033 3
+ int_to_string
+ 1112 1
+ 033
+ get_skillexperiences
+ 034 4
+ load_string ","
+ 036 1
+ 035
+ load_string " XP:"
+ 037 2
+ 036 2
+ 033 4
+ 035 1
+ 040 46
+ 036 3
+ load_int 0
+ 034 5
+ 025 4181
+ load_int 0
+ if_icmpeq LABEL29
+ jump LABEL60
+LABEL29:
+ 033 3
+ load_int 99
+ if_icmplt LABEL33
+ jump LABEL59
+LABEL33:
+ load_int 105
+ load_int 105
+ load_int 256
+ 033 3
+ load_int 1
+ iadd
+ 3408
+ 034 5
+ 035 2
+ load_string "|Next level at:|Remaining XP:"
+ concat_string
+ 036 2
+ 035 3
+ load_string "|"
+ 033 5
+ 035 1
+ 040 46
+ load_string "|"
+ 033 5
+ 033 4
+ isub
+ 035 1
+ 040 46
+ 037 4
+ concat_string
+ 036 3
+LABEL59:
+ jump LABEL78
+LABEL60:
+ 035 2
+ load_string "|Next level at:"
+ concat_string
+ 036 2
+ 035 3
+ load_string "|"
+ load_int 105
+ load_int 105
+ load_int 256
+ 033 3
+ load_int 1
+ iadd
+ 3408
+ 035 1
+ 040 46
+ 037 2
+ concat_string
+ 036 3
+LABEL78:
+ load_int 992
+ load_int -2147483645
+ load_int -1
+ 033 2
+ 035 2
+ 035 3
+ load_int 495
+ load_int 25
+ load_int 5
+ idiv
+ load_string "IiIssfi"
+ 033 1
+ 2412
+ load_int 0
+ 043 2
+ return