[Scummvm-git-logs] scummvm-tools master -> c17dc85e103380eb74633070a6a10529b7b5ca78
Die4Ever
noreply at scummvm.org
Sat Oct 22 21:09:28 UTC 2022
This automated email contains information about 1 new commit which have been
pushed to the 'scummvm-tools' repo located at https://github.com/scummvm/scummvm-tools .
Summary:
c17dc85e10 DECOMPILER: compiler and Groovie 2 support
Commit: c17dc85e103380eb74633070a6a10529b7b5ca78
https://github.com/scummvm/scummvm-tools/commit/c17dc85e103380eb74633070a6a10529b7b5ca78
Author: Die4Ever (30947252+Die4Ever at users.noreply.github.com)
Date: 2022-10-22T17:09:25-04:00
Commit Message:
DECOMPILER: compiler and Groovie 2 support
New Reassembler base class for compiling assembly back into a binary file. Use like:
./decompile -e groovie -v v2 SCRIPT.GRV.gasm -b SUSCRIPT.GRV
Also support for decompiling/compiling Groovie 2 script files.
Changed paths:
A decompiler/reassembler.cpp
A decompiler/reassembler.h
.gitignore
Makefile.common
decompiler/decompiler.cpp
decompiler/groovie/disassembler.cpp
decompiler/groovie/disassembler.h
decompiler/groovie/engine.cpp
decompiler/groovie/engine.h
decompiler/test/module.mk
diff --git a/.gitignore b/.gitignore
index 69e8dfab..73fb9c4e 100644
--- a/.gitignore
+++ b/.gitignore
@@ -74,6 +74,7 @@ ipch/
*.vcxproj*
*.bat
*.tss
+.vscode
#Ignore Mac DS_Store files
.DS_Store
diff --git a/Makefile.common b/Makefile.common
index d0dfa532..27112436 100644
--- a/Makefile.common
+++ b/Makefile.common
@@ -395,6 +395,7 @@ decompile_OBJS := \
decompiler/control_flow.o \
decompiler/decompiler.o \
decompiler/disassembler.o \
+ decompiler/reassembler.o \
decompiler/graph.o \
decompiler/instruction.o \
decompiler/simple_disassembler.o \
diff --git a/decompiler/decompiler.cpp b/decompiler/decompiler.cpp
index 4feb7abf..e4d17dd0 100644
--- a/decompiler/decompiler.cpp
+++ b/decompiler/decompiler.cpp
@@ -22,6 +22,7 @@
#include "objectFactory.h"
#include "disassembler.h"
+#include "reassembler.h"
#include "engine.h"
#include "instruction.h"
@@ -63,7 +64,9 @@ int main(int argc, char** argv) {
("only-graph,G", "Stops after control flow graph has been generated. Implies -g.")
("show-unreachable,u", "Show the address and contents of unreachable groups in the script.")
("variant,v", po::value<std::string>()->default_value(""), "Tell the engine that the script is from a specific variant. To see a list of variants supported by a specific engine, use the -h option and the -e option together.")
- ("no-stack-effect,s", "Leave out the stack effect when printing raw instructions.");
+ ("no-stack-effect,s", "Leave out the stack effect when printing raw instructions.")
+ ("dump-binary,b", po::value<std::string>(), "Compile the assembly to a binary file.");
+ // TODO: option for only outputting labels for lines that receive jumps
po::options_description args("");
args.add(visible).add_options()
@@ -134,6 +137,17 @@ int main(int argc, char** argv) {
Disassembler *disassembler = engine->getDisassembler(insts);
disassembler->open(inputFile.c_str());
+ if (vm.count("dump-binary")) {
+ std::ofstream of;
+
+ of.open(vm["dump-binary"].as<std::string>().c_str(), of.binary);
+ dynamic_cast<Reassembler*>(disassembler)->dumpBinary(of);
+
+ delete disassembler;
+ delete engine;
+ return 0;
+ }
+
disassembler->disassemble();
if (vm.count("dump-disassembly")) {
std::streambuf *buf;
diff --git a/decompiler/groovie/disassembler.cpp b/decompiler/groovie/disassembler.cpp
index 74846d8c..5d86f978 100644
--- a/decompiler/groovie/disassembler.cpp
+++ b/decompiler/groovie/disassembler.cpp
@@ -23,6 +23,7 @@
#include "opcodes.h"
#include "common/util.h"
+#include "common/endian.h"
namespace Groovie {
@@ -86,8 +87,8 @@ public:
// GroovieDisassembler
-GroovieDisassembler::GroovieDisassembler(InstVec &insts, const GroovieOpcode *opcodes) :
- Disassembler(insts), _opcodes(opcodes) {
+GroovieDisassembler::GroovieDisassembler(InstVec &insts, const std::vector<GroovieOpcode> &opcodes) :
+ Reassembler(insts), _opcodes(opcodes), _firstBit(false) {
}
InstPtr GroovieDisassembler::readInstruction() {
@@ -110,7 +111,7 @@ InstPtr GroovieDisassembler::createInstruction(byte opcode) {
opcode &= 0x7F;
// Verify it's a valid opcode
- if (opcode > 0x59) // TODO: make it depend on the real number of opcodes
+ if (opcode >= _opcodes.size())
throw UnknownOpcodeException(_address, opcode);
// Create the new instruction
@@ -167,12 +168,12 @@ InstPtr GroovieDisassembler::createInstruction(byte opcode) {
void GroovieDisassembler::readParams(InstPtr inst, const char *typeString) {
while (*typeString) {
- inst->_params.push_back(readParameter(inst, *typeString));
+ inst->_params.push_back(readParameter(*typeString));
typeString++;
}
}
-ValuePtr GroovieDisassembler::readParameter(InstPtr inst, char type) {
+ValuePtr GroovieDisassembler::readParameter(char type) {
ValuePtr retval = NULL;
switch (type) {
case '1': // 8 bits
@@ -185,9 +186,9 @@ ValuePtr GroovieDisassembler::readParameter(InstPtr inst, char type) {
break;
case '3': // 8 or 16 bits
if (_firstBit)
- retval = readParameter(inst, '1');
+ retval = readParameter('1');
else
- retval = readParameter(inst, '2');
+ retval = readParameter('2');
break;
case '4': // 32 bits
retval = new IntValue(_f.readUint32LE(), false);
@@ -252,7 +253,7 @@ ValuePtr GroovieDisassembler::readParameterIndexed(bool allow7C, bool limitVal,
ValueList idxs;
idxs.push_back(new IntValue(data - 0x61, false));
- result = new ArrayValue("M", idxs);
+ result = new ArrayValue("m", idxs);// TODO: can probably make writeParameterIndex smart enough to not need the lowercase m here
} else {
// Immediate value
result = new IntValue(data - 0x30, true);
@@ -301,14 +302,16 @@ ValuePtr GroovieDisassembler::readParameterVideoName() {
// Indexing a bidimensional array
idxs.push_back(readParameterIndexed(false, false, false));
idxs.push_back(readParameterIndexed(false, false, false));
+ values.push_back(new ArrayValue("M", idxs));
} else if (data == 0x23) {
// Indexing an unidimensional array
data = _f.readByte();
_address++;
idxs.push_back(new IntValue(data - 0x61, false));
+ values.push_back(new ArrayValue("m", idxs));// TODO: can probably make writeParameterVideoName smart enough to not need the lowercase m here
}
// TODO BinaryOpValue: M[...] + 0x30
- values.push_back(new ArrayValue("M", idxs));
+ //values.push_back(new ArrayValue("M", idxs));
} else {
// To lowercase?
if (data >= 0x41 && data <= 0x5A) {
@@ -354,4 +357,179 @@ void GroovieDisassembler::doDisassemble() throw (UnknownOpcodeException) {
_insts.push_back(createInstruction(0));
}
+GroovieOpcode GroovieDisassembler::getInstruction(const std::string &name) throw(std::exception) {
+ for(auto &i : _opcodes) {
+ if(name == i.name) {
+ return i;
+ }
+ }
+ throw std::runtime_error("Unable to find instruction: " + name);
+}
+
+void GroovieDisassembler::doAssembly(const std::string &label, std::string &instruction, const std::vector<std::string> &args, const std::string &comment) throw(std::exception) {
+ // find the matching instruction name
+ GroovieOpcode inst = getInstruction(instruction);
+
+ // build list of labels, parse arguments, and write bytes to _binary
+ std::vector<byte> bytes;
+ size_t jumpAddrStart;// where the address is stored so it can be overwritten when we have the full list of labels
+ std::string jumpToLabel;
+
+ _firstBit = false;
+ jumpAddrStart = writeParams(bytes, inst.params, args, jumpToLabel);
+ // use writeParams to guess _firstBit, then we write the opcode at the end
+ bytes.insert(bytes.begin(), inst.opcode | (_firstBit<<7));
+ jumpAddrStart++; // increment since we pushed a byte to the front
+
+ addInstruction(bytes, inst.type, jumpAddrStart, 2, label, jumpToLabel);
+}
+
+size_t GroovieDisassembler::writeParams(std::vector<byte> &bytes, const char *typeString, const std::vector<std::string> &args, std::string &jumpToLabel) {
+ size_t jumpAddrStart = 0;
+ for(int i=0; typeString[i]; i++) {
+ writeParameter(typeString[i], bytes, args[i], jumpAddrStart, jumpToLabel);
+ }
+ return jumpAddrStart;
+}
+
+void GroovieDisassembler::writeParameter(char type, std::vector<byte> &bytes, const std::string &arg, size_t &jumpAddrStart, std::string &jumpToLabel) {
+ int i;
+ uint16 i16;
+ uint32 u32;
+
+ switch (type) {
+ case '1': // 8 bits
+ i = std::stoi(arg);
+ bytes.push_back(i);
+ break;
+ case '2': // 16 bits
+ i16 = std::stoi(arg);
+ i16 = TO_LE_16(i16);
+ bytes.push_back(i16);
+ bytes.push_back(i16 >> 8);
+ break;
+ case '3': // 8 or 16 bits
+ i = std::stoi(arg);
+ if(i <= 0xFF || _firstBit) {
+ _firstBit = true;
+ bytes.push_back(i);
+ } else {
+ _firstBit = false;
+ i16 = TO_LE_16(i);
+ bytes.push_back(i16);
+ bytes.push_back(i16 >> 8);
+ }
+ break;
+ case '4': // 32 bits
+ u32 = std::stoul(arg);
+ u32 = TO_LE_32(u32);
+ bytes.push_back(u32);
+ bytes.push_back(u32 >> 8);
+ bytes.push_back(u32 >> 16);
+ bytes.push_back(u32 >> 24);
+ break;
+ case '@': // Address
+ jumpAddrStart = bytes.size();
+ // if arg is in 0xF3DE format, convert to 0000f3de
+ if(arg.find("0x") == 0)
+ jumpToLabel = (boost::format("%08x") % std::stoul(arg, 0, 16)).str();
+ else
+ jumpToLabel = arg;
+ bytes.push_back(0);
+ bytes.push_back(0);
+ break;
+ case 'A': // Array
+ // substring to remove the [ and ]
+ writeParameterArray(bytes, arg.substr(1, arg.length()-2));
+ break;
+ case 'S': // Script name
+ // ignore the quotes around it
+ for(size_t j=1; j<arg.length()-1; j++)
+ bytes.push_back(arg[j]);
+ bytes.push_back(0);
+ break;
+ case 'V': // Video name
+ // substring to remove the [ and ]
+ writeParameterVideoName(bytes, arg.substr(1, arg.length()-2));
+ break;
+ case 'C': // Indexed value
+ writeParameterIndexed(false, true, true, bytes, arg);
+ break;
+ default:
+ std::cout << "writeParameter UNKNOWN param type: " << type << "\n";
+ throw std::runtime_error(std::string() + "writeParameter UNKNOWN param type: " + type);
+ }
+}
+
+void GroovieDisassembler::splitArrayString(const std::string &arg, std::string &first, std::string &second) {
+ size_t e = getEndArgument(arg, 2);
+ first = arg.substr(2, e - 2);
+ second = arg.substr(e + 2);
+ second.pop_back();
+}
+
+void GroovieDisassembler::writeParameterVideoName(std::vector<byte> &bytes, const std::string &arg) {
+ size_t s = 0, e = 0;
+ while(e < arg.length()) {
+ e = getEndArgument(arg, s);
+ std::string a = arg.substr(s, e - s);
+ switch(a[0]) {
+ case '"':
+ for(size_t i=1; i < a.length()-1; i++) {
+ bytes.push_back(a[i]);
+ }
+ break;
+ case 'm':
+ a = a.substr(2);
+ a.pop_back();
+ bytes.push_back(0x23);
+ bytes.push_back(std::stoul(a) + 0x61);
+ break;
+ case 'M':
+ bytes.push_back(0x7C);
+ std::string first;
+ std::string second;
+ splitArrayString(a, first, second);
+ writeParameterIndexed(false, false, false, bytes, first);
+ writeParameterIndexed(false, false, false, bytes, second);
+ break;
+ }
+ s = e + 2;
+ }
+
+ bytes.push_back(0);
+}
+
+void GroovieDisassembler::writeParameterIndexed(bool allow7C, bool limitVal, bool limitVar, std::vector<byte> &bytes, const std::string &arg) {
+ if (allow7C && arg[0] == 'M') {
+ bytes.push_back(0x7C);
+ std::string first;
+ std::string second;
+ splitArrayString(arg, first, second);
+ writeParameterIndexed(false, false, false, bytes, first);
+ writeParameterIndexed(false, true, true, bytes, second);
+ } else if (arg[0] == 'm') {
+ bytes.push_back(0x23);
+ std::string a = arg.substr(2);
+ a.pop_back();
+ uint8 data = std::stoul(a);
+ bytes.push_back(data + 0x61);
+ } else {
+ // Immediate value
+ uint8 data = std::stoul(arg);
+ bytes.push_back(data + 0x30);
+ }
+}
+
+void GroovieDisassembler::writeParameterArray(std::vector<byte> &bytes, const std::string &arg) {
+ size_t s = 0, e = 0;
+ while(e < arg.length()) {
+ e = getEndArgument(arg, s);
+ std::string a = arg.substr(s, e - s);
+ writeParameterIndexed(true, true, true, bytes, a);
+ s = e + 2;
+ }
+ // terminate the array by using the first bit
+ bytes[bytes.size() - 1] |= 0x80;
+}
} // End of namespace Groovie
diff --git a/decompiler/groovie/disassembler.h b/decompiler/groovie/disassembler.h
index d97d2526..2c71ab7c 100644
--- a/decompiler/groovie/disassembler.h
+++ b/decompiler/groovie/disassembler.h
@@ -22,7 +22,7 @@
#ifndef GROOVIE_DISASSEMBLER_H
#define GROOVIE_DISASSEMBLER_H
-#include "decompiler/disassembler.h"
+#include "decompiler/reassembler.h"
namespace Groovie {
@@ -31,25 +31,33 @@ struct GroovieOpcode;
/**
* Disassembler for Groovie scripts.
*/
-class GroovieDisassembler : public Disassembler {
+class GroovieDisassembler : public Reassembler {
public:
- GroovieDisassembler(InstVec &insts, const GroovieOpcode *opcodes);
+ GroovieDisassembler(InstVec &insts, const std::vector<GroovieOpcode> &opcodes);
protected:
void doDisassemble() throw(UnknownOpcodeException);
-
+ GroovieOpcode getInstruction(const std::string &name) throw(std::exception);
+ void doAssembly(const std::string &label, std::string &instruction, const std::vector<std::string> &args, const std::string &comment) throw(std::exception);
InstPtr readInstruction();
InstPtr createInstruction(byte opcode);
void readParams(InstPtr inst, const char *typeString);
- ValuePtr readParameter(InstPtr inst, char type);
+ ValuePtr readParameter(char type);
+
+ size_t writeParams(std::vector<byte> &bytes, const char *typeString, const std::vector<std::string> &args, std::string &jumpToLabel);
+ void writeParameter(char type, std::vector<byte> &bytes, const std::string &arg, size_t &jumpAddrStart, std::string &jumpToLabel);
+ void writeParameterVideoName(std::vector<byte> &bytes, const std::string &arg);
+ void writeParameterIndexed(bool allow7C, bool limitVal, bool limitVar, std::vector<byte> &bytes, const std::string &arg);
+ void writeParameterArray(std::vector<byte> &bytes, const std::string &arg);
+ void splitArrayString(const std::string &arg, std::string &first, std::string &second);
ValuePtr readParameterIndexed(bool allow7C, bool limitVal, bool limitVar);
ValuePtr readParameterArray();
ValuePtr readParameterScriptName();
ValuePtr readParameterVideoName();
- const GroovieOpcode *_opcodes;
+ const std::vector<GroovieOpcode> _opcodes;
uint32 _address;
uint32 _maxTargetAddress;
bool _firstBit;
diff --git a/decompiler/groovie/engine.cpp b/decompiler/groovie/engine.cpp
index 803c51f9..e6a6caea 100644
--- a/decompiler/groovie/engine.cpp
+++ b/decompiler/groovie/engine.cpp
@@ -21,6 +21,7 @@
#include "engine.h"
#include "disassembler.h"
+#include <vector>
namespace Groovie {
@@ -41,7 +42,7 @@ CodeGenerator *GroovieEngine::getCodeGenerator(std::ostream &output) {
return NULL;
}
-const GroovieOpcode *GroovieEngine::getOpcodes() const {
+const std::vector<GroovieOpcode> &GroovieEngine::getOpcodes() const {
if (!_variant.compare("t7g"))
return opcodesT7G;
else if (!_variant.compare("v2"))
@@ -51,27 +52,27 @@ const GroovieOpcode *GroovieEngine::getOpcodes() const {
return opcodesT7G;
}
-const GroovieOpcode GroovieEngine::opcodesT7G[] = {
+const std::vector<GroovieOpcode> GroovieEngine::opcodesT7G = {
{0x00, kKernelCallInst, "Nop", ""},
{0x01, kKernelCallInst, "Nop", ""},
{0x02, kKernelCallInst, "PlaySong", "2"},
- {0x03, kKernelCallInst, "BitFlag 9 ON", ""},
- {0x04, kKernelCallInst, "Palette Fade Out", ""},
- {0x05, kKernelCallInst, "BitFlag 8 ON", ""},
- {0x06, kKernelCallInst, "BitFlag 6 ON", ""},
- {0x07, kKernelCallInst, "BitFlag 7 ON", ""},
+ {0x03, kKernelCallInst, "BitFlag-9-ON", ""},
+ {0x04, kKernelCallInst, "Palette-Fade-Out", ""},
+ {0x05, kKernelCallInst, "BitFlag-8-ON", ""},
+ {0x06, kKernelCallInst, "BitFlag-6-ON", ""},
+ {0x07, kKernelCallInst, "BitFlag-7-ON", ""},
{0x08, kKernelCallInst, "SetBackgroundSong", "2"},
{0x09, kKernelCallInst, "VideoFromRef", "2"},
- {0x0A, kKernelCallInst, "BitFlag 5 ON", ""},
- {0x0B, kKernelCallInst, "Input Loop Start", ""},
- {0x0C, kCondJumpInst, "Keyboard Action", "1@"},
- {0x0D, kCondJumpInst, "Hotspot Rect", "2222 at 1"},
- {0x0E, kCondJumpInst, "Hotspot Left", "@"},
- {0x0F, kCondJumpInst, "Hotspot Right", "@"},
- {0x10, kCondJumpInst, "Hotspot Center", "@"},
- {0x11, kCondJumpInst, "Hotspot Center", "@"},
- {0x12, kCondJumpInst, "Hotspot Current", "@"},
- {0x13, kJumpInst, "Input Loop End", ""},
+ {0x0A, kKernelCallInst, "BitFlag-5-ON", ""},
+ {0x0B, kKernelCallInst, "Input-Loop-Start", ""},
+ {0x0C, kCondJumpInst, "Keyboard-Action", "1@"},
+ {0x0D, kCondJumpInst, "Hotspot-Rect", "2222 at 1"},
+ {0x0E, kCondJumpInst, "Hotspot-Left", "@"},
+ {0x0F, kCondJumpInst, "Hotspot-Right", "@"},
+ {0x10, kCondJumpInst, "Hotspot-Center", "@"},
+ {0x11, kCondJumpInst, "Hotspot-Center", "@"},
+ {0x12, kCondJumpInst, "Hotspot-Current", "@"},
+ {0x13, kJumpInst, "Input-Loop-End", ""},
{0x14, kKernelCallInst, "Random", "31"},
{0x15, kJumpInst, "Jmp", "@"},
{0x16, kKernelCallInst, "LoadString", "3A"},
@@ -79,63 +80,63 @@ const GroovieOpcode GroovieEngine::opcodesT7G[] = {
{0x18, kCallInst, "Call", "@"},
{0x19, kKernelCallInst, "Sleep", "2"},
{0x1A, kCondJumpInst, "JmpStrCmp-NE", "3A@"},
- {0x1B, kKernelCallInst, "XOR Obfuscate", "3A"},
- {0x1C, kKernelCallInst, "VDX Transition", "2"},
+ {0x1B, kKernelCallInst, "XOR-Obfuscate", "3A"},
+ {0x1C, kKernelCallInst, "VDX-Transition", "2"},
{0x1D, kKernelCallInst, "Swap", "22"},
{0x1E, kKernelCallInst, "Nop8", "1"},
{0x1F, kUnaryOpPreInst, "Inc", "3"},
{0x20, kUnaryOpPreInst, "Dec", "3"},
{0x21, kCondJumpInst, "JmpStrCmpVar-NE", "2A@"},
- {0x22, kKernelCallInst, "Copy BG to FG", ""},
+ {0x22, kKernelCallInst, "Copy-BG-to-FG", ""},
{0x23, kCondJumpInst, "JmpStrCmp-EQ", "3A@"},
{0x24, kKernelCallInst, "Mov", "32"},
{0x25, kBinaryOpInst, "Add", "32"},
{0x26, kKernelCallInst, "VideoFromString1", "V"},
{0x27, kKernelCallInst, "VideoFromString2", "V"},
{0x28, kKernelCallInst, "Nop16", "2"},
- {0x29, kKernelCallInst, "Stop MIDI", ""},
- {0x2A, kKernelCallInst, "End Script", ""},
+ {0x29, kKernelCallInst, "Stop-MIDI", ""},
+ {0x2A, kKernelCallInst, "End-Script", ""},
{0x2B, kKernelCallInst, "Nop", ""},
- {0x2C, kCondJumpInst, "Set Hotspot Top", "@1"},
- {0x2D, kCondJumpInst, "Set Hotspot Bottom", "@1"},
- {0x2E, kKernelCallInst, "Load Game", "3"},
- {0x2F, kKernelCallInst, "Save Game", "3"},
- {0x30, kCondJumpInst, "Hotspot Bottom 4", "@"},
- {0x31, kKernelCallInst, "MIDI Volume", "22"},
+ {0x2C, kCondJumpInst, "Set-Hotspot-Top", "@1"},
+ {0x2D, kCondJumpInst, "Set-Hotspot-Bottom", "@1"},
+ {0x2E, kKernelCallInst, "Load-Game", "3"},
+ {0x2F, kKernelCallInst, "Save-Game", "3"},
+ {0x30, kCondJumpInst, "Hotspot-Bottom-4", "@"},
+ {0x31, kKernelCallInst, "MIDI-Volume", "22"},
{0x32, kCondJumpInst, "JNE", "32@"},
- {0x33, kKernelCallInst, "Load String Var", "3A"},
+ {0x33, kKernelCallInst, "Load-String-Var", "3A"},
{0x34, kCondJumpInst, "JmpCharGreat", "3A@"},
- {0x35, kKernelCallInst, "BitFlag 7 OFF", ""},
+ {0x35, kKernelCallInst, "BitFlag-7-OFF", ""},
{0x36, kCondJumpInst, "JmpCharLess", "3A@"},
- {0x37, kKernelCallInst, "Copy Rect to BG", "2222"},
- {0x38, kKernelCallInst, "Restore Stack Pointer", ""},
- {0x39, kKernelCallInst, "Obscure Swap", "CCCC"},
- {0x3A, kKernelCallInst, "Print String", "A"},
- {0x3B, kCondJumpInst, "Hotspot Slot", "12222 at 1"},
- {0x3C, kKernelCallInst, "Check Valid Saves", ""},
- {0x3D, kKernelCallInst, "Reset Variables", ""},
+ {0x37, kKernelCallInst, "Copy-Rect-to-BG", "2222"},
+ {0x38, kKernelCallInst, "Restore-Stack-Pointer", ""},
+ {0x39, kKernelCallInst, "Obscure-Swap", "CCCC"},
+ {0x3A, kKernelCallInst, "Print-String", "A"},
+ {0x3B, kCondJumpInst, "Hotspot-Slot", "12222 at 1"},
+ {0x3C, kKernelCallInst, "Check-Valid-Saves", ""},
+ {0x3D, kKernelCallInst, "Reset-Variables", ""},
{0x3E, kBinaryOpInst, "Mod", "31"},
- {0x3F, kKernelCallInst, "Load Script", "S"},
- {0x40, kKernelCallInst, "Set Video Origin", "22"},
+ {0x3F, kKernelCallInst, "Load-Script", "S"},
+ {0x40, kKernelCallInst, "Set-Video-Origin", "22"},
{0x41, kBinaryOpInst, "Sub", "32"},
- {0x42, kKernelCallInst, "Cell Move", "1"},
- {0x43, kKernelCallInst, "Return Script", "1"},
- {0x44, kCondJumpInst, "Set Hotspot Right", "@"},
- {0x45, kCondJumpInst, "Set Hotspot Left", "@"},
+ {0x42, kKernelCallInst, "Cell-Move", "1"},
+ {0x43, kKernelCallInst, "Return-Script", "1"},
+ {0x44, kCondJumpInst, "Set-Hotspot-Right", "@"},
+ {0x45, kCondJumpInst, "Set-Hotspot-Left", "@"},
{0x46, kKernelCallInst, "Nop", ""},
{0x47, kKernelCallInst, "Nop", ""},
{0x48, kKernelCallInst, "Nop8", "1"},
{0x49, kKernelCallInst, "Nop", ""},
{0x4A, kKernelCallInst, "Nop16", "2"},
{0x4B, kKernelCallInst, "Nop8", "1"},
- {0x4C, kKernelCallInst, "Get CD", ""},
- {0x4D, kKernelCallInst, "Play CD", "1"},
- {0x4E, kKernelCallInst, "Music Delay", "2"},
+ {0x4C, kKernelCallInst, "Get-CD", ""},
+ {0x4D, kKernelCallInst, "Play-CD", "1"},
+ {0x4E, kKernelCallInst, "Music-Delay", "2"},
{0x4F, kKernelCallInst, "Nop16", "2"},
{0x50, kKernelCallInst, "Nop16", "2"},
{0x51, kKernelCallInst, "Nop16", "2"},
{0x52, kKernelCallInst, "UNKNOWN52", "1"},
- {0x53, kCondJumpInst, "Hotspot OutRect", "2222@"},
+ {0x53, kCondJumpInst, "Hotspot-OutRect", "2222@"},
{0x54, kKernelCallInst, "Nop", ""},
{0x55, kKernelCallInst, "Nop16", "2"},
{0x56, kKernelCallInst, "Stub56", "411"},
@@ -144,105 +145,98 @@ const GroovieOpcode GroovieEngine::opcodesT7G[] = {
{0x59, kKernelCallInst, "UNKNOWN59", "31"},
};
-// TODO: fill with the known v2 opcodes
-const GroovieOpcode GroovieEngine::opcodesV2[] = {
- {0x00, kKernelCallInst, "", ""},
- {0x59, kKernelCallInst, "", ""},
-};
-
-/*
-const GroovieOpcode GroovieEngine::opcodesV2[] = {
- o_invalid, // 0x00
- o_nop,
- o2_playsong,
- o_nop,
- o_nop, // 0x04
- o_nop,
- o_nop,
- o_nop,
- o2_setbackgroundsong, // 0x08
- o2_videofromref,
- o_bf5on,
- o_inputloopstart,
- o_keyboardaction, // 0x0C
- o_hotspot_rect,
- o_hotspot_left,
- o_hotspot_right,
- o_hotspot_center, // 0x10
- o_hotspot_center,
- o_hotspot_current,
- o_inputloopend,
- o_random, // 0x14
- o_jmp,
- o_loadstring,
- o_ret,
- o_call, // 0x18
- o_sleep,
- o_strcmpnejmp,
- o_xor_obfuscate,
- o2_vdxtransition, // 0x1C
- o_swap,
- o_invalid,
- o_inc,
- o_dec, // 0x20
- o_strcmpnejmp_var,
- o_copybgtofg,
- o_strcmpeqjmp,
- o_mov, // 0x24
- o_add,
- o_videofromstring1,
- o_videofromstring2,
- o_invalid, // 0x28
- o_nop,
- o_endscript,
- o_invalid,
- o_sethotspottop, // 0x2C
- o_sethotspotbottom,
- o_loadgame,
- o_savegame,
- o_hotspotbottom_4, // 0x30
- o_midivolume,
- o_jne,
- o_loadstringvar,
- o_chargreatjmp, // 0x34
- o_bf7off,
- o_charlessjmp,
- o_copyrecttobg,
- o_restorestkpnt, // 0x38
- o_obscureswap,
- o_printstring,
- o_hotspot_slot,
- o_checkvalidsaves, // 0x3C
- o_resetvars,
- o_mod,
- o_loadscript,
- o_setvideoorigin, // 0x40
- o_sub,
- o_cellmove,
- o_returnscript,
- o_sethotspotright, // 0x44
- o_sethotspotleft,
- o_invalid,
- o_invalid,
- o_invalid, // 0x48
- o_invalid,
- o_nop16,
- o_invalid,
- o_invalid, // 0x4C
- o_invalid,
- o_invalid,
- o2_copyscreentobg,
- o2_copybgtoscreen, // 0x50
- o2_setvideoskip,
- o2_stub52,
- o_hotspot_outrect,
- o_invalid, // 0x54
- o2_setscriptend,
- o_stub56,
- o_invalid,
- o_invalid, // 0x58
- o_stub59
+const std::vector<GroovieOpcode> GroovieEngine::opcodesV2 = {
+ {0x00, kKernelCallInst, "Invalid", ""},
+ {0x01, kKernelCallInst, "Nop", ""},
+ {0x02, kKernelCallInst, "PlaySong", "4"},
+ {0x03, kKernelCallInst, "Nop", ""},
+ {0x04, kKernelCallInst, "Nop", ""},
+ {0x05, kKernelCallInst, "Nop", ""},
+ {0x06, kKernelCallInst, "Nop", ""},
+ {0x07, kKernelCallInst, "Nop", ""},
+ {0x08, kKernelCallInst, "SetBackgroundSong", "4"},
+ {0x09, kKernelCallInst, "VideoFromRef", "4"},
+ {0x0A, kKernelCallInst, "BitFlag-0-ON", ""},
+ {0x0B, kKernelCallInst, "Input-Loop-Start", ""},
+ {0x0C, kCondJumpInst, "Keyboard-Action", "1@"},
+ {0x0D, kCondJumpInst, "Hotspot-Rect", "2222 at 1"},
+ {0x0E, kCondJumpInst, "Hotspot-Left", "@"},
+ {0x0F, kCondJumpInst, "Hotspot-Right", "@"},
+ {0x10, kCondJumpInst, "Hotspot-Center", "@"},
+ {0x11, kCondJumpInst, "Hotspot-Center", "@"},
+ {0x12, kCondJumpInst, "Hotspot-Current", "@"},
+ {0x13, kJumpInst, "Input-Loop-End", ""},
+ {0x14, kKernelCallInst, "Random", "31"},
+ {0x15, kJumpInst, "Jmp", "@"},
+ {0x16, kKernelCallInst, "LoadString", "3A"},
+ {0x17, kReturnInst, "Return", "1"},
+ {0x18, kCallInst, "Call", "@"},
+ {0x19, kKernelCallInst, "Sleep", "2"},
+ {0x1A, kCondJumpInst, "JmpStrCmp-NE", "3A@"},
+ {0x1B, kKernelCallInst, "XOR-Obfuscate", "3A"},
+ {0x1C, kKernelCallInst, "VDX-Transition", "4"},
+ {0x1D, kKernelCallInst, "Swap", "32"},
+ {0x1E, kKernelCallInst, "Invalid", "1"},
+ {0x1F, kUnaryOpPreInst, "Inc", "3"},
+ {0x20, kUnaryOpPreInst, "Dec", "3"},
+ {0x21, kCondJumpInst, "JmpStrCmpVar-NE", "2A@"},
+ {0x22, kKernelCallInst, "Copy-BG-to-FG", ""},
+ {0x23, kCondJumpInst, "JmpStrCmp-EQ", "3A@"},
+ {0x24, kKernelCallInst, "Mov", "32"},
+ {0x25, kBinaryOpInst, "Add", "32"},
+ {0x26, kKernelCallInst, "VideoFromString1", "V"},
+ {0x27, kKernelCallInst, "VideoFromString2", "V"},
+ {0x28, kKernelCallInst, "Invalid", "2"},
+ {0x29, kKernelCallInst, "Nop", ""},
+ {0x2A, kKernelCallInst, "End-Script", ""},
+ {0x2B, kKernelCallInst, "Invalid", ""},
+ {0x2C, kCondJumpInst, "Set-Hotspot-Top", "@1"},
+ {0x2D, kCondJumpInst, "Set-Hotspot-Bottom", "@1"},
+ {0x2E, kKernelCallInst, "Load-Game", "3"},
+ {0x2F, kKernelCallInst, "Save-Game", "3"},
+ {0x30, kCondJumpInst, "Hotspot-Bottom-4", "@"},
+ {0x31, kKernelCallInst, "MIDI-Control", "22"},
+ {0x32, kCondJumpInst, "JNE", "32@"},
+ {0x33, kKernelCallInst, "Load-String-Var", "3A"},
+ {0x34, kCondJumpInst, "JmpCharGreat", "3A@"},
+ {0x35, kKernelCallInst, "BitFlag-7-OFF", ""},
+ {0x36, kCondJumpInst, "JmpCharLess", "3A@"},
+ {0x37, kKernelCallInst, "Copy-Rect-to-BG", "2222"},
+ {0x38, kKernelCallInst, "Restore-Stack-Pointer", ""},
+ {0x39, kKernelCallInst, "Obscure-Swap", "CCCC"},
+ {0x3A, kKernelCallInst, "Print-String", "22111V"},
+ {0x3B, kCondJumpInst, "Hotspot-Slot", "12222 at 1"},
+ {0x3C, kKernelCallInst, "Check-Valid-Saves", ""},
+ {0x3D, kKernelCallInst, "Reset-Variables", ""},
+ {0x3E, kBinaryOpInst, "Mod", "31"},
+ {0x3F, kKernelCallInst, "Load-Script", "S"},
+ {0x40, kKernelCallInst, "Set-Video-Origin", "22"},
+ {0x41, kBinaryOpInst, "Sub", "32"},
+ {0x42, kKernelCallInst, "Cell-Move", "1"},
+ {0x43, kKernelCallInst, "Return-Script", "1"},
+ {0x44, kCondJumpInst, "Set-Hotspot-Right", "@"},
+ {0x45, kCondJumpInst, "Set-Hotspot-Left", "@"},
+ {0x46, kKernelCallInst, "Invalid", ""},
+ {0x47, kKernelCallInst, "Invalid", ""},
+ {0x48, kKernelCallInst, "Invalid", "1"},
+ {0x49, kKernelCallInst, "Invalid", ""},
+ {0x4A, kKernelCallInst, "Nop16", "2"},
+ {0x4B, kKernelCallInst, "Invalid", "1"},
+ {0x4C, kKernelCallInst, "Invalid", ""},
+ {0x4D, kKernelCallInst, "Invalid", "1"},
+ {0x4E, kKernelCallInst, "Invalid", "2"},
+ {0x4F, kKernelCallInst, "Save-Screen", "2"},
+ {0x50, kKernelCallInst, "Restore-Screen", "2"},
+ {0x51, kCondJumpInst, "Set-Video-Skip", "@"},
+ {0x52, kKernelCallInst, "Copy-FG-to-BG", "1"},
+ {0x53, kCondJumpInst, "Hotspot-OutRect", "2222@"},
+ {0x54, kKernelCallInst, "Invalid", ""},
+ {0x55, kKernelCallInst, "Set-Script-End", "2"},// should this be an @?
+ {0x56, kKernelCallInst, "Play-Sound", "411"},
+ {0x57, kKernelCallInst, "Invalid", "4"},
+ {0x58, kKernelCallInst, "Wipe-Mask-From-String", "V"},
+ {0x59, kKernelCallInst, "Check-Sounds-Overlays", "31"},
+ {0x5A, kKernelCallInst, "Preview-Loadgame", "1"},
};
-*/
} // End of namespace Groovie
diff --git a/decompiler/groovie/engine.h b/decompiler/groovie/engine.h
index 50f0a3da..d1e3f517 100644
--- a/decompiler/groovie/engine.h
+++ b/decompiler/groovie/engine.h
@@ -24,6 +24,7 @@
#include "decompiler/engine.h"
#include "opcodes.h"
+#include <vector>
namespace Groovie {
@@ -39,10 +40,10 @@ public:
bool supportsCodeGen() const { return false; }
private:
- const GroovieOpcode *getOpcodes() const;
+ const std::vector<GroovieOpcode> &getOpcodes() const;
- static const GroovieOpcode opcodesT7G[];
- static const GroovieOpcode opcodesV2[];
+ static const std::vector<GroovieOpcode> opcodesT7G;
+ static const std::vector<GroovieOpcode> opcodesV2;
};
} // End of namespace Groovie
diff --git a/decompiler/reassembler.cpp b/decompiler/reassembler.cpp
new file mode 100644
index 00000000..486dc99c
--- /dev/null
+++ b/decompiler/reassembler.cpp
@@ -0,0 +1,173 @@
+/* ScummVM Tools
+ *
+ * ScummVM Tools is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "reassembler.h"
+#include "common/endian.h"
+
+Reassembler::Reassembler(InstVec &insts) : Disassembler(insts) { }
+
+void Reassembler::assemble() {
+ // Prepare to read the input script
+ _f.seek(0, SEEK_SET);
+ _binary.clear();
+
+ while(!_f.eos()) {
+ try {
+ std::string line = readLine();
+ auto comment = splitString(line, line.find(";"), 1, true);// remove comments
+ if(line.empty())
+ continue;
+
+ auto label = splitString(line, line.find(": "), 2);
+ auto instruction = splitString(line, line.find(" "), 1);
+ if(instruction.empty()) {
+ // if it didn't find a space, that means there are no arguments
+ instruction = line;
+ line = "";
+ }
+ std::cout << label << ": " << instruction;
+
+ // parse arguments
+ std::vector<std::string> args;
+ size_t s = 0, e = 0;
+ for(s = 0; s < line.length(); s = e + 2) {
+ if(s > 0) std::cout << ", ";
+ else std::cout << " ";
+ e = getEndArgument(line, s);
+ size_t len = e - s;
+ args.push_back(line.substr(s, len));
+ std::cout << args.back();
+ }
+ std::cout << "; " << comment << "\n";
+ // TODO: maybe parse the arguments into a ValueList of the Value subclasses
+
+ doAssembly(label, instruction, args, comment);
+ } catch(Common::FileException &e) {
+ break;
+ }
+ }
+
+ // 2nd pass in order to set jump addresses after reading all labels
+ for(const auto &j : _jumps) {
+ if(j._label.empty())
+ continue;
+
+ size_t addr = _labels.at(j._label);
+ uint16 u16;
+ uint32 u32;
+
+ switch(j.len) {
+ case 1:
+ _binary[j.start] = addr;
+ break;
+ case 2:
+ u16 = TO_LE_16(addr);
+ _binary[j.start] = u16;
+ _binary[j.start + 1] = u16 >> 8;
+ break;
+ case 4:
+ u32 = TO_LE_32(addr);
+ _binary[j.start] = u32;
+ _binary[j.start + 1] = u32 >> 8;
+ _binary[j.start + 2] = u32 >> 16;
+ _binary[j.start + 3] = u32 >> 24;
+ break;
+ }
+ }
+}
+
+void Reassembler::doDumpBinary(std::ostream &output) {
+ output.write((char*)_binary.data(), _binary.size());
+}
+
+void Reassembler::dumpBinary(std::ostream &output) {
+ assemble();
+ doDumpBinary(output);
+}
+
+std::string Reassembler::readLine() {
+ std::string line;
+ while(!_f.eos()) {
+ try {
+ char c = _f.readByte();
+ if(c == '\n')
+ break;
+ line += c;
+ } catch(Common::FileException &e) {
+ break;
+ }
+ }
+ return line;
+}
+
+std::string Reassembler::splitString(std::string &from, size_t pos, size_t separator_len, bool reverse) {
+ if(pos == std::string::npos)
+ return std::string();
+
+ if(reverse) {
+ // return the right side, from is set to the left side
+ std::string ret = from.substr(pos + separator_len);
+ from = from.substr(0, pos);
+ return ret;
+ }
+ // else we return the left side, and from is set to the right side
+ std::string ret = from.substr(0, pos);
+ from = from.substr(pos + separator_len);
+ return ret;
+}
+
+void Reassembler::addInstruction(const std::vector<byte> &bytes, int type, size_t jumpAddrStart, size_t jumpAddrLen, const std::string &label, const std::string &jumpToLabel) {
+ if(!label.empty()) {
+ _labels.emplace(label, _binary.size());
+ }
+
+ if(type == kCallInst || type == kCondJumpInst || type == kJumpInst) {
+ Jump j;
+ j._label = jumpToLabel;
+ j.start = jumpAddrStart + _binary.size();
+ j.len = jumpAddrLen;
+ _jumps.push_back(j);
+ }
+
+ _binary.insert(_binary.end(), bytes.begin(), bytes.end());
+}
+
+size_t Reassembler::getEndArgument(const std::string &s, size_t start) {
+ int brackets = 0;
+ for(size_t i = start; i < s.length(); i++) {
+ switch(s[i]) {
+ case '[':
+ brackets++;
+ break;
+ case ']':
+ brackets--;
+ if(brackets < 0)
+ return i;
+ break;
+
+ case ',':
+ if(brackets == 0)
+ return i;
+ break;
+ }
+ }
+ return s.length();
+}
diff --git a/decompiler/reassembler.h b/decompiler/reassembler.h
new file mode 100644
index 00000000..7161496a
--- /dev/null
+++ b/decompiler/reassembler.h
@@ -0,0 +1,58 @@
+/* ScummVM Tools
+ *
+ * ScummVM Tools is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef DEC_REASSEMBLER_H
+#define DEC_REASSEMBLER_H
+
+#include <iostream>
+#include <vector>
+#include <unordered_map>
+
+#include "disassembler.h"
+#include "instruction.h"
+#include "common/file.h"
+#include "unknown_opcode.h"
+#include "objectFactory.h"
+
+class Reassembler : public Disassembler {
+ struct Jump {
+ std::string _label;
+ size_t start, len;
+ };
+private:
+ std::vector<byte> _binary;
+ std::unordered_map<std::string, size_t> _labels;
+ std::vector<Jump> _jumps;
+
+protected:
+ virtual void doAssembly(const std::string &label, std::string &instruction, const std::vector<std::string> &args, const std::string &comment) throw(std::exception) = 0; // push_back to _binary
+ virtual void doDumpBinary(std::ostream &output);
+ std::string readLine();
+ std::string splitString(std::string &from, size_t pos, size_t separator_len=0, bool reverse=false);
+ void addInstruction(const std::vector<byte> &bytes, int type, size_t jumpAddrStart=0, size_t jumpAddrLen=0, const std::string &label="", const std::string &jumpToLabel="");// automatically pair the label with the instruction address
+public:
+ Reassembler(InstVec &insts);
+ void assemble();
+ void dumpBinary(std::ostream &output);
+ size_t getEndArgument(const std::string &s, size_t start);
+};
+
+#endif
diff --git a/decompiler/test/module.mk b/decompiler/test/module.mk
index c9cb01ba..a81fc823 100644
--- a/decompiler/test/module.mk
+++ b/decompiler/test/module.mk
@@ -11,6 +11,7 @@ TEST_LIBS := \
decompiler/codegen.o \
decompiler/control_flow.o \
decompiler/disassembler.o \
+ decompiler/reassembler.o \
decompiler/instruction.o \
decompiler/simple_disassembler.o \
decompiler/value.o \
More information about the Scummvm-git-logs
mailing list