[Scummvm-git-logs] scummvm master -> 139d5d16426f8f9538ba0295f0cd8a80ebc23169
whoozle
noreply at scummvm.org
Sat Jul 18 21:47:16 UTC 2026
This automated email contains information about 1 new commit which have been
pushed to the 'scummvm' repo located at https://api.github.com/repos/scummvm/scummvm .
Summary:
139d5d1642 PHOENIXVR: Factor text parser out
Commit: 139d5d16426f8f9538ba0295f0cd8a80ebc23169
https://github.com/scummvm/scummvm/commit/139d5d16426f8f9538ba0295f0cd8a80ebc23169
Author: Vladimir Menshakov (vladimir.menshakov at gmail.com)
Date: 2026-07-18T22:45:43+01:00
Commit Message:
PHOENIXVR: Factor text parser out
Reduce script parser code size, and prepare for script v2.
Changed paths:
A engines/phoenixvr/parser.h
engines/phoenixvr/script.cpp
engines/phoenixvr/script.h
diff --git a/engines/phoenixvr/parser.h b/engines/phoenixvr/parser.h
new file mode 100644
index 00000000000..967afc2aff0
--- /dev/null
+++ b/engines/phoenixvr/parser.h
@@ -0,0 +1,178 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef PHOENIXVR_PARSER_H
+#define PHOENIXVR_PARSER_H
+
+#include "common/array.h"
+#include "common/str.h"
+
+namespace PhoenixVR {
+class Parser {
+ const Common::String &_line;
+ uint _lineno;
+ uint _pos;
+
+public:
+ Parser(const Common::String &line, uint lineno) : _line(line), _lineno(lineno), _pos(0) {
+ skip();
+ }
+
+ void skip() {
+ // comment found in amerzone ",*****"
+ if (_pos == 0 && _line[_pos] == ',') {
+ _pos = _line.size();
+ return;
+ }
+ while (_pos < _line.size() && Common::isSpace(_line[_pos]))
+ ++_pos;
+ if (_pos < _line.size() && _line[_pos] == ';')
+ _pos = _line.size();
+ }
+
+ bool atEnd() const { return _pos >= _line.size(); }
+
+ int peek() const { return _pos < _line.size() ? _line[_pos] : 0; }
+ int next() { return _pos < _line.size() ? _line[_pos++] : 0; }
+
+ void expect(int expected) {
+ skip();
+ auto ch = next();
+ if (ch != expected)
+ error("expected '%c' at line %u, got %c in %s", expected, _lineno, ch, _line.c_str());
+ skip();
+ }
+
+ bool maybe(char ch) {
+ skip();
+ if (peek() == ch) {
+ next();
+ return true;
+ } else
+ return false;
+ }
+
+ bool peek(const Common::String &prefix) {
+ skip();
+ if (_pos + prefix.size() > _line.size())
+ return false;
+
+ auto n = prefix.size();
+ auto *ch0 = _line.c_str() + _pos;
+ auto *ch1 = prefix.c_str();
+ while (n--) {
+ if (tolower(*ch0++) != tolower(*ch1++))
+ return false;
+ }
+ return true;
+ }
+
+ bool maybe(const Common::String &prefix) {
+ if (peek(prefix)) {
+ _pos += prefix.size();
+ skip();
+ return true;
+ }
+ return false;
+ }
+
+ bool keyword(const Common::String &prefix) {
+ skip();
+ bool yes = peek(prefix);
+ // keywords ends either on non-alpha or eof
+ if (yes) {
+ auto end = _pos + prefix.size();
+ if (end >= _line.size() || !Common::isAlpha(_line[end])) {
+ _pos += prefix.size();
+ skip();
+ return true;
+ }
+ }
+ return false;
+ }
+
+ Common::String nextArg() {
+ skip();
+ auto begin = _pos;
+ while (_pos < _line.size() && _line[_pos] != ',' && _line[_pos] != ')')
+ ++_pos;
+ auto end = _pos;
+ while (end > begin && Common::isSpace(_line[end - 1]))
+ --end;
+ skip();
+ return _line.substr(begin, end - begin);
+ }
+
+ int nextInt() {
+ bool negative = false;
+ int value = 0;
+ if (maybe('-'))
+ negative = true;
+ do {
+ auto ch = next();
+ if (ch < '0' || ch > '9')
+ error("expected digit at %d, line: %u, %s", _pos, _lineno, _line.c_str());
+ value = value * 10 + (ch - '0');
+ } while (Common::isDigit(peek()));
+ return negative ? -value : value;
+ }
+
+ Common::String nextWord() {
+ skip();
+ auto begin = _pos;
+ while (_pos < _line.size() && !Common::isSpace(_line[_pos]) && _line[_pos] != ',' && _line[_pos] != '(' && _line[_pos] != '=' && _line[_pos] != ')')
+ ++_pos;
+ auto end = _pos;
+ skip();
+ return _line.substr(begin, end - begin);
+ }
+
+ Common::String nextString() {
+ skip();
+ expect('"');
+ Common::String str;
+ while (_pos < _line.size()) {
+ auto ch = _line[_pos++];
+ if (ch == '"')
+ break;
+ str += ch;
+ }
+ skip();
+ return str;
+ }
+
+ Common::Array<Common::String> readStringList() {
+ Common::Array<Common::String> list;
+ while (!atEnd() && peek() != ')') {
+ if (peek() == '"')
+ list.push_back(nextString());
+ else {
+ list.push_back(nextArg());
+ }
+ if (!atEnd() && peek() != ')')
+ expect(',');
+ }
+ return list;
+ }
+};
+} // namespace PhoenixVR
+
+#endif
diff --git a/engines/phoenixvr/script.cpp b/engines/phoenixvr/script.cpp
index 8c5412faf06..a4219aab369 100644
--- a/engines/phoenixvr/script.cpp
+++ b/engines/phoenixvr/script.cpp
@@ -2,301 +2,150 @@
#include "common/stream.h"
#include "common/textconsole.h"
#include "phoenixvr/commands.h"
+#include "phoenixvr/parser.h"
#include "phoenixvr/phoenixvr.h"
namespace PhoenixVR {
-namespace {
-class Parser {
- const Common::String &_line;
- uint _lineno;
- uint _pos;
-
-public:
- Parser(const Common::String &line, uint lineno) : _line(line), _lineno(lineno), _pos(0) {
- skip();
- }
-
- void skip() {
- // comment found in amerzone ",*****"
- if (_pos == 0 && _line[_pos] == ',') {
- _pos = _line.size();
- return;
- }
- while (_pos < _line.size() && Common::isSpace(_line[_pos]))
- ++_pos;
- if (_pos < _line.size() && _line[_pos] == ';')
- _pos = _line.size();
- }
-
- bool atEnd() const { return _pos >= _line.size(); }
-
- int peek() const { return _pos < _line.size() ? _line[_pos] : 0; }
- int next() { return _pos < _line.size() ? _line[_pos++] : 0; }
-
- void expect(int expected) {
- skip();
- auto ch = next();
- if (ch != expected)
- error("expected '%c' at line %u, got %c in %s", expected, _lineno, ch, _line.c_str());
- skip();
- }
-
- bool maybe(char ch) {
- skip();
- if (peek() == ch) {
- next();
- return true;
- } else
- return false;
- }
-
- bool peek(const Common::String &prefix) {
- skip();
- if (_pos + prefix.size() > _line.size())
- return false;
-
- auto n = prefix.size();
- auto *ch0 = _line.c_str() + _pos;
- auto *ch1 = prefix.c_str();
- while (n--) {
- if (tolower(*ch0++) != tolower(*ch1++))
- return false;
- }
- return true;
- }
-
- bool maybe(const Common::String &prefix) {
- if (peek(prefix)) {
- _pos += prefix.size();
- skip();
- return true;
- }
- return false;
- }
-
- bool keyword(const Common::String &prefix) {
- skip();
- bool yes = peek(prefix);
- // keywords ends either on non-alpha or eof
- if (yes) {
- auto end = _pos + prefix.size();
- if (end >= _line.size() || !Common::isAlpha(_line[end])) {
- _pos += prefix.size();
- skip();
- return true;
- }
+Script::CommandPtr Script::parseCommand(Parser &p) {
+ if (p.keyword("setcursordefault")) {
+ auto idx = p.nextWord();
+ p.expect(',');
+ bool valid = !idx.empty() && Common::isDigit(idx[0]);
+ // this skips garbage cursor default found in Amerzone
+ // e.g. `setcursordefault cursor1.pcx,cursor1`
+ return CommandPtr(new SetCursorDefault(valid ? atoi(idx.c_str()) : -1, p.nextWord()));
+ } else if (p.keyword("lockkey")) {
+ auto idx = p.nextInt();
+ p.expect(',');
+ auto fname = p.nextWord();
+ return CommandPtr(new LockKey(idx, Common::move(fname)));
+ } else if (p.keyword("resetlockkey")) {
+ return CommandPtr(new ResetLockKey());
+ } else if (p.maybe("fade=")) {
+ auto arg0 = p.nextInt();
+ p.expect(',');
+ auto arg1 = p.nextInt();
+ p.expect(',');
+ auto arg2 = p.nextInt();
+ return CommandPtr(new Fade(arg0, arg1, arg2));
+ } else if (p.keyword("transfade")) {
+ return CommandPtr(new Transfade(p.nextInt()));
+ } else if (p.maybe("setzoom=")) {
+ return CommandPtr(new SetZoom(toRadian(p.nextInt())));
+ } else if (p.maybe("setangle=") || p.keyword("setangle")) {
+ auto a0 = toAngle(p.nextInt());
+ p.expect(',');
+ auto a1 = toAngle(p.nextInt());
+ return CommandPtr(new SetAngle(a0, a1));
+ } else if (p.maybe("setnord=")) {
+ auto a0 = toAngle(p.nextInt());
+ return CommandPtr(new SetNord(a0));
+ } else if (p.keyword("interpolangle") || p.keyword("interpolanglezoom")) {
+ p.maybe(',');
+ p.maybe('=');
+ auto a0 = toAngle(p.nextInt());
+ p.expect(',');
+ auto a1 = toAngle(p.nextInt());
+ p.expect(',');
+ float a2 = p.nextInt();
+ float a3 = 0;
+ if (p.maybe(','))
+ a3 = p.nextInt();
+ // x, y, speed
+ // or
+ // x, y, zoom, speed
+ return CommandPtr(a3 != 0 ? new InterpolAngle(a0, a1, a3, toRadian(a2)) : new InterpolAngle(a0, a1, a2, 0));
+ } else if (p.maybe("anglexmax=") || p.keyword("anglexmax")) {
+ return CommandPtr(new AngleXMax(toAngle(p.nextInt())));
+ } else if (p.maybe("angleymax=") || p.keyword("angleymax")) {
+ auto y0 = toAngle(p.nextInt());
+ p.expect(',');
+ auto y1 = toAngle(p.nextInt());
+ return CommandPtr(new AngleYMax(y0, y1));
+ } else if (p.keyword("gotowarp")) {
+ return CommandPtr(new GoToWarp(p.nextWord()));
+ } else if (p.keyword("playsound3d")) {
+ auto sound = p.nextWord();
+ p.expect(',');
+ auto arg0 = p.nextInt();
+ p.expect(',');
+ auto arg1 = p.nextInt();
+ int arg2 = 0;
+ if (p.maybe(','))
+ arg2 = p.nextInt();
+ return CommandPtr(new PlaySound3D(Common::move(sound), arg0, toAngle(arg1), arg2));
+ } else if (p.keyword("playmusique")) {
+ auto sound = p.nextWord();
+ int vol = 255;
+ if (p.maybe(','))
+ vol = p.nextInt();
+ return CommandPtr(new PlayMusique(Common::move(sound), vol));
+ } else if (p.keyword("playsound")) {
+ auto sound = p.nextWord();
+ p.expect(',');
+ auto arg0 = p.nextInt();
+ int arg1 = 0;
+ if (p.maybe(','))
+ arg1 = p.nextInt();
+ return CommandPtr(new PlaySound(Common::move(sound), arg0, arg1));
+ } else if (p.keyword("playrndsound")) {
+ auto sound = p.nextWord();
+ p.expect(',');
+ auto arg0 = p.nextInt();
+ p.expect(',');
+ auto arg1 = p.nextInt();
+ int arg2 = 0;
+ if (p.maybe(','))
+ arg2 = p.nextInt();
+ return CommandPtr(new PlayRndSound(Common::move(sound), arg0, arg1, arg2));
+ } else if (p.keyword("stopsound3d")) {
+ return CommandPtr(new StopSound3D(p.nextWord()));
+ } else if (p.keyword("stopsound") || p.keyword("stopmusique")) {
+ return CommandPtr(new StopSound(p.nextWord()));
+ } else if (p.keyword("setcursor")) {
+ auto image = p.nextWord();
+ p.maybe(',');
+ auto warp = p.nextWord();
+ int idx = 0;
+ if (p.maybe(','))
+ idx = p.nextInt();
+ return CommandPtr(new SetCursor(Common::move(image), Common::move(warp), idx));
+ } else if (p.keyword("hidecursor")) {
+ auto warp = p.nextWord();
+ p.expect(',');
+ auto idx = p.nextInt();
+ return CommandPtr(new HideCursor(Common::move(warp), idx));
+ } else if (p.keyword("set")) {
+ auto var = p.nextWord();
+ if (p.maybe(',')) {
+ // this is typo in amerzone, this meant to be setCursor
+ auto warp = p.nextWord();
+ int idx = 0;
+ if (p.maybe(','))
+ idx = p.nextInt();
+ return CommandPtr(new SetCursor(Common::move(var), Common::move(warp), idx));
}
- return false;
- }
-
- Common::String nextArg() {
- skip();
- auto begin = _pos;
- while (_pos < _line.size() && _line[_pos] != ',' && _line[_pos] != ')')
- ++_pos;
- auto end = _pos;
- while (end > begin && Common::isSpace(_line[end - 1]))
- --end;
- skip();
- return _line.substr(begin, end - begin);
- }
-
- int nextInt() {
- bool negative = false;
int value = 0;
- if (maybe('-'))
- negative = true;
- do {
- auto ch = next();
- if (ch < '0' || ch > '9')
- error("expected digit at %d, line: %u, %s", _pos, _lineno, _line.c_str());
- value = value * 10 + (ch - '0');
- } while (Common::isDigit(peek()));
- return negative ? -value : value;
- }
-
- Common::String nextWord() {
- skip();
- auto begin = _pos;
- while (_pos < _line.size() && !Common::isSpace(_line[_pos]) && _line[_pos] != ',' && _line[_pos] != '(' && _line[_pos] != '=' && _line[_pos] != ')')
- ++_pos;
- auto end = _pos;
- skip();
- return _line.substr(begin, end - begin);
- }
-
- Common::String nextString() {
- skip();
- expect('"');
- Common::String str;
- while (_pos < _line.size()) {
- auto ch = _line[_pos++];
- if (ch == '"')
- break;
- str += ch;
+ if (p.maybe('=')) {
+ p.skip();
+ if (!p.atEnd() && p.peek() != '!')
+ value = p.nextInt();
}
- skip();
- return str;
+ return CommandPtr(new SetVar(Common::move(var), value));
+ } else if (p.keyword("not")) {
+ auto var = p.nextWord();
+ return CommandPtr(new Not(Common::move(var)));
+ } else if (p.keyword("gosub")) {
+ return CommandPtr(new GoSub(p.nextWord()));
+ } else if (p.keyword("return")) {
+ return CommandPtr{new Return()};
+ } else if (p.keyword("end")) {
+ return CommandPtr{new EndScript()};
}
-
- Common::Array<Common::String> readStringList() {
- Common::Array<Common::String> list;
- while (!atEnd() && peek() != ')') {
- if (peek() == '"')
- list.push_back(nextString());
- else {
- list.push_back(nextArg());
- }
- if (!atEnd() && peek() != ')')
- expect(',');
- }
- return list;
- }
-
- Script::CommandPtr parseCommand() {
- using CommandPtr = Script::CommandPtr;
- if (keyword("setcursordefault")) {
- auto idx = nextWord();
- expect(',');
- bool valid = !idx.empty() && Common::isDigit(idx[0]);
- // this skips garbage cursor default found in Amerzone
- // e.g. `setcursordefault cursor1.pcx,cursor1`
- return CommandPtr(new SetCursorDefault(valid ? atoi(idx.c_str()) : -1, nextWord()));
- } else if (keyword("lockkey")) {
- auto idx = nextInt();
- expect(',');
- auto fname = nextWord();
- return CommandPtr(new LockKey(idx, Common::move(fname)));
- } else if (keyword("resetlockkey")) {
- return CommandPtr(new ResetLockKey());
- } else if (maybe("fade=")) {
- auto arg0 = nextInt();
- expect(',');
- auto arg1 = nextInt();
- expect(',');
- auto arg2 = nextInt();
- return CommandPtr(new Fade(arg0, arg1, arg2));
- } else if (keyword("transfade")) {
- return CommandPtr(new Transfade(nextInt()));
- } else if (maybe("setzoom=")) {
- return CommandPtr(new SetZoom(toRadian(nextInt())));
- } else if (maybe("setangle=") || keyword("setangle")) {
- auto a0 = toAngle(nextInt());
- expect(',');
- auto a1 = toAngle(nextInt());
- return CommandPtr(new SetAngle(a0, a1));
- } else if (maybe("setnord=")) {
- auto a0 = toAngle(nextInt());
- return CommandPtr(new SetNord(a0));
- } else if (keyword("interpolangle") || keyword("interpolanglezoom")) {
- maybe(',');
- maybe('=');
- auto a0 = toAngle(nextInt());
- expect(',');
- auto a1 = toAngle(nextInt());
- expect(',');
- float a2 = nextInt();
- float a3 = 0;
- if (maybe(','))
- a3 = nextInt();
- // x, y, speed
- // or
- // x, y, zoom, speed
- return CommandPtr(a3 != 0 ? new InterpolAngle(a0, a1, a3, toRadian(a2)) : new InterpolAngle(a0, a1, a2, 0));
- } else if (maybe("anglexmax=") || keyword("anglexmax")) {
- return CommandPtr(new AngleXMax(toAngle(nextInt())));
- } else if (maybe("angleymax=") || keyword("angleymax")) {
- auto y0 = toAngle(nextInt());
- expect(',');
- auto y1 = toAngle(nextInt());
- return CommandPtr(new AngleYMax(y0, y1));
- } else if (keyword("gotowarp")) {
- return CommandPtr(new GoToWarp(nextWord()));
- } else if (keyword("playsound3d")) {
- auto sound = nextWord();
- expect(',');
- auto arg0 = nextInt();
- expect(',');
- auto arg1 = nextInt();
- int arg2 = 0;
- if (maybe(','))
- arg2 = nextInt();
- return CommandPtr(new PlaySound3D(Common::move(sound), arg0, toAngle(arg1), arg2));
- } else if (keyword("playmusique")) {
- auto sound = nextWord();
- int vol = 255;
- if (maybe(','))
- vol = nextInt();
- return CommandPtr(new PlayMusique(Common::move(sound), vol));
- } else if (keyword("playsound")) {
- auto sound = nextWord();
- expect(',');
- auto arg0 = nextInt();
- int arg1 = 0;
- if (maybe(','))
- arg1 = nextInt();
- return CommandPtr(new PlaySound(Common::move(sound), arg0, arg1));
- } else if (keyword("playrndsound")) {
- auto sound = nextWord();
- expect(',');
- auto arg0 = nextInt();
- expect(',');
- auto arg1 = nextInt();
- int arg2 = 0;
- if (maybe(','))
- arg2 = nextInt();
- return CommandPtr(new PlayRndSound(Common::move(sound), arg0, arg1, arg2));
- } else if (keyword("stopsound3d")) {
- return CommandPtr(new StopSound3D(nextWord()));
- } else if (keyword("stopsound") || keyword("stopmusique")) {
- return CommandPtr(new StopSound(nextWord()));
- } else if (keyword("setcursor")) {
- auto image = nextWord();
- maybe(',');
- auto warp = nextWord();
- int idx = 0;
- if (maybe(','))
- idx = nextInt();
- return CommandPtr(new SetCursor(Common::move(image), Common::move(warp), idx));
- } else if (keyword("hidecursor")) {
- auto warp = nextWord();
- expect(',');
- auto idx = nextInt();
- return CommandPtr(new HideCursor(Common::move(warp), idx));
- } else if (keyword("set")) {
- auto var = nextWord();
- if (maybe(',')) {
- // this is typo in amerzone, this meant to be setCursor
- auto warp = nextWord();
- int idx = 0;
- if (maybe(','))
- idx = nextInt();
- return CommandPtr(new SetCursor(Common::move(var), Common::move(warp), idx));
- }
- int value = 0;
- if (maybe('=')) {
- skip();
- if (!atEnd() && peek() != '!')
- value = nextInt();
- }
- return CommandPtr(new SetVar(Common::move(var), value));
- } else if (keyword("not")) {
- auto var = nextWord();
- return CommandPtr(new Not(Common::move(var)));
- } else if (keyword("gosub")) {
- return CommandPtr(new GoSub(nextWord()));
- } else if (keyword("return")) {
- return CommandPtr{new Return()};
- } else if (keyword("end")) {
- return CommandPtr{new EndScript()};
- }
- return {};
- };
+ return {};
};
-} // namespace
-
void Script::Scope::exec(ExecutionContext &ctx) const {
exec(ctx, 0);
}
@@ -428,7 +277,7 @@ void Script::parseLine(const Common::String &line, uint lineno) {
else
error("unhandled plugin command %s at line %d", line.c_str(), lineno);
} else {
- auto cmd = p.parseCommand();
+ auto cmd = parseCommand(p);
if (cmd) {
if (_conditional) {
_conditional->target = Common::move(cmd);
diff --git a/engines/phoenixvr/script.h b/engines/phoenixvr/script.h
index ab8d77426c8..99f8389b183 100644
--- a/engines/phoenixvr/script.h
+++ b/engines/phoenixvr/script.h
@@ -49,6 +49,7 @@ inline int fromAngle(float a) {
return static_cast<int>(floatToAngle * a);
}
} // namespace
+class Parser;
class Script {
public:
struct Scope;
@@ -126,6 +127,7 @@ private:
ConditionalPtr _conditional;
private:
+ Script::CommandPtr parseCommand(Parser &parser);
void parseLine(const Common::String &line, uint lineno);
public:
More information about the Scummvm-git-logs
mailing list