mirror of
https://github.com/captbaritone/webamp.git
synced 2026-07-20 16:49:52 +00:00
Get corneramp initializing without any error messages
This commit is contained in:
parent
1e86945f1e
commit
0e159c3bed
18 changed files with 567 additions and 33 deletions
|
|
@ -1,5 +1,6 @@
|
|||
# TODO Next
|
||||
|
||||
- [ ] Fix all `// FIXME`
|
||||
- [ ] SystemObject.getruntimeversion
|
||||
- [ ] SystemObject.getskinname
|
||||
- [ ] Ensure only wrapped variables go on the stack
|
||||
|
|
|
|||
|
|
@ -11,12 +11,24 @@ async function main() {
|
|||
await parser.parse();
|
||||
|
||||
for (const container of parser._containers) {
|
||||
container.init();
|
||||
container.init({ containers: parser._containers });
|
||||
}
|
||||
|
||||
let node = document.createElement("div");
|
||||
|
||||
for (const container of parser._containers) {
|
||||
document.body.appendChild(container.getDebugDom());
|
||||
node.appendChild(container.getDebugDom());
|
||||
}
|
||||
|
||||
document.body.appendChild(node);
|
||||
setInterval(() => {
|
||||
document.body.removeChild(node);
|
||||
node = document.createElement("div");
|
||||
for (const container of parser._containers) {
|
||||
node.appendChild(container.getDebugDom());
|
||||
}
|
||||
document.body.appendChild(node);
|
||||
}, 1000);
|
||||
}
|
||||
|
||||
main();
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ class Interpreter {
|
|||
variables: Variable[];
|
||||
methods: Method[];
|
||||
commands: Command[];
|
||||
debug: boolean;
|
||||
classResolver: (guid: string) => any;
|
||||
constructor(program: ParsedMaki, classResolver: (guid: string) => any) {
|
||||
const { commands, methods, variables, classes } = program;
|
||||
|
|
@ -29,6 +30,7 @@ class Interpreter {
|
|||
|
||||
this.stack = [];
|
||||
this.callStack = [];
|
||||
this.debug = false;
|
||||
}
|
||||
|
||||
interpret(start: number) {
|
||||
|
|
@ -36,6 +38,9 @@ class Interpreter {
|
|||
let ip = start;
|
||||
while (ip < this.commands.length) {
|
||||
const command = this.commands[ip];
|
||||
if (this.debug) {
|
||||
console.log(command);
|
||||
}
|
||||
|
||||
switch (command.opcode) {
|
||||
// push
|
||||
|
|
@ -70,7 +75,7 @@ class Interpreter {
|
|||
a.type == b.type,
|
||||
`Tried to compare a ${a.type} to a ${b.type}.`
|
||||
);
|
||||
const result = V.newInt(a.value === b.value);
|
||||
const result = V.newInt(b.value === a.value);
|
||||
this.stack.push(result);
|
||||
break;
|
||||
}
|
||||
|
|
@ -82,7 +87,7 @@ class Interpreter {
|
|||
a.type == b.type,
|
||||
`Tried to compare a ${a.type} to a ${b.type}.`
|
||||
);
|
||||
const result = V.newInt(a.value !== b.value);
|
||||
const result = V.newInt(b.value !== a.value);
|
||||
this.stack.push(result);
|
||||
break;
|
||||
}
|
||||
|
|
@ -104,7 +109,10 @@ class Interpreter {
|
|||
case "NULL":
|
||||
throw new Error("Tried to add non-numbers.");
|
||||
}
|
||||
this.stack.push(V.newInt(a.value > b.value));
|
||||
if (this.debug) {
|
||||
console.log(`${b.value} > ${a.value}`);
|
||||
}
|
||||
this.stack.push(V.newInt(b.value > a.value));
|
||||
break;
|
||||
}
|
||||
// >=
|
||||
|
|
@ -130,7 +138,11 @@ class Interpreter {
|
|||
case "NULL":
|
||||
throw new Error("Tried to add non-numbers.");
|
||||
}
|
||||
this.stack.push(V.newInt(a.value < b.value));
|
||||
if (this.debug) {
|
||||
console.log(`${b.value} < ${a.value}`);
|
||||
}
|
||||
|
||||
this.stack.push(V.newInt(b.value < a.value));
|
||||
break;
|
||||
}
|
||||
// <=
|
||||
|
|
@ -184,7 +196,7 @@ class Interpreter {
|
|||
// actually having access to the object instance.
|
||||
if (!klass.prototype[methodName]) {
|
||||
throw new Error(
|
||||
`Need to add missing method "${methodName}" to ${klass.name}`
|
||||
`Need to add missing method: ${klass.name}.${methodName}: ${returnType}`
|
||||
);
|
||||
}
|
||||
let argCount = klass.prototype[methodName].length;
|
||||
|
|
@ -200,13 +212,18 @@ class Interpreter {
|
|||
"Tried to call a method on a primitive."
|
||||
);
|
||||
let value = obj.value[methodName](...methodArgs);
|
||||
if (typeof value.then === "function") {
|
||||
throw new Error("Did not expect maki method to return promise");
|
||||
if (value === undefined && returnType !== "NULL") {
|
||||
throw new Error(
|
||||
`Did not expect ${klass.name}.${methodName}: ${returnType} to return undefined`
|
||||
);
|
||||
}
|
||||
if (value === null) {
|
||||
// variables[1] holds global NULL value
|
||||
value = this.variables[1];
|
||||
}
|
||||
if (this.debug) {
|
||||
console.log(`Calling method ${methodName}`);
|
||||
}
|
||||
this.stack.push({ type: returnType, value } as any);
|
||||
break;
|
||||
}
|
||||
|
|
@ -234,7 +251,6 @@ class Interpreter {
|
|||
case 48: {
|
||||
const a = this.stack.pop();
|
||||
const b = this.stack.pop();
|
||||
|
||||
assume(
|
||||
a.type === b.type,
|
||||
`Type mismatch: ${a.type} != ${b.type} at ip: ${ip}`
|
||||
|
|
@ -319,7 +335,7 @@ class Interpreter {
|
|||
throw new Error("Tried to add non-numbers.");
|
||||
}
|
||||
// TODO: Do we need to round the value if INT?
|
||||
this.stack.push({ type: a.type, value: a.value + b.value });
|
||||
this.stack.push({ type: a.type, value: b.value + a.value });
|
||||
break;
|
||||
}
|
||||
// - (subtract)
|
||||
|
|
@ -341,7 +357,7 @@ class Interpreter {
|
|||
throw new Error("Tried to add non-numbers.");
|
||||
}
|
||||
// TODO: Do we need to round the value if INT?
|
||||
this.stack.push({ type: a.type, value: a.value - b.value });
|
||||
this.stack.push({ type: a.type, value: b.value - a.value });
|
||||
break;
|
||||
}
|
||||
// * (multiply)
|
||||
|
|
@ -363,7 +379,7 @@ class Interpreter {
|
|||
throw new Error("Tried to add non-numbers.");
|
||||
}
|
||||
// TODO: Do we need to round the value if INT?
|
||||
this.stack.push({ type: a.type, value: a.value * b.value });
|
||||
this.stack.push({ type: a.type, value: b.value * a.value });
|
||||
break;
|
||||
}
|
||||
// / (divide)
|
||||
|
|
@ -384,7 +400,7 @@ class Interpreter {
|
|||
throw new Error("Tried to add non-numbers.");
|
||||
}
|
||||
// TODO: Do we need to round the value if INT?
|
||||
this.stack.push({ type: a.type, value: a.value / b.value });
|
||||
this.stack.push({ type: a.type, value: b.value / a.value });
|
||||
break;
|
||||
}
|
||||
// % (mod)
|
||||
|
|
@ -451,7 +467,28 @@ class Interpreter {
|
|||
}
|
||||
// logAnd (&&)
|
||||
case 80: {
|
||||
assume(false, "Unimplimented && operator");
|
||||
const a = this.stack.pop();
|
||||
const b = this.stack.pop();
|
||||
// Some of these are probably valid, but we'll enable them once we see usage.
|
||||
switch (a.type) {
|
||||
case "STRING":
|
||||
case "OBJECT":
|
||||
case "BOOL":
|
||||
case "NULL":
|
||||
throw new Error("Tried to add non-numbers.");
|
||||
}
|
||||
switch (b.type) {
|
||||
case "STRING":
|
||||
case "OBJECT":
|
||||
case "BOOL":
|
||||
case "NULL":
|
||||
throw new Error("Tried to add non-numbers.");
|
||||
}
|
||||
if (b.value && a.value) {
|
||||
this.stack.push(a);
|
||||
} else {
|
||||
this.stack.push(b);
|
||||
}
|
||||
break;
|
||||
}
|
||||
// logOr ||
|
||||
|
|
@ -473,10 +510,12 @@ class Interpreter {
|
|||
case "NULL":
|
||||
throw new Error("Tried to add non-numbers.");
|
||||
}
|
||||
if (a.value) {
|
||||
return a;
|
||||
if (b.value) {
|
||||
this.stack.push(b);
|
||||
} else {
|
||||
this.stack.push(a);
|
||||
}
|
||||
return b;
|
||||
break;
|
||||
}
|
||||
// <<
|
||||
case 88: {
|
||||
|
|
@ -494,7 +533,7 @@ class Interpreter {
|
|||
const guid = this.classes[classesOffset];
|
||||
const Klass = this.classResolver(guid);
|
||||
const klassInst = new Klass();
|
||||
this.stack.push(klassInst);
|
||||
this.stack.push({ type: "OBJECT", value: klassInst });
|
||||
break;
|
||||
}
|
||||
// delete
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@ export type ParsedMaki = {
|
|||
|
||||
export type Binding = {
|
||||
commandOffset: number;
|
||||
methodOffset: number;
|
||||
};
|
||||
|
||||
const MAGIC = "FG";
|
||||
|
|
|
|||
21
packages/webamp-modern-2/src/skin/BaseObject.ts
Normal file
21
packages/webamp-modern-2/src/skin/BaseObject.ts
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
/**
|
||||
Object Class.
|
||||
|
||||
@short This is the base class from which all other classes inherit.
|
||||
@author Nullsoft Inc.
|
||||
@ver 1.0
|
||||
*/
|
||||
export default class BaseObject {
|
||||
/**
|
||||
* Returns the class name for the object.
|
||||
*
|
||||
* @ret The class name.
|
||||
*/
|
||||
getClassName(): string {
|
||||
throw new Error("Unimplemented");
|
||||
}
|
||||
|
||||
getId() {
|
||||
throw new Error("Unimplemented");
|
||||
}
|
||||
}
|
||||
26
packages/webamp-modern-2/src/skin/Button.ts
Normal file
26
packages/webamp-modern-2/src/skin/Button.ts
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
import GuiObj from "./GuiObj";
|
||||
|
||||
// http://wiki.winamp.com/wiki/XML_GUI_Objects#.3Cbutton.2F.3E_.26_.3Ctogglebutton.2F.3E
|
||||
export default class Button extends GuiObj {
|
||||
setXmlAttr(key: string, value: string): boolean {
|
||||
if (super.setXmlAttr(key, value)) {
|
||||
return true;
|
||||
}
|
||||
switch (key) {
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
extern Button.onActivate(int activated);
|
||||
extern Button.onLeftClick();
|
||||
extern Button.onRightClick();
|
||||
extern Button.setActivated(Boolean onoff);
|
||||
extern Button.setActivatedNoCallback(Boolean onoff);
|
||||
extern Boolean Button.getActivated();
|
||||
extern Button.leftClick();
|
||||
extern Button.rightClick();e
|
||||
*/
|
||||
}
|
||||
|
|
@ -1,5 +1,7 @@
|
|||
import { SkinContext } from "../types";
|
||||
import { toBool } from "../utils";
|
||||
import Group from "./Group";
|
||||
import Layer from "./Layer";
|
||||
import Layout from "./Layout";
|
||||
import XmlObj from "./XmlObj";
|
||||
|
||||
|
|
@ -13,6 +15,7 @@ export default class Container extends XmlObj {
|
|||
_layouts: Layout[] = [];
|
||||
_activeLayout: Layout | null = null;
|
||||
_defaultVisible: boolean = true;
|
||||
_id: string;
|
||||
constructor() {
|
||||
super();
|
||||
}
|
||||
|
|
@ -22,6 +25,9 @@ export default class Container extends XmlObj {
|
|||
return true;
|
||||
}
|
||||
switch (key) {
|
||||
case "id":
|
||||
this._id = value.toLowerCase();
|
||||
break;
|
||||
case "default_visible":
|
||||
this._defaultVisible = toBool(value);
|
||||
break;
|
||||
|
|
@ -31,12 +37,35 @@ export default class Container extends XmlObj {
|
|||
return true;
|
||||
}
|
||||
|
||||
init() {
|
||||
init(context: SkinContext) {
|
||||
for (const layout of this._layouts) {
|
||||
layout.init();
|
||||
layout.init(context);
|
||||
}
|
||||
}
|
||||
|
||||
getId() {
|
||||
return this._id;
|
||||
}
|
||||
|
||||
/* Required for Maki */
|
||||
/**
|
||||
* Get the layout associated with the an id.
|
||||
* This corresponds to the "id=..." attribute in
|
||||
* the XML tag <layout .. />.
|
||||
*
|
||||
* @ret The layout associated with the id.
|
||||
* @param layout_id The id of the layout you wish to retrieve.
|
||||
*/
|
||||
getlayout(layoutId: string): Layout {
|
||||
const lower = layoutId.toLowerCase();
|
||||
for (const layout of this._layouts) {
|
||||
if (layout.getId() === lower) {
|
||||
return layout;
|
||||
}
|
||||
}
|
||||
throw new Error(`Could not find a container with the id; "${layoutId}"`);
|
||||
}
|
||||
|
||||
addLayout(layout: Layout) {
|
||||
layout.setParent(this);
|
||||
this._layouts.push(layout);
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ import * as Utils from "../utils";
|
|||
import UI_ROOT from "../UIRoot";
|
||||
import GuiObj from "./GuiObj";
|
||||
import SystemObject from "./SystemObject";
|
||||
import { SkinContext } from "../types";
|
||||
|
||||
export default class Group extends GuiObj {
|
||||
_background: string;
|
||||
|
|
@ -43,12 +44,12 @@ export default class Group extends GuiObj {
|
|||
return true;
|
||||
}
|
||||
|
||||
init() {
|
||||
init(context: SkinContext) {
|
||||
for (const systemObject of this._systemObjects) {
|
||||
systemObject.init();
|
||||
systemObject.init(context);
|
||||
}
|
||||
for (const child of this._children) {
|
||||
child.init();
|
||||
child.init(context);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -61,6 +62,18 @@ export default class Group extends GuiObj {
|
|||
this._children.push(child);
|
||||
}
|
||||
|
||||
/* Required for Maki */
|
||||
|
||||
getobject(objectId: string): GuiObj {
|
||||
const lower = objectId.toLowerCase();
|
||||
for (const obj of this._children) {
|
||||
if (obj.getId() === lower) {
|
||||
return obj;
|
||||
}
|
||||
}
|
||||
throw new Error(`Could not find an object with the id; "${objectId}"`);
|
||||
}
|
||||
|
||||
getDebugDom(): HTMLDivElement {
|
||||
const div = super.getDebugDom();
|
||||
div.style.height = Utils.px(this._maximumHeight);
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
import { SkinContext } from "../types";
|
||||
import * as Utils from "../utils";
|
||||
import XmlObj from "./XmlObj";
|
||||
|
||||
|
|
@ -10,11 +11,12 @@ export default class GuiObj extends XmlObj {
|
|||
_y: number = 0;
|
||||
_droptarget: string;
|
||||
_visible: boolean = true;
|
||||
_dirty: boolean = false;
|
||||
|
||||
setXmlAttr(key: string, value: string): boolean {
|
||||
switch (key) {
|
||||
case "id":
|
||||
this._id = value;
|
||||
this._id = value.toLowerCase();
|
||||
break;
|
||||
case "w":
|
||||
this._width = Utils.num(value);
|
||||
|
|
@ -40,10 +42,76 @@ export default class GuiObj extends XmlObj {
|
|||
return true;
|
||||
}
|
||||
|
||||
init() {
|
||||
init(context: SkinContext) {
|
||||
// pass
|
||||
}
|
||||
|
||||
getId(): string {
|
||||
return this._id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Trigger the show event.
|
||||
*/
|
||||
show() {
|
||||
this._visible = true;
|
||||
this._dirty = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Trigger the hide event.
|
||||
*/
|
||||
hide() {
|
||||
this._visible = false;
|
||||
this._dirty = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the Y position, in the screen, of the
|
||||
* top edge of the object.
|
||||
*
|
||||
* @ret The top edge's position (in screen coordinates).
|
||||
*/
|
||||
gettop(): number {
|
||||
return this._x;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the height of the object, in pixels.
|
||||
*
|
||||
* @ret The height of the object.
|
||||
*/
|
||||
getheight() {
|
||||
// FIXME
|
||||
return this._height || 100;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the width of the object, in pixels.
|
||||
*
|
||||
* @ret The width of the object.
|
||||
*/
|
||||
getwidth() {
|
||||
// FIXME
|
||||
return this._width || 100;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resize the object to the desired size and position.
|
||||
*
|
||||
* @param x The X position where to anchor the object before resize.
|
||||
* @param y The Y position where to anchor the object before resize.
|
||||
* @param w The width you wish the object to have.
|
||||
* @param h The height you wish the object to have.
|
||||
*/
|
||||
resize(x: number, y: number, w: number, h: number) {
|
||||
this._x = x;
|
||||
this._y = y;
|
||||
this._width = w;
|
||||
this._height = h;
|
||||
this._dirty = true;
|
||||
}
|
||||
|
||||
getDebugDom(): HTMLDivElement {
|
||||
const div = window.document.createElement("div");
|
||||
div.style.display = this._visible ? "inline-block" : "none";
|
||||
|
|
|
|||
19
packages/webamp-modern-2/src/skin/PopupMenu.ts
Normal file
19
packages/webamp-modern-2/src/skin/PopupMenu.ts
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
import BaseObject from "./BaseObject";
|
||||
|
||||
export default class PopupMenu extends BaseObject {
|
||||
addcommand(
|
||||
cmdText: string,
|
||||
cmd_id: number,
|
||||
checked: boolean,
|
||||
disabled: boolean
|
||||
) {}
|
||||
addseparator() {}
|
||||
/*
|
||||
extern PopupMenu.addSubMenu(PopupMenu submenu, String submenutext);
|
||||
extern Int PopupMenu.popAtXY(int x, int y);
|
||||
extern Int PopupMenu.popAtMouse();
|
||||
extern Int PopupMenu.getNumCommands();
|
||||
extern PopupMenu.checkCommand(int cmd_id, boolean check);
|
||||
extern PopupMenu.disableCommand(int cmd_id, boolean disable);
|
||||
*/
|
||||
}
|
||||
25
packages/webamp-modern-2/src/skin/Slider.ts
Normal file
25
packages/webamp-modern-2/src/skin/Slider.ts
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
import GuiObj from "./GuiObj";
|
||||
|
||||
// http://wiki.winamp.com/wiki/XML_GUI_Objects#.3Cslider.2F.3E_.26_.3CWasabi:HSlider.2F.3E_.26_.3CWasabi:VSlider.2F.3E
|
||||
export default class Slider extends GuiObj {
|
||||
setXmlAttr(key: string, value: string): boolean {
|
||||
if (super.setXmlAttr(key, value)) {
|
||||
return true;
|
||||
}
|
||||
switch (key) {
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
extern Slider.onSetPosition(int newpos);
|
||||
extern Slider.onPostedPosition(int newpos);
|
||||
extern Slider.onSetFinalPosition(int pos);
|
||||
extern Slider.setPosition(int pos);
|
||||
extern Int Slider.getPosition();
|
||||
extern Slider.lock(); // locks descendant core collbacks
|
||||
extern Slider.unlock(); // unloads the
|
||||
*/
|
||||
}
|
||||
16
packages/webamp-modern-2/src/skin/Status.ts
Normal file
16
packages/webamp-modern-2/src/skin/Status.ts
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
import GuiObj from "./GuiObj";
|
||||
|
||||
// Maybe this?
|
||||
// http://wiki.winamp.com/wiki/XML_GUI_Objects#.3CWasabi:StandardFrame:Status.2F.3E
|
||||
export default class Status extends GuiObj {
|
||||
setXmlAttr(key: string, value: string): boolean {
|
||||
if (super.setXmlAttr(key, value)) {
|
||||
return true;
|
||||
}
|
||||
switch (key) {
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,18 +1,30 @@
|
|||
import { interpret } from "../maki/interpreter";
|
||||
import { getClass } from "../maki/objects";
|
||||
import { ParsedMaki } from "../maki/parser";
|
||||
import { SkinContext } from "../types";
|
||||
import Button from "./Button";
|
||||
import Container from "./Container";
|
||||
|
||||
import Group from "./Group";
|
||||
import Layer from "./Layer";
|
||||
import Layout from "./Layout";
|
||||
import PopupMenu from "./PopupMenu";
|
||||
import Status from "./Status";
|
||||
import Text from "./Text";
|
||||
import ToggleButton from "./ToggleButton";
|
||||
|
||||
export default class SystemObject {
|
||||
_parentGroup: Group;
|
||||
_parsedScript: ParsedMaki;
|
||||
_context: SkinContext;
|
||||
|
||||
constructor(parsedScript: ParsedMaki) {
|
||||
this._parsedScript = parsedScript;
|
||||
}
|
||||
|
||||
init() {
|
||||
init(context: SkinContext) {
|
||||
this._context = context;
|
||||
// dumpScriptDebug(this._parsedScript);
|
||||
const initialVariable = this._parsedScript.variables[0];
|
||||
if (initialVariable.type !== "OBJECT") {
|
||||
throw new Error("First variable was not SystemObject.");
|
||||
|
|
@ -21,6 +33,12 @@ export default class SystemObject {
|
|||
// TODO: How should we setup bindings?
|
||||
// console.log(this._parsedScript.bindings);
|
||||
interpret(0, this._parsedScript, classResover);
|
||||
|
||||
interpret(
|
||||
this._parsedScript.bindings[0].commandOffset,
|
||||
this._parsedScript,
|
||||
classResover
|
||||
);
|
||||
}
|
||||
|
||||
setParentGroup(group: Group) {
|
||||
|
|
@ -35,16 +53,153 @@ export default class SystemObject {
|
|||
getskinname() {
|
||||
return "TODO: Get the Real skin name";
|
||||
}
|
||||
|
||||
/**
|
||||
* Read a private config entry of Integer type. Returns
|
||||
* the specified default value if the section and item isn't
|
||||
* found.
|
||||
*
|
||||
* @ret The value of the config entry.
|
||||
* @param section The section from which to read the entry.
|
||||
* @param item The name of the item to read.
|
||||
* @param defvalue The defautl value to return if no item is found.
|
||||
*/
|
||||
getprivateint(section: string, item: string, defvalue: number) {
|
||||
// TODO: Implement this!
|
||||
// FIXME
|
||||
return defvalue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a private config entry for your script, of Int type.
|
||||
*
|
||||
* @param section The section for the entry.
|
||||
* @param item The item name for the entry.
|
||||
* @param value The value of the entry.
|
||||
*/
|
||||
setprivateint(section: string, item: string, value: number) {
|
||||
// FIXME
|
||||
}
|
||||
|
||||
/**
|
||||
* Read the current time of the day. Returns a number that's
|
||||
* the number of milliseconds since the start of the day (0:00).
|
||||
*
|
||||
* @ret The number of milliseconds since midnight.
|
||||
**/
|
||||
gettimeofday(): number {
|
||||
const date = new Date();
|
||||
const dateTime = date.getTime();
|
||||
const dateCopy = new Date(dateTime);
|
||||
return dateTime - dateCopy.setHours(0, 0, 0, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* @ret The requested container.
|
||||
* @param container_id The containers identifier string.
|
||||
**/
|
||||
getcontainer(containerId: string): Container {
|
||||
const lower = containerId.toLowerCase();
|
||||
for (const container of this._context.containers) {
|
||||
if (container.getId() === lower) {
|
||||
return container;
|
||||
}
|
||||
}
|
||||
throw new Error(`Could not find a container with the id; "${containerId}"`);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the value of the left vu meter.
|
||||
* Range is from 0 to 255. Linear.
|
||||
*
|
||||
* @ret The value of the left vu meter.
|
||||
*/
|
||||
getleftvumeter(): number {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the value of the right vu meter.
|
||||
* Range is from 0 to 255. Linear.
|
||||
*
|
||||
* @ret The value of the right vu meter.
|
||||
*/
|
||||
getrightvumeter(): number {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current volume. Range is from 0 to 255.
|
||||
*
|
||||
* @ret The current volume.
|
||||
**/
|
||||
getvolume() {
|
||||
return 100;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the string representation of an integer.
|
||||
*
|
||||
* @ret The string equivalent of the integer.
|
||||
* @param value The integer to change into a string.
|
||||
*/
|
||||
integertostring(value: number): string {
|
||||
return String(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the user's screen width in pixels.
|
||||
*
|
||||
* @ret The width of the user's screen.
|
||||
*/
|
||||
getviewportwidth() {
|
||||
return window.document.documentElement.clientWidth;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the user's screen height in pixels.
|
||||
*
|
||||
* @ret The height of the user's screen.
|
||||
*/
|
||||
getviewportheight() {
|
||||
return window.document.documentElement.clientHeight;
|
||||
}
|
||||
}
|
||||
|
||||
function classResover(guid: string): any {
|
||||
switch (guid) {
|
||||
case "d6f50f6449b793fa66baf193983eaeef":
|
||||
return SystemObject;
|
||||
case "e90dc47b4ae7840d0b042cb0fcf775d2":
|
||||
return Container;
|
||||
case "60906d4e482e537e94cc04b072568861":
|
||||
return Layout;
|
||||
case "5ab9fa1545579a7d5765c8aba97cc6a6":
|
||||
return Layer;
|
||||
case "f4787af44ef7b2bb4be7fb9c8da8bea9":
|
||||
return PopupMenu;
|
||||
case "698eddcd4fec8f1e44f9129b45ff09f9":
|
||||
return Button;
|
||||
case "b4dccfff4bcc81fe0f721b96ff0fbed5":
|
||||
return ToggleButton;
|
||||
case "efaa867241fa310ea985dcb74bcb5b52":
|
||||
return Text;
|
||||
case "0f08c9404b23af39c4b8f38059bb7e8f":
|
||||
return Status;
|
||||
default:
|
||||
console.log();
|
||||
throw new Error(
|
||||
`Unresolvable class "${getClass(guid).name}" (guid: ${guid})`
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
function dumpScriptDebug(script: ParsedMaki) {
|
||||
for (const [i, binding] of script.bindings.entries()) {
|
||||
const method = script.methods[binding.methodOffset];
|
||||
const guid = script.classes[method.typeOffset];
|
||||
const klass = getClass(guid);
|
||||
console.log(
|
||||
`${i}. ${klass.name}.${method.name} => ${binding.commandOffset}`
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
24
packages/webamp-modern-2/src/skin/Text.ts
Normal file
24
packages/webamp-modern-2/src/skin/Text.ts
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
import GuiObj from "./GuiObj";
|
||||
|
||||
// http://wiki.winamp.com/wiki/XML_GUI_Objects#.3Ctext.2F.3E_.26_.3CWasabi:Text.2F.3E
|
||||
export default class Text extends GuiObj {
|
||||
setXmlAttr(key: string, value: string): boolean {
|
||||
if (super.setXmlAttr(key, value)) {
|
||||
return true;
|
||||
}
|
||||
switch (key) {
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
extern Text.setText(String txt); // changes the display/text="something" param
|
||||
extern Text.setAlternateText(String txt); // overrides the display/text parameter with a custom string, set "" to cancel
|
||||
extern String Text.getText();
|
||||
extern int Text.getTextWidth();
|
||||
extern Text.onTextChanged(String newtxt);
|
||||
*/
|
||||
}
|
||||
20
packages/webamp-modern-2/src/skin/ToggleButton.ts
Normal file
20
packages/webamp-modern-2/src/skin/ToggleButton.ts
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
import Button from "./Button";
|
||||
|
||||
// http://wiki.winamp.com/wiki/XML_GUI_Objects#.3Cbutton.2F.3E_.26_.3Ctogglebutton.2F.3E
|
||||
export default class ToggleButton extends Button {
|
||||
setXmlAttr(key: string, value: string): boolean {
|
||||
if (super.setXmlAttr(key, value)) {
|
||||
return true;
|
||||
}
|
||||
switch (key) {
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
extern ToggleButton.onToggle(Boolean onoff);
|
||||
extern int TOggleButton.getCurCfgVal()
|
||||
*/
|
||||
}
|
||||
|
|
@ -8,4 +8,8 @@ export default class XmlObj {
|
|||
setXmlAttr(_key: string, _value: string): boolean {
|
||||
return false;
|
||||
}
|
||||
|
||||
setxmlparam(key: string, value: string) {
|
||||
this.setXmlAttr(key, value);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,8 +8,13 @@ import Layout from "./Layout";
|
|||
import Group from "./Group";
|
||||
import Container from "./Container";
|
||||
import Layer from "./Layer";
|
||||
import Slider from "./Slider";
|
||||
import Button from "./Button";
|
||||
import Text from "./Text";
|
||||
import Status from "./Status";
|
||||
import { parse as parseMaki } from "../maki/parser";
|
||||
import SystemObject from "./SystemObject";
|
||||
import ToggleButton from "./ToggleButton";
|
||||
|
||||
class ParserContext {
|
||||
container: Container | null = null;
|
||||
|
|
@ -154,7 +159,16 @@ export default class SkinParser {
|
|||
"Unexpected children in <text> XML node."
|
||||
);
|
||||
|
||||
// TODO: Parse text
|
||||
const text = new Text();
|
||||
text.setXmlAttributes(node.attributes);
|
||||
const { parentGroup } = this._context;
|
||||
if (parentGroup == null) {
|
||||
console.warn(
|
||||
`FIXME: Expected <Text id="${text._id}"> to be within a <Layout> | <Group>`
|
||||
);
|
||||
return;
|
||||
}
|
||||
parentGroup.addChild(text);
|
||||
}
|
||||
|
||||
async script(node: XmlElement) {
|
||||
|
|
@ -202,7 +216,16 @@ export default class SkinParser {
|
|||
"Unexpected children in <button> XML node."
|
||||
);
|
||||
|
||||
// TODO: Parse buttons
|
||||
const button = new Button();
|
||||
button.setXmlAttributes(node.attributes);
|
||||
const { parentGroup } = this._context;
|
||||
if (parentGroup == null) {
|
||||
console.warn(
|
||||
`FIXME: Expected <Button id="${button._id}"> to be within a <Layout> | <Group>`
|
||||
);
|
||||
return;
|
||||
}
|
||||
parentGroup.addChild(button);
|
||||
}
|
||||
|
||||
async wasabiButton(node: XmlElement) {
|
||||
|
|
@ -220,7 +243,16 @@ export default class SkinParser {
|
|||
"Unexpected children in <button> XML node."
|
||||
);
|
||||
|
||||
// TODO: Parse buttons
|
||||
const button = new ToggleButton();
|
||||
button.setXmlAttributes(node.attributes);
|
||||
const { parentGroup } = this._context;
|
||||
if (parentGroup == null) {
|
||||
console.warn(
|
||||
`FIXME: Expected <ToggleButton id="${button._id}"> to be within a <Layout> | <Group>`
|
||||
);
|
||||
return;
|
||||
}
|
||||
parentGroup.addChild(button);
|
||||
}
|
||||
|
||||
async color(node: XmlElement) {
|
||||
|
|
@ -238,7 +270,16 @@ export default class SkinParser {
|
|||
"Unexpected children in <slider> XML node."
|
||||
);
|
||||
|
||||
// TODO: Parse slider
|
||||
const slider = new Slider();
|
||||
slider.setXmlAttributes(node.attributes);
|
||||
const { parentGroup } = this._context;
|
||||
if (parentGroup == null) {
|
||||
console.warn(
|
||||
`FIXME: Expected <Slider id="${slider._id}"> to be within a <Layout> | <Group>`
|
||||
);
|
||||
return;
|
||||
}
|
||||
parentGroup.addChild(slider);
|
||||
}
|
||||
|
||||
async groupdef(node: XmlElement) {
|
||||
|
|
@ -246,6 +287,11 @@ export default class SkinParser {
|
|||
}
|
||||
|
||||
async layer(node: XmlElement) {
|
||||
assume(
|
||||
node.children.length === 0,
|
||||
"Unexpected children in <layer> XML node."
|
||||
);
|
||||
|
||||
const layer = new Layer();
|
||||
layer.setXmlAttributes(node.attributes);
|
||||
const { parentGroup } = this._context;
|
||||
|
|
@ -256,7 +302,6 @@ export default class SkinParser {
|
|||
return;
|
||||
}
|
||||
parentGroup.addChild(layer);
|
||||
await this.traverseChildren(node);
|
||||
}
|
||||
|
||||
async layout(node: XmlElement) {
|
||||
|
|
@ -318,6 +363,17 @@ export default class SkinParser {
|
|||
node.children.length === 0,
|
||||
"Unexpected children in <status> XML node."
|
||||
);
|
||||
|
||||
const status = new Status();
|
||||
status.setXmlAttributes(node.attributes);
|
||||
const { parentGroup } = this._context;
|
||||
if (parentGroup == null) {
|
||||
console.warn(
|
||||
`FIXME: Expected <Status id="${status._id}"> to be within a <Layout> | <Group>`
|
||||
);
|
||||
return;
|
||||
}
|
||||
parentGroup.addChild(status);
|
||||
}
|
||||
async eqvis(node: XmlElement) {
|
||||
assume(
|
||||
|
|
|
|||
5
packages/webamp-modern-2/src/types.ts
Normal file
5
packages/webamp-modern-2/src/types.ts
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
import Container from "./skin/Container";
|
||||
|
||||
export type SkinContext = {
|
||||
containers: Container[];
|
||||
};
|
||||
Loading…
Add table
Add a link
Reference in a new issue