mirror of
https://github.com/captbaritone/webamp.git
synced 2026-07-22 17:47:16 +00:00
Improve variable parsing
This commit is contained in:
parent
5eacdc53b8
commit
a4c8705bf9
4 changed files with 192 additions and 25 deletions
|
|
@ -2,57 +2,99 @@ const fs = require("fs");
|
|||
const path = require("path");
|
||||
const { parse } = require("./");
|
||||
|
||||
const log = true;
|
||||
|
||||
function parseFile(relativePath) {
|
||||
const buffer = fs.readFileSync(path.join(__dirname, relativePath));
|
||||
return parse(buffer);
|
||||
}
|
||||
|
||||
class Group {}
|
||||
|
||||
const System = {
|
||||
getScriptGroup() {
|
||||
return new Group();
|
||||
}
|
||||
};
|
||||
|
||||
function interpret({ commands: rawCommands, variables, types }) {
|
||||
const commands = rawCommands.slice(0, 5);
|
||||
const environment = {
|
||||
System
|
||||
};
|
||||
// Debug utility to pretty print a value/variable
|
||||
function prettyPrint(offset) {
|
||||
const value = variables[offset] || offset;
|
||||
if (value == null) {
|
||||
return "NULL";
|
||||
}
|
||||
let name = value;
|
||||
if (value.type) {
|
||||
name = `Variable(${value.type.name}) ${
|
||||
value.getValue() ? "" : "(empty)"
|
||||
}`;
|
||||
}
|
||||
return name;
|
||||
}
|
||||
|
||||
const commands = rawCommands.slice(0, 86);
|
||||
const stack = [];
|
||||
let i = 0;
|
||||
while (i < commands.length) {
|
||||
const command = commands[i];
|
||||
|
||||
switch (command.opcode) {
|
||||
// push
|
||||
case 1: {
|
||||
// What are these? Do they have names?
|
||||
const offsetIntoVariables = command.arguments[0];
|
||||
const variable = variables[offsetIntoVariables];
|
||||
console.log(variable);
|
||||
stack.push(variable);
|
||||
break;
|
||||
}
|
||||
// pop
|
||||
case 2: {
|
||||
stack.pop();
|
||||
break;
|
||||
}
|
||||
// call
|
||||
case 24: {
|
||||
const funcDefinition = command.arguments[0];
|
||||
const { name } = funcDefinition;
|
||||
const { name, parameters } = funcDefinition;
|
||||
const methodArgs = [];
|
||||
// This might be the wrong order
|
||||
parameters.forEach(() => {
|
||||
methodArgs.push(stack.pop());
|
||||
});
|
||||
const variable = stack.pop();
|
||||
const type = types[variable.type];
|
||||
const obj = environment[type.name];
|
||||
const obj = variable.getValue();
|
||||
stack.push(obj[name]());
|
||||
break;
|
||||
}
|
||||
// return
|
||||
case 33: {
|
||||
const posVar = stack.pop();
|
||||
const pos = posVar.getValue();
|
||||
// TODO: What is this supposed to do?
|
||||
i += pos;
|
||||
break;
|
||||
}
|
||||
// mov
|
||||
case 48: {
|
||||
const a = stack.pop();
|
||||
const b = stack.pop();
|
||||
b.setValue(a);
|
||||
stack.push(a);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
throw new Error(`Unhandled opcode ${command.opcode}`);
|
||||
}
|
||||
i++;
|
||||
|
||||
// Print some debug info
|
||||
if (log) {
|
||||
console.log(
|
||||
i + 1,
|
||||
command.command.name.toUpperCase(),
|
||||
command.opcode,
|
||||
command.arguments.map(offset => {
|
||||
return prettyPrint(offset);
|
||||
})
|
||||
);
|
||||
stack.forEach((value, i) => {
|
||||
const name = prettyPrint(value);
|
||||
console.log(" ", i + 1, name);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -5,6 +5,14 @@ const Variable = require("./variable");
|
|||
const MAGIC = "FG";
|
||||
const ENCODING = "binary";
|
||||
|
||||
const PRIMITIVE_TYPES = {
|
||||
5: "BOOLEAN",
|
||||
2: "INT",
|
||||
3: "FLOAT",
|
||||
4: "DOUBLE",
|
||||
6: "STRING"
|
||||
};
|
||||
|
||||
class Parser {
|
||||
_readMagic() {
|
||||
const magic = this._readStringOfLength(MAGIC.length);
|
||||
|
|
@ -97,25 +105,50 @@ class Parser {
|
|||
}
|
||||
variables.push(new Variable({ ...props, type }));
|
||||
} else {
|
||||
const type = types[typeOffset];
|
||||
if (type == null) {
|
||||
const typeName = PRIMITIVE_TYPES[typeOffset];
|
||||
if (typeName == null) {
|
||||
throw new Error("Invalid type");
|
||||
}
|
||||
console.log(type.name);
|
||||
variables.push(new Variable({ ...props, type }));
|
||||
let value = null;
|
||||
|
||||
switch (typeName) {
|
||||
// BOOLEAN
|
||||
case PRIMITIVE_TYPES[5]:
|
||||
// INT
|
||||
case PRIMITIVE_TYPES[2]:
|
||||
value = uinit1;
|
||||
break;
|
||||
case PRIMITIVE_TYPES[3]:
|
||||
case PRIMITIVE_TYPES[4]:
|
||||
const exponent = (uint2 & 0xff80) >> 7;
|
||||
const mantisse = ((0x80 | (uint2 & 0x7f)) << 16) | uint1;
|
||||
value = mantisse * 2.0 ** (exponent - 0x96);
|
||||
break;
|
||||
case PRIMITIVE_TYPES[6]:
|
||||
// This will likely get set by constants later on.
|
||||
break;
|
||||
}
|
||||
if (value == null) {
|
||||
// throw new Error("Failed to set value");
|
||||
}
|
||||
const variable = new Variable({ ...props, type: { name: typeName } });
|
||||
variable.setValue(value);
|
||||
variables.push(variable);
|
||||
}
|
||||
}
|
||||
return variables;
|
||||
}
|
||||
|
||||
_readConstants() {
|
||||
_readConstants({ variables }) {
|
||||
let count = this._readUInt32LE();
|
||||
const constants = [];
|
||||
while (count--) {
|
||||
// This is an index into the variables array.
|
||||
const varNum = this._readUInt32LE();
|
||||
const i = this._readUInt32LE();
|
||||
const variable = variables[i];
|
||||
const value = this._readString();
|
||||
constants.push({ varNum, value });
|
||||
// TODO: Don't mutate
|
||||
variable.setValue(value);
|
||||
constants.push({ varNum: i, value });
|
||||
}
|
||||
return constants;
|
||||
}
|
||||
|
|
@ -230,7 +263,7 @@ class Parser {
|
|||
const types = this._readTypes();
|
||||
const functionNames = this._readFunctionsNames({ types });
|
||||
const variables = this._readVariables({ types });
|
||||
const constants = this._readConstants();
|
||||
const constants = this._readConstants({ variables });
|
||||
const functions = this._readFunctions();
|
||||
const commands = this._decodeCode({
|
||||
types,
|
||||
|
|
|
|||
|
|
@ -76,6 +76,67 @@ describe("standardframe.maki", () => {
|
|||
|
||||
test("can read variables", () => {
|
||||
expect(maki.variables.length).toBe(56);
|
||||
expect(maki.variables.map(variable => variable.type.name))
|
||||
.toMatchInlineSnapshot(`
|
||||
Array [
|
||||
"System",
|
||||
"INT",
|
||||
"Group",
|
||||
"Group",
|
||||
"Group",
|
||||
"STRING",
|
||||
"STRING",
|
||||
"STRING",
|
||||
"STRING",
|
||||
"STRING",
|
||||
"STRING",
|
||||
"STRING",
|
||||
"STRING",
|
||||
"Layer",
|
||||
"Button",
|
||||
"STRING",
|
||||
"STRING",
|
||||
"INT",
|
||||
"INT",
|
||||
"INT",
|
||||
"INT",
|
||||
"INT",
|
||||
"INT",
|
||||
"INT",
|
||||
"INT",
|
||||
"STRING",
|
||||
"STRING",
|
||||
"STRING",
|
||||
"STRING",
|
||||
"STRING",
|
||||
"STRING",
|
||||
"STRING",
|
||||
"STRING",
|
||||
"STRING",
|
||||
"STRING",
|
||||
"STRING",
|
||||
"STRING",
|
||||
"STRING",
|
||||
"STRING",
|
||||
"STRING",
|
||||
"INT",
|
||||
"INT",
|
||||
"STRING",
|
||||
"STRING",
|
||||
"STRING",
|
||||
"STRING",
|
||||
"STRING",
|
||||
"STRING",
|
||||
"STRING",
|
||||
"STRING",
|
||||
"STRING",
|
||||
"STRING",
|
||||
"STRING",
|
||||
"STRING",
|
||||
"STRING",
|
||||
"STRING",
|
||||
]
|
||||
`);
|
||||
maki.variables.forEach(variable => {
|
||||
expect(variable.type).not.toBe(undefined);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,7 +1,38 @@
|
|||
class GuiObject {}
|
||||
class Group extends GuiObject {}
|
||||
class PopupMenu {}
|
||||
class Container {}
|
||||
|
||||
const System = {
|
||||
getScriptGroup() {
|
||||
return new Group();
|
||||
},
|
||||
getToken(str, separator, tokennum) {
|
||||
return "Some Token String";
|
||||
},
|
||||
getParam() {
|
||||
return "Some String";
|
||||
}
|
||||
};
|
||||
|
||||
const environment = {
|
||||
System,
|
||||
Group,
|
||||
GuiObject,
|
||||
PopupMenu,
|
||||
Container
|
||||
};
|
||||
|
||||
class Variable {
|
||||
constructor(props) {
|
||||
this._props = props;
|
||||
this.type = props.type;
|
||||
if (props.system) {
|
||||
if (this.type.name != "System") {
|
||||
throw new Error("Can variables other than System be system variables?");
|
||||
}
|
||||
this._value = System;
|
||||
}
|
||||
}
|
||||
|
||||
getValue() {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue