webamp/experiments/maki-interpreter/index.js
2019-01-16 07:15:52 -08:00

186 lines
4.3 KiB
JavaScript

const { COMMANDS } = require("./constants");
const MAGIC = "FG";
const ENCODING = "utf8";
class Parser {
_readMagic() {
const magic = this._readStringOfLength(MAGIC.length);
if (magic !== MAGIC) {
throw new Error("Magic number does not mach. Is this a maki file?");
}
return magic;
}
_readVersion() {
// No idea what we're actually expecting here.
this._i += 2;
}
_readTypes() {
let count = this._readUInt32LE();
const types = [];
while (count--) {
let identifier = "";
let chunks = 4;
while (chunks--) {
identifier += this._readUInt32LE().toString(16);
}
types.push(identifier.padStart(32, "0"));
}
return types;
}
_readFunctionsNames() {
let count = this._readUInt32LE();
const functionNames = [];
while (count--) {
const func = {};
func.classCode = this._readUInt16LE();
// Offset into our parsed types
func.classType = func.classCode & 0xff;
func.dummy2 = this._readUInt16LE();
func.name = this._readString();
functionNames.push(func);
}
return functionNames;
}
_readVariables() {
let count = this._readUInt32LE();
const variables = [];
while (count--) {
const type = this._readUInt8();
const object = this._readUInt8();
const subClass = this._readUInt16LE();
const uinit1 = this._readUInt16LE();
const uinit2 = this._readUInt16LE();
const uinit3 = this._readUInt16LE();
const uinit4 = this._readUInt16LE();
const global = this._readUInt8();
const system = this._readUInt8();
const variable = {
type,
object,
subClass,
uinit1,
uinit2,
uinit3,
uinit4,
global,
system
};
variables.push(variable);
}
return variables;
}
_readConstants() {
let count = this._readUInt32LE();
const constants = [];
while (count--) {
const varNum = this._readUInt32LE();
const value = this._readString();
constants.push({ varNum, value });
}
return constants;
}
_readFunctions() {
let count = this._readUInt32LE();
const functions = [];
while (count--) {
const varNum = this._readUInt32LE();
const funcNum = this._readUInt32LE();
const offset = this._readUInt32LE();
functions.push({ varNum, offset, funcNum });
}
return functions;
}
_readCommands(code) {
let i = 0;
while (i < code.length) {
const opCode = code.charCodeAt(i);
const command = COMMANDS[opCode];
if (command.arg == null) {
i++;
continue;
}
switch (command.arg) {
case "var":
// This is the index into this._debug.variables
// this._readUInt32LE();
i += 5;
break;
default:
i += 5;
}
// console.log({ command });
}
}
_readFunctionsCode() {
const code = this._readStringOfLength(this._readUInt32LE());
return code;
}
_readUInt32LE() {
const int = this._buffer.readUInt32LE(this._i);
this._i += 4;
return int;
}
_readUInt16LE() {
const int = this._buffer.readUInt16LE(this._i);
this._i += 2;
return int;
}
_readUInt8() {
const int = this._buffer.readUInt8(this._i);
this._i++;
return int;
}
_readStringOfLength(length) {
const str = this._buffer.toString(ENCODING, this._i, this._i + length);
this._i += length;
return str;
}
_readString() {
return this._readStringOfLength(this._readUInt16LE());
}
parse(buffer) {
this._buffer = buffer;
this._i = 0;
const magic = this._readMagic();
this._readVersion();
this._readUInt32LE(); // Not sure what we are skipping over here. Just some UInt 32.
const types = this._readTypes();
const functionNames = this._readFunctionsNames();
const variables = this._readVariables();
const constants = this._readConstants();
const functions = this._readFunctions();
const functionsCode = this._readFunctionsCode();
this._readCommands(functionsCode);
return {
magic,
types,
functionNames,
variables,
constants,
functionsCode,
functions
};
}
}
function parse(buffer) {
const parser = new Parser();
return parser.parse(buffer);
}
module.exports = { parse };