Initial commit
This commit is contained in:
12
engines/director/lingo/lingodec/README.md
Normal file
12
engines/director/lingo/lingodec/README.md
Normal 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.
|
||||
274
engines/director/lingo/lingodec/ast.cpp
Normal file
274
engines/director/lingo/lingodec/ast.cpp
Normal 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
|
||||
864
engines/director/lingo/lingodec/ast.h
Normal file
864
engines/director/lingo/lingodec/ast.h
Normal 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
|
||||
876
engines/director/lingo/lingodec/codewritervisitor.cpp
Normal file
876
engines/director/lingo/lingodec/codewritervisitor.cpp
Normal 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
|
||||
92
engines/director/lingo/lingodec/codewritervisitor.h
Normal file
92
engines/director/lingo/lingodec/codewritervisitor.h
Normal 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
|
||||
84
engines/director/lingo/lingodec/context.cpp
Normal file
84
engines/director/lingo/lingodec/context.cpp
Normal 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
|
||||
70
engines/director/lingo/lingodec/context.h
Normal file
70
engines/director/lingo/lingodec/context.h
Normal 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
|
||||
216
engines/director/lingo/lingodec/enums.h
Normal file
216
engines/director/lingo/lingodec/enums.h
Normal 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
|
||||
1323
engines/director/lingo/lingodec/handler.cpp
Normal file
1323
engines/director/lingo/lingodec/handler.cpp
Normal file
File diff suppressed because it is too large
Load Diff
110
engines/director/lingo/lingodec/handler.h
Normal file
110
engines/director/lingo/lingodec/handler.h
Normal 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
|
||||
365
engines/director/lingo/lingodec/names.cpp
Normal file
365
engines/director/lingo/lingodec/names.cpp
Normal 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);
|
||||
}
|
||||
|
||||
}
|
||||
63
engines/director/lingo/lingodec/names.h
Normal file
63
engines/director/lingo/lingodec/names.h
Normal 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
|
||||
25
engines/director/lingo/lingodec/resolver.h
Normal file
25
engines/director/lingo/lingodec/resolver.h
Normal 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
|
||||
256
engines/director/lingo/lingodec/script.cpp
Normal file
256
engines/director/lingo/lingodec/script.cpp
Normal 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
|
||||
98
engines/director/lingo/lingodec/script.h
Normal file
98
engines/director/lingo/lingodec/script.h
Normal 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
|
||||
Reference in New Issue
Block a user