cache: add assembler and split scriptdumper test into disassembler/disassemblertest

This commit is contained in:
Adam
2017-03-05 16:28:39 -05:00
parent 6d23945efa
commit 633a184db9
11 changed files with 722 additions and 117 deletions

View File

@@ -0,0 +1,46 @@
/*
* Copyright (c) 2017, Adam <Adam@sigterm.info>
* 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) ;

View File

@@ -30,6 +30,7 @@ import java.util.Map;
public class Instructions
{
private static final Map<Integer, Instruction> instructions = new HashMap<>();
private static final Map<String, Instruction> 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);
}
}

View File

@@ -0,0 +1,74 @@
/*
* Copyright (c) 2017, Adam <Adam@sigterm.info>
* 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();
}
}

View File

@@ -0,0 +1,60 @@
/*
* Copyright (c) 2017, Adam <Adam@sigterm.info>
* 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<String, Integer> 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);
}
}

View File

@@ -0,0 +1,46 @@
/*
* Copyright (c) 2017, Adam <Adam@sigterm.info>
* 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;
}
}

View File

@@ -0,0 +1,132 @@
/*
* Copyright (c) 2017, Adam <Adam@sigterm.info>
* 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<Integer> opcodes = new ArrayList<>();
private List<Integer> iops = new ArrayList<>();
private List<String> 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;
}
}

View File

@@ -0,0 +1,147 @@
/*
* Copyright (c) 2017, Adam <Adam@sigterm.info>
* 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();
}
}

View File

@@ -0,0 +1,73 @@
/*
* Copyright (c) 2017, Adam <Adam@sigterm.info>
* 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);
}
}

View File

@@ -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");
}
}
}
}

View File

@@ -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