overhaul codebase
This commit is contained in:
parent
5d1e8ba4b3
commit
fd99d06b3d
37
T6Constants.js
Normal file
37
T6Constants.js
Normal file
@ -0,0 +1,37 @@
|
||||
function Constants(ctx, method) {
|
||||
this.ctx = ctx;
|
||||
this.method = method;
|
||||
|
||||
this.objs = [];
|
||||
}
|
||||
|
||||
Constants.prototype.read = function() {
|
||||
var size = this.ctx.reader.readMachine();
|
||||
|
||||
for (var i = 0; i < size; i++) {
|
||||
//console.log("reading constant at " + this.ctx.reader.index);
|
||||
var id = this.ctx.reader.readUByte();
|
||||
var type = consts.byId[id];
|
||||
|
||||
if (!type) {
|
||||
console.log("illegal constant type " + id);
|
||||
return false;
|
||||
}
|
||||
|
||||
var inst = new type();
|
||||
inst.undump(this.ctx.reader, this.ctx);
|
||||
this.objs.push(inst);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
Constants.prototype.write = function() {
|
||||
this.ctx.writer.writeMachine(this.objs.length);
|
||||
this.objs.forEach((obj) => {
|
||||
this.ctx.writer.writeUByte(obj.id);
|
||||
obj.dump(this.ctx.writer, this.ctx);
|
||||
});
|
||||
}
|
||||
|
||||
module.exports = Constants;
|
50
T6Debug.js
Normal file
50
T6Debug.js
Normal file
@ -0,0 +1,50 @@
|
||||
function Debug(ctx, method) {
|
||||
this.ctx = ctx;
|
||||
this.method = method;
|
||||
|
||||
this.type = 0;
|
||||
this.data = {};
|
||||
}
|
||||
|
||||
Debug.prototype.read = function() {
|
||||
this.type = this.ctx.reader.readInteger();
|
||||
|
||||
if (this.type == 1) {
|
||||
this.data["unknown"] = this.ctx.reader.readInteger();
|
||||
} else if (this.type == 0) {
|
||||
|
||||
} else if (this.type == 40) { //implemented in t6
|
||||
//TODO:
|
||||
} else if (this.type == 0x48) {
|
||||
/*
|
||||
hks::BytecodeWriter::dumpInt(this, 0x48);
|
||||
hks::BytecodeWriter::dumpInt(v6, v5->m_debug->line_defined);
|
||||
hks::BytecodeWriter::dumpInt(v6, v5->m_debug->last_line_defined);
|
||||
if ( v4 )
|
||||
v8 = 0i64;
|
||||
else
|
||||
v8 = v5->m_debug->source;
|
||||
hks::BytecodeWriter::dumpString(v6, v8);
|
||||
hks::BytecodeWriter::dumpString(v6, v5->m_debug->name);
|
||||
hks::BytecodeWriter::dumpVector<hksInstruction>(v6, 0i64, 0i64, 0);
|
||||
hks::BytecodeWriter::dumpInt(v6, 0);
|
||||
hks::BytecodeWriter::dumpInt(v6, 0);
|
||||
*/
|
||||
}
|
||||
}
|
||||
|
||||
Debug.prototype.write = function() {
|
||||
this.ctx.writer.writeInteger(this.type);
|
||||
|
||||
if (this.type == 1) {
|
||||
this.ctx.writer.writeInteger(this.data["unknown"]);
|
||||
} else if (this.type == 0) {
|
||||
return;
|
||||
} else if (this.type == 40) { //implemented in t6 - but seemingly unused
|
||||
//TODO:
|
||||
} else if (this.type == 0x48) {
|
||||
//TODO:
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = Debug;
|
83
T6Header.js
Normal file
83
T6Header.js
Normal file
@ -0,0 +1,83 @@
|
||||
function Header(ctx) {
|
||||
this.magic = undefined;
|
||||
this.version = 0;
|
||||
//this.nonCompliant = true;
|
||||
this.isLE = true;
|
||||
this.intSize = 4;
|
||||
this.instructionSize = 4;
|
||||
this.machineSize = 4;
|
||||
this.numberSize = 4;
|
||||
this.platformFlags = 0;
|
||||
this.numOfTypes = 13;
|
||||
this.sharingMode = 0; // sharing mode (0 - none, 1 - shared, 2 - secure) ? 1 : 0
|
||||
this.ctx = ctx;
|
||||
}
|
||||
|
||||
Header.prototype.read = function() {
|
||||
var buffer = this.ctx.reader.readBytes(false, 14);
|
||||
this.magic = buffer.readUInt32LE();
|
||||
this.version = buffer.readUInt8(4);
|
||||
this.platformFlags = buffer.readUInt8(12); // extensions etc
|
||||
|
||||
this.intSize = buffer.readUInt8(7); // sizeOfInt
|
||||
this.machineSize = buffer.readUInt8(8); // sizeOfSize
|
||||
this.instructionSize = buffer.readUInt8(9); // sizeOfLuaN
|
||||
this.numberSize = buffer.readUInt8(10); // sizeOfNumber
|
||||
|
||||
var idk = buffer.readUInt8(5); //??
|
||||
this.isLE = buffer.readUInt8(6) ? true : false;
|
||||
this.sharingMode = buffer.readUInt8(13) ? true : false;
|
||||
|
||||
this.numOfTypes = this.ctx.reader.readInteger();
|
||||
if (this.numOfTypes != 13) {
|
||||
console.log("There should only be 13 types");
|
||||
return false;
|
||||
}
|
||||
|
||||
for (var i in this.ctx.types.list) {
|
||||
var type = this.ctx.types.list[i];
|
||||
var id = this.ctx.reader.readInteger();
|
||||
if (id != type.id)
|
||||
{
|
||||
console.log(fmt("illegal type index %i expected: %s (%i)", id, type.id, type.name));
|
||||
return false;
|
||||
}
|
||||
var str = this.ctx.reader.readCString();
|
||||
if (str != type.tname)
|
||||
{
|
||||
console.log(fmt("illegal type name %s expected: %s", str, type.tname));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
Header.prototype.write = function(ctx) {
|
||||
this.ctx.writer.writeUByte(0x1B);
|
||||
this.ctx.writer.writeUByte(0x4C); //L
|
||||
this.ctx.writer.writeUByte(0x75); //U
|
||||
this.ctx.writer.writeUByte(0x61); //A
|
||||
|
||||
this.ctx.writer.writeUByte(this.version);
|
||||
this.ctx.writer.writeUByte(13); // ???
|
||||
this.ctx.writer.writeUByte(this.isLE ? 1 : 0);
|
||||
|
||||
this.ctx.writer.writeUByte(this.intSize);
|
||||
this.ctx.writer.writeUByte(this.machineSize);
|
||||
this.ctx.writer.writeUByte(this.instructionSize);
|
||||
this.ctx.writer.writeUByte(this.numberSize);
|
||||
|
||||
this.ctx.writer.writeUByte(0); //TODO
|
||||
this.ctx.writer.writeUByte(this.platformFlags);
|
||||
this.ctx.writer.writeUByte(this.sharingMode ? 1 : 0);
|
||||
|
||||
this.ctx.writer.writeInteger(this.numOfTypes);
|
||||
|
||||
this.ctx.types.list.forEach((type) => {
|
||||
this.ctx.writer.writeInteger(type.id);
|
||||
this.ctx.writer.writeCString(type.tname);
|
||||
});
|
||||
}
|
||||
|
||||
module.exports = Header;
|
105
T6Method.js
Normal file
105
T6Method.js
Normal file
@ -0,0 +1,105 @@
|
||||
const decoder = require("./instructions/instructionDecoder.js");
|
||||
const ops = require("./instructions/instructions.js");
|
||||
const fmt = require("util").format;
|
||||
|
||||
const Constants = require("./T6Constants.js");
|
||||
const Debug = require("./T6Debug.js");
|
||||
|
||||
function Method(ctx) {
|
||||
this.ctx = ctx;
|
||||
this.debug = new Debug(ctx, this);
|
||||
this.constants = new Constants(ctx, this);
|
||||
this.upvals = 0;
|
||||
this.params = 0;
|
||||
this.flags = 0;
|
||||
this.registers = 0;
|
||||
this.code = [];
|
||||
this.decoded = [];
|
||||
this.closures = [];
|
||||
}
|
||||
|
||||
Method.prototype.read = function() {
|
||||
this.upvals = this.ctx.reader.readInteger();
|
||||
this.params = this.ctx.reader.readInteger();
|
||||
this.flags = this.ctx.reader.readUByte();
|
||||
this.registers = this.ctx.reader.readInteger();
|
||||
|
||||
if (this.ctx.header.sharingMode) {
|
||||
console.log("illegal sharing mode");
|
||||
return;
|
||||
}
|
||||
|
||||
this.code = this.ctx.reader.readVectorDWORD();
|
||||
this.parseInstructions();
|
||||
|
||||
this.constants.read();
|
||||
this.debug.read();
|
||||
|
||||
var items = this.ctx.reader.readInteger();
|
||||
|
||||
for (var i = 0; i < items; i++){
|
||||
var methods = new Method(this.ctx);
|
||||
this.closures.push(methods);
|
||||
methods.read();
|
||||
}
|
||||
}
|
||||
|
||||
Method.prototype.write = function() {
|
||||
this.ctx.writer.writeInteger(this.upvals);
|
||||
this.ctx.writer.writeInteger(this.params);
|
||||
this.ctx.writer.writeUByte(this.flags);
|
||||
this.ctx.writer.writeInteger(this.registers);
|
||||
|
||||
if (this.ctx.header.sharingMode) {
|
||||
console.log("illegal sharing mode");
|
||||
return;
|
||||
}
|
||||
|
||||
this.ctx.writer.writeVectorDWORD(this.code);
|
||||
|
||||
this.constants.write();
|
||||
this.debug.write();
|
||||
|
||||
this.ctx.writer.writeInteger(this.closures.length);
|
||||
this.closures.forEach((closure) => {
|
||||
closure.write();
|
||||
});
|
||||
}
|
||||
|
||||
Method.prototype.parseInstructions = function() {
|
||||
this.code.forEach((code) => {
|
||||
var data = {};
|
||||
var instruction;
|
||||
|
||||
var I = decoder.getOp(code);
|
||||
|
||||
if (!(instruction = ops.instructionById[I])) {
|
||||
console.log(fmt("WTF... ILLEGAL INSTRUCTION [index: %i]", I));
|
||||
return;
|
||||
}
|
||||
|
||||
data.I = I;
|
||||
data.instruction = instruction;
|
||||
|
||||
if (instruction.type == decoder.instructionTypes.iABC) {
|
||||
var temp = decoder.decodeABC(code);
|
||||
data.a = temp.a;
|
||||
data.b = temp.b;
|
||||
data.c = temp.c;
|
||||
} else if (instruction.type == decoder.instructionTypes.iABx) {
|
||||
var temp = decoder.decodeABx(code);
|
||||
data.a = temp.a;
|
||||
data.b = temp.b;
|
||||
} else if (instruction.type == decoder.instructionTypes.iAsBx) {
|
||||
var temp = decoder.decodeAsBx(code);
|
||||
data.a = temp.a;
|
||||
data.b = temp.b;
|
||||
} else {
|
||||
console.log("WTF... ILLEGAL INSTRUCTION");
|
||||
}
|
||||
|
||||
this.decoded.push(data);
|
||||
});
|
||||
}
|
||||
|
||||
module.exports = Method;
|
153
asm/assembler.js
153
asm/assembler.js
@ -1,19 +1,20 @@
|
||||
const consts = require("./../constants.js");
|
||||
const insts = require("./../instructions.js");
|
||||
const fmt = require("util").format;
|
||||
const consts = require("./../constants.js");
|
||||
const insts = require("./../instructions/instructions.js");
|
||||
const decoder = require("./../instructions/instructionDecoder.js");
|
||||
const fmt = require("util").format;
|
||||
|
||||
function parseLine(line) {
|
||||
var ret = [];
|
||||
|
||||
if (line.indexOf(';') != -1)
|
||||
line = line.substring(0, line.indexOf(';'));
|
||||
//if (line.indexOf(';') != -1)
|
||||
// line = line.substring(0, line.indexOf(';'));
|
||||
|
||||
var objs = line.split("\t").join(" ") // convert tabs into spaces
|
||||
.split(",").join(" ") // convert ,'s into spaces
|
||||
.split(",").join(" ") // convert ,'s into spaces
|
||||
.split(":").join("") // get rid of optional :'s
|
||||
.split("(").join("") // get rid of optional brackets
|
||||
.split(")").join("") // get rid of optional brackets
|
||||
.split(" "); // split
|
||||
.split(" "); // split
|
||||
|
||||
var hack = false;
|
||||
|
||||
@ -38,7 +39,6 @@ function parseLine(line) {
|
||||
|
||||
if (obj.length == 0) return;
|
||||
|
||||
|
||||
if (parseFloat(obj)) {
|
||||
parsed.number.present = true;
|
||||
parsed.number.value = parseFloat(obj);
|
||||
@ -49,7 +49,8 @@ function parseLine(line) {
|
||||
parsed.bool.value = obj == "true";
|
||||
}
|
||||
|
||||
if (obj.charAt(0) == '{' && line.charAt(line.length - 1)) {
|
||||
|
||||
if (obj.charAt(0) == '{' && line.charAt(line.length - 1) == '}') {
|
||||
var json = line.substring(line.indexOf("{"));
|
||||
parsed.raw = json;
|
||||
parsed.JSON.present = true;
|
||||
@ -58,6 +59,12 @@ function parseLine(line) {
|
||||
hack = true;
|
||||
}
|
||||
|
||||
if (obj.charAt(0) == '"' && line.charAt(line.length - 1) == '"') {
|
||||
parsed.raw = line.substring(line.indexOf('"') + 1, line.length - 1).replace('""', "");
|
||||
ret.push(parsed);
|
||||
hack = true;
|
||||
}
|
||||
|
||||
if (!hack)
|
||||
ret.push(parsed);
|
||||
});
|
||||
@ -97,6 +104,7 @@ function readBlock(buffer, context, title, callback, data) {
|
||||
var start = "START_" + title;
|
||||
var end = "END_" + title;
|
||||
var temp;
|
||||
var shouldLoop;
|
||||
|
||||
temp = buffer.nextKey();
|
||||
if (temp != start) {
|
||||
@ -104,7 +112,10 @@ function readBlock(buffer, context, title, callback, data) {
|
||||
return false;
|
||||
}
|
||||
|
||||
callback(buffer, context, data);
|
||||
do
|
||||
{
|
||||
shouldLoop = callback(buffer, context, data);
|
||||
} while (shouldLoop);
|
||||
|
||||
temp = buffer.nextKey();
|
||||
if (temp != end) {
|
||||
@ -116,100 +127,120 @@ function readBlock(buffer, context, title, callback, data) {
|
||||
}
|
||||
|
||||
function handleHeader(buffer, context) {
|
||||
|
||||
if (buffer.peakKey() == "START_NUMBER_SIZES") {
|
||||
readBlock(buffer, context, "NUMBER_SIZES", handleHeaderNumberics);
|
||||
return handleHeader(buffer, context);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (buffer.peakKey() == "VERSION") {
|
||||
//TODO assert
|
||||
context.header.version = buffer.next()[1].number.value;
|
||||
return handleHeader(buffer, context);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (buffer.peakKey() == "LENDIAN") {
|
||||
//TODO assert
|
||||
context.header.isLE = buffer.next()[1].bool.value;
|
||||
return handleHeader(buffer, context);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (buffer.peakKey() == "UNK") {
|
||||
//TODO assert
|
||||
// DEPRECATED
|
||||
//context.header.unkByte = buffer.next()[1].number.value;
|
||||
return handleHeader(buffer, context);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (buffer.peakKey() == "FLAGS") {
|
||||
//TODO assert
|
||||
context.header.platformFlags = buffer.next()[1].number.value;
|
||||
return handleHeader(buffer, context);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (buffer.peakKey() == "TYPES") {
|
||||
//TODO assert
|
||||
context.header.numOfTypes = buffer.next()[1].number.value;
|
||||
return handleHeader(buffer, context);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (buffer.peakKey() == "SHARE") {
|
||||
//TODO assert
|
||||
context.header.share = buffer.next()[1].bool.value;
|
||||
return handleHeader(buffer, context);
|
||||
return true;
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}
|
||||
|
||||
function handleHeaderNumberics(buffer, context) {
|
||||
|
||||
if (buffer.peakKey() == "NUMBER") {
|
||||
//TODO assert
|
||||
context.header.numberSize = buffer.next()[1].number.value;
|
||||
return handleHeaderNumberics(buffer, context);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (buffer.peakKey() == "INTEGER") {
|
||||
//TODO assert
|
||||
context.header.intSize = buffer.next()[1].number.value;
|
||||
return handleHeaderNumberics(buffer, context);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (buffer.peakKey() == "INSTRUCTION") {
|
||||
//TODO assert
|
||||
context.header.instructionSize = buffer.next()[1].number.value;
|
||||
return handleHeaderNumberics(buffer, context);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (buffer.peakKey() == "SIZE_T") {
|
||||
//TODO assert
|
||||
context.header.machineSize = buffer.next()[1].number.value;
|
||||
return handleHeaderNumberics(buffer, context);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
function handleMethodDebug(buffer, context, dbg ) {
|
||||
|
||||
if (buffer.peakKey() == "TYPE") {
|
||||
//TODO assert
|
||||
dbg.type = buffer.next()[1].number.value;
|
||||
return handleMethodDebug(buffer, context, dbg);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (buffer.peakKey() == "DATA") {
|
||||
//TODO assert
|
||||
dbg.data = buffer.next()[1].JSON.value;
|
||||
return handleMethodDebug(buffer, context, dbg);
|
||||
return true;
|
||||
}
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
function handleMethodConstants(buffer, context, constants) {
|
||||
if (buffer.peakKey() == "END_CONSTANTS") return true;
|
||||
|
||||
if (buffer.peakKey() == "END_CONSTANTS")
|
||||
return false;
|
||||
|
||||
for (let Type of consts.list) {
|
||||
if (Type.tname == buffer.peakKey()) {
|
||||
var type = new Type();
|
||||
var ahh = buffer.next();
|
||||
if (ahh[1])
|
||||
type.fromString(ahh[1].raw);
|
||||
else if (Type.tname == "TSTRING")
|
||||
type.value = "";
|
||||
constants.objs.push(type);
|
||||
return handleMethodConstants(buffer, context, constants);
|
||||
}
|
||||
if (Type.tname != buffer.peakKey())
|
||||
continue;
|
||||
|
||||
var type = new Type();
|
||||
var readValue = buffer.next();
|
||||
|
||||
if (readValue[1])
|
||||
type.fromString(readValue[1].raw);
|
||||
else if (Type.tname == "TSTRING")
|
||||
type.value = "";
|
||||
|
||||
constants.objs.push(type);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -217,91 +248,93 @@ function handleMethodCode(buffer, context, opcodes) {
|
||||
while (buffer.peakKey() != "END_CODE") {
|
||||
var nxt = buffer.next();
|
||||
|
||||
if (Object.keys(insts.instructionTypes).indexOf(nxt[0].raw) != -1)
|
||||
if (Object.keys(decoder.instructionTypes).indexOf(nxt[0].raw) != -1)
|
||||
nxt.shift();
|
||||
|
||||
var instruction = insts.instructionByName[nxt[0].raw];
|
||||
nxt.shift();
|
||||
|
||||
if (instruction.type == insts.instructionTypes.iABC) {
|
||||
opcodes.push(insts.encodeABC(instruction.opcode, nxt[0].number.value, nxt[1].number.value, nxt[2].number.value));
|
||||
} else if (instruction.type == insts.instructionTypes.iABx) {
|
||||
opcodes.push(insts.encodeABx(instruction.opcode, nxt[0].number.value, nxt[1].number.value));
|
||||
} else if (instruction.type == insts.instructionTypes.iAsBx) {
|
||||
opcodes.push(insts.encodeAsBx(instruction.opcode, nxt[0].number.value, nxt[1].number.value));
|
||||
if (instruction.type == decoder.instructionTypes.iABC) {
|
||||
opcodes.push(decoder.encodeABC(instruction.opcode, nxt[0].number.value, nxt[1].number.value, nxt[2].number.value));
|
||||
} else if (instruction.type == decoder.instructionTypes.iABx) {
|
||||
opcodes.push(decoder.encodeABx(instruction.opcode, nxt[0].number.value, nxt[1].number.value));
|
||||
} else if (instruction.type == decoder.instructionTypes.iAsBx) {
|
||||
opcodes.push(decoder.encodeAsBx(instruction.opcode, nxt[0].number.value, nxt[1].number.value));
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
function handleClosure(buffer, context, data) {
|
||||
var id = data.id;
|
||||
var newMethod = new (require("./../index.js").Method)(context);
|
||||
var newMethod = new context.MethodType(context);
|
||||
readBlock(buffer, context, "METHOD", handleMethod, newMethod);
|
||||
data.parent.closures.push(newMethod);
|
||||
return false;
|
||||
}
|
||||
|
||||
function handleMethod(buffer, context, method) {
|
||||
|
||||
if (buffer.peakKey() == "START_DBG") {
|
||||
if (!readBlock(buffer, context, "DBG", handleMethodDebug, method.debug))
|
||||
return false;
|
||||
return handleMethod(buffer, context, method);
|
||||
return readBlock(buffer, context, "DBG", handleMethodDebug, method.debug);
|
||||
}
|
||||
|
||||
if (buffer.peakKey() == "START_CONSTANTS") {
|
||||
if (!readBlock(buffer, context, "CONSTANTS", handleMethodConstants, method.constants))
|
||||
return false;
|
||||
return handleMethod(buffer, context, method);
|
||||
return readBlock(buffer, context, "CONSTANTS", handleMethodConstants, method.constants);
|
||||
}
|
||||
|
||||
if (buffer.peakKey() == "START_CODE") {
|
||||
if (!readBlock(buffer, context, "CODE", handleMethodCode, method.code))
|
||||
return false;
|
||||
method.parseInstructions();
|
||||
return handleMethod(buffer, context, method);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (buffer.peakKey() == "START_CLOSURE") {
|
||||
if (!readBlock(buffer, context, "CLOSURE", handleClosure, {parent: method, id: buffer.peak()[1].number.value}))
|
||||
return false;
|
||||
return handleMethod(buffer, context, method);
|
||||
return readBlock(buffer, context, "CLOSURE", handleClosure, {parent: method, id: buffer.peak()[1].number.value});
|
||||
}
|
||||
|
||||
if (buffer.peakKey() == "FLAGS") {
|
||||
//TODO assert
|
||||
method.flags = buffer.next()[1].number.value;
|
||||
return handleMethod(buffer, context, method);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (buffer.peakKey() == "PARAMS") {
|
||||
//TODO assert
|
||||
method.params = buffer.next()[1].number.value;
|
||||
return handleMethod(buffer, context, method);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (buffer.peakKey() == "UPVALS") {
|
||||
//TODO assert
|
||||
method.upvals = buffer.next()[1].number.value;
|
||||
return handleMethod(buffer, context, method);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (buffer.peakKey() == "REGCNT") {
|
||||
//TODO assert
|
||||
method.registers = buffer.next()[1].number.value;
|
||||
return handleMethod(buffer, context, method);
|
||||
return true;
|
||||
}
|
||||
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
function handleScript(buffer, context) {
|
||||
|
||||
if (buffer.peakKey() == "START_HEADER") {
|
||||
readBlock(buffer, context, "HEADER", handleHeader);
|
||||
return handleScript(buffer, context);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (buffer.peakKey() == "START_METHOD") {
|
||||
readBlock(buffer, context, "METHOD", handleMethod, context.mainMethod);
|
||||
return handleScript(buffer, context);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
function asmContext(string, context) {
|
||||
|
@ -1,5 +1,5 @@
|
||||
const consts = require("./../constants.js");
|
||||
const insts = require("./../instructions.js");
|
||||
const insts = require("./../instructions/instructions.js");
|
||||
const fmt = require("util").format;
|
||||
|
||||
function append(_, level, str) {
|
||||
|
@ -1,5 +1,3 @@
|
||||
const iset = require("./instructions.js");
|
||||
|
||||
function Reader(buffer, ctx) {
|
||||
this.buffer = buffer;
|
||||
this.index = 0;
|
||||
@ -64,45 +62,50 @@ Reader.prototype.readNumber = function(peak) {
|
||||
}
|
||||
|
||||
Reader.prototype.readMachine = function(peak) {
|
||||
if (this.ctx.header.machineSize == 4) {
|
||||
var i = this.ctx.header.isLE ? this.buffer.readUInt32LE(this.index) : this.buffer.readUInt32BE(this.index);
|
||||
if (peak)
|
||||
return i;
|
||||
this.index += 4;
|
||||
return i;
|
||||
}
|
||||
|
||||
//TODO:
|
||||
console.log("int64 not supported");
|
||||
return -203;
|
||||
if (this.ctx.header.machineSize == 4) {
|
||||
return this.consumeInt32(peak);
|
||||
} else if (this.ctx.header.machineSize == 8) {
|
||||
return this.consumeInt64(peak);
|
||||
} else {
|
||||
return -420;
|
||||
}
|
||||
}
|
||||
|
||||
Reader.prototype.readInstruction = function(peak) {
|
||||
if (this.ctx.header.instructionSize == 4) {
|
||||
var i = this.ctx.header.isLE ? this.buffer.readUInt32LE(this.index) : this.buffer.readUInt32BE(this.index);
|
||||
if (peak)
|
||||
return i;
|
||||
this.index += 4;
|
||||
return i;
|
||||
}
|
||||
|
||||
//TODO:
|
||||
console.log("int64 not supported");
|
||||
return -203;
|
||||
if (this.ctx.header.instructionSize == 4) {
|
||||
return this.consumeInt32(peak);
|
||||
} else if (this.ctx.header.instructionSize == 8) {
|
||||
return this.consumeInt64(peak);
|
||||
} else {
|
||||
return -420;
|
||||
}
|
||||
}
|
||||
|
||||
Reader.prototype.readInteger = function(peak) {
|
||||
if (this.ctx.header.intSize == 4) {
|
||||
var i = this.ctx.header.isLE ? this.buffer.readInt32LE(this.index) : this.buffer.readInt32BE(this.index);
|
||||
if (peak)
|
||||
return i;
|
||||
this.index += 4;
|
||||
return i;
|
||||
}
|
||||
return this.consumeInt32(peak);
|
||||
} else if (this.ctx.header.intSize == 8) {
|
||||
return this.consumeInt64(peak);
|
||||
} else {
|
||||
return -420;
|
||||
}
|
||||
}
|
||||
|
||||
//TODO:
|
||||
console.log("int64 not supported");
|
||||
return -203;
|
||||
Reader.prototype.consumeInt64 = function(peak) {
|
||||
var lo = this.buffer.readInt32LE(this.index);
|
||||
this.index += 4;
|
||||
var hi = this.buffer.readInt32LE(this.index);
|
||||
this.index += 4;
|
||||
if (peak)
|
||||
this.index -= 8;
|
||||
return BigInt(hi) << 32n | lo;
|
||||
}
|
||||
|
||||
Reader.prototype.consumeInt32 = function(peak) {
|
||||
var lo = this.ctx.header.isLE ? this.buffer.readUInt32LE(this.index) : this.buffer.readUInt32BE(this.index);
|
||||
if (!peak)
|
||||
this.index += 4;
|
||||
return lo;
|
||||
}
|
||||
|
||||
Reader.prototype.readVectorDWORD = function(peak) {
|
||||
@ -114,5 +117,4 @@ Reader.prototype.readVectorDWORD = function(peak) {
|
||||
return buffer;
|
||||
}
|
||||
|
||||
|
||||
module.exports = Reader;
|
281
index.js
281
index.js
@ -1,276 +1,23 @@
|
||||
const StreamReader = require("./streamReader.js");
|
||||
const StreamWriter = require("./streamWriter.js");
|
||||
const StreamReader = require("./bin/streamReader.js");
|
||||
const StreamWriter = require("./bin/streamWriter.js");
|
||||
const fio = require("fs");
|
||||
const consts = require("./constants.js");
|
||||
const insts = require("./instructions.js");
|
||||
const fmt = require("util").format;
|
||||
const process = require("process");
|
||||
const dasm = require("./asm/disassembler.js");
|
||||
const asm = require("./asm/assembler.js");
|
||||
|
||||
function Header(ctx) {
|
||||
this.magic = undefined;
|
||||
this.version = 0;
|
||||
//this.nonCompliant = true;
|
||||
this.isLE = true;
|
||||
this.intSize = 4;
|
||||
this.instructionSize = 4;
|
||||
this.machineSize = 4;
|
||||
this.numberSize = 4;
|
||||
this.platformFlags = 0;
|
||||
this.numOfTypes = 13;
|
||||
this.sharingMode = 0; // sharing mode (0 - none, 1 - shared, 2 - secure) ? 1 : 0
|
||||
this.ctx = ctx;
|
||||
}
|
||||
const instructions = require("./instructions/instructions.js");
|
||||
|
||||
Header.prototype.read = function() {
|
||||
var buffer = this.ctx.reader.readBytes(false, 14);
|
||||
this.magic = buffer.readUInt32LE();
|
||||
this.version = buffer.readUInt8(4);
|
||||
this.platformFlags = buffer.readUInt8(12); // extensions etc
|
||||
|
||||
this.intSize = buffer.readUInt8(7); // sizeOfInt
|
||||
this.machineSize = buffer.readUInt8(8); // sizeOfSize
|
||||
this.instructionSize = buffer.readUInt8(9); // sizeOfLuaN
|
||||
this.numberSize = buffer.readUInt8(10); // sizeOfNumber
|
||||
|
||||
var idk = buffer.readUInt8(5); //??
|
||||
this.isLE = buffer.readUInt8(6) ? true : false;
|
||||
this.sharingMode = buffer.readUInt8(13) ? true : false;
|
||||
|
||||
this.numOfTypes = this.ctx.reader.readInteger();
|
||||
if (this.numOfTypes != 13) {
|
||||
console.log("There should only be 13 types");
|
||||
return false;
|
||||
}
|
||||
|
||||
for (var i in consts.list) {
|
||||
var type = consts.list[i];
|
||||
var id = this.ctx.reader.readInteger();
|
||||
if (id != type.id)
|
||||
{
|
||||
console.log(fmt("illegal type index %i expected: %s (%i)", id, type.id, type.name));
|
||||
return false;
|
||||
}
|
||||
var str = this.ctx.reader.readCString();
|
||||
if (str != type.tname)
|
||||
{
|
||||
console.log(fmt("illegal type name %s expected: %s", str, type.tname));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
Header.prototype.write = function(ctx) {
|
||||
this.ctx.writer.writeUByte(0x1B);
|
||||
this.ctx.writer.writeUByte(0x4C); //L
|
||||
this.ctx.writer.writeUByte(0x75); //U
|
||||
this.ctx.writer.writeUByte(0x61); //A
|
||||
|
||||
this.ctx.writer.writeUByte(this.version);
|
||||
this.ctx.writer.writeUByte(13); // ???
|
||||
this.ctx.writer.writeUByte(this.isLE ? 1 : 0);
|
||||
|
||||
this.ctx.writer.writeUByte(this.intSize);
|
||||
this.ctx.writer.writeUByte(this.machineSize);
|
||||
this.ctx.writer.writeUByte(this.instructionSize);
|
||||
this.ctx.writer.writeUByte(this.numberSize);
|
||||
|
||||
this.ctx.writer.writeUByte(0); //TODO
|
||||
this.ctx.writer.writeUByte(this.platformFlags);
|
||||
this.ctx.writer.writeUByte(this.sharingMode ? 1 : 0);
|
||||
|
||||
this.ctx.writer.writeInteger(this.numOfTypes);
|
||||
|
||||
consts.list.forEach((type) => {
|
||||
this.ctx.writer.writeInteger(type.id);
|
||||
this.ctx.writer.writeCString(type.tname);
|
||||
});
|
||||
}
|
||||
|
||||
function Debug(ctx, method) {
|
||||
this.ctx = ctx;
|
||||
this.method = method;
|
||||
|
||||
this.type = 0;
|
||||
this.data = {};
|
||||
}
|
||||
|
||||
Debug.prototype.read = function() {
|
||||
this.type = this.ctx.reader.readInteger();
|
||||
|
||||
if (this.type == 1) {
|
||||
this.data["unknown"] = this.ctx.reader.readInteger();
|
||||
} else if (this.type == 0) {
|
||||
|
||||
} else if (this.type == 40) { //implemented in t6
|
||||
//TODO:
|
||||
} else if (this.type == 0x48) {
|
||||
/*
|
||||
hks::BytecodeWriter::dumpInt(this, 0x48);
|
||||
hks::BytecodeWriter::dumpInt(v6, v5->m_debug->line_defined);
|
||||
hks::BytecodeWriter::dumpInt(v6, v5->m_debug->last_line_defined);
|
||||
if ( v4 )
|
||||
v8 = 0i64;
|
||||
else
|
||||
v8 = v5->m_debug->source;
|
||||
hks::BytecodeWriter::dumpString(v6, v8);
|
||||
hks::BytecodeWriter::dumpString(v6, v5->m_debug->name);
|
||||
hks::BytecodeWriter::dumpVector<hksInstruction>(v6, 0i64, 0i64, 0);
|
||||
hks::BytecodeWriter::dumpInt(v6, 0);
|
||||
hks::BytecodeWriter::dumpInt(v6, 0);
|
||||
*/
|
||||
}
|
||||
}
|
||||
|
||||
Debug.prototype.write = function() {
|
||||
this.ctx.writer.writeInteger(this.type);
|
||||
|
||||
if (this.type == 1) {
|
||||
this.ctx.writer.writeInteger(this.data["unknown"]);
|
||||
} else if (this.type == 0) {
|
||||
return;
|
||||
} else if (this.type == 40) { //implemented in t6 - but seemingly unused
|
||||
//TODO:
|
||||
} else if (this.type == 0x48) {
|
||||
//TODO:
|
||||
}
|
||||
}
|
||||
|
||||
function Constants(ctx, method) {
|
||||
this.ctx = ctx;
|
||||
this.method = method;
|
||||
|
||||
this.objs = [];
|
||||
}
|
||||
|
||||
Constants.prototype.read = function() {
|
||||
var size = this.ctx.reader.readMachine();
|
||||
|
||||
for (var i = 0; i < size; i++) {
|
||||
//console.log("reading constant at " + this.ctx.reader.index);
|
||||
var id = this.ctx.reader.readUByte();
|
||||
var type = consts.byId[id];
|
||||
|
||||
if (!type) {
|
||||
console.log("illegal constant type " + id);
|
||||
return false;
|
||||
}
|
||||
|
||||
var inst = new type();
|
||||
inst.undump(this.ctx.reader, this.ctx);
|
||||
this.objs.push(inst);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
Constants.prototype.write = function() {
|
||||
this.ctx.writer.writeMachine(this.objs.length);
|
||||
this.objs.forEach((obj) => {
|
||||
this.ctx.writer.writeUByte(obj.id);
|
||||
obj.dump(this.ctx.writer, this.ctx);
|
||||
});
|
||||
}
|
||||
|
||||
function Method(ctx) {
|
||||
this.ctx = ctx;
|
||||
this.debug = new Debug(ctx, this);
|
||||
this.constants = new Constants(ctx, this);
|
||||
this.upvals = 0;
|
||||
this.params = 0;
|
||||
this.flags = 0;
|
||||
this.registers = 0;
|
||||
this.code = [];
|
||||
this.decoded = [];
|
||||
this.closures = [];
|
||||
}
|
||||
|
||||
Method.prototype.read = function() {
|
||||
this.upvals = this.ctx.reader.readInteger();
|
||||
this.params = this.ctx.reader.readInteger();
|
||||
this.flags = this.ctx.reader.readUByte();
|
||||
this.registers = this.ctx.reader.readInteger();
|
||||
|
||||
if (this.ctx.header.sharingMode) {
|
||||
console.log("illegal sharing mode");
|
||||
return;
|
||||
}
|
||||
|
||||
this.code = this.ctx.reader.readVectorDWORD();
|
||||
this.parseInstructions();
|
||||
|
||||
this.constants.read();
|
||||
this.debug.read();
|
||||
|
||||
var items = this.ctx.reader.readInteger();
|
||||
|
||||
for (var i = 0; i < items; i++){
|
||||
var methods = new Method(this.ctx);
|
||||
this.closures.push(methods);
|
||||
methods.read();
|
||||
}
|
||||
}
|
||||
Method.prototype.write = function() {
|
||||
this.ctx.writer.writeInteger(this.upvals);
|
||||
this.ctx.writer.writeInteger(this.params);
|
||||
this.ctx.writer.writeUByte(this.flags);
|
||||
this.ctx.writer.writeInteger(this.registers);
|
||||
|
||||
if (this.ctx.header.sharingMode) {
|
||||
console.log("illegal sharing mode");
|
||||
return;
|
||||
}
|
||||
|
||||
this.ctx.writer.writeVectorDWORD(this.code);
|
||||
|
||||
this.constants.write();
|
||||
this.debug.write();
|
||||
|
||||
this.ctx.writer.writeInteger(this.closures.length);
|
||||
this.closures.forEach((closure) => {
|
||||
closure.write();
|
||||
});
|
||||
}
|
||||
|
||||
Method.prototype.parseInstructions = function() {
|
||||
this.code.forEach((code) => {
|
||||
var data = {};
|
||||
var I = insts.getOp(code);
|
||||
var instruction = insts.instructionById[I];
|
||||
if (!instruction) {
|
||||
console.log(fmt("WTF... ILLEGAL INSTRUCTION [index: %i]", I));
|
||||
return;
|
||||
}
|
||||
data.I = I;
|
||||
data.instruction = instruction;
|
||||
if (instruction.type == insts.instructionTypes.iABC) {
|
||||
var temp = insts.decodeABC(code);
|
||||
data.a = temp.a;
|
||||
data.b = temp.b;
|
||||
data.c = temp.c;
|
||||
} else if (instruction.type == insts.instructionTypes.iABx) {
|
||||
var temp = insts.decodeABx(code);
|
||||
data.a = temp.a;
|
||||
data.b = temp.b;
|
||||
} else if (instruction.type == insts.instructionTypes.iAsBx) {
|
||||
var temp = insts.decodeAsBx(code);
|
||||
data.a = temp.a;
|
||||
data.b = temp.b;
|
||||
} else {
|
||||
console.log("WTF... ILLEGAL INSTRUCTION");
|
||||
}
|
||||
this.decoded.push(data);
|
||||
});
|
||||
}
|
||||
const T6Header = require("./T6Header.js");
|
||||
const T6Method = require("./T6Method.js");
|
||||
|
||||
|
||||
function Context() {
|
||||
this.types = consts;
|
||||
this.header = new Header(this);
|
||||
this.mainMethod = new Method(this);
|
||||
this.header = new T6Header(this);
|
||||
this.mainMethod = new T6Method(this);
|
||||
this.HeaderType = T6Header;
|
||||
this.MethodType = T6Method;
|
||||
this.reader = undefined;
|
||||
this.writer = undefined;
|
||||
}
|
||||
@ -315,11 +62,7 @@ Context.prototype.assembleFile = function(file) {
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
Context: Context,
|
||||
Method: Method,
|
||||
Constants: Constants,
|
||||
Debug: Debug,
|
||||
Header: Header,
|
||||
constants: consts,
|
||||
instructions: insts
|
||||
T6Context: Context,
|
||||
types: consts,
|
||||
instructions: instructions
|
||||
}
|
@ -1,5 +1,3 @@
|
||||
const opcodes = require("./instructions_a.js");
|
||||
|
||||
const e_iABC = 0;
|
||||
const e_iABx = 1;
|
||||
const e_iAsBx = 2;
|
||||
@ -8,17 +6,6 @@ const itypes = { "iABC": e_iABC,
|
||||
"iABx": e_iABx,
|
||||
"iAsBx": e_iAsBx };
|
||||
|
||||
var instructionById = {};
|
||||
var instructionByName = {};
|
||||
|
||||
opcodes.forEach((obj) => {
|
||||
instructionById[obj.opcode] = obj;
|
||||
});
|
||||
|
||||
opcodes.forEach((obj) => {
|
||||
instructionByName[obj.name] = obj;
|
||||
});
|
||||
|
||||
function getOp(code) {
|
||||
return (code >>> 25) & 0x7f;
|
||||
}
|
||||
@ -66,15 +53,12 @@ function decodeAsBx(code) {
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
getOp: getOp,
|
||||
encodeABC: encodeABC,
|
||||
decodeABC: decodeABC,
|
||||
encodeABx: encodeABx,
|
||||
decodeABx: decodeABx,
|
||||
encodeAsBx: encodeAsBx,
|
||||
decodeAsBx: decodeAsBx,
|
||||
instructions: opcodes,
|
||||
instructionById: instructionById,
|
||||
instructionByName: instructionByName,
|
||||
instructionTypes: itypes
|
||||
getOp: getOp,
|
||||
encodeABC: encodeABC,
|
||||
decodeABC: decodeABC,
|
||||
encodeABx: encodeABx,
|
||||
decodeABx: decodeABx,
|
||||
encodeAsBx: encodeAsBx,
|
||||
decodeAsBx: decodeAsBx,
|
||||
instructionTypes: itypes
|
||||
};
|
18
instructions/instructions.js
Normal file
18
instructions/instructions.js
Normal file
@ -0,0 +1,18 @@
|
||||
const opcodes = require("./instructionsT6.js");
|
||||
|
||||
var instructionById = {};
|
||||
var instructionByName = {};
|
||||
|
||||
opcodes.forEach((obj) => {
|
||||
instructionById[obj.opcode] = obj;
|
||||
});
|
||||
|
||||
opcodes.forEach((obj) => {
|
||||
instructionByName[obj.name] = obj;
|
||||
});
|
||||
|
||||
module.exports = {
|
||||
instructions: opcodes,
|
||||
instructionById: instructionById,
|
||||
instructionByName: instructionByName
|
||||
};
|
Loading…
Reference in New Issue
Block a user