Initial commit

This commit is contained in:
2026-02-02 04:50:13 +01:00
commit 5b11698731
22592 changed files with 7677434 additions and 0 deletions

View File

@@ -0,0 +1,12 @@
This directory is coming straight from the ProjectorRays project
https://github.com/ProjectorRays/ProjectorRays/tree/master/src/lingodec
And should be kept in sync.
At the time of writing, the changes have not yet been merged within
ProjectorRays and as such, live in 'scummvm' branch:
https://github.com/ProjectorRays/ProjectorRays/tree/scummvm/src/lingodec
The original code is licensed under the Mozilla Public License 2.0.

View File

@@ -0,0 +1,274 @@
/*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
#include "common/util.h"
#include "./ast.h"
#include "./handler.h"
#include "./names.h"
#include "./script.h"
namespace LingoDec {
/* Datum */
int Datum::toInt() {
switch (type) {
case kDatumInt:
return i;
case kDatumFloat:
return f;
default:
break;
}
return 0;
}
/* AST */
void AST::addStatement(Common::SharedPtr<Node> statement) {
currentBlock->addChild(Common::move(statement));
}
void AST::enterBlock(BlockNode *block) {
currentBlock = block;
}
void AST::exitBlock() {
auto ancestorStatement = currentBlock->ancestorStatement();
if (!ancestorStatement) {
currentBlock = nullptr;
return;
}
ancestorStatement->_endOffset = currentBlock->_endOffset;
auto block = ancestorStatement->parent;
if (!block || block->type != kBlockNode) {
currentBlock = nullptr;
return;
}
currentBlock = static_cast<BlockNode *>(block);
}
/* Node */
Common::SharedPtr<Datum> Node::getValue() {
return Common::SharedPtr<Datum>(new Datum());
}
Node *Node::ancestorStatement() {
Node *ancestor = parent;
while (ancestor && !ancestor->isStatement) {
ancestor = ancestor->parent;
}
return ancestor;
}
LoopNode *Node::ancestorLoop() {
Node *ancestor = parent;
while (ancestor && !ancestor->isLoop) {
ancestor = ancestor->parent;
}
return static_cast<LoopNode *>(ancestor);
}
bool Node::hasSpaces(bool) {
return true;
}
/* ErrorNode */
bool ErrorNode::hasSpaces(bool) {
return false;
}
/* LiteralNode */
Common::SharedPtr<Datum> LiteralNode::getValue() {
return value;
}
bool LiteralNode::hasSpaces(bool) {
return false;
}
/* BlockNode */
void BlockNode::addChild(Common::SharedPtr<Node> child) {
child->parent = this;
children.push_back(Common::move(child));
}
/* BinaryOpNode */
unsigned int BinaryOpNode::getPrecedence() const {
switch (opcode) {
case kOpMul:
case kOpDiv:
case kOpMod:
return 1;
case kOpAdd:
case kOpSub:
return 2;
case kOpLt:
case kOpLtEq:
case kOpNtEq:
case kOpEq:
case kOpGt:
case kOpGtEq:
return 3;
case kOpAnd:
return 4;
case kOpOr:
return 5;
default:
break;
}
return 0;
}
/* MemberExprNode */
bool MemberExprNode::hasSpaces(bool dot) {
return !dot;
}
/* VarNode */
bool VarNode::hasSpaces(bool) {
return false;
}
/* CaseStmtNode */
void CaseStmtNode::addOtherwise(uint32 offset) {
otherwise = Common::SharedPtr<OtherwiseNode>(new OtherwiseNode(offset));
otherwise->parent = this;
otherwise->block->endPos = endPos;
}
/* CallNode */
bool CallNode::noParens() const {
if (isStatement) {
// TODO: Make a complete list of commonly paren-less commands
if (name == "put")
return true;
if (name == "return")
return true;
}
return false;
}
bool CallNode::isMemberExpr() const {
if (isExpression) {
size_t nargs = argList->getValue()->l.size();
if (name == "cast" && (nargs == 1 || nargs == 2))
return true;
if (name == "member" && (nargs == 1 || nargs == 2))
return true;
if (name == "script" && (nargs == 1 || nargs == 2))
return true;
if (name == "castLib" && nargs == 1)
return true;
if (name == "window" && nargs == 1)
return true;
}
return false;
}
bool CallNode::hasSpaces(bool dot) {
if (!dot && isMemberExpr())
return true;
if (noParens())
return true;
return false;
}
/* ObjCallNode */
bool ObjCallNode::hasSpaces(bool) {
return false;
}
/* ObjCallV4Node */
bool ObjCallV4Node::hasSpaces(bool) {
return false;
}
/* ObjPropExprNode */
bool ObjPropExprNode::hasSpaces(bool dot) {
return !dot;
}
/* ObjBracketExprNode */
bool ObjBracketExprNode::hasSpaces(bool) {
return false;
}
/* ObjPropIndexExprNode */
bool ObjPropIndexExprNode::hasSpaces(bool) {
return false;
}
void ErrorNode::accept(NodeVisitor &visitor) const { visitor.visit(*this); }
void CommentNode::accept(NodeVisitor &visitor) const { visitor.visit(*this); }
void LiteralNode::accept(NodeVisitor &visitor) const { visitor.visit(*this); }
void IfStmtNode::accept(NodeVisitor &visitor) const { visitor.visit(*this); }
void NewObjNode::accept(NodeVisitor &visitor) const { visitor.visit(*this); }
void EndCaseNode::accept(NodeVisitor &visitor) const { visitor.visit(*this); }
void HandlerNode::accept(NodeVisitor &visitor) const { visitor.visit(*this); }
void ObjCallNode::accept(NodeVisitor &visitor) const { visitor.visit(*this); }
void PutStmtNode::accept(NodeVisitor &visitor) const { visitor.visit(*this); }
void TheExprNode::accept(NodeVisitor &visitor) const { visitor.visit(*this); }
void BinaryOpNode::accept(NodeVisitor &visitor) const { visitor.visit(*this); }
void CaseStmtNode::accept(NodeVisitor &visitor) const { visitor.visit(*this); }
void ExitStmtNode::accept(NodeVisitor &visitor) const { visitor.visit(*this); }
void TellStmtNode::accept(NodeVisitor &visitor) const { visitor.visit(*this); }
void WhenStmtNode::accept(NodeVisitor &visitor) const { visitor.visit(*this); }
void CaseLabelNode::accept(NodeVisitor &visitor) const { visitor.visit(*this); }
void ChunkExprNode::accept(NodeVisitor &visitor) const { visitor.visit(*this); }
void InverseOpNode::accept(NodeVisitor &visitor) const { visitor.visit(*this); }
void ObjCallV4Node::accept(NodeVisitor &visitor) const { visitor.visit(*this); }
void OtherwiseNode::accept(NodeVisitor &visitor) const { visitor.visit(*this); }
void MemberExprNode::accept(NodeVisitor &visitor) const { visitor.visit(*this); }
void ObjPropExprNode::accept(NodeVisitor &visitor) const { visitor.visit(*this); }
void PlayCmdStmtNode::accept(NodeVisitor &visitor) const { visitor.visit(*this); }
void ThePropExprNode::accept(NodeVisitor &visitor) const { visitor.visit(*this); }
void MenuPropExprNode::accept(NodeVisitor &visitor) const { visitor.visit(*this); }
void SoundCmdStmtNode::accept(NodeVisitor &visitor) const { visitor.visit(*this); }
void SoundPropExprNode::accept(NodeVisitor &visitor) const { visitor.visit(*this); }
void AssignmentStmtNode::accept(NodeVisitor &visitor) const { visitor.visit(*this); }
void ExitRepeatStmtNode::accept(NodeVisitor &visitor) const { visitor.visit(*this); }
void NextRepeatStmtNode::accept(NodeVisitor &visitor) const { visitor.visit(*this); }
void ObjBracketExprNode::accept(NodeVisitor &visitor) const { visitor.visit(*this); }
void SpritePropExprNode::accept(NodeVisitor &visitor) const { visitor.visit(*this); }
void ChunkDeleteStmtNode::accept(NodeVisitor &visitor) const { visitor.visit(*this); }
void ChunkHiliteStmtNode::accept(NodeVisitor &visitor) const { visitor.visit(*this); }
void RepeatWhileStmtNode::accept(NodeVisitor &visitor) const { visitor.visit(*this); }
void MenuItemPropExprNode::accept(NodeVisitor &visitor) const { visitor.visit(*this); }
void ObjPropIndexExprNode::accept(NodeVisitor &visitor) const { visitor.visit(*this); }
void RepeatWithInStmtNode::accept(NodeVisitor &visitor) const { visitor.visit(*this); }
void RepeatWithToStmtNode::accept(NodeVisitor &visitor) const { visitor.visit(*this); }
void SpriteWithinExprNode::accept(NodeVisitor &visitor) const { visitor.visit(*this); }
void LastStringChunkExprNode::accept(NodeVisitor &visitor) const { visitor.visit(*this); }
void SpriteIntersectsExprNode::accept(NodeVisitor &visitor) const { visitor.visit(*this); }
void StringChunkCountExprNode::accept(NodeVisitor &visitor) const { visitor.visit(*this); }
void VarNode::accept(NodeVisitor &visitor) const { visitor.visit(*this); }
void CallNode::accept(NodeVisitor &visitor) const { visitor.visit(*this); }
void BlockNode::accept(NodeVisitor &visitor) const { visitor.visit(*this); }
void NotOpNode::accept(NodeVisitor &visitor) const { visitor.visit(*this); }
} // namespace LingoDec

View File

@@ -0,0 +1,864 @@
/*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
#ifndef LINGODEC_AST_H
#define LINGODEC_AST_H
#include "common/array.h"
#include "common/ptr.h"
#include "common/str.h"
#include "common/util.h"
#include "./enums.h"
namespace LingoDec {
struct CaseLabelNode;
struct Handler;
struct LoopNode;
struct Node;
struct RepeatWithInStmtNode;
/* Datum */
struct Datum {
DatumType type;
int i = -1;
double f = 0.0f;
Common::String s;
Common::Array<Common::SharedPtr<Node>> l;
Datum() {
type = kDatumVoid;
}
Datum(int val) {
type = kDatumInt;
i = val;
}
Datum(double val) {
type = kDatumFloat;
f = val;
}
Datum(DatumType t, Common::String val) {
type = t;
s = val;
}
Datum(DatumType t, Common::Array<Common::SharedPtr<Node>> val) {
type = t;
l = val;
}
int toInt();
};
class NodeVisitor;
/* Node */
struct Node {
NodeType type;
bool isExpression;
bool isStatement;
bool isLabel;
bool isLoop;
Node *parent;
uint32 _startOffset;
uint32 _endOffset;
Node(NodeType t, uint32 offset) : type(t), isExpression(false), isStatement(false), isLabel(false), isLoop(false), parent(nullptr), _startOffset(offset), _endOffset(offset) {}
virtual ~Node() {}
virtual void accept(NodeVisitor& visitor) const = 0;
virtual Common::SharedPtr<Datum> getValue();
Node *ancestorStatement();
LoopNode *ancestorLoop();
virtual bool hasSpaces(bool dot);
};
/* ExprNode */
struct ExprNode : Node {
ExprNode(NodeType t, uint32 offset) : Node(t, offset) {
isExpression = true;
}
};
/* StmtNode */
struct StmtNode : Node {
StmtNode(NodeType t, uint32 offset) : Node(t, offset) {
isStatement = true;
}
};
/* LabelNode */
struct LabelNode : Node {
LabelNode(NodeType t, uint32 offset) : Node(t, offset) {
isLabel = true;
}
};
/* LoopNode */
struct LoopNode : StmtNode {
uint32 startIndex;
LoopNode(NodeType t, uint32 startIndex_, uint32 offset) : StmtNode(t, offset), startIndex(startIndex_) {
isLoop = true;
}
};
/* ErrorNode */
struct ErrorNode : ExprNode {
explicit ErrorNode(uint32 offset) : ExprNode(kErrorNode, offset) {}
bool hasSpaces(bool dot) override;
void accept(NodeVisitor &visitor) const override;
};
/* CommentNode */
struct CommentNode : Node {
Common::String text;
CommentNode(uint32 offset, Common::String t) : Node(kCommentNode, offset), text(t) {}
void accept(NodeVisitor &visitor) const override;
};
/* LiteralNode */
struct LiteralNode : ExprNode {
Common::SharedPtr<Datum> value;
LiteralNode(uint32 offset, Common::SharedPtr<Datum> d) : ExprNode(kLiteralNode, offset) {
value = Common::move(d);
}
Common::SharedPtr<Datum> getValue() override;
bool hasSpaces(bool dot) override;
void accept(NodeVisitor &visitor) const override;
};
/* BlockNode */
struct BlockNode : Node {
Common::Array<Common::SharedPtr<Node>> children;
// for use during translation:
uint32 endPos;
CaseLabelNode *currentCaseLabel = nullptr;
explicit BlockNode(uint32 offset) : Node(kBlockNode, offset), endPos(Common::String::npos) {}
void addChild(Common::SharedPtr<Node> child);
void accept(NodeVisitor &visitor) const override;
};
/* HandlerNode */
struct HandlerNode : Node {
Handler *handler;
Common::SharedPtr<BlockNode> block;
HandlerNode(uint32 offset, Handler *h)
: Node(kHandlerNode, offset), handler(h) {
block = Common::SharedPtr<BlockNode>(new BlockNode(offset));
block->parent = this;
}
void accept(NodeVisitor &visitor) const override;
};
/* ExitStmtNode */
struct ExitStmtNode : StmtNode {
explicit ExitStmtNode(uint32 offset) : StmtNode(kExitStmtNode, offset) {}
void accept(NodeVisitor &visitor) const override;
};
/* InverseOpNode */
struct InverseOpNode : ExprNode {
Common::SharedPtr<Node> operand;
InverseOpNode(uint32 offset, Common::SharedPtr<Node> o) : ExprNode(kInverseOpNode, offset) {
operand = Common::move(o);
operand->parent = this;
}
void accept(NodeVisitor &visitor) const override;
};
/* NotOpNode */
struct NotOpNode : ExprNode {
Common::SharedPtr<Node> operand;
NotOpNode(uint32 offset, Common::SharedPtr<Node> o) : ExprNode(kNotOpNode, offset) {
operand = Common::move(o);
operand->parent = this;
}
void accept(NodeVisitor &visitor) const override;
};
/* BinaryOpNode */
struct BinaryOpNode : ExprNode {
OpCode opcode;
Common::SharedPtr<Node> left;
Common::SharedPtr<Node> right;
BinaryOpNode(uint32 offset, OpCode op, Common::SharedPtr<Node> a, Common::SharedPtr<Node> b)
: ExprNode(kBinaryOpNode, offset), opcode(op) {
left = Common::move(a);
left->parent = this;
right = Common::move(b);
right->parent = this;
}
virtual unsigned int getPrecedence() const;
void accept(NodeVisitor &visitor) const override;
};
/* ChunkExprNode */
struct ChunkExprNode : ExprNode {
ChunkExprType type;
Common::SharedPtr<Node> first;
Common::SharedPtr<Node> last;
Common::SharedPtr<Node> string;
ChunkExprNode(uint32 offset, ChunkExprType t, Common::SharedPtr<Node> a, Common::SharedPtr<Node> b, Common::SharedPtr<Node> s)
: ExprNode(kChunkExprNode, offset), type(t) {
first = Common::move(a);
first->parent = this;
last = Common::move(b);
last->parent = this;
string = Common::move(s);
string->parent = this;
}
void accept(NodeVisitor &visitor) const override;
};
/* ChunkHiliteStmtNode */
struct ChunkHiliteStmtNode : StmtNode {
Common::SharedPtr<Node> chunk;
ChunkHiliteStmtNode(uint32 offset, Common::SharedPtr<Node> c) : StmtNode(kChunkHiliteStmtNode, offset) {
chunk = Common::move(c);
chunk->parent = this;
}
void accept(NodeVisitor &visitor) const override;
};
/* ChunkDeleteStmtNode */
struct ChunkDeleteStmtNode : StmtNode {
Common::SharedPtr<Node> chunk;
ChunkDeleteStmtNode(uint32 offset, Common::SharedPtr<Node> c) : StmtNode(kChunkDeleteStmtNode, offset) {
chunk = Common::move(c);
chunk->parent = this;
}
void accept(NodeVisitor &visitor) const override;
};
/* SpriteIntersectsExprNode */
struct SpriteIntersectsExprNode : ExprNode {
Common::SharedPtr<Node> firstSprite;
Common::SharedPtr<Node> secondSprite;
SpriteIntersectsExprNode(uint32 offset, Common::SharedPtr<Node> a, Common::SharedPtr<Node> b)
: ExprNode(kSpriteIntersectsExprNode, offset) {
firstSprite = Common::move(a);
firstSprite->parent = this;
secondSprite = Common::move(b);
secondSprite->parent = this;
}
void accept(NodeVisitor &visitor) const override;
};
/* SpriteWithinExprNode */
struct SpriteWithinExprNode : ExprNode {
Common::SharedPtr<Node> firstSprite;
Common::SharedPtr<Node> secondSprite;
SpriteWithinExprNode(uint32 offset, Common::SharedPtr<Node> a, Common::SharedPtr<Node> b)
: ExprNode(kSpriteWithinExprNode, offset) {
firstSprite = Common::move(a);
firstSprite->parent = this;
secondSprite = Common::move(b);
secondSprite->parent = this;
}
void accept(NodeVisitor &visitor) const override;
};
/* MemberExprNode */
struct MemberExprNode : ExprNode {
Common::String type;
Common::SharedPtr<Node> memberID;
Common::SharedPtr<Node> castID;
MemberExprNode(uint32 offset, Common::String type_, Common::SharedPtr<Node> memberID_, Common::SharedPtr<Node> castID_)
: ExprNode(kMemberExprNode, offset), type(type_) {
this->memberID = Common::move(memberID_);
this->memberID->parent = this;
if (castID_) {
this->castID = Common::move(castID_);
this->castID->parent = this;
}
}
bool hasSpaces(bool dot) override;
void accept(NodeVisitor &visitor) const override;
};
/* VarNode */
struct VarNode : ExprNode {
Common::String varName;
VarNode(uint32 offset, Common::String v) : ExprNode(kVarNode, offset), varName(v) {}
bool hasSpaces(bool dot) override;
void accept(NodeVisitor &visitor) const override;
};
/* AssignmentStmtNode */
struct AssignmentStmtNode : StmtNode {
Common::SharedPtr<Node> variable;
Common::SharedPtr<Node> value;
bool forceVerbose;
AssignmentStmtNode(uint32 offset, Common::SharedPtr<Node> var, Common::SharedPtr<Node> val, bool forceVerbose_ = false)
: StmtNode(kAssignmentStmtNode, offset), forceVerbose(forceVerbose_) {
variable = Common::move(var);
variable->parent = this;
value = Common::move(val);
value->parent = this;
}
void accept(NodeVisitor &visitor) const override;
};
/* IfStmtNode */
struct IfStmtNode : StmtNode {
bool hasElse;
Common::SharedPtr<Node> condition;
Common::SharedPtr<BlockNode> block1;
Common::SharedPtr<BlockNode> block2;
IfStmtNode(uint32 offset, Common::SharedPtr<Node> c) : StmtNode(kIfStmtNode, offset), hasElse(false) {
condition = Common::move(c);
condition->parent = this;
block1 = Common::SharedPtr<BlockNode>(new BlockNode(offset));
block1->parent = this;
block2 = Common::SharedPtr<BlockNode>(new BlockNode(offset));
block2->parent = this;
}
void accept(NodeVisitor &visitor) const override;
};
/* RepeatWhileStmtNode */
struct RepeatWhileStmtNode : LoopNode {
Common::SharedPtr<Node> condition;
Common::SharedPtr<BlockNode> block;
RepeatWhileStmtNode(uint32 startIndex_, Common::SharedPtr<Node> c, uint32 offset)
: LoopNode(kRepeatWhileStmtNode, startIndex_, offset) {
condition = Common::move(c);
condition->parent = this;
block = Common::SharedPtr<BlockNode>(new BlockNode(offset));
block->parent = this;
}
void accept(NodeVisitor &visitor) const override;
};
/* RepeatWithInStmtNode */
struct RepeatWithInStmtNode : LoopNode {
Common::String varName;
Common::SharedPtr<Node> list;
Common::SharedPtr<BlockNode> block;
RepeatWithInStmtNode(uint32 startIndex_, Common::String v, Common::SharedPtr<Node> l, uint32 offset)
: LoopNode(kRepeatWithInStmtNode, startIndex_, offset) {
varName = v;
list = Common::move(l);
list->parent = this;
block = Common::SharedPtr<BlockNode>(new BlockNode(offset));
block->parent = this;
}
void accept(NodeVisitor &visitor) const override;
};
/* RepeatWithToStmtNode */
struct RepeatWithToStmtNode : LoopNode {
Common::String varName;
Common::SharedPtr<Node> start;
bool up;
Common::SharedPtr<Node> end;
Common::SharedPtr<BlockNode> block;
RepeatWithToStmtNode(uint32 startIndex_, Common::String v, Common::SharedPtr<Node> s, bool _up, Common::SharedPtr<Node> e, uint32 offset)
: LoopNode(kRepeatWithToStmtNode, startIndex_, offset), up(_up) {
varName = v;
start = Common::move(s);
start->parent = this;
end = Common::move(e);
end->parent = this;
block = Common::SharedPtr<BlockNode>(new BlockNode(offset));
block->parent = this;
}
void accept(NodeVisitor &visitor) const override;
};
/* CaseLabelNode */
struct CaseLabelNode : LabelNode {
Common::SharedPtr<Node> value;
CaseExpect expect;
Common::SharedPtr<CaseLabelNode> nextOr;
Common::SharedPtr<CaseLabelNode> nextLabel;
Common::SharedPtr<BlockNode> block;
CaseLabelNode(uint32 offset, Common::SharedPtr<Node> v, CaseExpect e) : LabelNode(kCaseLabelNode, offset), expect(e) {
value = Common::move(v);
value->parent = this;
}
void accept(NodeVisitor &visitor) const override;
};
/* OtherwiseNode */
struct OtherwiseNode : LabelNode {
Common::SharedPtr<BlockNode> block;
explicit OtherwiseNode(uint32 offset) : LabelNode(kOtherwiseNode, offset) {
block = Common::SharedPtr<BlockNode>(new BlockNode(offset));
block->parent = this;
}
void accept(NodeVisitor &visitor) const override;
};
/* EndCaseNode */
struct EndCaseNode : LabelNode {
explicit EndCaseNode(uint32 offset) : LabelNode(kEndCaseNode, offset) {}
void accept(NodeVisitor &visitor) const override;
};
/* CaseStmtNode */
struct CaseStmtNode : StmtNode {
Common::SharedPtr<Node> value;
Common::SharedPtr<CaseLabelNode> firstLabel;
Common::SharedPtr<OtherwiseNode> otherwise;
// for use during translation:
int32 endPos = -1;
int32 potentialOtherwisePos = -1;
CaseStmtNode(uint32 offset, Common::SharedPtr<Node> v) : StmtNode(kCaseStmtNode, offset) {
value = Common::move(v);
value->parent = this;
}
void addOtherwise(uint32 offset);
void accept(NodeVisitor &visitor) const override;
};
/* TellStmtNode */
struct TellStmtNode : StmtNode {
Common::SharedPtr<Node> window;
Common::SharedPtr<BlockNode> block;
TellStmtNode(uint32 offset, Common::SharedPtr<Node> w) : StmtNode(kTellStmtNode, offset) {
window = Common::move(w);
window->parent = this;
block = Common::SharedPtr<BlockNode>(new BlockNode(offset));
block->parent = this;
}
void accept(NodeVisitor &visitor) const override;
};
/* SoundCmdStmtNode */
struct SoundCmdStmtNode : StmtNode {
Common::String cmd;
Common::SharedPtr<Node> argList;
SoundCmdStmtNode(uint32 offset, Common::String c, Common::SharedPtr<Node> a) : StmtNode(kSoundCmdStmtNode, offset) {
cmd = c;
argList = Common::move(a);
argList->parent = this;
}
void accept(NodeVisitor &visitor) const override;
};
/* PlayCmdStmtNode */
struct PlayCmdStmtNode : StmtNode {
Common::SharedPtr<Node> argList;
PlayCmdStmtNode(uint32 offset, Common::SharedPtr<Node> a) : StmtNode(kPlayCmdStmtNode, offset) {
argList = Common::move(a);
argList->parent = this;
}
void accept(NodeVisitor &visitor) const override;
};
/* CallNode */
struct CallNode : Node {
Common::String name;
Common::SharedPtr<Node> argList;
CallNode(uint32 offset, Common::String n, Common::SharedPtr<Node> a) : Node(kCallNode, offset) {
name = n;
argList = Common::move(a);
argList->parent = this;
if (argList->getValue()->type == kDatumArgListNoRet)
isStatement = true;
else
isExpression = true;
}
bool noParens() const;
bool isMemberExpr() const;
bool hasSpaces(bool dot) override;
void accept(NodeVisitor &visitor) const override;
};
/* ObjCallNode */
struct ObjCallNode : Node {
Common::String name;
Common::SharedPtr<Node> argList;
ObjCallNode(uint32 offset, Common::String n, Common::SharedPtr<Node> a) : Node(kObjCallNode, offset) {
name = n;
argList = Common::move(a);
argList->parent = this;
if (argList->getValue()->type == kDatumArgListNoRet)
isStatement = true;
else
isExpression = true;
}
bool hasSpaces(bool dot) override;
void accept(NodeVisitor &visitor) const override;
};
/* ObjCallV4Node */
struct ObjCallV4Node : Node {
Common::SharedPtr<Node> obj;
Common::SharedPtr<Node> argList;
ObjCallV4Node(uint32 offset, Common::SharedPtr<Node> o, Common::SharedPtr<Node> a) : Node(kObjCallV4Node, offset) {
obj = o;
argList = Common::move(a);
argList->parent = this;
if (argList->getValue()->type == kDatumArgListNoRet)
isStatement = true;
else
isExpression = true;
}
bool hasSpaces(bool dot) override;
void accept(NodeVisitor &visitor) const override;
};
/* TheExprNode */
struct TheExprNode : ExprNode {
Common::String prop;
TheExprNode(uint32 offset, Common::String p) : ExprNode(kTheExprNode, offset), prop(p) {}
void accept(NodeVisitor &visitor) const override;
};
/* LastStringChunkExprNode */
struct LastStringChunkExprNode : ExprNode {
ChunkExprType type;
Common::SharedPtr<Node> obj;
LastStringChunkExprNode(uint32 offset, ChunkExprType t, Common::SharedPtr<Node> o)
: ExprNode(kLastStringChunkExprNode, offset), type(t) {
obj = Common::move(o);
obj->parent = this;
}
void accept(NodeVisitor &visitor) const override;
};
/* StringChunkCountExprNode */
struct StringChunkCountExprNode : ExprNode {
ChunkExprType type;
Common::SharedPtr<Node> obj;
StringChunkCountExprNode(uint32 offset, ChunkExprType t, Common::SharedPtr<Node> o)
: ExprNode(kStringChunkCountExprNode, offset), type(t) {
obj = Common::move(o);
obj->parent = this;
}
void accept(NodeVisitor &visitor) const override;
};
/* MenuPropExprNode */
struct MenuPropExprNode : ExprNode {
Common::SharedPtr<Node> menuID;
unsigned int prop;
MenuPropExprNode(uint32 offset, Common::SharedPtr<Node> m, unsigned int p)
: ExprNode(kMenuPropExprNode, offset), prop(p) {
menuID = Common::move(m);
menuID->parent = this;
}
void accept(NodeVisitor &visitor) const override;
};
/* MenuItemPropExprNode */
struct MenuItemPropExprNode : ExprNode {
Common::SharedPtr<Node> menuID;
Common::SharedPtr<Node> itemID;
unsigned int prop;
MenuItemPropExprNode(uint32 offset, Common::SharedPtr<Node> m, Common::SharedPtr<Node> i, unsigned int p)
: ExprNode(kMenuItemPropExprNode, offset), prop(p) {
menuID = Common::move(m);
menuID->parent = this;
itemID = Common::move(i);
itemID->parent = this;
}
void accept(NodeVisitor &visitor) const override;
};
/* SoundPropExprNode */
struct SoundPropExprNode : ExprNode {
Common::SharedPtr<Node> soundID;
unsigned int prop;
SoundPropExprNode(uint32 offset, Common::SharedPtr<Node> s, unsigned int p)
: ExprNode(kSoundPropExprNode, offset), prop(p) {
soundID = Common::move(s);
soundID->parent = this;
}
void accept(NodeVisitor &visitor) const override;
};
/* SpritePropExprNode */
struct SpritePropExprNode : ExprNode {
Common::SharedPtr<Node> spriteID;
unsigned int prop;
SpritePropExprNode(uint32 offset, Common::SharedPtr<Node> s, unsigned int p)
: ExprNode(kSpritePropExprNode, offset), prop(p) {
spriteID = Common::move(s);
spriteID->parent = this;
}
void accept(NodeVisitor &visitor) const override;
};
/* ThePropExprNode */
struct ThePropExprNode : ExprNode {
Common::SharedPtr<Node> obj;
Common::String prop;
ThePropExprNode(uint32 offset, Common::SharedPtr<Node> o, Common::String p)
: ExprNode(kThePropExprNode, offset), prop(p) {
obj = Common::move(o);
obj->parent = this;
}
void accept(NodeVisitor &visitor) const override;
};
/* ObjPropExprNode */
struct ObjPropExprNode : ExprNode {
Common::SharedPtr<Node> obj;
Common::String prop;
ObjPropExprNode(uint32 offset, Common::SharedPtr<Node> o, Common::String p)
: ExprNode(kObjPropExprNode, offset), prop(p) {
obj = Common::move(o);
obj->parent = this;
}
bool hasSpaces(bool dot) override;
void accept(NodeVisitor &visitor) const override;
};
/* ObjBracketExprNode */
struct ObjBracketExprNode : ExprNode {
Common::SharedPtr<Node> obj;
Common::SharedPtr<Node> prop;
ObjBracketExprNode(uint32 offset, Common::SharedPtr<Node> o, Common::SharedPtr<Node> p)
: ExprNode(kObjBracketExprNode, offset) {
obj = Common::move(o);
obj->parent = this;
prop = Common::move(p);
prop->parent = this;
}
bool hasSpaces(bool dot) override;
void accept(NodeVisitor &visitor) const override;
};
/* ObjPropIndexExprNode */
struct ObjPropIndexExprNode : ExprNode {
Common::SharedPtr<Node> obj;
Common::String prop;
Common::SharedPtr<Node> index;
Common::SharedPtr<Node> index2;
ObjPropIndexExprNode(uint32 offset, Common::SharedPtr<Node> o, Common::String p, Common::SharedPtr<Node> i, Common::SharedPtr<Node> i2)
: ExprNode(kObjPropIndexExprNode, offset), prop(p) {
obj = Common::move(o);
obj->parent = this;
index = Common::move(i);
index->parent = this;
if (i2) {
index2 = Common::move(i2);
index2->parent = this;
}
}
bool hasSpaces(bool dot) override;
void accept(NodeVisitor &visitor) const override;
};
/* ExitRepeatStmtNode */
struct ExitRepeatStmtNode : StmtNode {
explicit ExitRepeatStmtNode(uint32 offset) : StmtNode(kExitRepeatStmtNode, offset) {}
void accept(NodeVisitor &visitor) const override;
};
/* NextRepeatStmtNode */
struct NextRepeatStmtNode : StmtNode {
explicit NextRepeatStmtNode(uint32 offset) : StmtNode(kNextRepeatStmtNode, offset) {}
void accept(NodeVisitor &visitor) const override;
};
/* PutStmtNode */
struct PutStmtNode : StmtNode {
PutType type;
Common::SharedPtr<Node> variable;
Common::SharedPtr<Node> value;
PutStmtNode(uint32 offset, PutType t, Common::SharedPtr<Node> var, Common::SharedPtr<Node> val)
: StmtNode(kPutStmtNode, offset), type(t) {
variable = Common::move(var);
variable->parent = this;
value = Common::move(val);
value->parent = this;
}
void accept(NodeVisitor &visitor) const override;
};
/* WhenStmtNode */
struct WhenStmtNode : StmtNode {
int event;
Common::String script;
WhenStmtNode(uint32 offset, int e, Common::String s)
: StmtNode(kWhenStmtNode, offset), event(e), script(s) {}
void accept(NodeVisitor &visitor) const override;
};
/* NewObjNode */
struct NewObjNode : ExprNode {
Common::String objType;
Common::SharedPtr<Node> objArgs;
NewObjNode(uint32 offset, Common::String o, Common::SharedPtr<Node> args) : ExprNode(kNewObjNode, offset), objType(o), objArgs(args) {}
void accept(NodeVisitor &visitor) const override;
};
class NodeVisitor {
public:
virtual ~NodeVisitor() {}
virtual void visit(const HandlerNode &node) { defaultVisit(node); }
virtual void visit(const ErrorNode &node) { defaultVisit(node); }
virtual void visit(const CommentNode &node) { defaultVisit(node); }
virtual void visit(const NewObjNode &node) { defaultVisit(node); }
virtual void visit(const LiteralNode &node) { defaultVisit(node); }
virtual void visit(const IfStmtNode &node) { defaultVisit(node); }
virtual void visit(const EndCaseNode &node) { defaultVisit(node); }
virtual void visit(const ObjCallNode &node) { defaultVisit(node); }
virtual void visit(const PutStmtNode &node) { defaultVisit(node); }
virtual void visit(const TheExprNode &node) { defaultVisit(node); }
virtual void visit(const BinaryOpNode &node) { defaultVisit(node); }
virtual void visit(const CaseStmtNode &node) { defaultVisit(node); }
virtual void visit(const ExitStmtNode &node) { defaultVisit(node); }
virtual void visit(const TellStmtNode &node) { defaultVisit(node); }
virtual void visit(const WhenStmtNode &node) { defaultVisit(node); }
virtual void visit(const CaseLabelNode &node) { defaultVisit(node); }
virtual void visit(const ChunkExprNode &node) { defaultVisit(node); }
virtual void visit(const InverseOpNode &node) { defaultVisit(node); }
virtual void visit(const ObjCallV4Node &node) { defaultVisit(node); }
virtual void visit(const OtherwiseNode &node) { defaultVisit(node); }
virtual void visit(const MemberExprNode &node) { defaultVisit(node); }
virtual void visit(const ObjPropExprNode &node) { defaultVisit(node); }
virtual void visit(const PlayCmdStmtNode &node) { defaultVisit(node); }
virtual void visit(const ThePropExprNode &node) { defaultVisit(node); }
virtual void visit(const MenuPropExprNode &node) { defaultVisit(node); }
virtual void visit(const SoundCmdStmtNode &node) { defaultVisit(node); }
virtual void visit(const SoundPropExprNode &node) { defaultVisit(node); }
virtual void visit(const AssignmentStmtNode &node) { defaultVisit(node); }
virtual void visit(const ExitRepeatStmtNode &node) { defaultVisit(node); }
virtual void visit(const NextRepeatStmtNode &node) { defaultVisit(node); }
virtual void visit(const ObjBracketExprNode &node) { defaultVisit(node); }
virtual void visit(const SpritePropExprNode &node) { defaultVisit(node); }
virtual void visit(const ChunkDeleteStmtNode &node) { defaultVisit(node); }
virtual void visit(const ChunkHiliteStmtNode &node) { defaultVisit(node); }
virtual void visit(const RepeatWhileStmtNode &node) { defaultVisit(node); }
virtual void visit(const MenuItemPropExprNode &node) { defaultVisit(node); }
virtual void visit(const ObjPropIndexExprNode &node) { defaultVisit(node); }
virtual void visit(const RepeatWithInStmtNode &node) { defaultVisit(node); }
virtual void visit(const RepeatWithToStmtNode &node) { defaultVisit(node); }
virtual void visit(const SpriteWithinExprNode &node) { defaultVisit(node); }
virtual void visit(const LastStringChunkExprNode &node) { defaultVisit(node); }
virtual void visit(const SpriteIntersectsExprNode &node) { defaultVisit(node); }
virtual void visit(const StringChunkCountExprNode &node) { defaultVisit(node); }
virtual void visit(const VarNode &node) { defaultVisit(node); }
virtual void visit(const CallNode &node) { defaultVisit(node); }
virtual void visit(const BlockNode &node) { defaultVisit(node); }
virtual void visit(const NotOpNode &node) { defaultVisit(node); }
virtual void defaultVisit(const Node &) {}
};
/* AST */
struct AST {
Common::SharedPtr<HandlerNode> root;
BlockNode *currentBlock;
AST(uint32 offset, Handler *handler){
root = Common::SharedPtr<HandlerNode>(new HandlerNode(offset, handler));
currentBlock = root->block.get();
}
void addStatement(Common::SharedPtr<Node> statement);
void enterBlock(BlockNode *block);
void exitBlock();
};
} // namespace LingoDec
#endif // LINGODEC_AST_H

View File

@@ -0,0 +1,876 @@
/*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
#include "common/ptr.h"
#include "./ast.h"
#include "./handler.h"
#include "./names.h"
#include "./script.h"
#include "./codewritervisitor.h"
namespace LingoDec {
void CodeWriterVisitor::visit(const HandlerNode &node) {
if (node.handler->isGenericEvent) {
node.block->accept(*this);
} else {
Script *script = node.handler->script;
bool isMethod = script->isFactory();
if (isMethod) {
write("method ");
} else {
write("on ");
}
write(node.handler->name);
if (node.handler->argumentNames.size() > 0) {
write(" ");
for (size_t i = 0; i < node.handler->argumentNames.size(); i++) {
if (i > 0)
write(", ");
write(node.handler->argumentNames[i]);
}
}
writeLine();
indent();
if (isMethod && script->propertyNames.size() > 0 && node.handler == &script->handlers[0]) {
write("instance ");
for (size_t i = 0; i < script->propertyNames.size(); i++) {
if (i > 0)
write(", ");
write(script->propertyNames[i]);
}
writeLine();
}
if (node.handler->globalNames.size() > 0) {
write("global ");
for (size_t i = 0; i < node.handler->globalNames.size(); i++) {
if (i > 0)
write(", ");
write(node.handler->globalNames[i]);
}
writeLine();
}
unindent();
node.block->accept(*this);
if (!isMethod) {
writeLine("end");
}
}
}
void CodeWriterVisitor::visit(const ErrorNode &) {
write("ERROR");
}
void CodeWriterVisitor::visit(const CommentNode &node) {
write("-- ");
write(node.text);
}
void CodeWriterVisitor::visit(const NewObjNode &node) {
write("new ");
write(node.objType);
write("(");
node.objArgs->accept(*this);
write(")");
}
void CodeWriterVisitor::visit(const LiteralNode &node) {
write(*node.value);
}
void CodeWriterVisitor::visit(const IfStmtNode &node) {
write("if ");
node.condition->accept(*this);
write(" then");
if (_sum) {
if (node.hasElse) {
write(" / else");
}
} else {
writeLine();
node.block1->accept(*this);
if (node.hasElse) {
writeLine("else");
node.block2->accept(*this);
}
write("end if");
}
}
void CodeWriterVisitor::visit(const EndCaseNode &) {
write("end case");
}
void CodeWriterVisitor::visit(const ObjCallNode &node) {
auto &rawArgs = node.argList->getValue()->l;
auto &obj = rawArgs[0];
bool parenObj = obj->hasSpaces(_dot);
if (parenObj) {
write("(");
}
obj->accept(*this);
if (parenObj) {
write(")");
}
write(".");
write(node.name);
write("(");
for (size_t i = 1; i < rawArgs.size(); i++) {
if (i > 1)
write(", ");
rawArgs[i]->accept(*this);
}
write(")");
}
void CodeWriterVisitor::visit(const PutStmtNode &node) {
write("put ");
node.value->accept(*this);
write(" ");
write(StandardNames::putTypeNames[node.type]);
write(" ");
node.variable->accept(*this); // TODO: we want the variable to always be verbose
}
void CodeWriterVisitor::visit(const TheExprNode &node) {
write("the ");
write(node.prop);
}
void CodeWriterVisitor::visit(const BinaryOpNode &node) {
unsigned int precedence = node.getPrecedence();
bool parenLeft = false;
bool parenRight = false;
if (precedence) {
if (node.left->type == kBinaryOpNode) {
auto leftBinaryOpNode = static_cast<BinaryOpNode *>(node.left.get());
parenLeft = (leftBinaryOpNode->getPrecedence() != precedence);
}
parenRight = (node.right->type == kBinaryOpNode);
}
if (parenLeft) {
write("(");
}
node.left->accept(*this);
if (parenLeft) {
write(")");
}
write(" ");
write(StandardNames::binaryOpNames[node.opcode]);
write(" ");
if (parenRight) {
write("(");
}
node.right->accept(*this);
if (parenRight) {
write(")");
}
}
void CodeWriterVisitor::visit(const CaseStmtNode &node) {
write("case ");
node.value->accept(*this);
write(" of");
if (_sum) {
if (!node.firstLabel) {
if (node.otherwise) {
write(" / otherwise:");
} else {
write(" / end case");
}
}
} else {
writeLine();
indent();
if (node.firstLabel) {
node.firstLabel->accept(*this);
}
if (node.otherwise) {
node.otherwise->accept(*this);
}
unindent();
write("end case");
}
}
void CodeWriterVisitor::visit(const ExitStmtNode &) {
write("exit");
}
void CodeWriterVisitor::visit(const TellStmtNode &node) {
write("tell ");
node.window->accept(*this);
if (!_sum) {
writeLine();
node.block->accept(*this);
write("end tell");
}
}
void CodeWriterVisitor::visit(const WhenStmtNode &node) {
write("when ");
write(StandardNames::whenEventNames[node.event]);
write(" then");
for (size_t i = 0; i < node.script.size(); i++) {
char ch = node.script[i];
if (ch == '\r') {
if (i != node.script.size() - 1) {
writeLine();
}
} else {
write(ch);
}
}
}
void CodeWriterVisitor::visit(const CaseLabelNode &node) {
if (_sum) {
write("(case) ");
if (node.parent->type == kCaseLabelNode) {
auto parentLabel = static_cast<CaseLabelNode *>(node.parent);
if (parentLabel->nextOr.get() == &node) {
write("..., ");
}
}
bool parenValue = node.value->hasSpaces(_dot);
if (parenValue) {
write("(");
}
node.value->accept(*this);
if (parenValue) {
write(")");
}
if (node.nextOr) {
write(", ...");
} else {
write(":");
}
} else {
bool parenValue = node.value->hasSpaces(_dot);
if (parenValue) {
write("(");
}
node.value->accept(*this);
if (parenValue) {
write(")");
}
if (node.nextOr) {
write(", ");
node.nextOr->accept(*this);
} else {
writeLine(":");
node.block->accept(*this);
}
if (node.nextLabel) {
node.nextLabel->accept(*this);
}
}
}
void CodeWriterVisitor::visit(const ChunkExprNode &node) {
write(StandardNames::chunkTypeNames[node.type]);
write(" ");
node.first->accept(*this);
if (!(node.last->type == kLiteralNode && node.last->getValue()->type == kDatumInt && node.last->getValue()->i == 0)) {
write(" to ");
node.last->accept(*this);
}
write(" of ");
node.string->accept(*this); // TODO: we want the string to always be verbose
}
void CodeWriterVisitor::visit(const InverseOpNode &node) {
write("-");
bool parenOperand = node.operand->hasSpaces(_dot);
if (parenOperand) {
write("(");
}
node.operand->accept(*this);
if (parenOperand) {
write(")");
}
}
void CodeWriterVisitor::visit(const ObjCallV4Node &node) {
node.obj->accept(*this);
write("(");
node.argList->accept(*this);
write(")");
}
void CodeWriterVisitor::visit(const OtherwiseNode &node) {
if (_sum) {
write("(case) otherwise:");
} else {
writeLine("otherwise:");
node.block->accept(*this);
}
}
void CodeWriterVisitor::visit(const MemberExprNode &node) {
bool hasCastID = node.castID && !(node.castID->type == kLiteralNode && node.castID->getValue()->type == kDatumInt && node.castID->getValue()->i == 0);
write(node.type);
if (_dot) {
write("(");
node.memberID->accept(*this);
if (hasCastID) {
write(", ");
node.castID->accept(*this);
}
write(")");
} else {
write(" ");
bool parenMemberID = (node.memberID->type == kBinaryOpNode);
if (parenMemberID) {
write("(");
}
node.memberID->accept(*this);
if (parenMemberID) {
write(")");
}
if (hasCastID) {
write(" of castLib ");
bool parenCastID = (node.castID->type == kBinaryOpNode);
if (parenCastID) {
write("(");
}
node.castID->accept(*this);
if (parenCastID) {
write(")");
}
}
}
}
void CodeWriterVisitor::visit(const ObjPropExprNode &node) {
if (_dot) {
bool parenObj = node.obj->hasSpaces(_dot);
if (parenObj) {
write("(");
}
node.obj->accept(*this);
if (parenObj) {
write(")");
}
write(".");
write(node.prop);
} else {
write("the ");
write(node.prop);
write(" of ");
bool parenObj = (node.obj->type == kBinaryOpNode);
if (parenObj) {
write("(");
}
node.obj->accept(*this);
if (parenObj) {
write(")");
}
}
}
void CodeWriterVisitor::visit(const PlayCmdStmtNode &node) {
auto &rawArgs = node.argList->getValue()->l;
write("play");
if (rawArgs.size() == 0) {
write(" done");
return;
}
auto &frame = rawArgs[0];
if (rawArgs.size() == 1) {
write(" frame ");
frame->accept(*this);
return;
}
auto &movie = rawArgs[1];
if (!(frame->type == kLiteralNode && frame->getValue()->type == kDatumInt && frame->getValue()->i == 1)) {
write(" frame ");
frame->accept(*this);
write(" of");
}
write(" movie ");
movie->accept(*this);
}
void CodeWriterVisitor::visit(const ThePropExprNode &node) {
write("the ");
write(node.prop);
write(" of ");
bool parenObj = (node.obj->type == kBinaryOpNode);
if (parenObj) {
write("(");
}
node.obj->accept(*this); // TODO: we want the object to always be verbose
if (parenObj) {
write(")");
}
}
void CodeWriterVisitor::visit(const MenuPropExprNode &node) {
write("the ");
write(StandardNames::menuPropertyNames[node.prop]);
write(" of menu ");
bool parenMenuID = (node.menuID->type == kBinaryOpNode);
if (parenMenuID) {
write("(");
}
node.menuID->accept(*this);
if (parenMenuID) {
write(")");
}
}
void CodeWriterVisitor::visit(const SoundCmdStmtNode &node) {
write("sound ");
write(node.cmd);
if (node.argList->getValue()->l.size() > 0) {
write(" ");
node.argList->accept(*this);
}
}
void CodeWriterVisitor::visit(const SoundPropExprNode &node) {
write("the ");
write(StandardNames::soundPropertyNames[node.prop]);
write(" of sound ");
bool parenSoundID = (node.soundID->type == kBinaryOpNode);
if (parenSoundID) {
write("(");
}
node.soundID->accept(*this);
if (parenSoundID) {
write(")");
}
}
void CodeWriterVisitor::visit(const AssignmentStmtNode &node) {
if (!_dot) { // TODO: forceVerboseÒ
write("set ");
node.variable->accept(*this); // TODO: we want the variable to always be verbose
write(" to ");
node.value->accept(*this);
} else {
node.variable->accept(*this);
write(" = ");
node.value->accept(*this);
}
}
void CodeWriterVisitor::visit(const ExitRepeatStmtNode &) {
write("exit repeat");
}
void CodeWriterVisitor::visit(const NextRepeatStmtNode &) {
write("next repeat");
}
void CodeWriterVisitor::visit(const ObjBracketExprNode &node) {
bool parenObj = node.obj->hasSpaces(_dot);
if (parenObj) {
write("(");
}
node.obj->accept(*this);
if (parenObj) {
write(")");
}
write("[");
node.prop->accept(*this);
write("]");
}
void CodeWriterVisitor::visit(const SpritePropExprNode &node) {
write("the ");
write(StandardNames::spritePropertyNames[node.prop]);
write(" of sprite ");
bool parenSpriteID = (node.spriteID->type == kBinaryOpNode);
if (parenSpriteID) {
write("(");
}
node.spriteID->accept(*this);
if (parenSpriteID) {
write(")");
}
}
void CodeWriterVisitor::visit(const ChunkDeleteStmtNode &node) {
write("delete ");
node.chunk->accept(*this);
}
void CodeWriterVisitor::visit(const ChunkHiliteStmtNode &node) {
write("hilite ");
node.chunk->accept(*this);
}
void CodeWriterVisitor::visit(const RepeatWhileStmtNode &node) {
write("repeat while ");
node.condition->accept(*this);
if (!_sum) {
writeLine();
node.block->accept(*this);
write("end repeat");
}
}
void CodeWriterVisitor::visit(const MenuItemPropExprNode &node) {
write("the ");
write(StandardNames::menuItemPropertyNames[node.prop]);
write(" of menuItem ");
bool parenItemID = (node.itemID->type == kBinaryOpNode);
if (parenItemID) {
write("(");
}
node.itemID->accept(*this);
if (parenItemID) {
write(")");
}
write(" of menu ");
bool parenMenuID = (node.menuID->type ==kBinaryOpNode);
if (parenMenuID) {
write("(");
}
node.menuID->accept(*this);
if (parenMenuID) {
write(")");
}
}
void CodeWriterVisitor::visit(const ObjPropIndexExprNode &node) {
bool parenObj = node.obj->hasSpaces(_dot);
if (parenObj) {
write("(");
}
node.obj->accept(*this);
if (parenObj) {
write(")");
}
write(".");
write(node.prop);
write("[");
node.index->accept(*this);
if (node.index2) {
write("..");
node.index2->accept(*this);
}
write("]");
}
void CodeWriterVisitor::visit(const RepeatWithInStmtNode &node) {
write("repeat with ");
write(node.varName);
write(" in ");
node.list->accept(*this);
if (!_sum) {
writeLine();
node.block->accept(*this);
write("end repeat");
}
}
void CodeWriterVisitor::visit(const RepeatWithToStmtNode &node) {
write("repeat with ");
write(node.varName);
write(" = ");
node.start->accept(*this);
if (node.up) {
write(" to ");
} else {
write(" down to ");
}
node.end->accept(*this);
if (!_sum) {
writeLine();
node.block->accept(*this);
write("end repeat");
}
}
void CodeWriterVisitor::visit(const SpriteWithinExprNode &node) {
write("sprite ");
bool parenFirstSprite = (node.firstSprite->type == kBinaryOpNode);
if (parenFirstSprite) {
write("(");
}
node.firstSprite->accept(*this);
if (parenFirstSprite) {
write(")");
}
write(" within ");
bool parenSecondSprite = (node.secondSprite->type == kBinaryOpNode);
if (parenSecondSprite) {
write("(");
}
node.secondSprite->accept(*this);
if (parenSecondSprite) {
write(")");
}
}
void CodeWriterVisitor::visit(const LastStringChunkExprNode &node) {
write("the last ");
write(StandardNames::chunkTypeNames[node.type]);
write(" in ");
bool parenObj = (node.obj->type == kBinaryOpNode);
if (parenObj) {
write("(");
}
node.obj->accept(*this); // TODO: we want the object to always be verbose
if (parenObj) {
write(")");
}
}
void CodeWriterVisitor::visit(const SpriteIntersectsExprNode &node) {
write("sprite ");
bool parenFirstSprite = (node.firstSprite->type == kBinaryOpNode);
if (parenFirstSprite) {
write("(");
}
node.firstSprite->accept(*this);
if (parenFirstSprite) {
write(")");
}
write(" intersects ");
bool parenSecondSprite = (node.secondSprite->type == kBinaryOpNode);
if (parenSecondSprite) {
write("(");
}
node.secondSprite->accept(*this);
if (parenSecondSprite) {
write(")");
}
}
void CodeWriterVisitor::visit(const StringChunkCountExprNode &node) {
write("the number of ");
write(StandardNames::chunkTypeNames[node.type]); // we want the object to always be verbose
write("s in ");
bool parenObj = (node.obj->type == kBinaryOpNode);
if (parenObj) {
write("(");
}
node.obj->accept(*this); // TODO dot false?
if (parenObj) {
write(")");
}
}
void CodeWriterVisitor::visit(const VarNode &node) {
write(node.varName);
}
void CodeWriterVisitor::visit(const CallNode &node) {
if (node.isExpression && node.argList->getValue()->l.size() == 0) {
if (node.name == "pi") {
write("PI");
return;
}
if (node.name == "space") {
write("SPACE");
return;
}
if (node.name == "void") {
write("VOID");
return;
}
}
if (!_dot && node.isMemberExpr()) {
/**
* In some cases, member expressions such as `member 1 of castLib 1` compile
* to the function call `member(1, 1)`. However, this doesn't parse correctly
* in pre-dot-syntax versions of Director, and `put(member(1, 1))` does not
* compile. Therefore, we rewrite these expressions to the verbose syntax when
* in verbose mode.
*/
write(node.name);
write(" ");
auto memberID = node.argList->getValue()->l[0];
bool parenMemberID = (memberID->type == kBinaryOpNode);
if (parenMemberID) {
write("(");
}
memberID->accept(*this);
if (parenMemberID) {
write(")");
}
if (node.argList->getValue()->l.size() == 2) {
write(" of castLib ");
auto castID = node.argList->getValue()->l[1];
bool parenCastID = (castID->type == kBinaryOpNode);
if (parenCastID) {
write("(");
}
castID->accept(*this);
if (parenCastID) {
write(")");
}
}
return;
}
write(node.name);
if (node.noParens()) {
write(" ");
node.argList->accept(*this);
} else {
write("(");
node.argList->accept(*this);
write(")");
}
}
void CodeWriterVisitor::visit(const BlockNode &node) {
indent();
for (const auto &child : node.children) {
child->accept(*this);
writeLine();
}
unindent();
}
void CodeWriterVisitor::visit(const NotOpNode &node) {
write("not ");
bool parenOperand = node.operand->hasSpaces(_dot);
if (parenOperand) {
write("(");
}
node.operand->accept(*this);
if (parenOperand) {
write(")");
}
}
void CodeWriterVisitor::indent() {
_indent++;
}
void CodeWriterVisitor::unindent() {
if (_indent > 0)
_indent--;
}
void CodeWriterVisitor::writeIndentation() {
if (_indentWritten)
return;
for (int i = 0; i < _indent; i++) {
_str += _indentation;
}
_indentWritten = true;
_lineWidth = _indent * _indentation.size();
}
void CodeWriterVisitor::write(char c) {
writeIndentation();
_str += c;
_lineWidth++;
}
void CodeWriterVisitor::write(const Common::String &s) {
writeIndentation();
_str += s;
_lineWidth += s.size();
}
void CodeWriterVisitor::writeLine() {
_str += _lineEnding;
_lineWidth += _lineEnding.size();
_indentWritten = false;
_lineWidth = 0;
}
void CodeWriterVisitor::writeLine(const Common::String &s) {
writeIndentation();
_str += s;
_lineWidth += s.size();
_str += _lineEnding;
_lineWidth += _lineEnding.size();
_indentWritten = false;
_lineWidth = 0;
}
void CodeWriterVisitor::write(Datum &datum) {
switch (datum.type) {
case kDatumVoid:
write("VOID");
return;
case kDatumSymbol:
write("#" + datum.s);
return;
case kDatumVarRef:
write(datum.s);
return;
case kDatumString:
if (datum.s.size() == 0) {
write("EMPTY");
return;
}
if (datum.s.size() == 1) {
switch (datum.s[0]) {
case '\x03':
write("ENTER");
return;
case '\x08':
write("BACKSPACE");
return;
case '\t':
write("TAB");
return;
case '\r':
write("RETURN");
return;
case '"':
write("QUOTE");
return;
default:
break;
}
}
if (_sum) {
write("\"" + Common::toPrintable(datum.s) + "\"");
return;
}
write("\"" + datum.s + "\"");
return;
case kDatumInt:
write(Common::String::format("%d", datum.i));
return;
case kDatumFloat:
write(Common::String::format("%g", datum.f));
return;
case kDatumList:
case kDatumArgList:
case kDatumArgListNoRet: {
if (datum.type == kDatumList)
write("[");
for (size_t ii = 0; ii < datum.l.size(); ii++) {
if (ii > 0)
write(", ");
datum.l[ii]->accept(*this);
}
if (datum.type == kDatumList)
write("]");
}
return;
case kDatumPropList: {
write("[");
if (datum.l.size() == 0) {
write(":");
} else {
for (size_t ii = 0; ii < datum.l.size(); ii += 2) {
if (ii > 0)
write(", ");
datum.l[ii]->accept(*this);
write(": ");
datum.l[ii + 1]->accept(*this);
}
}
write("]");
}
return;
}
}
} // namespace LingoDec

View File

@@ -0,0 +1,92 @@
/*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
#ifndef LINGODEC_CODEWRITERVISITOR_H
#define LINGODEC_CODEWRITERVISITOR_H
#include "./ast.h"
namespace LingoDec {
class CodeWriterVisitor: public LingoDec::NodeVisitor {
public:
CodeWriterVisitor(bool dotSyntax, bool sum, const Common::String &lineEnding = "\n", const Common::String &indentation = " ")
: _dot(dotSyntax), _sum(sum), _lineEnding(lineEnding), _indentation(indentation) {}
virtual ~CodeWriterVisitor() {}
virtual void visit(const LingoDec::HandlerNode& node) override;
virtual void visit(const LingoDec::ErrorNode& node) override;
virtual void visit(const LingoDec::CommentNode& node) override;
virtual void visit(const LingoDec::NewObjNode& node) override;
virtual void visit(const LingoDec::LiteralNode& node) override;
virtual void visit(const LingoDec::IfStmtNode& node) override;
virtual void visit(const LingoDec::EndCaseNode& node) override;
virtual void visit(const LingoDec::ObjCallNode& node) override;
virtual void visit(const LingoDec::PutStmtNode& node) override;
virtual void visit(const LingoDec::TheExprNode& node) override;
virtual void visit(const LingoDec::BinaryOpNode& node) override;
virtual void visit(const LingoDec::CaseStmtNode& node) override;
virtual void visit(const LingoDec::ExitStmtNode& node) override;
virtual void visit(const LingoDec::TellStmtNode& node) override;
virtual void visit(const LingoDec::WhenStmtNode& node) override;
virtual void visit(const LingoDec::CaseLabelNode& node) override;
virtual void visit(const LingoDec::ChunkExprNode& node) override;
virtual void visit(const LingoDec::InverseOpNode& node) override;
virtual void visit(const LingoDec::ObjCallV4Node& node) override;
virtual void visit(const LingoDec::OtherwiseNode& node) override;
virtual void visit(const LingoDec::MemberExprNode& node) override;
virtual void visit(const LingoDec::ObjPropExprNode& node) override;
virtual void visit(const LingoDec::PlayCmdStmtNode& node) override;
virtual void visit(const LingoDec::ThePropExprNode& node) override;
virtual void visit(const LingoDec::MenuPropExprNode& node) override;
virtual void visit(const LingoDec::SoundCmdStmtNode& node) override;
virtual void visit(const LingoDec::SoundPropExprNode& node) override;
virtual void visit(const LingoDec::AssignmentStmtNode& node) override;
virtual void visit(const LingoDec::ExitRepeatStmtNode& node) override;
virtual void visit(const LingoDec::NextRepeatStmtNode& node) override;
virtual void visit(const LingoDec::ObjBracketExprNode& node) override;
virtual void visit(const LingoDec::SpritePropExprNode& node) override;
virtual void visit(const LingoDec::ChunkDeleteStmtNode& node) override;
virtual void visit(const LingoDec::ChunkHiliteStmtNode& node) override;
virtual void visit(const LingoDec::RepeatWhileStmtNode& node) override;
virtual void visit(const LingoDec::MenuItemPropExprNode& node) override;
virtual void visit(const LingoDec::ObjPropIndexExprNode& node) override;
virtual void visit(const LingoDec::RepeatWithInStmtNode& node) override;
virtual void visit(const LingoDec::RepeatWithToStmtNode& node) override;
virtual void visit(const LingoDec::SpriteWithinExprNode& node) override;
virtual void visit(const LingoDec::LastStringChunkExprNode& node) override;
virtual void visit(const LingoDec::SpriteIntersectsExprNode& node) override;
virtual void visit(const LingoDec::StringChunkCountExprNode& node) override;
virtual void visit(const LingoDec::VarNode& node) override;
virtual void visit(const LingoDec::CallNode& node) override;
virtual void visit(const LingoDec::BlockNode& node) override;
virtual void visit(const LingoDec::NotOpNode& node) override;
size_t lineWidth() const { return _lineWidth; }
void indent();
void unindent();
void writeIndentation();
void write(char c);
void write(const Common::String& s);
void writeLine();
void writeLine(const Common::String& s);
void write(LingoDec::Datum& datum);
public:
Common::String _str;
private:
bool _dot = false;
bool _sum = false;
Common::String _lineEnding;
Common::String _indentation = " ";
bool _indentWritten = false;
int _indent = 0;
size_t _lineWidth = 0;
};
} // namespace LingoDec
#endif // LINGODEC_CODEWRITERVISITOR_H

View File

@@ -0,0 +1,84 @@
/*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
#include "common/stream.h"
#include "common/util.h"
#include "./context.h"
#include "./names.h"
#include "./resolver.h"
#include "./script.h"
namespace LingoDec {
struct ScriptContextMapEntry;
/* ScriptContext */
void ScriptContext::read(Common::SeekableReadStream &stream) {
// Lingo scripts are always big endian regardless of file endianness
unknown0 = stream.readSint32BE();
unknown1 = stream.readSint32BE();
entryCount = stream.readUint32BE();
entryCount2 = stream.readUint32BE();
entriesOffset = stream.readUint16BE();
unknown2 = stream.readSint16BE();
unknown3 = stream.readSint32BE();
unknown4 = stream.readSint32BE();
unknown5 = stream.readSint32BE();
lnamSectionID = stream.readSint32BE();
validCount = stream.readUint16BE();
flags = stream.readUint16BE();
freePointer = stream.readSint16BE();
stream.seek(entriesOffset);
sectionMap.resize(entryCount);
for (auto &entry : sectionMap) {
entry.read(stream);
}
lnam = resolver->getScriptNames(lnamSectionID);
for (uint32 i = 1; i <= entryCount; i++) {
auto section = sectionMap[i - 1];
if (section.sectionID > -1) {
Script *script = resolver->getScript(section.sectionID);
script->setContext(this);
scripts[i] = script;
}
}
for (auto it = scripts.begin(); it != scripts.end(); ++it) {
Script *script = it->second;
if (script->isFactory()) {
Script *parent = scripts[script->parentNumber + 1];
parent->factories.push_back(script);
}
}
}
bool ScriptContext::validName(int id) const {
return lnam->validName(id);
}
Common::String ScriptContext::getName(int id) const {
return lnam->getName(id);
}
void ScriptContext::parseScripts() {
for (auto it = scripts.begin(); it != scripts.end(); ++it) {
it->second->parse();
}
}
/* ScriptContextMapEntry */
void ScriptContextMapEntry::read(Common::SeekableReadStream &stream) {
unknown0 = stream.readSint32BE();
sectionID = stream.readSint32BE();
unknown1 = stream.readUint16BE();
unknown2 = stream.readUint16BE();
}
} // namespace LingoDec

View File

@@ -0,0 +1,70 @@
/*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
#ifndef LINGODEC_CONTEXT_H
#define LINGODEC_CONTEXT_H
#include "common/array.h"
#include "common/stablemap.h"
namespace Common {
class SeekableReadStream;
}
namespace LingoDec {
class ChunkResolver;
struct Script;
struct ScriptContextMapEntry;
struct ScriptNames;
/* ScriptContext */
struct ScriptContext {
int32 unknown0;
int32 unknown1;
uint32 entryCount;
uint32 entryCount2;
uint16 entriesOffset;
int16 unknown2;
int32 unknown3;
int32 unknown4;
int32 unknown5;
int32 lnamSectionID;
uint16 validCount;
uint16 flags;
int16 freePointer;
unsigned int version;
ChunkResolver *resolver;
ScriptNames *lnam;
Common::Array<ScriptContextMapEntry> sectionMap;
Common::StableMap<uint32, Script *> scripts;
ScriptContext(unsigned int version_, ChunkResolver *resolver_) : version(version_),
resolver(resolver_),
lnam(nullptr) {}
void read(Common::SeekableReadStream &stream);
bool validName(int id) const;
Common::String getName(int id) const;
void parseScripts();
};
/* ScriptContextMapEntry */
struct ScriptContextMapEntry {
int32 unknown0;
int32 sectionID;
uint16 unknown1;
uint16 unknown2;
void read(Common::SeekableReadStream &stream);
};
} // namespace LingoDec
#endif // LINGODEC_CONTEXT_H

View File

@@ -0,0 +1,216 @@
/*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
#ifndef LINGODEC_ENUMS_H
#define LINGODEC_ENUMS_H
namespace LingoDec {
enum OpCode {
// single-byte
kOpRet = 0x01,
kOpRetFactory = 0x02,
kOpPushZero = 0x03,
kOpMul = 0x04,
kOpAdd = 0x05,
kOpSub = 0x06,
kOpDiv = 0x07,
kOpMod = 0x08,
kOpInv = 0x09,
kOpJoinStr = 0x0a,
kOpJoinPadStr = 0x0b,
kOpLt = 0x0c,
kOpLtEq = 0x0d,
kOpNtEq = 0x0e,
kOpEq = 0x0f,
kOpGt = 0x10,
kOpGtEq = 0x11,
kOpAnd = 0x12,
kOpOr = 0x13,
kOpNot = 0x14,
kOpContainsStr = 0x15,
kOpContains0Str = 0x16,
kOpGetChunk = 0x17,
kOpHiliteChunk = 0x18,
kOpOntoSpr = 0x19,
kOpIntoSpr = 0x1a,
kOpGetField = 0x1b,
kOpStartTell = 0x1c,
kOpEndTell = 0x1d,
kOpPushList = 0x1e,
kOpPushPropList = 0x1f,
kOpSwap = 0x21,
// multi-byte
kOpPushInt8 = 0x41,
kOpPushArgListNoRet = 0x42,
kOpPushArgList = 0x43,
kOpPushCons = 0x44,
kOpPushSymb = 0x45,
kOpPushVarRef = 0x46,
kOpGetGlobal2 = 0x48,
kOpGetGlobal = 0x49,
kOpGetProp = 0x4a,
kOpGetParam = 0x4b,
kOpGetLocal = 0x4c,
kOpSetGlobal2 = 0x4e,
kOpSetGlobal = 0x4f,
kOpSetProp = 0x50,
kOpSetParam = 0x51,
kOpSetLocal = 0x52,
kOpJmp = 0x53,
kOpEndRepeat = 0x54,
kOpJmpIfZ = 0x55,
kOpLocalCall = 0x56,
kOpExtCall = 0x57,
kOpObjCallV4 = 0x58,
kOpPut = 0x59,
kOpPutChunk = 0x5a,
kOpDeleteChunk = 0x5b,
kOpGet = 0x5c,
kOpSet = 0x5d,
kOpGetMovieProp = 0x5f,
kOpSetMovieProp = 0x60,
kOpGetObjProp = 0x61,
kOpSetObjProp = 0x62,
kOpTellCall = 0x63,
kOpPeek = 0x64,
kOpPop = 0x65,
kOpTheBuiltin = 0x66,
kOpObjCall = 0x67,
kOpPushChunkVarRef = 0x6d,
kOpPushInt16 = 0x6e,
kOpPushInt32 = 0x6f,
kOpGetChainedProp = 0x70,
kOpPushFloat32 = 0x71,
kOpGetTopLevelProp = 0x72,
kOpNewObj = 0x73
};
enum DatumType {
kDatumVoid,
kDatumSymbol,
kDatumVarRef,
kDatumString,
kDatumInt,
kDatumFloat,
kDatumList,
kDatumArgList,
kDatumArgListNoRet,
kDatumPropList
};
enum ChunkExprType {
kChunkChar = 0x01,
kChunkWord = 0x02,
kChunkItem = 0x03,
kChunkLine = 0x04
};
enum PutType {
kPutInto = 0x01,
kPutAfter = 0x02,
kPutBefore = 0x03
};
enum NodeType {
kNoneNode,
kErrorNode,
kTempNode,
kCommentNode,
kLiteralNode,
kBlockNode,
kHandlerNode,
kExitStmtNode,
kInverseOpNode,
kNotOpNode,
kBinaryOpNode,
kChunkExprNode,
kChunkHiliteStmtNode,
kChunkDeleteStmtNode,
kSpriteIntersectsExprNode,
kSpriteWithinExprNode,
kMemberExprNode,
kVarNode,
kAssignmentStmtNode,
kIfStmtNode,
kRepeatWhileStmtNode,
kRepeatWithInStmtNode,
kRepeatWithToStmtNode,
kCaseStmtNode,
kCaseLabelNode,
kOtherwiseNode,
kEndCaseNode,
kTellStmtNode,
kSoundCmdStmtNode,
kPlayCmdStmtNode,
kCallNode,
kObjCallNode,
kObjCallV4Node,
kTheExprNode,
kLastStringChunkExprNode,
kStringChunkCountExprNode,
kMenuPropExprNode,
kMenuItemPropExprNode,
kSoundPropExprNode,
kSpritePropExprNode,
kThePropExprNode,
kObjPropExprNode,
kObjBracketExprNode,
kObjPropIndexExprNode,
kExitRepeatStmtNode,
kNextRepeatStmtNode,
kPutStmtNode,
kWhenStmtNode,
kNewObjNode
};
enum BytecodeTag {
kTagNone,
kTagSkip,
kTagRepeatWhile,
kTagRepeatWithIn,
kTagRepeatWithTo,
kTagRepeatWithDownTo,
kTagNextRepeatTarget,
kTagEndCase
};
enum CaseExpect {
kCaseExpectEnd,
kCaseExpectOr,
kCaseExpectNext,
kCaseExpectOtherwise
};
enum ScriptFlag {
kScriptFlagUnused = (1 << 0x0),
kScriptFlagFuncsGlobal = (1 << 0x1),
kScriptFlagVarsGlobal = (1 << 0x2), // Occurs in event scripts (which have no local vars). Correlated with use of alternate global var opcodes.
kScriptFlagUnk3 = (1 << 0x3),
kScriptFlagFactoryDef = (1 << 0x4),
kScriptFlagUnk5 = (1 << 0x5),
kScriptFlagUnk6 = (1 << 0x6),
kScriptFlagUnk7 = (1 << 0x7),
kScriptFlagHasFactory = (1 << 0x8),
kScriptFlagEventScript = (1 << 0x9),
kScriptFlagEventScript2 = (1 << 0xa),
kScriptFlagUnkB = (1 << 0xb),
kScriptFlagUnkC = (1 << 0xc),
kScriptFlagUnkD = (1 << 0xd),
kScriptFlagUnkE = (1 << 0xe),
kScriptFlagUnkF = (1 << 0xf)
};
enum LiteralType {
kLiteralString = 1,
kLiteralInt = 4,
kLiteralFloat = 9
};
}
#endif // LINGODEC_ENUMS_H

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,110 @@
/*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
#ifndef LINGODEC_HANDLER_H
#define LINGODEC_HANDLER_H
#include "common/array.h"
#include "common/stablemap.h"
#include "common/str.h"
#include "./enums.h"
namespace Common {
class SeekableReadStream;
}
namespace LingoDec {
struct AST;
struct Bytecode;
class CodeWriterVisitor;
struct Node;
struct Script;
/* Handler */
struct Handler {
int16 nameID = 0;
uint16 vectorPos = 0;
uint32 compiledLen = 0;
uint32 compiledOffset = 0;
uint16 argumentCount = 0;
uint32 argumentOffset = 0;
uint16 localsCount = 0;
uint32 localsOffset = 0;
uint16 globalsCount = 0;
uint32 globalsOffset = 0;
uint32 unknown1 = 0;
uint16 unknown2 = 0;
uint16 lineCount = 0;
uint32 lineOffset = 0;
uint32 stackHeight = 0;
Common::Array<int16> argumentNameIDs;
Common::Array<int16> localNameIDs;
Common::Array<int16> globalNameIDs;
Script *script = nullptr;
Common::Array<Bytecode> bytecodeArray;
Common::StableMap<uint32, size_t> bytecodePosMap;
Common::Array<Common::String> argumentNames;
Common::Array<Common::String> localNames;
Common::Array<Common::String> globalNames;
Common::String name;
Common::Array<Common::SharedPtr<Node>> stack;
AST ast;
bool isGenericEvent = false;
Handler(): ast(0, this) {}
void setScript(Script *s) {
script = s;
}
void readRecord(Common::SeekableReadStream &stream);
void readData(Common::SeekableReadStream &stream);
Common::Array<int16> readVarnamesTable(Common::SeekableReadStream &stream, uint16 count, uint32 offset);
void readNames();
bool validName(int id) const;
Common::String getName(int id) const;
Common::String getArgumentName(int id) const;
Common::String getLocalName(int id) const;
Common::SharedPtr<Node> pop();
int variableMultiplier();
Common::SharedPtr<Node> readVar(int varType);
Common::String getVarNameFromSet(const Bytecode &bytecode);
Common::SharedPtr<Node> readV4Property(uint32 offset, int propertyType, int propertyID);
Common::SharedPtr<Node> readChunkRef(uint32 offset, Common::SharedPtr<Node> string);
void tagLoops();
bool isRepeatWithIn(uint32 startIndex, uint32 endIndex);
BytecodeTag identifyLoop(uint32 startIndex, uint32 endIndex);
void parse();
uint32 translateBytecode(Bytecode &bytecode, uint32 index);
void writeBytecodeText(CodeWriterVisitor &code) const;
};
/* Bytecode */
struct Bytecode {
byte opID;
OpCode opcode;
int32 obj;
uint32 pos;
BytecodeTag tag;
uint32 ownerLoop;
Common::SharedPtr<Node> translation;
Bytecode(byte op, int32 o, uint32 p)
: opID(op), obj(o), pos(p), tag(kTagNone), ownerLoop(0xffffffff) {
opcode = static_cast<OpCode>(op >= 0x40 ? 0x40 + op % 0x40 : op);
}
};
}
#endif // LINGODEC_HANDLER_H

View File

@@ -0,0 +1,365 @@
/*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
#include "common/str.h"
#include "common/stream.h"
#include "common/util.h"
#include "./enums.h"
#include "./names.h"
namespace LingoDec {
namespace StandardNames {
/* StandardNames */
const char *const opcodeNamesS[] = {
// single-byte
"unk00",
"ret", // kOpRet 0x01
"retfactory", // kOpRetFactory 0x02
"pushzero", // kOpPushZero 0x03
"mul", // kOpMul 0x04
"add", // kOpAdd 0x05
"sub", // kOpSub 0x06
"div", // kOpDiv 0x07
"mod", // kOpMod 0x08
"inv", // kOpInv 0x09
"joinstr", // kOpJoinStr 0x0a
"joinpadstr", // kOpJoinPadStr 0x0b
"lt", // kOpLt 0x0c
"lteq", // kOpLtEq 0x0d
"nteq", // kOpNtEq 0x0e
"eq", // kOpEq 0x0f
"gt", // kOpGt 0x10
"gteq", // kOpGtEq 0x11
"and", // kOpAnd 0x12
"or", // kOpOr 0x13
"not", // kOpNot 0x14
"containsstr", // kOpContainsStr 0x15
"contains0str", // kOpContains0Str 0x16
"getchunk", // kOpGetChunk 0x17
"hilitechunk", // kOpHiliteChunk 0x18
"ontospr", // kOpOntoSpr 0x19
"intospr", // kOpIntoSpr 0x1a
"getfield", // kOpGetField 0x1b
"starttell", // kOpStartTell 0x1c
"endtell", // kOpEndTell 0x1d
"pushlist", // kOpPushList 0x1e
"pushproplist", // kOpPushPropList 0x1f
"unk20",
"swap", // kOpSwap 0x21
};
const char *const opcodeNamesM[] = {
// multi-byte
"unk40",
"pushint8", // kOpPushInt8 0x41
"pusharglistnoret", // kOpPushArgListNoRet 0x42
"pusharglist", // kOpPushArgList 0x43
"pushcons", // kOpPushCons 0x44
"pushsymb", // kOpPushSymb 0x45
"pushvarref", // kOpPushVarRef 0x46
"unk47",
"getglobal2", // kOpGetGlobal2 0x48
"getglobal", // kOpGetGlobal 0x49
"getprop", // kOpGetProp 0x4a
"getparam", // kOpGetParam 0x4b
"getlocal", // kOpGetLocal 0x4c
"unk4d",
"setglobal2", // kOpSetGlobal2 0x4e
"setglobal", // kOpSetGlobal 0x4f
"setprop", // kOpSetProp 0x50
"setparam", // kOpSetParam 0x51
"setlocal", // kOpSetLocal 0x52
"jmp", // kOpJmp 0x53
"endrepeat", // kOpEndRepeat 0x54
"jmpifz", // kOpJmpIfZ 0x55
"localcall", // kOpLocalCall 0x56
"extcall", // kOpExtCall 0x57
"objcallv4", // kOpObjCallV4 0x58
"put", // kOpPut 0x59
"putchunk", // kOpPutChunk 0x5a
"deletechunk", // kOpDeleteChunk 0x5b
"get", // kOpGet 0x5c
"set", // kOpSet 0x5d
"unk5e",
"getmovieprop", // kOpGetMovieProp 0x5f
"setmovieprop", // kOpSetMovieProp 0x60
"getobjprop", // kOpGetObjProp 0x61
"setobjprop", // kOpSetObjProp 0x62
"tellcall", // kOpTellCall 0x63
"peek", // kOpPeek 0x64
"pop", // kOpPop 0x65
"thebuiltin", // kOpTheBuiltin 0x66
"objcall", // kOpObjCall 0x67
"unk68",
"unk69",
"unk6a",
"unk6b",
"unk6c",
"pushchunkvarref", // kOpPushChunkVarRef 0x6d
"pushint16", // kOpPushInt16 0x6e
"pushint32", // kOpPushInt32 0x6f
"getchainedprop", // kOpGetChainedProp 0x70
"pushfloat32", // kOpPushFloat32 0x71
"gettoplevelprop", // kOpGetTopLevelProp 0x72
"newobj", // kOpNewObj 0x73
};
const char *const binaryOpNames[] = {
"unk00",
"unk01",
"unk02",
"unk03",
"*", // kOpMul 0x04
"+", // kOpAdd 0x05
"-", // kOpSub 0x06
"/", // kOpDiv 0x07
"mod", // kOpMod 0x08
"unk09",
"&", // kOpJoinStr 0x0a
"&&", // kOpJoinPadStr 0x0b
"<", // kOpLt 0x0c
"<=", // kOpLtEq 0x0d
"<>", // kOpNtEq 0x0e
"=", // kOpEq 0x0f
">", // kOpGt 0x10
">=", // kOpGtEq 0x11
"and", // kOpAnd 0x12
"or", // kOpOr 0x13
"unk14",
"contains", // kOpContainsStr 0x15
"starts", // kOpContains0Str 0x16
};
const char *const chunkTypeNames[] = {
"unk00",
"char", // kChunkChar 0x01
"word", // kChunkWord 0x02
"item", // kChunkItem 0x03
"line", // kChunkLine 0x04
};
const char *const putTypeNames[] = {
"unk00",
"into", // kPutInto 0x01
"after", // kPutAfter 0x02
"before", // kPutBefore 0x03
};
const char *const moviePropertyNames[] = {
"floatPrecision", // 0x00
"mouseDownScript", // 0x01
"mouseUpScript", // 0x02
"keyDownScript", // 0x03
"keyUpScript", // 0x04
"timeoutScript", // 0x05
"short time", // 0x06
"abbr time", // 0x07
"long time", // 0x08
"short date", // 0x09
"abbr date", // 0x0a
"long date", // 0x0b
};
const char *const whenEventNames[] = {
"unk00",
"mouseDown",// 0x01
"mouseUp", // 0x02
"keyDown", // 0x03
"keyUp", // 0x04
"timeOut", // 0x05
};
const char *const menuPropertyNames[] = {
"unk00",
"name", // 0x01
"number of menuItems", // 0x02
};
const char *const menuItemPropertyNames[] = {
"unk00",
"name", // 0x01
"checkMark",// 0x02
"enabled", // 0x03
"script", // 0x04
};
const char *const soundPropertyNames[] = {
"unk00",
"volume", // 0x01
};
const char *const spritePropertyNames[] = {
"unk00",
"type", // 0x01
"backColor", // 0x02
"bottom", // 0x03
"castNum", // 0x04
"constraint", // 0x05
"cursor", // 0x06
"foreColor", // 0x07
"height", // 0x08
"immediate", // 0x09
"ink", // 0x0a
"left", // 0x0b
"lineSize", // 0x0c
"locH", // 0x0d
"locV", // 0x0e
"movieRate", // 0x0f
"movieTime", // 0x10
"pattern", // 0x11
"puppet", // 0x12
"right", // 0x13
"startTime", // 0x14
"stopTime", // 0x15
"stretch", // 0x16
"top", // 0x17
"trails", // 0x18
"visible", // 0x19
"volume", // 0x1a
"width", // 0x1b
"blend", // 0x1c
"scriptNum", // 0x1d
"moveableSprite", // 0x1e
"editableText", // 0x1f
"scoreColor", // 0x20
"loc", // 0x21
"rect", // 0x22
"memberNum", // 0x23
"castLibNum", // 0x24
"member", // 0x25
"scriptInstanceList", // 0x26
"currentTime", // 0x27
"mostRecentCuePoint", // 0x28
"tweened", // 0x29
"name", // 0x2a
};
const char *const animationPropertyNames[] = {
"unk00",
"beepOn", // 0x01
"buttonStyle", // 0x02
"centerStage", // 0x03
"checkBoxAccess", // 0x04
"checkboxType", // 0x05
"colorDepth", // 0x06
"colorQD", // 0x07
"exitLock", // 0x08
"fixStageSize", // 0x09
"fullColorPermit", // 0x0a
"imageDirect", // 0x0b
"doubleClick", // 0x0c
"key", // 0x0d
"lastClick", // 0x0e
"lastEvent", // 0x0f
"keyCode", // 0x10
"lastKey", // 0x11
"lastRoll", // 0x12
"timeoutLapsed", // 0x13
"multiSound", // 0x14
"pauseState", // 0x15
"quickTimePresent", // 0x16
"selEnd", // 0x17
"selStart", // 0x18
"soundEnabled", // 0x19
"soundLevel", // 0x1a
"stageColor", // 0x1b
// 0x1c indicates dontPassEvent was called.
// It doesn't seem to have a Lingo-accessible name.
"unk1c",
"switchColorDepth", // 0x1d
"timeoutKeyDown", // 0x1e
"timeoutLength", // 0x1f
"timeoutMouse", // 0x20
"timeoutPlay", // 0x21
"timer", // 0x22
"preLoadRAM", // 0x23
"videoForWindowsPresent", // 0x24
"netPresent", // 0x25
"safePlayer", // 0x26
"soundKeepDevice", // 0x27
"soundMixMedia", // 0x28
};
const char *const animation2PropertyNames[] = {
"unk00",
"perFrameHook", // 0x01
"number of castMembers",// 0x02
"number of menus", // 0x03
"number of castLibs", // 0x04
"number of xtras", // 0x05
};
const char *const memberPropertyNames[] = {
"unk00",
"name", // 0x01
"text", // 0x02
"textStyle", // 0x03
"textFont", // 0x04
"textHeight", // 0x05
"textAlign", // 0x06
"textSize", // 0x07
"picture", // 0x08
"hilite", // 0x09
"number", // 0x0a
"size", // 0x0b
"loop", // 0x0c
"duration", // 0x0d
"controller", // 0x0e
"directToStage",// 0x0f
"sound", // 0x10
"foreColor", // 0x11
"backColor", // 0x12
"type", // 0x13
};
Common::String getOpcodeName(byte id) {
if (id < 0x22)
return opcodeNamesS[id];
if (id < 0x40)
return Common::String::format("unk%02x" , id);
id = id % 0x40;
if (id < 0x34)
return opcodeNamesM[id];
return Common::String::format("unk%02x" , id);
}
} // namespace StandardNames
/* ScriptNames */
void ScriptNames::read(Common::SeekableReadStream &stream) {
// Lingo scripts are always big endian regardless of file endianness
unknown0 = stream.readSint32BE();
unknown1 = stream.readSint32BE();
len1 = stream.readUint32BE();
len2 = stream.readUint32BE();
namesOffset = stream.readUint16BE();
namesCount = stream.readUint16BE();
stream.seek(namesOffset);
names.resize(namesCount);
for (auto &name : names) {
name = stream.readPascalString();
}
}
bool ScriptNames::validName(int id) const {
return -1 < id && (unsigned)id < names.size();
}
Common::String ScriptNames::getName(int id) const {
if (validName(id))
return names[id];
return Common::String::format("UNKNOWN_NAME_%d", id);
}
}

View File

@@ -0,0 +1,63 @@
/*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
#ifndef LINGODEC_NAMES_H
#define LINGODEC_NAMES_H
#include "common/array.h"
#include "common/stablemap.h"
namespace Common {
class SeekableReadStream;
class String;
}
namespace LingoDec {
/* StandardNames */
namespace StandardNames {
extern const char *const opcodeNamesS[];
extern const char *const opcodeNamesM[];
extern const char *const binaryOpNames[];
extern const char *const chunkTypeNames[];
extern const char *const putTypeNames[];
extern const char *const moviePropertyNames[];
extern const char *const whenEventNames[];
extern const char *const menuPropertyNames[];
extern const char *const menuItemPropertyNames[];
extern const char *const soundPropertyNames[];
extern const char *const spritePropertyNames[];
extern const char *const animationPropertyNames[];
extern const char *const animation2PropertyNames[];
extern const char *const memberPropertyNames[];
Common::String getOpcodeName(byte id);
}
/* ScriptNames */
struct ScriptNames {
int32 unknown0 = 0;
int32 unknown1 = 0;
uint32 len1 = 0;
uint32 len2 = 0;
uint16 namesOffset = 0;
uint16 namesCount = 0;
Common::Array<Common::String> names;
unsigned int version;
ScriptNames(unsigned int version_) : version(version_) {}
void read(Common::SeekableReadStream &stream);
bool validName(int id) const;
Common::String getName(int id) const;
};
} // namespace LingoDec
#endif // LINGODEC_NAMES_H

View File

@@ -0,0 +1,25 @@
/*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
#ifndef LINGODEC_RESOLVER_H
#define LINGODEC_RESOLVER_H
namespace LingoDec {
struct Script;
struct ScriptNames;
class ChunkResolver {
public:
ChunkResolver() {}
virtual ~ChunkResolver() {}
virtual Script *getScript(int32 id) = 0;
virtual ScriptNames *getScriptNames(int32 id) = 0;
};
}
#endif // LINGODEC_RESOLVER_H

View File

@@ -0,0 +1,256 @@
/*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
#include "common/stream.h"
#include "./ast.h"
#include "./codewritervisitor.h"
#include "./context.h"
#include "./handler.h"
#include "./script.h"
double readAppleFloat80(void *ptr);
namespace LingoDec {
/* Script */
Script::Script(unsigned int version_) :
version(version_),
context(nullptr) {}
Script::~Script() = default;
void Script::read(Common::SeekableReadStream &stream) {
// Lingo scripts are always big endian regardless of file endianness
stream.seek(8);
/* 8 */ totalLength = stream.readUint32BE();
/* 12 */ totalLength2 = stream.readUint32BE();
/* 16 */ headerLength = stream.readUint16BE();
/* 18 */ scriptNumber = stream.readUint16BE();
/* 20 */ unk20 = stream.readSint16BE();
/* 22 */ parentNumber = stream.readSint16BE();
stream.seek(38);
/* 38 */ scriptFlags = stream.readUint32BE();
/* 42 */ unk42 = stream.readSint16BE();
/* 44 */ unk43 = stream.readSint16BE();
/* 46 */ castID = stream.readSint16BE();
/* 48 */ factoryNameID = stream.readSint16BE();
/* 50 */ handlerVectorsCount = stream.readUint16BE();
/* 52 */ handlerVectorsOffset = stream.readUint32BE();
/* 56 */ handlerVectorsSize = stream.readUint32BE();
/* 60 */ propertiesCount = stream.readUint16BE();
/* 62 */ propertiesOffset = stream.readUint32BE();
/* 66 */ globalsCount = stream.readUint16BE();
/* 68 */ globalsOffset = stream.readUint32BE();
/* 72 */ handlersCount = stream.readUint16BE();
/* 74 */ handlersOffset = stream.readUint32BE();
/* 78 */ literalsCount = stream.readUint16BE();
/* 80 */ literalsOffset = stream.readUint32BE();
/* 84 */ literalsDataCount = stream.readUint32BE();
/* 88 */ literalsDataOffset = stream.readUint32BE();
propertyNameIDs = readVarnamesTable(stream, propertiesCount, propertiesOffset);
globalNameIDs = readVarnamesTable(stream, globalsCount, globalsOffset);
handlers.resize(handlersCount);
for (auto &handler : handlers) {
handler.setScript(this);
}
if ((scriptFlags & LingoDec::kScriptFlagEventScript) && handlersCount > 0) {
handlers[0].isGenericEvent = true;
}
stream.seek(handlersOffset);
for (auto &handler : handlers) {
handler.readRecord(stream);
}
for (auto &handler : handlers) {
handler.readData(stream);
}
stream.seek(literalsOffset);
literals.resize(literalsCount);
for (auto &literal : literals) {
literal.readRecord(stream, version);
}
for (auto &literal : literals) {
literal.readData(stream, literalsDataOffset);
}
}
Common::Array<int16> Script::readVarnamesTable(Common::SeekableReadStream &stream, uint16 count, uint32 offset) {
stream.seek(offset);
Common::Array<int16> nameIDs(count);
for (uint16 i = 0; i < count; i++) {
nameIDs[i] = stream.readSint16BE();
}
return nameIDs;
}
bool Script::validName(int id) const {
return context->validName(id);
}
Common::String Script::getName(int id) const {
return context->getName(id);
}
void Script::setContext(ScriptContext *ctx) {
this->context = ctx;
if (factoryNameID != -1) {
factoryName = getName(factoryNameID);
}
for (auto nameID : propertyNameIDs) {
if (validName(nameID)) {
Common::String name = getName(nameID);
if (isFactory() && name == "me")
continue;
propertyNames.push_back(name);
}
}
for (auto nameID : globalNameIDs) {
if (validName(nameID)) {
globalNames.push_back(getName(nameID));
}
}
for (auto &handler : handlers) {
handler.readNames();
}
}
void Script::parse() {
for (auto &handler : handlers) {
handler.parse();
}
}
void Script::writeVarDeclarations(CodeWriterVisitor &code) const {
if (!isFactory()) {
if (propertyNames.size() > 0) {
code.write("property ");
for (size_t i = 0; i < propertyNames.size(); i++) {
if (i > 0)
code.write(", ");
code.write(propertyNames[i]);
}
code.writeLine();
}
}
if (globalNames.size() > 0) {
code.write("global ");
for (size_t i = 0; i < globalNames.size(); i++) {
if (i > 0)
code.write(", ");
code.write(globalNames[i]);
}
code.writeLine();
}
}
void Script::writeScriptText(CodeWriterVisitor &code) const {
size_t origSize = code._str.size();
writeVarDeclarations(code);
if (isFactory()) {
if (code._str.size() != origSize) {
code.writeLine();
}
code.write("factory ");
code.writeLine(factoryName);
}
for (size_t i = 0; i < handlers.size(); i++) {
if ((!isFactory() || i > 0) && code._str.size() != origSize) {
code.writeLine();
}
handlers[i].ast.root->accept(code);
}
for (auto factory : factories) {
if (code._str.size() != origSize) {
code.writeLine();
}
factory->writeScriptText(code);
}
}
Common::String Script::scriptText(const char *lineEnding, bool dotSyntax) const {
CodeWriterVisitor code(dotSyntax, false, lineEnding);
writeScriptText(code);
return code._str;
}
void Script::writeBytecodeText(CodeWriterVisitor &code) const {
size_t origSize = code._str.size();
writeVarDeclarations(code);
if (isFactory()) {
if (code._str.size() != origSize) {
code.writeLine();
}
code.write("factory ");
code.writeLine(factoryName);
}
for (size_t i = 0; i < handlers.size(); i++) {
if ((!isFactory() || i > 0) && code._str.size() != origSize) {
code.writeLine();
}
handlers[i].writeBytecodeText(code);
}
for (auto factory : factories) {
if (code._str.size() != origSize) {
code.writeLine();
}
factory->writeBytecodeText(code);
}
}
Common::String Script::bytecodeText(const char *lineEnding, bool dotSyntax) const {
CodeWriterVisitor code(dotSyntax, true, lineEnding);
writeBytecodeText(code);
return code._str;
}
bool Script::isFactory() const {
return (scriptFlags & LingoDec::kScriptFlagFactoryDef);
}
/* LiteralStore */
void LiteralStore::readRecord(Common::SeekableReadStream &stream, int version) {
if (version >= 500)
type = static_cast<LiteralType>(stream.readUint32BE());
else
type = static_cast<LiteralType>(stream.readUint16BE());
offset = stream.readUint32BE();
}
void LiteralStore::readData(Common::SeekableReadStream &stream, uint32 startOffset) {
if (type == kLiteralInt) {
value = Common::SharedPtr<LingoDec::Datum>(new LingoDec::Datum((int)offset));
} else {
stream.seek(startOffset + offset);
auto length = stream.readUint32BE();
if (type == kLiteralString) {
char *buf = new char[length];
stream.read(buf, length - 1);
buf[length - 1] = '\0';
value = Common::SharedPtr<LingoDec::Datum>(new LingoDec::Datum(LingoDec::kDatumString, Common::String(buf)));
delete[] buf;
} else if (type == kLiteralFloat) {
double floatVal = 0.0;
if (length == 8) {
floatVal = stream.readDoubleBE();
} else if (length == 10) {
byte buf[10];
stream.read(buf, 10);
floatVal = readAppleFloat80(buf);
}
value = Common::SharedPtr<LingoDec::Datum>(new LingoDec::Datum(floatVal));
} else {
value = Common::SharedPtr<LingoDec::Datum>(new LingoDec::Datum());
}
}
}
} // namespace LingoDec

View File

@@ -0,0 +1,98 @@
/*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
#ifndef LINGODEC_SCRIPT_H
#define LINGODEC_SCRIPT_H
#include "common/ptr.h"
#include "common/str.h"
#include "./enums.h"
namespace Common {
class ReadStream;
}
namespace LingoDec {
class CodeWriterVisitor;
struct Datum;
struct Handler;
struct ScriptContext;
/* LiteralStore */
struct LiteralStore {
LiteralType type;
uint32 offset;
Common::SharedPtr<Datum> value;
void readRecord(Common::SeekableReadStream &stream, int version);
void readData(Common::SeekableReadStream &stream, uint32 startOffset);
};
/* Script */
struct Script {
/* 8 */ uint32 totalLength;
/* 12 */ uint32 totalLength2;
/* 16 */ uint16 headerLength;
/* 18 */ uint16 scriptNumber;
/* 20 */ int16 unk20;
/* 22 */ int16 parentNumber;
/* 38 */ uint32 scriptFlags;
/* 42 */ int16 unk42;
/* 44 */ int16 unk43;
// This castID is not reliable
/* 46 */ int16 castID;
/* 48 */ int16 factoryNameID;
/* 50 */ uint16 handlerVectorsCount;
/* 52 */ uint32 handlerVectorsOffset;
/* 56 */ uint32 handlerVectorsSize;
/* 60 */ uint16 propertiesCount;
/* 62 */ uint32 propertiesOffset;
/* 66 */ uint16 globalsCount;
/* 68 */ uint32 globalsOffset;
/* 72 */ uint16 handlersCount;
/* 74 */ uint32 handlersOffset;
/* 78 */ uint16 literalsCount;
/* 80 */ uint32 literalsOffset;
/* 84 */ uint32 literalsDataCount;
/* 88 */ uint32 literalsDataOffset;
Common::Array<int16> propertyNameIDs;
Common::Array<int16> globalNameIDs;
Common::String factoryName;
Common::Array<Common::String> propertyNames;
Common::Array<Common::String> globalNames;
Common::Array<Handler> handlers;
Common::Array<LiteralStore> literals;
Common::Array<Script *> factories;
unsigned int version;
ScriptContext *context;
Script(unsigned int version_);
~Script();
void read(Common::SeekableReadStream &stream);
Common::Array<int16> readVarnamesTable(Common::SeekableReadStream &stream, uint16 count, uint32 offset);
bool validName(int id) const;
Common::String getName(int id) const;
void setContext(ScriptContext *ctx);
void parse();
void writeVarDeclarations(CodeWriterVisitor &code) const;
void writeScriptText(CodeWriterVisitor &code) const;
Common::String scriptText(const char *lineEnding, bool dotSyntax) const;
void writeBytecodeText(CodeWriterVisitor &code) const;
Common::String bytecodeText(const char *lineEnding, bool dotSyntax) const;
bool isFactory() const;
};
} // namespace LingoDec
#endif // LINGODEC_SCRIPT_H