[Scummvm-git-logs] scummvm master -> 27b9864ce0423a509e7d1a8fe4e3050e88f7eafb

whoozle noreply at scummvm.org
Sun Jul 19 11:21:33 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:
27b9864ce0 PHOENIXVR: Add initial V2 support


Commit: 27b9864ce0423a509e7d1a8fe4e3050e88f7eafb
    https://github.com/scummvm/scummvm/commit/27b9864ce0423a509e7d1a8fe4e3050e88f7eafb
Author: Vladimir Menshakov (vladimir.menshakov at gmail.com)
Date: 2026-07-19T12:20:33+01:00

Commit Message:
PHOENIXVR: Add initial V2 support

Add detection entries for
- Sherlock Holmes: The Mystery of the Mummy (Steam release)
- The Cameron Files: Pharaoh's Curse (USA CD release)

Changed paths:
  A engines/phoenixvr/commands_v2.cpp
  A engines/phoenixvr/commands_v2.h
  A engines/phoenixvr/script_v2.cpp
  A engines/phoenixvr/script_v2.h
    engines/phoenixvr/detection.h
    engines/phoenixvr/detection_tables.h
    engines/phoenixvr/module.mk
    engines/phoenixvr/phoenixvr.cpp
    engines/phoenixvr/script.cpp


diff --git a/engines/phoenixvr/commands_v2.cpp b/engines/phoenixvr/commands_v2.cpp
new file mode 100644
index 00000000000..41d1eabef7d
--- /dev/null
+++ b/engines/phoenixvr/commands_v2.cpp
@@ -0,0 +1,100 @@
+/* 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/>.
+ *
+ */
+
+#include "phoenixvr/commands_v2.h"
+#include "phoenixvr/phoenixvr.h"
+
+namespace PhoenixVR {
+namespace {
+
+struct Add : public Command {
+	Common::String dstVar;
+	Common::String arg0;
+	Common::String arg1;
+
+	Add(const Common::Array<Common::String> &args) : dstVar(args[0]), arg0(args[1]), arg1(args[2]) {}
+
+	void exec(ExecutionContext &ctx) const override {
+		debug("add %s %s %s", dstVar.c_str(), arg0.c_str(), arg1.c_str());
+		g_engine->setVariable(dstVar, valueOf(arg0) + valueOf(arg1));
+	}
+};
+
+struct Sub : public Command {
+	Common::String dstVar;
+	Common::String arg0;
+	Common::String arg1;
+
+	Sub(const Common::Array<Common::String> &args) : dstVar(args[0]), arg0(args[1]), arg1(args[2]) {}
+
+	void exec(ExecutionContext &ctx) const override {
+		debug("sub %s %s %s", dstVar.c_str(), arg0.c_str(), arg1.c_str());
+		g_engine->setVariable(dstVar, valueOf(arg0) - valueOf(arg1));
+	}
+};
+
+struct Play_AnimBloc : public Command {
+	Common::String name;
+	Common::String dstVar;
+	int dstVarValue;
+	float speed; // ticks per second
+
+	Play_AnimBloc(const Common::Array<Common::String> &args) : name(args[0]), dstVar(args[1]), dstVarValue(atoi(args[2].c_str())), speed(args.size() >= 4 ? atof(args[3].c_str()) : 25) {}
+	void exec(ExecutionContext &ctx) const override {
+		debug("Play_AnimBloc %s %s %d %g", name.c_str(), dstVar.c_str(), dstVarValue, speed);
+		g_engine->playAnimation(name, dstVar, dstVarValue, speed);
+	}
+};
+
+struct Play_Sound : public Command {
+	Common::String sound;
+	int volume;
+	int loops;
+	Audio::Mixer::SoundType type;
+
+	Play_Sound(const Common::Array<Common::String> &args, Audio::Mixer::SoundType t = Audio::Mixer::kSFXSoundType) : sound(args[0]),
+																													 volume(args.size() > 1 ? atoi(args[1].c_str()) : 100),
+																													 loops(args.size() > 2 ? atoi(args[2].c_str()) : 0), type(t) {}
+
+	void exec(ExecutionContext &ctx) const override {
+		g_engine->playSound(sound, type, volume, loops);
+	}
+};
+
+} // namespace
+
+#define COMMAND_LIST(E) \
+	E(Add)              \
+	E(Sub)              \
+	E(Play_Sound)       \
+	E(Play_AnimBloc)
+
+#define ADD_COMMAND(NAME)            \
+	if (cmd.equalsIgnoreCase(#NAME)) \
+		return CommandPtr(new NAME(args));
+
+CommandPtr createV2Command(const Common::String &cmd, const Common::Array<Common::String> &args, int lineno) {
+	COMMAND_LIST(ADD_COMMAND)
+	warning("unhandled command %s at line %d", cmd.c_str(), lineno);
+	return nullptr;
+}
+
+} // namespace PhoenixVR
diff --git a/engines/phoenixvr/commands_v2.h b/engines/phoenixvr/commands_v2.h
new file mode 100644
index 00000000000..98945438b66
--- /dev/null
+++ b/engines/phoenixvr/commands_v2.h
@@ -0,0 +1,34 @@
+/* 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_COMMANDS_V1_H
+#define PHOENIXVR_COMMANDS_V1_H
+
+#include "phoenixvr/commands.h"
+
+namespace PhoenixVR {
+class Parser;
+
+CommandPtr createV2Command(const Common::String &cmd, const Common::Array<Common::String> &args, int lineno);
+
+} // namespace PhoenixVR
+
+#endif
diff --git a/engines/phoenixvr/detection.h b/engines/phoenixvr/detection.h
index 37a43895c89..5333dca27c5 100644
--- a/engines/phoenixvr/detection.h
+++ b/engines/phoenixvr/detection.h
@@ -34,6 +34,10 @@ enum PhoenixVRDebugChannels {
 	kDebugScript,
 };
 
+enum PhoenixVRGameFlag {
+	PHOENIXVR_V2 = (1 << 0),
+};
+
 extern const PlainGameDescriptor phoenixvrGames[];
 
 extern const ADGameDescription gameDescriptions[];
diff --git a/engines/phoenixvr/detection_tables.h b/engines/phoenixvr/detection_tables.h
index 078aad255d2..61faf8e0879 100644
--- a/engines/phoenixvr/detection_tables.h
+++ b/engines/phoenixvr/detection_tables.h
@@ -20,6 +20,8 @@
  */
 
 #include "advancedDetector.h"
+#include "phoenixvr/detection.h"
+
 namespace PhoenixVR {
 
 // clang-format off
@@ -31,6 +33,9 @@ const PlainGameDescriptor phoenixvrGames[] = {
 	{"dracula1", "Dracula: Resurrection"},
 	{"dracula2", "Dracula 2: The Last Sanctuary"},
 	{"amerzone", "Amerzone: The Explorer's Legacy"},
+	// V2 games
+	{"mysteryofmummy", "Sherlock Holmes: The Mystery of the Mummy"},
+	{"pharaoncurse", "The Cameron Files: Pharaoh's Curse "},
 	{0, 0}
 };
 
@@ -614,6 +619,29 @@ const ADGameDescription gameDescriptions[] = {
 		ADGF_DROPPLATFORM | ADGF_UNSTABLE | ADGF_CD,
 		GUIO1(GUIO_NONE)
 	},
+	{"mysteryofmummy",
+		"Steam release - V2 games are not yet supported",
+		AD_ENTRY3s("Main_Menu.vr", "47561a674ad17d3facc4d87e169d6825", 97668,
+				   "data/EN/items.txt", "80f72fc361e24420f45eaa3c31046e0f", 1067,
+				   "Script.lst", "b5c2c9005c90a53c6efee063820f2152", 796
+		),
+		Common::EN_ANY,
+		Common::kPlatformWindows,
+		ADGF_DROPPLATFORM | PHOENIXVR_V2 | ADGF_UNSUPPORTED,
+		GUIO1(GUIO_NONE)
+	},
+	{"pharaoncurse",
+		"USA CD release - V2 games are not yet supported",
+		AD_ENTRY3s(
+			"Level_1/script.lst", "1bacc45e3ca6eea715ba5abd73986577", 211816,
+			"install/Start/script.lst", "40b17997a4d319605f024303d1513039", 17142,
+			"install/Start/L0P01R01S00.vr", "183dde040975248675338c24830e8ed5", 194569
+		),
+		Common::EN_ANY,
+		Common::kPlatformWindows,
+		ADGF_DROPPLATFORM | ADGF_CD | PHOENIXVR_V2 | ADGF_UNSUPPORTED,
+		GUIO1(GUIO_NONE)
+	},
 
 	AD_TABLE_END_MARKER
 };
diff --git a/engines/phoenixvr/module.mk b/engines/phoenixvr/module.mk
index bb330490f64..6d3ef3e84ff 100644
--- a/engines/phoenixvr/module.mk
+++ b/engines/phoenixvr/module.mk
@@ -5,6 +5,7 @@ MODULE_OBJS = \
 	bigf.o \
 	commands.o \
 	commands_v1.o \
+	commands_v2.o \
 	game_state.o \
 	console.o \
 	metaengine.o \
@@ -14,6 +15,7 @@ MODULE_OBJS = \
 	region_set.o \
 	script.o \
 	script_v1.o \
+	script_v2.o \
 	variables.o \
 	vr.o
 
diff --git a/engines/phoenixvr/phoenixvr.cpp b/engines/phoenixvr/phoenixvr.cpp
index d6dbd61d0b8..ec856dcc018 100644
--- a/engines/phoenixvr/phoenixvr.cpp
+++ b/engines/phoenixvr/phoenixvr.cpp
@@ -27,6 +27,7 @@
 #include "common/config-manager.h"
 #include "common/events.h"
 #include "common/file.h"
+#include "common/formats/ini-file.h"
 #include "common/language.h"
 #include "common/memstream.h"
 #include "common/savefile.h"
@@ -312,6 +313,32 @@ PhoenixVREngine::PhoenixVREngine(OSystem *syst, const ADGameDescription *gameDes
 		_levels.push_back("04VR_FLEUVE");
 		_levels.push_back("05VR_VILLAGEMARAIS");
 		_levels.push_back("07VRTEMPLE_VOLCAN");
+	} else if (gameIdMatches("mysteryofmummy")) {
+		_levels.push_back("level1");
+		_levels.push_back("level2");
+		_levels.push_back("level3");
+		_levels.push_back("level4");
+		_levels.push_back("level5");
+		setNextLevel();
+	} else if (gameIdMatches("pharaoncurse")) {
+		Common::INIFile file;
+		if (!file.loadFromFile("pharaohs.wbm"))
+			error("can't open install/pharaohs.wbm");
+		Common::String strNumLevels;
+		if (!file.getKey("LEVELS", "GAME", strNumLevels))
+			error("can't find levels number");
+		auto numLevels = atoi(strNumLevels.c_str());
+		for (int i = 0; i < numLevels; ++i) {
+			Common::String media;
+			Common::String path;
+			if (!file.getKey("MEDIA", Common::String::format("LEVEL_%d", i), media))
+				error("no media in level section");
+			if (!file.getKey("PATH", Common::String::format("LEVEL_%d", i), path))
+				error("no path in level section");
+			if (media == "HD")
+				path = "install\\" + path;
+			_levels.push_back(path);
+		}
 	}
 }
 
@@ -425,7 +452,8 @@ bool PhoenixVREngine::setNextLevel() {
 	if (_nextLevel < _levels.size()) {
 		auto &level = _levels[_nextLevel++];
 		debug("next level is %s", level.c_str());
-		setNextScript(Common::String::format("%s\\%s.lst", level.c_str(), _gameDescription->gameId));
+		auto mainScript = gameIdMatches("amerzone") ? "amerzone" : "script";
+		setNextScript(Common::String::format("%s\\%s.lst", level.c_str(), mainScript));
 		_loaded = true;
 
 		// reset flag or interface.vr will skip menu
@@ -461,7 +489,8 @@ void PhoenixVREngine::loadNextScript() {
 	if (!s)
 		error("can't open script file %s", nextScript.c_str());
 
-	_script.reset(Script::load(*s, 1));
+	int version = (_gameDescription->flags & PHOENIXVR_V2) ? 2 : 1;
+	_script.reset(Script::load(*s, version));
 	for (auto &var : _script->getVarNames())
 		declareVariable(var);
 	if (gameIdMatches("dracula1")) {
@@ -1679,10 +1708,12 @@ Common::Error PhoenixVREngine::run() {
 	}
 
 	// try load level-specific script first (amerzone)
-	if (gameIdMatches("amerzone")) {
+	if (gameIdMatches("amerzone"))
 		setNextScript("intro.lst");
-	} else if (gameIdMatches("lochness"))
+	else if (gameIdMatches("lochness"))
 		setNextScript("first.lst");
+	else if (_gameDescription->flags & PHOENIXVR_V2)
+		setNextLevel();
 	else
 		setNextScript("script.lst");
 
diff --git a/engines/phoenixvr/script.cpp b/engines/phoenixvr/script.cpp
index 79c794ba4a3..3875e81e600 100644
--- a/engines/phoenixvr/script.cpp
+++ b/engines/phoenixvr/script.cpp
@@ -23,6 +23,7 @@
 #include "common/stream.h"
 #include "common/textconsole.h"
 #include "phoenixvr/script_v1.h"
+#include "phoenixvr/script_v2.h"
 
 namespace PhoenixVR {
 
@@ -49,6 +50,9 @@ Script *Script::load(Common::SeekableReadStream &s, int version) {
 	case 1:
 		script.reset(new ScriptV1);
 		break;
+	case 2:
+		script.reset(new ScriptV2);
+		break;
 	default:
 		error("unsupported script version: %d", version);
 	}
diff --git a/engines/phoenixvr/script_v2.cpp b/engines/phoenixvr/script_v2.cpp
new file mode 100644
index 00000000000..3165ef4a762
--- /dev/null
+++ b/engines/phoenixvr/script_v2.cpp
@@ -0,0 +1,126 @@
+/* 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/>.
+ *
+ */
+#include "phoenixvr/script_v2.h"
+#include "phoenixvr/commands_v2.h"
+#include "phoenixvr/parser.h"
+#include "phoenixvr/phoenixvr.h"
+
+namespace PhoenixVR {
+
+namespace {
+struct SetVariable : public Command {
+	Common::String name;
+	Common::String value;
+	SetVariable(const Common::String &n, const Common::String &v) : name(n), value(v) {}
+	void exec(ExecutionContext &ctx) const override {
+		g_engine->setVariable(name, valueOf(value));
+	}
+};
+} // namespace
+
+void ScriptV2::parseLine(const Common::String &line, uint lineno) {
+	if (line.empty())
+		return;
+
+	Parser p(line, lineno);
+	if (p.atEnd())
+		return;
+
+	if (p.maybe("//"))
+		return;
+
+	if (p.maybe('[')) {
+		if (p.maybe("var]:")) {
+			auto name = p.nextWord();
+			int value = 0;
+			if (p.maybe('=')) {
+				value = p.nextInt();
+			}
+			debug("declared var %s: %d", name.c_str(), value);
+			_vars.push_back(name);
+		} else if (p.maybe("warp]:")) {
+			auto vr = p.nextWord();
+			Common::String test;
+			if (p.maybe(','))
+				test = p.nextWord();
+			_currentWarp.reset(new Warp{vr, Common::move(test), {}});
+			if (!_warpsIndex.contains(vr))
+				_warpsIndex[vr] = _warps.size();
+			else
+				warning("duplicate warp %s", vr.c_str());
+			_warps.push_back(_currentWarp);
+			_warpNames.push_back(vr);
+		} else if (p.maybe("test]:")) {
+			if (!_currentWarp)
+				error("test without warp");
+			auto idx = p.nextInt();
+			auto hover = 0;
+			if (!_currentWarp)
+				error("text must have parent wrap section");
+			if (p.maybe(',')) {
+				hover = p.nextInt();
+			}
+			_currentTest.reset(new Test{idx, hover, {}});
+			_currentWarp->tests.push_back(_currentTest);
+		} else if (p.maybe("ifand]:")) {
+			if (!_currentTest)
+				error("ifand without test");
+		} else if (p.maybe("ifor]:")) {
+			if (!_currentTest)
+				error("ifor without test");
+		} else if (p.maybe("else]")) {
+		} else if (p.maybe("endif]")) {
+		} else if (p.maybe("clic]")) {
+			if (!_currentTest)
+				error("clic without test");
+		} else if (p.maybe("in]")) {
+			if (!_currentTest)
+				error("clic without test");
+		} else if (p.maybe("out]")) {
+			if (!_currentTest)
+				error("clic without test");
+		} else {
+			error("invalid [] directive on line %u: %s", lineno, line.c_str());
+		}
+	} else if (_currentTest) {
+		auto name = p.nextWord();
+		CommandPtr command;
+		if (p.maybe('=')) {
+			auto value = p.nextWord();
+			if (!p.atEnd())
+				error("garbage at the end of the assignment, line %d", lineno);
+			command.reset(new SetVariable(name, Common::move(value)));
+		} else {
+			auto args = p.readStringList();
+			p.expect(')');
+			command = createV2Command(name, args, lineno);
+		}
+
+		auto &commands = _currentTest->scope.commands;
+		if (command)
+			commands.push_back(command);
+		else {
+			warning("unimplemented command %s at line %d", name.c_str(), lineno);
+		}
+	}
+}
+
+} // namespace PhoenixVR
diff --git a/engines/phoenixvr/script_v2.h b/engines/phoenixvr/script_v2.h
new file mode 100644
index 00000000000..b56090d3477
--- /dev/null
+++ b/engines/phoenixvr/script_v2.h
@@ -0,0 +1,48 @@
+/* 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_SCRIPT_V2_H
+#define PHOENIXVR_SCRIPT_V2_H
+
+#include "phoenixvr/script.h"
+
+namespace PhoenixVR {
+class ScriptV2 : public Script {
+public:
+	struct Conditional : public Command {
+		Common::Array<Common::String> conditions;
+		CommandPtr target;
+		Conditional(Common::Array<Common::String> args) : conditions(Common::move(args)) {}
+	};
+	using ConditionalPtr = Common::SharedPtr<Conditional>;
+
+private:
+	WarpPtr _currentWarp;
+	TestPtr _currentTest;
+	ScopePtr _pluginScope;
+	ConditionalPtr _conditional;
+
+private:
+	void parseLine(const Common::String &line, uint lineno) override;
+};
+} // namespace PhoenixVR
+
+#endif




More information about the Scummvm-git-logs mailing list