aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEugene Sandulenko2016-08-04 13:23:04 +0200
committerGitHub2016-08-04 13:23:04 +0200
commit902d49ce099df7ce8bb5eb1b6ea4c00f63d69c86 (patch)
treec1c1b0839baa18c738a27294db526eef4cb498d2
parentcb195a813bdc710864acfc26130e77a86e264493 (diff)
parent96e288c4c91bca55b89e0730bad76d69f6a05a94 (diff)
downloadscummvm-rg350-902d49ce099df7ce8bb5eb1b6ea4c00f63d69c86.tar.gz
scummvm-rg350-902d49ce099df7ce8bb5eb1b6ea4c00f63d69c86.tar.bz2
scummvm-rg350-902d49ce099df7ce8bb5eb1b6ea4c00f63d69c86.zip
Merge pull request #787 from sev-/director
DIRECTOR: New engine
-rw-r--r--.gitignore3
-rw-r--r--engines/director/configure.engine3
-rw-r--r--engines/director/detection.cpp252
-rw-r--r--engines/director/detection_tables.h469
-rw-r--r--engines/director/dib.cpp111
-rw-r--r--engines/director/dib.h67
-rw-r--r--engines/director/director.cpp368
-rw-r--r--engines/director/director.h122
-rw-r--r--engines/director/lingo/lingo-builtins.cpp263
-rw-r--r--engines/director/lingo/lingo-code.cpp794
-rw-r--r--engines/director/lingo/lingo-codegen.cpp266
-rw-r--r--engines/director/lingo/lingo-funcs.cpp225
-rw-r--r--engines/director/lingo/lingo-gr.cpp2681
-rw-r--r--engines/director/lingo/lingo-gr.h192
-rw-r--r--engines/director/lingo/lingo-gr.y517
-rw-r--r--engines/director/lingo/lingo-lex.cpp2308
-rw-r--r--engines/director/lingo/lingo-lex.l207
-rw-r--r--engines/director/lingo/lingo-the.cpp603
-rw-r--r--engines/director/lingo/lingo-the.h153
-rw-r--r--engines/director/lingo/lingo.cpp395
-rw-r--r--engines/director/lingo/lingo.h335
-rw-r--r--engines/director/lingo/tests/Lingo bytecode.html2360
-rw-r--r--engines/director/lingo/tests/factory.lingo69
-rw-r--r--engines/director/lingo/tests/goto.lingo5
-rw-r--r--engines/director/lingo/tests/if.lingo27
-rw-r--r--engines/director/lingo/tests/ilk.lingo7
-rw-r--r--engines/director/lingo/tests/lingotests.lingo0
-rw-r--r--engines/director/lingo/tests/loops.lingo22
-rw-r--r--engines/director/lingo/tests/macros.lingo42
-rw-r--r--engines/director/lingo/tests/macros2.lingo11
-rw-r--r--engines/director/lingo/tests/math.lingo22
-rw-r--r--engines/director/lingo/tests/mci.lingo2
-rw-r--r--engines/director/lingo/tests/point.lingo3
-rw-r--r--engines/director/lingo/tests/strings.lingo13
-rw-r--r--engines/director/lingo/tests/the.lingo6
-rw-r--r--engines/director/module.mk33
-rw-r--r--engines/director/movie.cpp66
-rw-r--r--engines/director/movie.h51
-rw-r--r--engines/director/resource.cpp440
-rw-r--r--engines/director/resource.h110
-rw-r--r--engines/director/score.cpp1617
-rw-r--r--engines/director/score.h455
-rw-r--r--engines/director/sound.cpp99
-rw-r--r--engines/director/sound.h52
44 files changed, 15846 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
index 2ad104214e..270fb560fe 100644
--- a/.gitignore
+++ b/.gitignore
@@ -196,3 +196,6 @@ ScummVM.includes
#Ignore MS Visual C++ temporary files/subdirectories (except create_project.bat)
dists/msvc*/**
!dists/msvc*/create_project.bat
+
+#Ignore bison debug output
+*.output
diff --git a/engines/director/configure.engine b/engines/director/configure.engine
new file mode 100644
index 0000000000..41f9a6249d
--- /dev/null
+++ b/engines/director/configure.engine
@@ -0,0 +1,3 @@
+# This file is included from the main "configure" script
+# add_engine [name] [desc] [build-by-default] [subengines] [base games] [deps]
+add_engine director "Macromedia Director" no
diff --git a/engines/director/detection.cpp b/engines/director/detection.cpp
new file mode 100644
index 0000000000..1f2e970539
--- /dev/null
+++ b/engines/director/detection.cpp
@@ -0,0 +1,252 @@
+/* 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 2
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "base/plugins.h"
+
+#include "engines/advancedDetector.h"
+#include "common/savefile.h"
+#include "common/system.h"
+#include "common/textconsole.h"
+
+#include "director/director.h"
+
+namespace Director {
+
+struct DirectorGameDescription {
+ ADGameDescription desc;
+
+ DirectorGameID gameID;
+ uint16 version;
+};
+
+DirectorGameID DirectorEngine::getGameID() const {
+ return _gameDescription->gameID;
+}
+
+Common::Platform DirectorEngine::getPlatform() const {
+ return _gameDescription->desc.platform;
+}
+
+uint16 DirectorEngine::getVersion() const {
+ return _gameDescription->version;
+}
+
+Common::Language DirectorEngine::getLanguage() const {
+ return _gameDescription->desc.language;
+}
+
+Common::String DirectorEngine::getEXEName() const {
+ return _gameDescription->desc.filesDescriptions[0].fileName;
+}
+
+bool DirectorEngine::hasFeature(EngineFeature f) const {
+ return
+ (f == kSupportsRTL);
+}
+
+} // End of Namespace Director
+
+static const PlainGameDescriptor directorGames[] = {
+ { "director", "Macromedia Director Game" },
+ { "directortest", "Macromedia Director Test Target" },
+ { "theapartment", "The Apartment, D3 interactive demo" },
+ { "gundam0079", "Gundam 0079: The War for Earth" },
+ { "jewels", "Jewels of the Oracle" },
+ { "jman", "The Journeyman Project" },
+ { "majestic", "Majestic Part I: Alien Encounter" },
+ { "melements", "Masters of the Elements" },
+ { "spyclub", "Spy Club" },
+ { "amber", "AMBER: Journeys Beyond"},
+ { "vvvampire", "Victor Vector & Yondo: The Vampire's Coffin"},
+ { "vvdinosaur", "Victor Vector & Yondo: The Last Dinosaur Egg"},
+ { "warlock", "Spaceship Warlock"},
+ { 0, 0 }
+};
+
+#include "director/detection_tables.h"
+
+static const char *directoryGlobs[] = {
+ "install",
+ 0
+};
+
+class DirectorMetaEngine : public AdvancedMetaEngine {
+public:
+ DirectorMetaEngine() : AdvancedMetaEngine(Director::gameDescriptions, sizeof(Director::DirectorGameDescription), directorGames) {
+ _singleId = "director";
+ _maxScanDepth = 2,
+ _directoryGlobs = directoryGlobs;
+ }
+
+ virtual const char *getName() const {
+ return "Macromedia Director";
+ }
+
+ virtual const char *getOriginalCopyright() const {
+ return "Macromedia Director (C) Macromedia";
+ }
+
+ const ADGameDescription *fallbackDetect(const FileMap &allFiles, const Common::FSList &fslist) const;
+ virtual bool createInstance(OSystem *syst, Engine **engine, const ADGameDescription *desc) const;
+};
+
+bool DirectorMetaEngine::createInstance(OSystem *syst, Engine **engine, const ADGameDescription *desc) const {
+ const Director::DirectorGameDescription *gd = (const Director::DirectorGameDescription *)desc;
+
+ if (gd)
+ *engine = new Director::DirectorEngine(syst, gd);
+
+ return (gd != 0);
+}
+
+static Director::DirectorGameDescription s_fallbackDesc = {
+ {
+ "director",
+ "",
+ AD_ENTRY1(0, 0),
+ Common::UNK_LANG,
+ Common::kPlatformWindows,
+ ADGF_NO_FLAGS,
+ GUIO0()
+ },
+ Director::GID_GENERIC,
+ 0
+};
+
+static char s_fallbackFileNameBuffer[51];
+
+const ADGameDescription *DirectorMetaEngine::fallbackDetect(const FileMap &allFiles, const Common::FSList &fslist) const {
+ // TODO: Handle Mac fallback
+
+ // reset fallback description
+ Director::DirectorGameDescription *desc = &s_fallbackDesc;
+ desc->desc.gameId = "director";
+ desc->desc.extra = "";
+ desc->desc.language = Common::UNK_LANG;
+ desc->desc.flags = ADGF_NO_FLAGS;
+ desc->desc.platform = Common::kPlatformWindows;
+ desc->desc.guiOptions = GUIO0();
+ desc->desc.filesDescriptions[0].fileName = 0;
+ desc->version = 0;
+ desc->gameID = Director::GID_GENERIC;
+
+ for (Common::FSList::const_iterator file = fslist.begin(); file != fslist.end(); ++file) {
+ if (file->isDirectory())
+ continue;
+
+ Common::String fileName = file->getName();
+ fileName.toLowercase();
+ if (!fileName.hasSuffix(".exe"))
+ continue;
+
+ SearchMan.clear();
+ SearchMan.addDirectory(file->getParent().getName(), file->getParent());
+
+ Common::SeekableReadStream *stream = SearchMan.createReadStreamForMember(file->getName());
+
+ if (!stream)
+ continue;
+
+ stream->seek(-4, SEEK_END);
+
+ uint32 offset = stream->readUint32LE();
+
+ if (stream->eos() || offset == 0 || offset >= (uint32)(stream->size() - 4)) {
+ delete stream;
+ continue;
+ }
+
+ stream->seek(offset);
+
+ uint32 tag = stream->readUint32LE();
+
+ switch (tag) {
+ case MKTAG('3', '9', 'J', 'P'):
+ desc->version = 4;
+ break;
+ case MKTAG('P', 'J', '9', '5'):
+ desc->version = 5;
+ break;
+ case MKTAG('P', 'J', '0', '0'):
+ desc->version = 7;
+ break;
+ default:
+ // Prior to version 4, there was no tag here. So we'll use a bit of a
+ // heuristic to detect. The first field is the entry count, of which
+ // there should only be one.
+ if ((tag & 0xFFFF) != 1) {
+ delete stream;
+ continue;
+ }
+
+ stream->skip(3);
+
+ uint32 mmmSize = stream->readUint32LE();
+
+ if (stream->eos() || mmmSize == 0) {
+ delete stream;
+ continue;
+ }
+
+ byte fileNameSize = stream->readByte();
+
+ if (stream->eos()) {
+ delete stream;
+ continue;
+ }
+
+ stream->skip(fileNameSize);
+ byte directoryNameSize = stream->readByte();
+
+ if (stream->eos()) {
+ delete stream;
+ continue;
+ }
+
+ stream->skip(directoryNameSize);
+
+ if (stream->pos() != stream->size() - 4) {
+ delete stream;
+ continue;
+ }
+
+ // Assume v3 at this point (for now at least)
+ desc->version = 3;
+ }
+
+ strncpy(s_fallbackFileNameBuffer, fileName.c_str(), 50);
+ s_fallbackFileNameBuffer[50] = '\0';
+ desc->desc.filesDescriptions[0].fileName = s_fallbackFileNameBuffer;
+
+ warning("Director fallback detection D%d", desc->version);
+
+ return (ADGameDescription *)desc;
+ }
+
+ return 0;
+}
+
+#if PLUGIN_ENABLED_DYNAMIC(DIRECTOR)
+ REGISTER_PLUGIN_DYNAMIC(DIRECTOR, PLUGIN_TYPE_ENGINE, DirectorMetaEngine);
+#else
+ REGISTER_PLUGIN_STATIC(DIRECTOR, PLUGIN_TYPE_ENGINE, DirectorMetaEngine);
+#endif
diff --git a/engines/director/detection_tables.h b/engines/director/detection_tables.h
new file mode 100644
index 0000000000..65eff50fc9
--- /dev/null
+++ b/engines/director/detection_tables.h
@@ -0,0 +1,469 @@
+/* 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 2
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef DIRECTOR_DETECTION_TABLES_H
+#define DIRECTOR_DETECTION_TABLES_H
+
+namespace Director {
+
+static const DirectorGameDescription gameDescriptions[] = {
+ {
+ {
+ "directortest",
+ "",
+ AD_ENTRY1("lingotests.lingo", 0),
+ Common::EN_ANY,
+ Common::kPlatformWindows,
+ ADGF_NO_FLAGS,
+ GUIO1(GUIO_NOASPECT)
+ },
+ GID_TEST,
+ 3
+ },
+
+ {
+ {
+ "theapartment",
+ "",
+ AD_ENTRY1s("Main Menu", "9e838fe1a6af7992d656ca325e38dee5", 47911),
+ Common::EN_ANY,
+ Common::kPlatformMacintosh,
+ ADGF_MACRESFORK,
+ GUIO1(GUIO_NOASPECT)
+ },
+ GID_GENERIC,
+ 3
+ },
+
+ {
+ {
+ "gundam0079",
+ "",
+ AD_ENTRY1("Gundam0079.exe", "1a7acbba10a7246ba58c1d53fc7203f5"),
+ Common::EN_ANY,
+ Common::kPlatformWindows,
+ ADGF_NO_FLAGS,
+ GUIO1(GUIO_NOASPECT)
+ },
+ GID_GENERIC,
+ 5
+ },
+
+ {
+ {
+ "gundam0079",
+ "",
+ AD_ENTRY1("Gundam0079", "4c38a51a21a1ad231f218c4786ff771d"),
+ Common::EN_ANY,
+ Common::kPlatformMacintosh,
+ ADGF_MACRESFORK,
+ GUIO1(GUIO_NOASPECT)
+ },
+ GID_GENERIC,
+ 5
+ },
+
+ {
+ {
+ "jewels",
+ "",
+ AD_ENTRY1("JEWELS.EXE", "bb6d81471d166088260090472c6c3a87"),
+ Common::EN_ANY,
+ Common::kPlatformWindows,
+ ADGF_NO_FLAGS,
+ GUIO1(GUIO_NOASPECT)
+ },
+ GID_GENERIC,
+ 4
+ },
+
+ {
+ {
+ "jewels",
+ "",
+ AD_ENTRY1("Jewels.exe", "c1a2e8b7e41fa204009324a9c7db1030"),
+ Common::EN_ANY,
+ Common::kPlatformWindows,
+ ADGF_NO_FLAGS,
+ GUIO1(GUIO_NOASPECT)
+ },
+ GID_GENERIC,
+ 7
+ },
+
+ {
+ {
+ "jewels",
+ "Two-Minute Demo",
+ AD_ENTRY1("DEMO.EXE", "ebee52d3c4280674c600177df5b09da0"),
+ Common::EN_ANY,
+ Common::kPlatformWindows,
+ ADGF_DEMO,
+ GUIO1(GUIO_NOASPECT)
+ },
+ GID_GENERIC,
+ 4
+ },
+
+ // Note: There are four versions of the binary included on the disc.
+ // 5.6, 6, and 9 Meg variants all exist too.
+ {
+ {
+ "jewels",
+ "",
+ AD_ENTRY1("Jewels 11 Meg", "339c89a148c4ff2c5c815c62ac006325"),
+ Common::EN_ANY,
+ Common::kPlatformMacintosh,
+ ADGF_MACRESFORK,
+ GUIO1(GUIO_NOASPECT)
+ },
+ GID_GENERIC,
+ 4
+ },
+
+ {
+ {
+ "jewels",
+ "Two-Minute Demo",
+ AD_ENTRY1("Two-Minute Demo", "01be45e7241194dad07938e7059b88e3"),
+ Common::EN_ANY,
+ Common::kPlatformMacintosh,
+ ADGF_MACRESFORK | ADGF_DEMO,
+ GUIO1(GUIO_NOASPECT)
+ },
+ GID_GENERIC,
+ 4
+ },
+
+ {
+ {
+ "jewels",
+ "",
+ AD_ENTRY1("Jewels of the Oracle", "fa52f0136cde568a46249ce74f01a324"),
+ Common::EN_ANY,
+ Common::kPlatformMacintosh,
+ ADGF_MACRESFORK,
+ GUIO1(GUIO_NOASPECT)
+ },
+ GID_GENERIC,
+ 7
+ },
+
+ {
+ {
+ "jewels",
+ "Demo",
+ AD_ENTRY1("JEWELS.EXE", "abcc448c035e88d4edb4a29034fd1e34"),
+ Common::EN_ANY,
+ Common::kPlatformWindows,
+ ADGF_NO_FLAGS | ADGF_DEMO,
+ GUIO1(GUIO_NOASPECT)
+ },
+ GID_GENERIC,
+ 4
+ },
+
+ {
+ {
+ "jman",
+ "",
+ AD_ENTRY1s("JMAN.EXE", "7c8230a804abf9353b05627a675b5ffb", 375282),
+ Common::EN_ANY,
+ Common::kPlatformWindows,
+ ADGF_NO_FLAGS,
+ GUIO1(GUIO_NOASPECT)
+ },
+ GID_GENERIC,
+ 3
+ },
+
+ {
+ {
+ "jman",
+ "",
+ AD_ENTRY1s("JMDEMO.EXE", "7c8230a804abf9353b05627a675b5ffb", 375305),
+ Common::EN_ANY,
+ Common::kPlatformWindows,
+ ADGF_DEMO,
+ GUIO1(GUIO_NOASPECT)
+ },
+ GID_GENERIC,
+ 3
+ },
+
+ {
+ {
+ "jman",
+ "",
+ AD_ENTRY1("JOURNEY.EXE", "65d06b5fef155a2473434571aff5bc29"),
+ Common::JA_JPN,
+ Common::kPlatformWindows,
+ ADGF_NO_FLAGS,
+ GUIO1(GUIO_NOASPECT)
+ },
+ GID_GENERIC,
+ 3
+ },
+
+ {
+ {
+ "jman",
+ "Turbo!",
+ AD_ENTRY1("JMP Turbo\xE2\x84\xA2", "cc3321069072b90f091f220bba16e4d4"), // Trademark symbol (UTF-8)
+ Common::EN_ANY,
+ Common::kPlatformMacintosh,
+ ADGF_MACRESFORK,
+ GUIO1(GUIO_NOASPECT)
+ },
+ GID_GENERIC,
+ 4
+ },
+
+ {
+ {
+ "majestic",
+ "",
+ AD_ENTRY1("MAJESTIC.EXE", "624267f70253e5327981003a6fc0aeba"),
+ Common::EN_ANY,
+ Common::kPlatformWindows,
+ ADGF_NO_FLAGS,
+ GUIO1(GUIO_NOASPECT)
+ },
+ GID_GENERIC,
+ 4
+ },
+
+ {
+ {
+ // Masters of the Elements - English (from rootfather)
+ // Developed by IJsfontein, published by Tivola
+ // File version of MVM.EXE is 6.0.2.32
+ // The game disc is a hybrid CD-ROM containing both the Windows and the Macintosh release.
+
+ "melements", "",
+ {
+ {"CHECK.DXR", 0, "c31ee30eebd24a8cf31691fc9926daa4", 901820},
+ {"MVM.EXE", 0, 0, 2565921},
+ AD_LISTEND
+ },
+ Common::EN_ANY,
+ Common::kPlatformWindows,
+ ADGF_NO_FLAGS,
+ GUIO1(GUIO_NOASPECT)
+ },
+ GID_GENERIC,
+ 5
+ },
+
+ {
+ {
+ // Masters of the Elements - English (from rootfather)
+
+ "melements", "",
+ {
+ {"check.dxr", 0, "36f42340e819d1532c850880afe16581", 898206},
+ {"Masters of the Elements", 0, 0, 1034962},
+ AD_LISTEND
+ },
+ Common::EN_ANY,
+ Common::kPlatformMacintosh,
+ ADGF_MACRESFORK,
+ GUIO1(GUIO_NOASPECT)
+ },
+ GID_GENERIC,
+ 5
+ },
+
+ {
+ {
+ // Masters of the Elements - German (from rootfather)
+ // Released in Germany as "Meister Zufall und die Herrscher der Elemente"
+ // Developed by IJsfontein, published by Tivola
+ // File version of MVM.EXE is 6.0.2.32
+ // The game disc is a hybrid CD-ROM containing both the Windows and the Macintosh release.
+
+ "melements", "",
+ {
+ {"CHECK.DXR", 0, "d1cd0ed95b0e30597e0089bf3e5caf0f", 575414},
+ {"MVM.EXE", 0, 0, 1512503},
+ AD_LISTEND
+ },
+ Common::DE_DEU,
+ Common::kPlatformWindows,
+ ADGF_NO_FLAGS,
+ GUIO1(GUIO_NOASPECT)
+ },
+ GID_GENERIC,
+ 5
+ },
+
+ {
+ {
+ // Masters of the Elements - German (from rootfather)
+
+ "melements", "",
+ {
+ {"check.dxr", 0, "9c81934b7616ab077f44825b8afaa83e", 575426},
+ {"Meister Zufall", 0, 0, 1034962},
+ AD_LISTEND
+ },
+ Common::DE_DEU,
+ Common::kPlatformMacintosh,
+ ADGF_MACRESFORK,
+ GUIO1(GUIO_NOASPECT)
+ },
+ GID_GENERIC,
+ 5
+ },
+
+ {
+ {
+ "spyclub",
+ "",
+ AD_ENTRY1("SPYCLUB.EXE", "65d06b5fef155a2473434571aff5bc29"),
+ Common::EN_ANY,
+ Common::kPlatformWindows,
+ ADGF_NO_FLAGS,
+ GUIO1(GUIO_NOASPECT)
+ },
+ GID_GENERIC,
+ 3
+ },
+
+ {
+ {
+ "amber",
+ "",
+ AD_ENTRY1("amber_jb.exe", "1a7acbba10a7246ba58c1d53fc7203f5"),
+ Common::EN_ANY,
+ Common::kPlatformWindows,
+ ADGF_NO_FLAGS,
+ GUIO1(GUIO_NOASPECT)
+ },
+ GID_GENERIC,
+ 5
+ },
+
+ {
+ {
+ "vvvampire",
+ "",
+ AD_ENTRY1("VAMPIRE.EXE", "88f4f7406f34ec36e751a64f7c76f2c4"),
+ Common::EN_ANY,
+ Common::kPlatformWindows,
+ ADGF_NO_FLAGS,
+ GUIO1(GUIO_NOASPECT)
+ },
+ GID_GENERIC,
+ 3
+ },
+
+ {
+ {
+ "vvvampire",
+ "",
+ AD_ENTRY1("The Vampire's Coffin", "d41d8cd98f00b204e9800998ecf8427e"),
+ Common::EN_ANY,
+ Common::kPlatformMacintosh,
+ ADGF_MACRESFORK,
+ GUIO1(GUIO_NOASPECT)
+ },
+ GID_GENERIC,
+ 3
+ },
+
+ {
+ {
+ "vvdinosaur",
+ "",
+ AD_ENTRY1("DINOSAUR.EXE", "4e6303630f4dd588e730d09241cf7e76"),
+ Common::EN_ANY,
+ Common::kPlatformWindows,
+ ADGF_NO_FLAGS,
+ GUIO1(GUIO_NOASPECT)
+ },
+ GID_GENERIC,
+ 3
+ },
+
+ {
+ {
+ "vvdinosaur",
+ "",
+ AD_ENTRY1("Start Game", "d41d8cd98f00b204e9800998ecf8427e"),
+ Common::EN_ANY,
+ Common::kPlatformMacintosh,
+ ADGF_MACRESFORK,
+ GUIO1(GUIO_NOASPECT)
+ },
+ GID_GENERIC,
+ 3
+ },
+
+ {
+ {
+ "warlock",
+ "",
+ AD_ENTRY1s("Spaceship Warlock.bin", "cfa68a1bc49251497ebde18e5fc9c217", 271107),
+ Common::EN_ANY,
+ Common::kPlatformMacintosh,
+ ADGF_MACRESFORK,
+ GUIO1(GUIO_NOASPECT)
+ },
+ GID_GENERIC,
+ 2
+ },
+
+ {
+ {
+ "warlock",
+ "",
+ AD_ENTRY1s("SSWARLCK.EXE", "65d06b5fef155a2473434571aff5bc29", 370867),
+ Common::EN_ANY,
+ Common::kPlatformWindows,
+ ADGF_NO_FLAGS,
+ GUIO1(GUIO_NOASPECT)
+ },
+ GID_GENERIC,
+ 2
+ },
+
+ {
+ {
+ "warlock",
+ "",
+ AD_ENTRY1s("SSWDEMO.EXE", "65d06b5fef155a2473434571aff5bc29", 370934),
+ Common::EN_ANY,
+ Common::kPlatformWindows,
+ ADGF_DEMO,
+ GUIO1(GUIO_NOASPECT)
+ },
+ GID_GENERIC,
+ 2
+ },
+
+ { AD_TABLE_END_MARKER, GID_GENERIC, 0 }
+};
+
+} // End of Namespace Director
+
+#endif
diff --git a/engines/director/dib.cpp b/engines/director/dib.cpp
new file mode 100644
index 0000000000..8c54ba5363
--- /dev/null
+++ b/engines/director/dib.cpp
@@ -0,0 +1,111 @@
+/* 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 2
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "director/dib.h"
+
+#include "common/stream.h"
+#include "common/substream.h"
+#include "common/textconsole.h"
+#include "graphics/pixelformat.h"
+#include "graphics/surface.h"
+#include "graphics/palette.h"
+#include "image/codecs/codec.h"
+#include "common/util.h"
+#include "common/debug.h"
+#include "image/codecs/bmp_raw.h"
+#include "common/system.h"
+
+namespace Director {
+
+DIBDecoder::DIBDecoder() {
+ _surface = 0;
+ _palette = 0;
+ _paletteColorCount = 0;
+ _codec = 0;
+}
+
+DIBDecoder::~DIBDecoder() {
+ destroy();
+}
+
+void DIBDecoder::destroy() {
+ _surface = 0;
+
+ delete[] _palette;
+ _palette = 0;
+ _paletteColorCount = 0;
+
+ delete _codec;
+ _codec = 0;
+}
+
+void DIBDecoder::loadPalette(Common::SeekableReadStream &stream) {
+ uint16 steps = stream.size() / 6;
+ uint16 index = (steps * 3) - 1;
+ _paletteColorCount = steps;
+ _palette = new byte[index];
+
+ for (uint8 i = 0; i < steps; i++) {
+ _palette[index - 2] = stream.readByte();
+ stream.readByte();
+
+ _palette[index - 1] = stream.readByte();
+ stream.readByte();
+
+ _palette[index] = stream.readByte();
+ stream.readByte();
+ index -= 3;
+ }
+}
+
+bool DIBDecoder::loadStream(Common::SeekableReadStream &stream) {
+ uint32 headerSize = stream.readUint32LE();
+ if (headerSize != 40)
+ return false;
+
+ uint32 width = stream.readUint32LE();
+ uint32 height = stream.readUint32LE();
+ stream.readUint16LE(); // planes
+ uint16 bitsPerPixel = stream.readUint16LE();
+ uint32 compression = stream.readUint32BE();
+ uint32 imageSize = stream.readUint32LE();
+ /* uint32 pixelsPerMeterX = */ stream.readUint32LE();
+ /* uint32 pixelsPerMeterY = */ stream.readUint32LE();
+ _paletteColorCount = stream.readUint32LE();
+ /* uint32 colorsImportant = */ stream.readUint32LE();
+
+ _paletteColorCount = (_paletteColorCount == 0) ? 255: _paletteColorCount;
+
+ uint16 imageRawSize = stream.size() - 40;
+ Common::SeekableSubReadStream subStream(&stream, 40, stream.size());
+
+ _codec = Image::createBitmapCodec(compression, width, height, bitsPerPixel);
+
+ if (!_codec)
+ return false;
+
+ _surface = _codec->decodeFrame(subStream);
+
+ return true;
+}
+
+} // End of namespace Director
diff --git a/engines/director/dib.h b/engines/director/dib.h
new file mode 100644
index 0000000000..e3763be2bf
--- /dev/null
+++ b/engines/director/dib.h
@@ -0,0 +1,67 @@
+/* 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 2
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef DIRECTOR_DIB_H
+#define DIRECTOR_DIB_H
+
+#include "common/scummsys.h"
+#include "common/str.h"
+#include "image/image_decoder.h"
+#include "image/codecs/bmp_raw.h"
+
+namespace Common {
+class SeekableReadStream;
+}
+
+namespace Graphics {
+struct Surface;
+}
+
+namespace Image {
+class Codec;
+}
+
+namespace Director {
+
+class DIBDecoder : public Image::ImageDecoder {
+public:
+ DIBDecoder();
+ virtual ~DIBDecoder();
+
+ // ImageDecoder API
+ void destroy();
+ virtual bool loadStream(Common::SeekableReadStream &stream);
+ virtual const Graphics::Surface *getSurface() const { return _surface; }
+ const byte *getPalette() const { return _palette; }
+ void loadPalette(Common::SeekableReadStream &stream);
+ uint16 getPaletteColorCount() const { return _paletteColorCount; }
+
+private:
+ Image::Codec *_codec;
+ const Graphics::Surface *_surface;
+ byte *_palette;
+ uint8 _paletteColorCount;
+};
+
+} // End of namespace Director
+
+#endif
diff --git a/engines/director/director.cpp b/engines/director/director.cpp
new file mode 100644
index 0000000000..469aeb80cb
--- /dev/null
+++ b/engines/director/director.cpp
@@ -0,0 +1,368 @@
+/* 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 2
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "audio/mixer.h"
+
+#include "common/config-manager.h"
+#include "common/debug.h"
+#include "common/scummsys.h"
+#include "common/error.h"
+#include "common/events.h"
+#include "common/macresman.h"
+#include "common/stream.h"
+#include "common/system.h"
+#include "common/textconsole.h"
+#include "common/fs.h"
+
+#include "engines/util.h"
+
+#include "graphics/surface.h"
+#include "graphics/macgui/macwindowmanager.h"
+
+#include "director/director.h"
+#include "director/dib.h"
+#include "director/resource.h"
+#include "director/score.h"
+#include "director/lingo/lingo.h"
+#include "director/sound.h"
+
+namespace Director {
+
+DirectorEngine::DirectorEngine(OSystem *syst, const DirectorGameDescription *gameDesc) : Engine(syst), _gameDescription(gameDesc),
+ _rnd("director") {
+ if (!_mixer->isReady())
+ error("Sound initialization failed");
+
+ // Setup mixer
+ syncSoundSettings();
+
+ _sharedCasts = nullptr;
+ _sharedSound = nullptr;
+ _sharedBMP = nullptr;
+ _sharedSTXT = nullptr;
+ _sharedDIB = nullptr;
+
+ _mainArchive = nullptr;
+ _macBinary = nullptr;
+
+ _movies = nullptr;
+
+ _wm = nullptr;
+
+ const Common::FSNode gameDataDir(ConfMan.get("path"));
+ SearchMan.addSubDirectoryMatching(gameDataDir, "data");
+ SearchMan.addSubDirectoryMatching(gameDataDir, "install");
+}
+
+DirectorEngine::~DirectorEngine() {
+ delete _sharedCasts;
+ delete _sharedSound;
+ delete _sharedBMP;
+ delete _sharedSTXT;
+ delete _sharedDIB;
+ delete _movies;
+
+ delete _mainArchive;
+ delete _macBinary;
+ delete _soundManager;
+ delete _lingo;
+ delete _currentScore;
+ delete _currentPalette;
+}
+
+Common::Error DirectorEngine::run() {
+ debug("Starting v%d Director game", getVersion());
+
+ //FIXME
+ _sharedMMM = "SHARDCST.MMM";
+
+ _currentPalette = nullptr;
+
+ _macBinary = nullptr;
+ _soundManager = nullptr;
+
+ _wm = new Graphics::MacWindowManager;
+
+ _lingo = new Lingo(this);
+ _soundManager = new DirectorSound();
+
+ if (getGameID() == GID_TEST) {
+ _mainArchive = nullptr;
+ _currentScore = nullptr;
+
+ _lingo->runTests();
+
+ return Common::kNoError;
+ }
+
+ //FIXME
+ //_mainArchive = new RIFFArchive();
+ //_mainArchive->openFile("bookshelf_example.mmm");
+
+ if (getPlatform() == Common::kPlatformWindows)
+ loadEXE();
+ else
+ loadMac();
+
+ _currentScore = new Score(this);
+ debug(0, "Score name %s", _currentScore->getMacName().c_str());
+
+ _currentScore->loadArchive();
+ _currentScore->startLoop();
+
+ return Common::kNoError;
+}
+
+Common::HashMap<Common::String, Score *> DirectorEngine::loadMMMNames(Common::String folder) {
+ Common::FSNode directory(folder);
+ Common::FSList movies;
+
+ Common::HashMap<Common::String, Score *> nameMap;
+ directory.getChildren(movies, Common::FSNode::kListFilesOnly);
+
+ if (!movies.empty()) {
+ for (Common::FSList::const_iterator i = movies.begin(); i != movies.end(); ++i) {
+ if (i->getName() == _sharedMMM) {
+ loadSharedCastsFrom(i->getPath());
+ continue;
+ }
+
+ RIFFArchive *arc = new RIFFArchive();
+ arc->openFile(i->getPath());
+ Score *sc = new Score(this);
+ nameMap[sc->getMacName()] = sc;
+ }
+ }
+
+ return nameMap;
+}
+
+void DirectorEngine::loadEXE() {
+ Common::SeekableReadStream *exeStream = SearchMan.createReadStreamForMember(getEXEName());
+ if (!exeStream)
+ error("Failed to open EXE '%s'", getEXEName().c_str());
+
+ _lingo->processEvent(kEventStart, 0);
+
+ exeStream->seek(-4, SEEK_END);
+ exeStream->seek(exeStream->readUint32LE());
+
+ switch (getVersion()) {
+ case 3:
+ loadEXEv3(exeStream);
+ break;
+ case 4:
+ loadEXEv4(exeStream);
+ break;
+ case 5:
+ loadEXEv5(exeStream);
+ break;
+ case 7:
+ loadEXEv7(exeStream);
+ break;
+ default:
+ error("Unhandled Windows EXE version %d", getVersion());
+ }
+}
+
+void DirectorEngine::loadEXEv3(Common::SeekableReadStream *stream) {
+ uint16 entryCount = stream->readUint16LE();
+ if (entryCount != 1)
+ error("Unhandled multiple entry v3 EXE");
+
+ stream->skip(5); // unknown
+
+ stream->readUint32LE(); // Main MMM size
+ Common::String mmmFileName = readPascalString(*stream);
+ Common::String directoryName = readPascalString(*stream);
+
+ debug("Main MMM: '%s'", mmmFileName.c_str());
+ debug("Directory Name: '%s'", directoryName.c_str());
+
+ _mainArchive = new RIFFArchive();
+
+ if (!_mainArchive->openFile(mmmFileName))
+ error("Could not open '%s'", mmmFileName.c_str());
+
+ delete stream;
+}
+
+void DirectorEngine::loadEXEv4(Common::SeekableReadStream *stream) {
+ if (stream->readUint32BE() != MKTAG('P', 'J', '9', '3'))
+ error("Invalid projector tag found in v4 EXE");
+
+ uint32 rifxOffset = stream->readUint32LE();
+ /* uint32 fontMapOffset = */ stream->readUint32LE();
+ /* uint32 resourceForkOffset1 = */ stream->readUint32LE();
+ /* uint32 resourceForkOffset2 = */ stream->readUint32LE();
+ stream->readUint32LE(); // graphics DLL offset
+ stream->readUint32LE(); // sound DLL offset
+ /* uint32 rifxOffsetAlt = */ stream->readUint32LE(); // equivalent to rifxOffset
+
+ loadEXERIFX(stream, rifxOffset);
+}
+
+void DirectorEngine::loadEXEv5(Common::SeekableReadStream *stream) {
+ if (stream->readUint32LE() != MKTAG('P', 'J', '9', '5'))
+ error("Invalid projector tag found in v5 EXE");
+
+ uint32 rifxOffset = stream->readUint32LE();
+ stream->readUint32LE(); // unknown
+ stream->readUint32LE(); // unknown
+ stream->readUint32LE(); // unknown
+ /* uint16 screenWidth = */ stream->readUint16LE();
+ /* uint16 screenHeight = */ stream->readUint16LE();
+ stream->readUint32LE(); // unknown
+ stream->readUint32LE(); // unknown
+ /* uint32 fontMapOffset = */ stream->readUint32LE();
+
+ loadEXERIFX(stream, rifxOffset);
+}
+
+void DirectorEngine::loadEXEv7(Common::SeekableReadStream *stream) {
+ if (stream->readUint32LE() != MKTAG('P', 'J', '0', '0'))
+ error("Invalid projector tag found in v7 EXE");
+
+ uint32 rifxOffset = stream->readUint32LE();
+ stream->readUint32LE(); // unknown
+ stream->readUint32LE(); // unknown
+ stream->readUint32LE(); // unknown
+ stream->readUint32LE(); // unknown
+ stream->readUint32LE(); // some DLL offset
+
+ loadEXERIFX(stream, rifxOffset);
+}
+
+void DirectorEngine::loadEXERIFX(Common::SeekableReadStream *stream, uint32 offset) {
+ _mainArchive = new RIFXArchive();
+
+ if (!_mainArchive->openStream(stream, offset))
+ error("Failed to load RIFX from EXE");
+}
+
+void DirectorEngine::loadMac() {
+ if (getVersion() < 4) {
+ // The data is part of the resource fork of the executable
+ _mainArchive = new MacArchive();
+
+ if (!_mainArchive->openFile(getEXEName()))
+ error("Failed to open Mac binary '%s'", getEXEName().c_str());
+ } else {
+ // The RIFX is located in the data fork of the executable
+ _macBinary = new Common::MacResManager();
+
+ if (!_macBinary->open(getEXEName()) || !_macBinary->hasDataFork())
+ error("Failed to open Mac binary '%s'", getEXEName().c_str());
+
+ Common::SeekableReadStream *dataFork = _macBinary->getDataFork();
+ _mainArchive = new RIFXArchive();
+
+ // First we need to detect PPC vs. 68k
+
+ uint32 tag = dataFork->readUint32BE();
+ uint32 startOffset;
+
+ if (SWAP_BYTES_32(tag) == MKTAG('P', 'J', '9', '3') || tag == MKTAG('P', 'J', '9', '5') || tag == MKTAG('P', 'J', '0', '0')) {
+ // PPC: The RIFX shares the data fork with the binary
+ startOffset = dataFork->readUint32BE();
+ } else {
+ // 68k: The RIFX is the only thing in the data fork
+ startOffset = 0;
+ }
+
+ if (!_mainArchive->openStream(dataFork, startOffset))
+ error("Failed to load RIFX from Mac binary");
+ }
+}
+
+Common::String DirectorEngine::readPascalString(Common::SeekableReadStream &stream) {
+ byte length = stream.readByte();
+ Common::String x;
+
+ while (length--)
+ x += (char)stream.readByte();
+
+ return x;
+}
+
+void DirectorEngine::setPalette(byte *palette, uint16 count) {
+ _currentPalette = palette;
+ _currentPaletteLength = count;
+}
+
+void DirectorEngine::loadSharedCastsFrom(Common::String filename) {
+ Archive *shardcst;
+
+ if (getVersion() < 4) {
+ shardcst = new RIFFArchive();
+ } else {
+ shardcst = new RIFXArchive();
+ }
+
+ shardcst->openFile(filename);
+
+ Score *castScore = new Score(this);
+ Common::SeekableSubReadStreamEndian *castStream = shardcst->getResource(MKTAG('V','W','C','R'), 1024);
+
+ castScore->loadCastData(*castStream);
+ *_sharedCasts = castScore->_casts;
+
+ Common::Array<uint16> dib = shardcst->getResourceIDList(MKTAG('D','I','B',' '));
+
+ if (dib.size() != 0) {
+ Common::Array<uint16>::iterator iterator;
+ for (iterator = dib.begin(); iterator != dib.end(); ++iterator) {
+ debug(3, "Shared DIB %d", *iterator);
+ _sharedDIB->setVal(*iterator, shardcst->getResource(MKTAG('D','I','B',' '), *iterator));
+ }
+ }
+
+ Common::Array<uint16> stxt = shardcst->getResourceIDList(MKTAG('S','T','X','T'));
+
+ if (stxt.size() != 0) {
+ Common::Array<uint16>::iterator iterator;
+ for (iterator = stxt.begin(); iterator != stxt.end(); ++iterator) {
+ debug(3, "Shared STXT %d", *iterator);
+ _sharedSTXT->setVal(*iterator, shardcst->getResource(MKTAG('S','T','X','T'), *iterator));
+ }
+ }
+
+ Common::Array<uint16> bmp = shardcst->getResourceIDList(MKTAG('B','I','T','D'));
+
+ if (bmp.size() != 0) {
+ Common::Array<uint16>::iterator iterator;
+ for (iterator = bmp.begin(); iterator != bmp.end(); ++iterator) {
+ _sharedBMP->setVal(*iterator, shardcst->getResource(MKTAG('B','I','T','D'), *iterator));
+ }
+ }
+
+ Common::Array<uint16> sound = shardcst->getResourceIDList(MKTAG('S','N','D',' '));
+
+ if (stxt.size() != 0) {
+ Common::Array<uint16>::iterator iterator;
+ for (iterator = sound.begin(); iterator != sound.end(); ++iterator) {
+ _sharedSound->setVal(*iterator, shardcst->getResource(MKTAG('S','N','D',' '), *iterator));
+ }
+ }
+}
+
+} // End of namespace Director
diff --git a/engines/director/director.h b/engines/director/director.h
new file mode 100644
index 0000000000..6208df2197
--- /dev/null
+++ b/engines/director/director.h
@@ -0,0 +1,122 @@
+/* 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 2
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef DIRECTOR_DIRECTOR_H
+#define DIRECTOR_DIRECTOR_H
+
+#include "common/scummsys.h"
+#include "common/random.h"
+#include "common/substream.h"
+
+#include "common/str.h"
+#include "common/hashmap.h"
+#include "engines/engine.h"
+#include "engines/director/sound.h"
+
+namespace Common {
+class MacResManager;
+}
+
+namespace Graphics {
+class MacWindowManager;
+}
+
+namespace Director {
+
+enum DirectorGameID {
+ GID_GENERIC,
+ GID_TEST
+};
+
+class Archive;
+struct DirectorGameDescription;
+class Lingo;
+class Score;
+struct Cast;
+
+class DirectorEngine : public ::Engine {
+public:
+ DirectorEngine(OSystem *syst, const DirectorGameDescription *gameDesc);
+ ~DirectorEngine();
+
+ // Detection related functions
+
+ DirectorGameID getGameID() const;
+ uint16 getVersion() const;
+ Common::Platform getPlatform() const;
+ Common::Language getLanguage() const;
+ Common::String getEXEName() const;
+ DirectorSound *getSoundManager() const { return _soundManager; }
+ Archive *getMainArchive() const { return _mainArchive; }
+ Lingo *getLingo() const { return _lingo; }
+ Score *getCurrentScore() const { return _currentScore; }
+ void setPalette(byte *palette, uint16 count);
+ bool hasFeature(EngineFeature f) const;
+ const byte *getPalette() const { return _currentPalette; }
+ uint16 getPaletteColorCount() const { return _currentPaletteLength; }
+ void loadSharedCastsFrom(Common::String filename);
+ Common::HashMap<int, Common::SeekableSubReadStreamEndian *> *getSharedDIB() const { return _sharedDIB; }
+ Common::HashMap<int, Common::SeekableSubReadStreamEndian *> *getSharedBMP() const { return _sharedBMP; }
+ Common::HashMap<int, Common::SeekableSubReadStreamEndian *> *getSharedSTXT() const { return _sharedSTXT; }
+ Common::HashMap<int, Cast *> *getSharedCasts() const { return _sharedCasts; }
+
+ Common::HashMap<Common::String, Score *> *_movies;
+ Score *_currentScore;
+
+ Common::RandomSource _rnd;
+ Graphics::MacWindowManager *_wm;
+
+protected:
+ virtual Common::Error run();
+
+private:
+ const DirectorGameDescription *_gameDescription;
+
+ Common::HashMap<Common::String, Score *> loadMMMNames(Common::String folder);
+ void loadEXE();
+ void loadEXEv3(Common::SeekableReadStream *stream);
+ void loadEXEv4(Common::SeekableReadStream *stream);
+ void loadEXEv5(Common::SeekableReadStream *stream);
+ void loadEXEv7(Common::SeekableReadStream *stream);
+ void loadEXERIFX(Common::SeekableReadStream *stream, uint32 offset);
+ void loadMac();
+
+ Common::String readPascalString(Common::SeekableReadStream &stream);
+
+ Common::String _sharedMMM;
+ Common::HashMap<int, Cast *> *_sharedCasts;
+ Common::HashMap<int, Common::SeekableSubReadStreamEndian *> *_sharedDIB;
+ Common::HashMap<int, Common::SeekableSubReadStreamEndian *> *_sharedSTXT;
+ Common::HashMap<int, Common::SeekableSubReadStreamEndian *> *_sharedSound;
+ Common::HashMap<int, Common::SeekableSubReadStreamEndian *> *_sharedBMP;
+
+ Archive *_mainArchive;
+ Common::MacResManager *_macBinary;
+ DirectorSound *_soundManager;
+ byte *_currentPalette;
+ uint16 _currentPaletteLength;
+ Lingo *_lingo;
+};
+
+} // End of namespace Director
+
+#endif
diff --git a/engines/director/lingo/lingo-builtins.cpp b/engines/director/lingo/lingo-builtins.cpp
new file mode 100644
index 0000000000..e44fc69c51
--- /dev/null
+++ b/engines/director/lingo/lingo-builtins.cpp
@@ -0,0 +1,263 @@
+/* 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 2
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "engines/director/lingo/lingo.h"
+
+namespace Director {
+
+static struct BuiltinProto {
+ const char *name;
+ void (*func)(void);
+ int nparams;
+} builtins[] = {
+ // Math
+ { "abs", Lingo::b_abs, 1 },
+ { "atan", Lingo::b_atan, 1 },
+ { "cos", Lingo::b_cos, 1 },
+ { "exp", Lingo::b_exp, 1 },
+ { "float", Lingo::b_float, 1 },
+ { "integer",Lingo::b_integer, 1 },
+ { "log", Lingo::b_log, 1 },
+ { "pi", Lingo::b_pi, 0 },
+ { "power", Lingo::b_power, 2 },
+ { "random", Lingo::b_random, 1 },
+ { "sin", Lingo::b_sin, 1 },
+ { "sqrt", Lingo::b_sqrt, 1 },
+ { "tan", Lingo::b_tan, 1 },
+ // String
+ { "chars", Lingo::b_chars, 3 },
+ { "length", Lingo::b_length, 1 },
+ { "string", Lingo::b_string, 1 },
+ // Misc
+ { "dontpassevent", Lingo::b_dontpassevent, -1 },
+ { "updatestage", Lingo::b_updatestage, -1 },
+ { "ilk", Lingo::b_ilk, 1 },
+ // point
+ { "point", Lingo::b_point, 2 },
+ { 0, 0, 0 }
+};
+
+void Lingo::initBuiltIns() {
+ for (BuiltinProto *blt = builtins; blt->name; blt++) {
+ Symbol *sym = new Symbol;
+
+ sym->name = (char *)calloc(strlen(blt->name) + 1, 1);
+ Common::strlcpy(sym->name, blt->name, strlen(blt->name));
+ sym->type = BLTIN;
+ sym->nargs = blt->nparams;
+ sym->u.func = blt->func;
+
+ _handlers[blt->name] = sym;
+ }
+}
+
+///////////////////
+// Math
+///////////////////
+void Lingo::b_abs() {
+ Datum d = g_lingo->pop();
+
+ if (d.type == INT)
+ d.u.i = ABS(d.u.i);
+ else if (d.type == FLOAT)
+ d.u.f = ABS(d.u.f);
+
+ g_lingo->push(d);
+}
+
+void Lingo::b_atan() {
+ Datum d = g_lingo->pop();
+ d.toFloat();
+ d.u.f = atan(d.u.f);
+ g_lingo->push(d);
+}
+
+void Lingo::b_cos() {
+ Datum d = g_lingo->pop();
+ d.toFloat();
+ d.u.f = cos(d.u.f);
+ g_lingo->push(d);
+}
+
+void Lingo::b_exp() {
+ Datum d = g_lingo->pop();
+ d.toInt(); // Lingo uses int, so we're enforcing it
+ d.toFloat();
+ d.u.f = exp(d.u.f);
+ g_lingo->push(d);
+}
+
+void Lingo::b_float() {
+ Datum d = g_lingo->pop();
+ d.toFloat();
+ g_lingo->push(d);
+}
+
+void Lingo::b_integer() {
+ Datum d = g_lingo->pop();
+ d.toInt();
+ g_lingo->push(d);
+}
+
+void Lingo::b_log() {
+ Datum d = g_lingo->pop();
+ d.toFloat();
+ d.u.f = log(d.u.f);
+ g_lingo->push(d);
+}
+
+void Lingo::b_pi() {
+ Datum d;
+ d.toFloat();
+ d.u.f = M_PI;
+ g_lingo->push(d);
+}
+
+void Lingo::b_power() {
+ Datum d1 = g_lingo->pop();
+ Datum d2 = g_lingo->pop();
+ d1.toFloat();
+ d2.toFloat();
+ d1.u.f = pow(d2.u.f, d1.u.f);
+ g_lingo->push(d1);
+}
+
+void Lingo::b_random() {
+ Datum max = g_lingo->pop();
+ Datum res;
+
+ max.toInt();
+
+ res.u.i = g_lingo->_vm->_rnd.getRandomNumber(max.u.i);
+ res.type = INT;
+
+ g_lingo->push(res);
+}
+
+void Lingo::b_sin() {
+ Datum d = g_lingo->pop();
+ d.toFloat();
+ d.u.f = sin(d.u.f);
+ g_lingo->push(d);
+}
+
+void Lingo::b_sqrt() {
+ Datum d = g_lingo->pop();
+ d.toFloat();
+ d.u.f = sqrt(d.u.f);
+ g_lingo->push(d);
+}
+
+void Lingo::b_tan() {
+ Datum d = g_lingo->pop();
+ d.toFloat();
+ d.u.f = tan(d.u.f);
+ g_lingo->push(d);
+}
+
+///////////////////
+// String
+///////////////////
+void Lingo::b_chars() {
+ Datum to = g_lingo->pop();
+ Datum from = g_lingo->pop();
+ Datum s = g_lingo->pop();
+
+ if (s.type != STRING)
+ error("Incorrect type for 'chars' function: %s", s.type2str());
+
+ to.toInt();
+ from.toInt();
+
+ int len = strlen(s.u.s->c_str());
+ int f = MAX(0, MIN(len, from.u.i - 1));
+ int t = MAX(0, MIN(len, to.u.i));
+
+ Common::String *res = new Common::String(&(s.u.s->c_str()[f]), &(s.u.s->c_str()[t]));
+
+ delete s.u.s;
+
+ s.u.s = res;
+ s.type = STRING;
+ g_lingo->push(s);
+}
+
+void Lingo::b_length() {
+ Datum d = g_lingo->pop();
+
+ if (d.type != STRING)
+ error("Incorrect type for 'length' function: %s", d.type2str());
+
+ int len = strlen(d.u.s->c_str());
+ delete d.u.s;
+
+ d.u.i = len;
+ d.type = INT;
+ g_lingo->push(d);
+}
+
+void Lingo::b_string() {
+ Datum d = g_lingo->pop();
+ d.toString();
+ g_lingo->push(d);
+}
+
+///////////////////
+// Misc
+///////////////////
+void Lingo::b_dontpassevent() {
+ warning("STUB: b_dontpassevent");
+}
+
+void Lingo::b_updatestage() {
+ warning("STUB: b_updatestage");
+}
+
+void Lingo::b_ilk() {
+ Datum d = g_lingo->pop();
+ d.u.i = d.type;
+ d.type = SYMBOL;
+ g_lingo->push(d);
+}
+
+///////////////////
+// Point
+///////////////////
+void Lingo::b_point() {
+ Datum y = g_lingo->pop();
+ Datum x = g_lingo->pop();
+ Datum d;
+
+ x.toFloat();
+ y.toFloat();
+
+ d.u.arr = new FloatArray;
+
+ d.u.arr->push_back(x.u.f);
+ d.u.arr->push_back(y.u.f);
+ d.type = POINT;
+
+ g_lingo->push(d);
+}
+
+
+} // End of namespace Director
diff --git a/engines/director/lingo/lingo-code.cpp b/engines/director/lingo/lingo-code.cpp
new file mode 100644
index 0000000000..4c88e5d6e9
--- /dev/null
+++ b/engines/director/lingo/lingo-code.cpp
@@ -0,0 +1,794 @@
+/* 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 2
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+// Heavily inspired by hoc
+// Copyright (C) AT&T 1995
+// All Rights Reserved
+//
+// Permission to use, copy, modify, and distribute this software and
+// its documentation for any purpose and without fee is hereby
+// granted, provided that the above copyright notice appear in all
+// copies and that both that the copyright notice and this
+// permission notice and warranty disclaimer appear in supporting
+// documentation, and that the name of AT&T or any of its entities
+// not be used in advertising or publicity pertaining to
+// distribution of the software without specific, written prior
+// permission.
+//
+// AT&T DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
+// INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS.
+// IN NO EVENT SHALL AT&T OR ANY OF ITS ENTITIES BE LIABLE FOR ANY
+// SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
+// IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
+// ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
+// THIS SOFTWARE.
+
+#include "engines/director/lingo/lingo.h"
+#include "common/file.h"
+#include "audio/decoders/wave.h"
+
+#include "director/lingo/lingo-gr.h"
+
+namespace Director {
+
+void Lingo::push(Datum d) {
+ _stack.push_back(d);
+}
+
+void Lingo::pushVoid() {
+ Datum d;
+ d.u.i = 0;
+ d.type = VOID;
+ push(d);
+}
+
+Datum Lingo::pop(void) {
+ if (_stack.size() == 0)
+ error("stack underflow");
+
+ Datum ret = _stack.back();
+ _stack.pop_back();
+
+ return ret;
+}
+
+void Lingo::c_xpop() {
+ g_lingo->pop();
+}
+
+void Lingo::c_printtop(void) {
+ Datum d = g_lingo->pop();
+
+ switch (d.type) {
+ case VOID:
+ warning("Void");
+ break;
+ case INT:
+ warning("%d", d.u.i);
+ break;
+ case FLOAT:
+ warning(g_lingo->_floatPrecisionFormat.c_str(), d.u.f);
+ break;
+ case VAR:
+ if (!d.u.sym) {
+ warning("Inconsistent stack: var, val: %d", d.u.i);
+ } else {
+ if (d.u.sym->name)
+ warning("var: %s", d.u.sym->name);
+ else
+ warning("Nameless var. val: %d", d.u.sym->u.i);
+ }
+ break;
+ case STRING:
+ warning("%s", d.u.s->c_str());
+ break;
+ case POINT:
+ warning("point(%d, %d)", (int)((*d.u.arr)[0]), (int)((*d.u.arr)[1]));
+ break;
+ case SYMBOL:
+ warning("%s", d.type2str(true));
+ break;
+ default:
+ warning("--unknown--");
+ }
+}
+
+void Lingo::c_constpush() {
+ Datum d;
+ inst i = (*g_lingo->_currentScript)[g_lingo->_pc++];
+ d.u.i = READ_UINT32(&i);
+ d.type = INT;
+ g_lingo->push(d);
+}
+
+void Lingo::c_fconstpush() {
+ Datum d;
+ inst i = (*g_lingo->_currentScript)[g_lingo->_pc];
+ d.u.f = *((double *)&i);
+ d.type = FLOAT;
+
+ g_lingo->_pc += g_lingo->calcCodeAlignment(sizeof(double));
+
+ g_lingo->push(d);
+}
+
+void Lingo::c_stringpush() {
+ Datum d;
+ char *s = (char *)&(*g_lingo->_currentScript)[g_lingo->_pc];
+ g_lingo->_pc += g_lingo->calcStringAlignment(s);
+
+ d.u.s = new Common::String(s);
+ d.type = STRING;
+ g_lingo->push(d);
+}
+
+void Lingo::c_varpush() {
+ char *name = (char *)&(*g_lingo->_currentScript)[g_lingo->_pc];
+ Datum d;
+
+ d.u.sym = g_lingo->lookupVar(name);
+ if (d.u.sym->type == CASTREF) {
+ d.type = INT;
+ int val = d.u.sym->u.i;
+
+ delete d.u.sym;
+
+ d.u.i = val;
+ } else {
+ d.type = VAR;
+ }
+
+ g_lingo->_pc += g_lingo->calcStringAlignment(name);
+
+ g_lingo->push(d);
+}
+
+void Lingo::c_assign() {
+ Datum d1, d2;
+ d1 = g_lingo->pop();
+ d2 = g_lingo->pop();
+
+ if (d1.type != VAR) {
+ warning("assignment to non-variable");
+ return;
+ }
+
+ if (d1.u.sym->type != INT && d1.u.sym->type != VOID &&
+ d1.u.sym->type != FLOAT && d1.u.sym->type != STRING) {
+ warning("assignment to non-variable '%s'", d1.u.sym->name);
+ return;
+ }
+
+ if (d1.u.sym->type == STRING) // Free memory if needed
+ delete d1.u.sym->u.s;
+
+ if (d1.u.sym->type == POINT || d1.u.sym->type == RECT || d1.u.sym->type == ARRAY)
+ delete d1.u.sym->u.arr;
+
+ if (d2.type == INT) {
+ d1.u.sym->u.i = d2.u.i;
+ } else if (d2.type == FLOAT) {
+ d1.u.sym->u.f = d2.u.f;
+ } else if (d2.type == STRING) {
+ d1.u.sym->u.s = new Common::String(*d2.u.s);
+ delete d2.u.s;
+ } else if (d2.type == POINT) {
+ d1.u.sym->u.arr = new FloatArray(*d2.u.arr);
+ delete d2.u.arr;
+ } else if (d2.type == SYMBOL) {
+ d1.u.sym->u.i = d2.u.i;
+ } else {
+ warning("c_assign: unhandled type: %s", d2.type2str());
+ }
+
+ d1.u.sym->type = d2.type;
+
+ g_lingo->push(d1);
+}
+
+bool Lingo::verify(Symbol *s) {
+ if (s->type != INT && s->type != VOID && s->type != FLOAT && s->type != STRING && s->type != POINT) {
+ warning("attempt to evaluate non-variable '%s'", s->name);
+
+ return false;
+ }
+
+ if (s->type == VOID)
+ warning("Variable used before assigning a value '%s'", s->name);
+
+ return true;
+}
+
+void Lingo::c_eval() {
+ g_lingo->c_varpush();
+
+ Datum d;
+ d = g_lingo->pop();
+
+ if (d.type != VAR) { // It could be cast ref
+ g_lingo->push(d);
+ return;
+ }
+
+ if (!g_lingo->verify(d.u.sym))
+ return;
+
+ d.type = d.u.sym->type;
+
+ if (d.u.sym->type == INT)
+ d.u.i = d.u.sym->u.i;
+ else if (d.u.sym->type == FLOAT)
+ d.u.f = d.u.sym->u.f;
+ else if (d.u.sym->type == STRING)
+ d.u.s = new Common::String(*d.u.sym->u.s);
+ else if (d.u.sym->type == POINT)
+ d.u.arr = d.u.sym->u.arr;
+ else if (d.u.sym->type == SYMBOL)
+ d.u.i = d.u.sym->u.i;
+ else
+ warning("c_eval: unhandled type: %s", d.type2str());
+
+ g_lingo->push(d);
+}
+
+void Lingo::c_theentitypush() {
+ inst e = (*g_lingo->_currentScript)[g_lingo->_pc++];
+ inst f = (*g_lingo->_currentScript)[g_lingo->_pc++];
+ Datum id = g_lingo->pop();
+
+ int entity = READ_UINT32(&e);
+ int field = READ_UINT32(&f);
+
+ Datum d = g_lingo->getTheEntity(entity, id, field);
+ g_lingo->push(d);
+}
+
+void Lingo::c_theentityassign() {
+ inst e = (*g_lingo->_currentScript)[g_lingo->_pc++];
+ inst f = (*g_lingo->_currentScript)[g_lingo->_pc++];
+ Datum id = g_lingo->pop();
+
+ int entity = READ_UINT32(&e);
+ int field = READ_UINT32(&f);
+
+ Datum d = g_lingo->pop();
+ g_lingo->setTheEntity(entity, id, field, d);
+
+ g_lingo->push(d); // Dummy value
+}
+
+void Lingo::c_swap() {
+ Datum d2 = g_lingo->pop();
+ Datum d1 = g_lingo->pop();
+ g_lingo->push(d2);
+ g_lingo->push(d1);
+}
+
+void Lingo::c_add() {
+ Datum d2 = g_lingo->pop();
+ Datum d1 = g_lingo->pop();
+
+ if (g_lingo->alignTypes(d1, d2) == FLOAT) {
+ d1.u.f += d2.u.f;
+ } else {
+ d1.u.i += d2.u.i;
+ }
+ g_lingo->push(d1);
+}
+
+void Lingo::c_sub() {
+ Datum d2 = g_lingo->pop();
+ Datum d1 = g_lingo->pop();
+
+ if (g_lingo->alignTypes(d1, d2) == FLOAT) {
+ d1.u.f -= d2.u.f;
+ } else {
+ d1.u.i -= d2.u.i;
+ }
+ g_lingo->push(d1);
+}
+
+void Lingo::c_mul() {
+ Datum d2 = g_lingo->pop();
+ Datum d1 = g_lingo->pop();
+
+ if (g_lingo->alignTypes(d1, d2) == FLOAT) {
+ d1.u.f *= d2.u.f;
+ } else {
+ d1.u.i *= d2.u.i;
+ }
+ g_lingo->push(d1);
+}
+
+void Lingo::c_div() {
+ Datum d2 = g_lingo->pop();
+
+ if ((d2.type == INT && d2.u.i == 0) ||
+ (d2.type == FLOAT && d2.u.f == 0.0))
+ error("division by zero");
+
+ Datum d1 = g_lingo->pop();
+
+ if (g_lingo->alignTypes(d1, d2) == FLOAT) {
+ d1.u.f /= d2.u.f;
+ } else {
+ d1.u.i /= d2.u.i;
+ }
+ g_lingo->push(d1);
+}
+
+void Lingo::c_negate() {
+ Datum d = g_lingo->pop();
+
+ if (d.type == INT)
+ d.u.i = -d.u.i;
+ else if (d.type == FLOAT)
+ d.u.f = -d.u.f;
+
+ g_lingo->push(d);
+}
+
+void Lingo::c_ampersand() {
+ Datum d2 = g_lingo->pop();
+ Datum d1 = g_lingo->pop();
+
+ d1.toString();
+ d2.toString();
+
+ *d1.u.s += *d2.u.s;
+
+ delete d2.u.s;
+
+ g_lingo->push(d1);
+}
+
+void Lingo::c_concat() {
+ Datum d2 = g_lingo->pop();
+ Datum d1 = g_lingo->pop();
+
+ d1.toString();
+ d2.toString();
+
+ *d1.u.s += " ";
+ *d1.u.s += *d2.u.s;
+
+ delete d2.u.s;
+
+ g_lingo->push(d1);
+}
+
+void Lingo::c_contains() {
+ Datum d2 = g_lingo->pop();
+ Datum d1 = g_lingo->pop();
+
+ d1.toString();
+ d2.toString();
+
+ Common::String *s1 = g_lingo->toLowercaseMac(d1.u.s);
+ Common::String *s2 = g_lingo->toLowercaseMac(d2.u.s);
+
+ int res = s1->contains(*s2) ? 1 : 0;
+
+ delete d1.u.s;
+ delete d2.u.s;
+ delete s1;
+ delete s2;
+
+ d1.type = INT;
+ d1.u.i = res;
+
+ g_lingo->push(d1);
+}
+
+void Lingo::c_starts() {
+ Datum d2 = g_lingo->pop();
+ Datum d1 = g_lingo->pop();
+
+ d1.toString();
+ d2.toString();
+
+ Common::String *s1 = g_lingo->toLowercaseMac(d1.u.s);
+ Common::String *s2 = g_lingo->toLowercaseMac(d2.u.s);
+
+ int res = s1->hasPrefix(*s2) ? 1 : 0;
+
+ delete d1.u.s;
+ delete d2.u.s;
+ delete s1;
+ delete s2;
+
+ d1.type = INT;
+ d1.u.i = res;
+
+ g_lingo->push(d1);
+}
+
+void Lingo::c_intersects() {
+ Datum d2 = g_lingo->pop();
+ Datum d1 = g_lingo->pop();
+
+ warning("STUB: c_intersects");
+
+ g_lingo->push(d1);
+}
+
+void Lingo::c_within() {
+ Datum d2 = g_lingo->pop();
+ Datum d1 = g_lingo->pop();
+
+ warning("STUB: c_within");
+
+ g_lingo->push(d1);
+}
+
+void Lingo::c_and() {
+ Datum d2 = g_lingo->pop();
+ Datum d1 = g_lingo->pop();
+
+ d1.toInt();
+ d2.toInt();
+
+ d1.u.i = (d1.u.i && d2.u.i) ? 1 : 0;
+
+ g_lingo->push(d1);
+}
+
+void Lingo::c_or() {
+ Datum d2 = g_lingo->pop();
+ Datum d1 = g_lingo->pop();
+
+ d1.toInt();
+ d2.toInt();
+
+ d1.u.i = (d1.u.i || d2.u.i) ? 1 : 0;
+
+ g_lingo->push(d1);
+}
+
+void Lingo::c_not() {
+ Datum d = g_lingo->pop();
+
+ d.toInt();
+
+ d.u.i = ~d.u.i ? 1 : 0;
+
+ g_lingo->push(d);
+}
+
+void Lingo::c_eq() {
+ Datum d2 = g_lingo->pop();
+ Datum d1 = g_lingo->pop();
+
+ if (g_lingo->alignTypes(d1, d2) == FLOAT) {
+ d1.u.i = (d1.u.f == d2.u.f) ? 1 : 0;
+ d1.type = INT;
+ } else {
+ d1.u.i = (d1.u.i == d2.u.i) ? 1 : 0;
+ }
+ g_lingo->push(d1);
+}
+
+void Lingo::c_neq() {
+ Datum d2 = g_lingo->pop();
+ Datum d1 = g_lingo->pop();
+
+ if (g_lingo->alignTypes(d1, d2) == FLOAT) {
+ d1.u.i = (d1.u.f != d2.u.f) ? 1 : 0;
+ d1.type = INT;
+ } else {
+ d1.u.i = (d1.u.i != d2.u.i) ? 1 : 0;
+ }
+ g_lingo->push(d1);
+}
+
+void Lingo::c_gt() {
+ Datum d2 = g_lingo->pop();
+ Datum d1 = g_lingo->pop();
+
+ if (g_lingo->alignTypes(d1, d2) == FLOAT) {
+ d1.u.i = (d1.u.f > d2.u.f) ? 1 : 0;
+ d1.type = INT;
+ } else {
+ d1.u.i = (d1.u.i > d2.u.i) ? 1 : 0;
+ }
+ g_lingo->push(d1);
+}
+
+void Lingo::c_lt() {
+ Datum d2 = g_lingo->pop();
+ Datum d1 = g_lingo->pop();
+
+ if (g_lingo->alignTypes(d1, d2) == FLOAT) {
+ d1.u.i = (d1.u.f < d2.u.f) ? 1 : 0;
+ d1.type = INT;
+ } else {
+ d1.u.i = (d1.u.i < d2.u.i) ? 1 : 0;
+ }
+ g_lingo->push(d1);
+}
+
+void Lingo::c_ge() {
+ Datum d2 = g_lingo->pop();
+ Datum d1 = g_lingo->pop();
+
+ if (g_lingo->alignTypes(d1, d2) == FLOAT) {
+ d1.u.i = (d1.u.f >= d2.u.f) ? 1 : 0;
+ d1.type = INT;
+ } else {
+ d1.u.i = (d1.u.i >= d2.u.i) ? 1 : 0;
+ }
+ g_lingo->push(d1);
+}
+
+void Lingo::c_le() {
+ Datum d2 = g_lingo->pop();
+ Datum d1 = g_lingo->pop();
+
+ if (g_lingo->alignTypes(d1, d2) == FLOAT) {
+ d1.u.i = (d1.u.f <= d2.u.f) ? 1 : 0;
+ d1.type = INT;
+ } else {
+ d1.u.i = (d1.u.i <= d2.u.i) ? 1 : 0;
+ }
+ g_lingo->push(d1);
+}
+
+void Lingo::c_repeatwhilecode(void) {
+ Datum d;
+ int savepc = g_lingo->_pc;
+
+ int body = READ_UINT32(&(*g_lingo->_currentScript)[savepc]);
+ int end = READ_UINT32(&(*g_lingo->_currentScript)[savepc + 1]);
+
+ g_lingo->execute(savepc + 2); /* condition */
+ d = g_lingo->pop();
+ d.toInt();
+
+ while (d.u.i) {
+ g_lingo->execute(body); /* body */
+ if (g_lingo->_returning)
+ break;
+
+ g_lingo->execute(savepc + 2); /* condition */
+ d = g_lingo->pop();
+ d.toInt();
+ }
+
+ if (!g_lingo->_returning)
+ g_lingo->_pc = end; /* next stmt */
+}
+
+void Lingo::c_repeatwithcode(void) {
+ Datum d;
+ int savepc = g_lingo->_pc;
+
+ int init = READ_UINT32(&(*g_lingo->_currentScript)[savepc]);
+ int finish = READ_UINT32(&(*g_lingo->_currentScript)[savepc + 1]);
+ int body = READ_UINT32(&(*g_lingo->_currentScript)[savepc + 2]);
+ int inc = (int32)READ_UINT32(&(*g_lingo->_currentScript)[savepc + 3]);
+ int end = READ_UINT32(&(*g_lingo->_currentScript)[savepc + 4]);
+ Common::String countername((char *)&(*g_lingo->_currentScript)[savepc + 5]);
+ Symbol *counter = g_lingo->lookupVar(countername.c_str());
+
+ if (counter->type == CASTREF) {
+ error("Cast ref used as index: %s", countername.c_str());
+ }
+
+ g_lingo->execute(init); /* condition */
+ d = g_lingo->pop();
+ d.toInt();
+ counter->u.i = d.u.i;
+ counter->type = INT;
+
+ while (true) {
+ g_lingo->execute(body); /* body */
+ if (g_lingo->_returning)
+ break;
+
+ counter->u.i += inc;
+ g_lingo->execute(finish); /* condition */
+ d = g_lingo->pop();
+ d.toInt();
+
+ if (counter->u.i == d.u.i + inc)
+ break;
+ }
+
+ if (!g_lingo->_returning)
+ g_lingo->_pc = end; /* next stmt */
+}
+
+void Lingo::c_ifcode() {
+ Datum d;
+ int savepc = g_lingo->_pc; /* then part */
+
+ int then = READ_UINT32(&(*g_lingo->_currentScript)[savepc]);
+ int elsep = READ_UINT32(&(*g_lingo->_currentScript)[savepc + 1]);
+ int end = READ_UINT32(&(*g_lingo->_currentScript)[savepc + 2]);
+ int skipEnd = READ_UINT32(&(*g_lingo->_currentScript)[savepc + 3]);
+
+ debug(8, "executing cond (have to %s end)", skipEnd ? "skip" : "execute");
+ g_lingo->execute(savepc + 4); /* condition */
+
+ d = g_lingo->pop();
+
+ if (d.toInt()) {
+ debug(8, "executing then");
+ g_lingo->execute(then);
+ } else if (elsep) { /* else part? */
+ debug(8, "executing else");
+ g_lingo->execute(elsep);
+ }
+
+ if (!g_lingo->_returning && !skipEnd) {
+ g_lingo->_pc = end; /* next stmt */
+ debug(8, "executing end");
+ } else
+ debug(8, "Skipped end");
+}
+
+//************************
+// Built-in functions
+//************************
+void Lingo::c_mci() {
+ Common::String s((char *)&(*g_lingo->_currentScript)[g_lingo->_pc]);
+
+ g_lingo->func_mci(s);
+
+ g_lingo->_pc += g_lingo->calcStringAlignment(s.c_str());
+}
+
+void Lingo::c_mciwait() {
+ Common::String s((char *)&(*g_lingo->_currentScript)[g_lingo->_pc]);
+
+ g_lingo->func_mciwait(s);
+
+ g_lingo->_pc += g_lingo->calcStringAlignment(s.c_str());
+}
+
+void Lingo::c_goto() {
+ Common::String frame((char *)&(*g_lingo->_currentScript)[g_lingo->_pc]);
+ g_lingo->_pc += g_lingo->calcStringAlignment(frame.c_str());
+
+ Common::String movie((char *)&(*g_lingo->_currentScript)[g_lingo->_pc]);
+ g_lingo->_pc += g_lingo->calcStringAlignment(movie.c_str());
+
+ g_lingo->func_goto(frame, movie);
+}
+
+void Lingo::c_gotoloop() {
+ g_lingo->func_gotoloop();
+}
+
+void Lingo::c_gotonext() {
+ g_lingo->func_gotonext();
+}
+
+void Lingo::c_gotoprevious() {
+ g_lingo->func_gotoprevious();
+}
+
+void Lingo::c_call() {
+ Common::String name((char *)&(*g_lingo->_currentScript)[g_lingo->_pc]);
+ g_lingo->_pc += g_lingo->calcStringAlignment(name.c_str());
+
+ int nargs = READ_UINT32(&(*g_lingo->_currentScript)[g_lingo->_pc++]);
+
+ if (!g_lingo->_handlers.contains(name)) {
+ warning("Call to undefined handler '%s'. Dropping %d stack items", name.c_str(), nargs);
+
+ for (int i = 0; i < nargs; i++)
+ g_lingo->pop();
+
+ // Push dummy value
+ g_lingo->pushVoid();
+
+ return;
+ }
+
+ Symbol *sym = g_lingo->_handlers[name];
+
+ if (sym->nargs < nargs) {
+ warning("Incorrect number of arguments for function %s. Dropping extra %d", name.c_str(), nargs - sym->nargs);
+ for (int i = 0; i < nargs - sym->nargs; i++)
+ g_lingo->pop();
+ }
+
+ if (sym->type == BLTIN) {
+ if (sym->nargs > 0 && nargs < sym->nargs) {
+ warning("Too few arguments for function %s. Expecting %d but got %d", name.c_str(), sym->nargs, nargs);
+ for (int i = 0; i < nargs; i++)
+ g_lingo->pop();
+
+ g_lingo->pushVoid();
+
+ return;
+ }
+ (*sym->u.func)();
+
+ return;
+ }
+
+ for (int i = nargs; i < sym->nargs; i++) {
+ Datum d;
+
+ d.u.i = 0;
+ d.type = VOID;
+ g_lingo->push(d);
+ }
+
+ CFrame *fp = new CFrame;
+
+ fp->sp = sym;
+ fp->retpc = g_lingo->_pc;
+ fp->retscript = g_lingo->_currentScript;
+ fp->localvars = g_lingo->_localvars;
+
+ // Create new set of local variables
+ g_lingo->_localvars = new SymbolHash;
+
+ g_lingo->_callstack.push_back(fp);
+
+ g_lingo->_currentScript = sym->u.defn;
+ g_lingo->execute(0);
+
+ g_lingo->_returning = false;
+}
+
+void Lingo::c_procret() {
+ if (!g_lingo->_callstack.size()) {
+ warning("Call stack underflow");
+ g_lingo->_returning = true;
+ return;
+ }
+
+ CFrame *fp = g_lingo->_callstack.back();
+
+ g_lingo->_currentScript = fp->retscript;
+ g_lingo->_pc = fp->retpc;
+
+ g_lingo->cleanLocalVars();
+
+ // Restore local variables
+ g_lingo->_localvars = fp->localvars;
+
+ delete fp;
+
+ g_lingo->_returning = true;
+}
+
+void Lingo::c_global() {
+ Common::String name((char *)&(*g_lingo->_currentScript)[g_lingo->_pc]);
+
+ Symbol *s = g_lingo->lookupVar(name.c_str(), false);
+ if (s && !s->global) {
+ warning("Local variable %s declared as global", name.c_str());
+ }
+
+ s = g_lingo->lookupVar(name.c_str(), true, true);
+ s->global = true;
+
+ g_lingo->_pc += g_lingo->calcStringAlignment(name.c_str());
+}
+
+}
diff --git a/engines/director/lingo/lingo-codegen.cpp b/engines/director/lingo/lingo-codegen.cpp
new file mode 100644
index 0000000000..07fb52290c
--- /dev/null
+++ b/engines/director/lingo/lingo-codegen.cpp
@@ -0,0 +1,266 @@
+/* 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 2
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+// Heavily inspired by hoc
+// Copyright (C) AT&T 1995
+// All Rights Reserved
+//
+// Permission to use, copy, modify, and distribute this software and
+// its documentation for any purpose and without fee is hereby
+// granted, provided that the above copyright notice appear in all
+// copies and that both that the copyright notice and this
+// permission notice and warranty disclaimer appear in supporting
+// documentation, and that the name of AT&T or any of its entities
+// not be used in advertising or publicity pertaining to
+// distribution of the software without specific, written prior
+// permission.
+//
+// AT&T DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
+// INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS.
+// IN NO EVENT SHALL AT&T OR ANY OF ITS ENTITIES BE LIABLE FOR ANY
+// SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
+// IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
+// ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
+// THIS SOFTWARE.
+
+#include "engines/director/lingo/lingo.h"
+#include "common/file.h"
+#include "audio/decoders/wave.h"
+
+#include "director/lingo/lingo-gr.h"
+
+namespace Director {
+
+void Lingo::execute(int pc) {
+ for(_pc = pc; (*_currentScript)[_pc] != STOP && !_returning;) {
+
+ for (int i = 0; i < _stack.size(); i++) {
+ debugN(5, "%d ", _stack[i].u.i);
+ }
+ debug(5, "");
+
+ _pc++;
+ (*((*_currentScript)[_pc - 1]))();
+ }
+}
+
+Symbol *Lingo::lookupVar(const char *name, bool create, bool putInGlobalList) {
+ Symbol *sym;
+
+ // Looking for the cast member constants
+ if (_vm->getVersion() < 4) { // TODO: There could be a flag 'Allow Outdated Lingo' in Movie Info in D4
+ if (strlen(name) == 3) {
+ if (tolower(name[0]) >= 'a' && tolower(name[0]) <= 'h' &&
+ name[1] >= '1' && name[1] <= '8' &&
+ name[2] >= '1' && name[2] <= '8') {
+
+ if (!create)
+ error("Cast reference used in wrong context: %s", name);
+
+ int val = (tolower(name[0]) - 'a') * 64 + (name[1] - '1') * 8 + (name[2] - '1') + 1;
+ sym = new Symbol;
+
+ sym->type = CASTREF;
+ sym->u.i = val;
+
+ return sym;
+ }
+ }
+ }
+
+ if (!_localvars->contains(name)) { // Create variable if it was not defined
+ if (!create)
+ return NULL;
+
+ sym = new Symbol;
+ sym->name = (char *)calloc(strlen(name) + 1, 1);
+ Common::strlcpy(sym->name, name, strlen(name) + 1);
+ sym->type = VOID;
+ sym->u.i = 0;
+
+ (*_localvars)[name] = sym;
+
+ if (putInGlobalList) {
+ sym->global = true;
+ _globalvars[name] = sym;
+ }
+ } else {
+ sym = (*_localvars)[name];
+
+ if (sym->global)
+ sym = _globalvars[name];
+ }
+
+ return sym;
+}
+
+void Lingo::cleanLocalVars() {
+ // Clean up current scope local variables and clean up memory
+ for (SymbolHash::const_iterator h = _localvars->begin(); h != _localvars->end(); ++h) {
+ if (!h->_value->global)
+ delete h->_value;
+ }
+ delete g_lingo->_localvars;
+}
+
+void Lingo::define(Common::String &name, int start, int nargs, Common::String *prefix) {
+ Symbol *sym;
+
+ if (prefix)
+ name = *prefix + "-" + name;
+
+ debug(3, "define(\"%s\", %d, %d, %d)", name.c_str(), start, _currentScript->size() - 1, nargs);
+
+ if (!_handlers.contains(name)) { // Create variable if it was not defined
+ sym = new Symbol;
+
+ sym->name = (char *)calloc(name.size() + 1, 1);
+ Common::strlcpy(sym->name, name.c_str(), name.size() + 1);
+ sym->type = HANDLER;
+
+ _handlers[name] = sym;
+ } else {
+ sym = g_lingo->_handlers[name];
+
+ warning("Redefining handler '%s'", name.c_str());
+ delete sym->u.defn;
+ }
+
+ sym->u.defn = new ScriptData(&(*_currentScript)[start], _currentScript->size() - start + 1);
+ sym->nargs = nargs;
+}
+
+int Lingo::codeString(const char *str) {
+ int numInsts = calcStringAlignment(str);
+
+ // Where we copy the string over
+ int pos = _currentScript->size();
+
+ // Allocate needed space in script
+ for (int i = 0; i < numInsts; i++)
+ _currentScript->push_back(0);
+
+ byte *dst = (byte *)&_currentScript->front() + pos * sizeof(inst);
+
+ memcpy(dst, str, strlen(str) + 1);
+
+ return _currentScript->size();
+}
+
+int Lingo::codeFloat(double f) {
+ int numInsts = calcCodeAlignment(sizeof(double));
+
+ // Where we copy the string over
+ int pos = _currentScript->size();
+
+ // Allocate needed space in script
+ for (int i = 0; i < numInsts; i++)
+ _currentScript->push_back(0);
+
+ double *dst = (double *)((byte *)&_currentScript->front() + pos * sizeof(inst));
+
+ *dst = f;
+
+ return _currentScript->size();
+}
+
+void Lingo::codeArg(Common::String *s) {
+ _argstack.push_back(s);
+}
+
+void Lingo::codeArgStore() {
+ while (true) {
+ if (_argstack.empty()) {
+ break;
+ }
+
+ Common::String *arg = _argstack.back();
+ _argstack.pop_back();
+
+ code1(c_varpush);
+ codeString(arg->c_str());
+ code1(c_assign);
+ code1(c_xpop);
+
+ delete arg;
+ }
+}
+
+int Lingo::codeFunc(Common::String *s, int numpar) {
+ int ret = g_lingo->code1(g_lingo->c_call);
+
+ if (s->equalsIgnoreCase("me")) {
+ if (!g_lingo->_currentFactory.empty()) {
+ g_lingo->codeString(g_lingo->_currentFactory.c_str());
+ debug(2, "Repaced 'me' with %s", g_lingo->_currentFactory.c_str());
+ } else {
+ warning("'me' out of factory method");
+ g_lingo->codeString(s->c_str());
+ }
+ } else {
+ g_lingo->codeString(s->c_str());
+ }
+
+ inst num = 0;
+ WRITE_UINT32(&num, numpar);
+ g_lingo->code1(num);
+
+ return ret;
+}
+
+void Lingo::codeLabel(int label) {
+ _labelstack.push_back(label);
+}
+
+void Lingo::processIf(int elselabel, int endlabel) {
+ inst ielse1, iend;
+ int else1 = elselabel;
+
+ WRITE_UINT32(&iend, endlabel);
+
+ while (true) {
+ if (_labelstack.empty()) {
+ warning("Label stack underflow");
+ break;
+ }
+
+ int label = _labelstack.back();
+ _labelstack.pop_back();
+
+ // This is beginning of our if()
+ if (!label)
+ break;
+
+ WRITE_UINT32(&ielse1, else1);
+ (*_currentScript)[label + 2] = ielse1; /* elsepart */
+ (*_currentScript)[label + 3] = iend; /* end, if cond fails */
+
+ else1 = label;
+ }
+}
+
+void Lingo::codeFactory(Common::String &name) {
+ _currentFactory = name;
+}
+
+}
diff --git a/engines/director/lingo/lingo-funcs.cpp b/engines/director/lingo/lingo-funcs.cpp
new file mode 100644
index 0000000000..08b3d455ca
--- /dev/null
+++ b/engines/director/lingo/lingo-funcs.cpp
@@ -0,0 +1,225 @@
+/* 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 2
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+// Heavily inspired by hoc
+// Copyright (C) AT&T 1995
+// All Rights Reserved
+//
+// Permission to use, copy, modify, and distribute this software and
+// its documentation for any purpose and without fee is hereby
+// granted, provided that the above copyright notice appear in all
+// copies and that both that the copyright notice and this
+// permission notice and warranty disclaimer appear in supporting
+// documentation, and that the name of AT&T or any of its entities
+// not be used in advertising or publicity pertaining to
+// distribution of the software without specific, written prior
+// permission.
+//
+// AT&T DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
+// INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS.
+// IN NO EVENT SHALL AT&T OR ANY OF ITS ENTITIES BE LIABLE FOR ANY
+// SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
+// IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
+// ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
+// THIS SOFTWARE.
+
+#include "engines/director/lingo/lingo.h"
+#include "common/file.h"
+#include "audio/decoders/wave.h"
+#include "common/util.h"
+#include "director/lingo/lingo-gr.h"
+
+namespace Director {
+
+enum MCITokenType {
+ kMCITokenNone,
+
+ kMCITokenOpen,
+ kMCITokenWait,
+ kMCITokenPlay,
+
+ kMCITokenType,
+ kMCITokenAlias,
+ kMCITokenBuffer,
+ kMCITokenFrom,
+ kMCITokenTo,
+ kMCITokenRepeat
+};
+
+struct MCIToken {
+ MCITokenType command; // Command this flag belongs to
+ MCITokenType flag;
+ const char *token;
+ int pos; // Position of parameter to store. 0 is always filename. Negative parameters mean boolean
+} MCITokens[] = {
+ { kMCITokenNone, kMCITokenOpen, "open", 0 },
+ { kMCITokenOpen, kMCITokenType, "type", 1 },
+ { kMCITokenOpen, kMCITokenAlias, "alias", 2 },
+ { kMCITokenOpen, kMCITokenBuffer, "buffer", 3 },
+
+ { kMCITokenNone, kMCITokenPlay, "play", 0 },
+ { kMCITokenPlay, kMCITokenFrom, "from", 1 },
+ { kMCITokenPlay, kMCITokenTo, "to", 2 },
+ { kMCITokenPlay, kMCITokenRepeat, "repeat", -3 }, // This is boolean parameter
+
+ { kMCITokenNone, kMCITokenWait, "wait", 0 },
+
+ { kMCITokenNone, kMCITokenNone, 0, 0 }
+};
+
+void Lingo::func_mci(Common::String &s) {
+ Common::String params[5];
+ MCITokenType command = kMCITokenNone;
+
+ s.trim();
+ s.toLowercase();
+
+ MCITokenType state = kMCITokenNone;
+ Common::String token;
+ const char *ptr = s.c_str();
+ int respos = -1;
+
+ while (*ptr) {
+ while (*ptr && *ptr == ' ')
+ ptr++;
+
+ token.clear();
+
+ while (*ptr && *ptr != ' ')
+ token += *ptr++;
+
+ switch (state) {
+ case kMCITokenNone:
+ {
+ MCIToken *f = MCITokens;
+
+ while (f->token) {
+ if (command == f->command && token == f->token)
+ break;
+
+ f++;
+ }
+
+ if (command == kMCITokenNone) { // We caught command
+ command = f->flag; // Switching to processing this command parameters
+ } else if (f->flag == kMCITokenNone) { // Unmatched token, storing as filename
+ if (!params[0].empty())
+ warning("Duplicate filename in MCI command: %s -> %s", params[0].c_str(), token.c_str());
+ params[0] = token;
+ } else { // This is normal parameter, storing next token to designated position
+ if (f->pos > 0) { // This is normal parameter
+ state = f->flag;
+ respos = f->pos;
+ } else { // This is boolean
+ params[-f->pos] = "true";
+ state = kMCITokenNone;
+ }
+ }
+ break;
+ }
+ default:
+ params[respos] = token;
+ state = kMCITokenNone;
+ break;
+ }
+ }
+
+ switch (command) {
+ case kMCITokenOpen:
+ {
+ warning("MCI open file: %s, type: %s, alias: %s buffer: %s", params[0].c_str(), params[1].c_str(), params[2].c_str(), params[3].c_str());
+
+ Common::File *file = new Common::File();
+
+ if (!file->open(params[0])) {
+ warning("Failed to open %s", params[0].c_str());
+ delete file;
+ return;
+ }
+
+ if (params[1] == "waveaudio") {
+ Audio::AudioStream *sound = Audio::makeWAVStream(file, DisposeAfterUse::YES);
+ _audioAliases[params[2]] = sound;
+ } else {
+ warning("Unhandled audio type %s", params[2].c_str());
+ }
+ }
+ break;
+ case kMCITokenPlay:
+ {
+ warning("MCI play file: %s, from: %s, to: %s, repeat: %s", params[0].c_str(), params[1].c_str(), params[2].c_str(), params[3].c_str());
+
+ if (!_audioAliases.contains(params[0])) {
+ warning("Unknown alias %s", params[0].c_str());
+ return;
+ }
+
+ uint32 from = strtol(params[1].c_str(), 0, 10);
+ uint32 to = strtol(params[2].c_str(), 0, 10);
+
+ _vm->getSoundManager()->playMCI(*_audioAliases[params[0]], from, to);
+ }
+ break;
+ default:
+ warning("Unhandled MCI command: %s", s.c_str());
+ }
+}
+
+void Lingo::func_mciwait(Common::String &s) {
+ warning("STUB: MCI wait file: %s", s.c_str());
+}
+
+void Lingo::func_goto(Common::String &frame, Common::String &movie) {
+ if (!_vm->_movies || !_vm->_movies->contains(movie)) {
+ warning("Movie %s does not exist", movie.c_str());
+ return;
+ }
+
+ _vm->_currentScore = _vm->_movies->getVal(movie);
+ _vm->_currentScore->loadArchive();
+
+ if (frame.empty())
+ return;
+
+ for (uint16 i = 0; i < frame.size(); i++) {
+ if (!Common::isDigit(frame[i])) {
+ _vm->_currentScore->setStartToLabel(frame);
+ return;
+ }
+ }
+ _vm->_currentScore->setCurrentFrame(strtol(frame.c_str(), 0, 10));
+}
+
+void Lingo::func_gotoloop() {
+ _vm->_currentScore->gotoloop();
+}
+
+void Lingo::func_gotonext() {
+ _vm->_currentScore->gotonext();
+}
+
+void Lingo::func_gotoprevious() {
+ _vm->_currentScore->gotoprevious();
+}
+
+}
diff --git a/engines/director/lingo/lingo-gr.cpp b/engines/director/lingo/lingo-gr.cpp
new file mode 100644
index 0000000000..3791239782
--- /dev/null
+++ b/engines/director/lingo/lingo-gr.cpp
@@ -0,0 +1,2681 @@
+/* A Bison parser, made by GNU Bison 2.3. */
+
+/* Skeleton implementation for Bison's Yacc-like parsers in C
+
+ Copyright (C) 1984, 1989, 1990, 2000, 2001, 2002, 2003, 2004, 2005, 2006
+ Free Software Foundation, Inc.
+
+ 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 2, 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, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA. */
+
+/* As a special exception, you may create a larger work that contains
+ part or all of the Bison parser skeleton and distribute that work
+ under terms of your choice, so long as that work isn't itself a
+ parser generator using the skeleton or a modified version thereof
+ as a parser skeleton. Alternatively, if you modify or redistribute
+ the parser skeleton itself, you may (at your option) remove this
+ special exception, which will cause the skeleton and the resulting
+ Bison output files to be licensed under the GNU General Public
+ License without this special exception.
+
+ This special exception was added by the Free Software Foundation in
+ version 2.2 of Bison. */
+
+/* C LALR(1) parser skeleton written by Richard Stallman, by
+ simplifying the original so-called "semantic" parser. */
+
+/* All symbols defined below should begin with yy or YY, to avoid
+ infringing on user name space. This should be done even for local
+ variables, as they might otherwise be expanded by user macros.
+ There are some unavoidable exceptions within include files to
+ define necessary library symbols; they are noted "INFRINGES ON
+ USER NAME SPACE" below. */
+
+/* Identify Bison output. */
+#define YYBISON 1
+
+/* Bison version. */
+#define YYBISON_VERSION "2.3"
+
+/* Skeleton name. */
+#define YYSKELETON_NAME "yacc.c"
+
+/* Pure parsers. */
+#define YYPURE 0
+
+/* Using locations. */
+#define YYLSP_NEEDED 0
+
+
+
+/* Tokens. */
+#ifndef YYTOKENTYPE
+# define YYTOKENTYPE
+ /* Put the tokens into the symbol table, so that GDB and other debuggers
+ know about them. */
+ enum yytokentype {
+ CASTREF = 258,
+ UNARY = 259,
+ VOID = 260,
+ VAR = 261,
+ POINT = 262,
+ RECT = 263,
+ ARRAY = 264,
+ SYMBOL = 265,
+ INT = 266,
+ THEENTITY = 267,
+ THEENTITYWITHID = 268,
+ FLOAT = 269,
+ BLTIN = 270,
+ BLTINNOARGS = 271,
+ ID = 272,
+ STRING = 273,
+ HANDLER = 274,
+ tDOWN = 275,
+ tELSE = 276,
+ tNLELSIF = 277,
+ tEND = 278,
+ tEXIT = 279,
+ tFRAME = 280,
+ tGLOBAL = 281,
+ tGO = 282,
+ tIF = 283,
+ tINTO = 284,
+ tLOOP = 285,
+ tMACRO = 286,
+ tMCI = 287,
+ tMCIWAIT = 288,
+ tMOVIE = 289,
+ tNEXT = 290,
+ tOF = 291,
+ tPREVIOUS = 292,
+ tPUT = 293,
+ tREPEAT = 294,
+ tSET = 295,
+ tTHEN = 296,
+ tTO = 297,
+ tWHEN = 298,
+ tWITH = 299,
+ tWHILE = 300,
+ tNLELSE = 301,
+ tFACTORY = 302,
+ tMETHOD = 303,
+ tGE = 304,
+ tLE = 305,
+ tGT = 306,
+ tLT = 307,
+ tEQ = 308,
+ tNEQ = 309,
+ tAND = 310,
+ tOR = 311,
+ tNOT = 312,
+ tCONCAT = 313,
+ tCONTAINS = 314,
+ tSTARTS = 315,
+ tSPRITE = 316,
+ tINTERSECTS = 317,
+ tWITHIN = 318
+ };
+#endif
+/* Tokens. */
+#define CASTREF 258
+#define UNARY 259
+#define VOID 260
+#define VAR 261
+#define POINT 262
+#define RECT 263
+#define ARRAY 264
+#define SYMBOL 265
+#define INT 266
+#define THEENTITY 267
+#define THEENTITYWITHID 268
+#define FLOAT 269
+#define BLTIN 270
+#define BLTINNOARGS 271
+#define ID 272
+#define STRING 273
+#define HANDLER 274
+#define tDOWN 275
+#define tELSE 276
+#define tNLELSIF 277
+#define tEND 278
+#define tEXIT 279
+#define tFRAME 280
+#define tGLOBAL 281
+#define tGO 282
+#define tIF 283
+#define tINTO 284
+#define tLOOP 285
+#define tMACRO 286
+#define tMCI 287
+#define tMCIWAIT 288
+#define tMOVIE 289
+#define tNEXT 290
+#define tOF 291
+#define tPREVIOUS 292
+#define tPUT 293
+#define tREPEAT 294
+#define tSET 295
+#define tTHEN 296
+#define tTO 297
+#define tWHEN 298
+#define tWITH 299
+#define tWHILE 300
+#define tNLELSE 301
+#define tFACTORY 302
+#define tMETHOD 303
+#define tGE 304
+#define tLE 305
+#define tGT 306
+#define tLT 307
+#define tEQ 308
+#define tNEQ 309
+#define tAND 310
+#define tOR 311
+#define tNOT 312
+#define tCONCAT 313
+#define tCONTAINS 314
+#define tSTARTS 315
+#define tSPRITE 316
+#define tINTERSECTS 317
+#define tWITHIN 318
+
+
+
+
+/* Copy the first part of user declarations. */
+#line 49 "engines/director/lingo/lingo-gr.y"
+
+#define FORBIDDEN_SYMBOL_ALLOW_ALL
+
+#include "common/hash-str.h"
+
+#include "director/lingo/lingo.h"
+#include "director/lingo/lingo-gr.h"
+
+extern int yylex();
+extern int yyparse();
+
+using namespace Director;
+void yyerror(char *s) {
+ g_lingo->_hadError = true;
+ warning("%s at line %d col %d", s, g_lingo->_linenumber, g_lingo->_colnumber);
+}
+
+
+
+
+/* Enabling traces. */
+#ifndef YYDEBUG
+# define YYDEBUG 1
+#endif
+
+/* Enabling verbose error messages. */
+#ifdef YYERROR_VERBOSE
+# undef YYERROR_VERBOSE
+# define YYERROR_VERBOSE 1
+#else
+# define YYERROR_VERBOSE 0
+#endif
+
+/* Enabling the token table. */
+#ifndef YYTOKEN_TABLE
+# define YYTOKEN_TABLE 0
+#endif
+
+#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED
+typedef union YYSTYPE
+#line 69 "engines/director/lingo/lingo-gr.y"
+{
+ Common::String *s;
+ int i;
+ double f;
+ int e[2]; // Entity + field
+ int code;
+ int narg; /* number of arguments */
+ Common::Array<double> *arr;
+}
+/* Line 193 of yacc.c. */
+#line 252 "engines/director/lingo/lingo-gr.cpp"
+ YYSTYPE;
+# define yystype YYSTYPE /* obsolescent; will be withdrawn */
+# define YYSTYPE_IS_DECLARED 1
+# define YYSTYPE_IS_TRIVIAL 1
+#endif
+
+
+
+/* Copy the second part of user declarations. */
+
+
+/* Line 216 of yacc.c. */
+#line 265 "engines/director/lingo/lingo-gr.cpp"
+
+#ifdef short
+# undef short
+#endif
+
+#ifdef YYTYPE_UINT8
+typedef YYTYPE_UINT8 yytype_uint8;
+#else
+typedef unsigned char yytype_uint8;
+#endif
+
+#ifdef YYTYPE_INT8
+typedef YYTYPE_INT8 yytype_int8;
+#elif (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+typedef signed char yytype_int8;
+#else
+typedef short int yytype_int8;
+#endif
+
+#ifdef YYTYPE_UINT16
+typedef YYTYPE_UINT16 yytype_uint16;
+#else
+typedef unsigned short int yytype_uint16;
+#endif
+
+#ifdef YYTYPE_INT16
+typedef YYTYPE_INT16 yytype_int16;
+#else
+typedef short int yytype_int16;
+#endif
+
+#ifndef YYSIZE_T
+# ifdef __SIZE_TYPE__
+# define YYSIZE_T __SIZE_TYPE__
+# elif defined size_t
+# define YYSIZE_T size_t
+# elif ! defined YYSIZE_T && (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+# include <stddef.h> /* INFRINGES ON USER NAME SPACE */
+# define YYSIZE_T size_t
+# else
+# define YYSIZE_T unsigned int
+# endif
+#endif
+
+#define YYSIZE_MAXIMUM ((YYSIZE_T) -1)
+
+#ifndef YY_
+# if defined YYENABLE_NLS && YYENABLE_NLS
+# if ENABLE_NLS
+# include <libintl.h> /* INFRINGES ON USER NAME SPACE */
+# define YY_(msgid) dgettext ("bison-runtime", msgid)
+# endif
+# endif
+# ifndef YY_
+# define YY_(msgid) msgid
+# endif
+#endif
+
+/* Suppress unused-variable warnings by "using" E. */
+#if ! defined lint || defined __GNUC__
+# define YYUSE(e) ((void) (e))
+#else
+# define YYUSE(e) /* empty */
+#endif
+
+/* Identity function, used to suppress warnings about constant conditions. */
+#ifndef lint
+# define YYID(n) (n)
+#else
+#if (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+static int
+YYID (int i)
+#else
+static int
+YYID (i)
+ int i;
+#endif
+{
+ return i;
+}
+#endif
+
+#if ! defined yyoverflow || YYERROR_VERBOSE
+
+/* The parser invokes alloca or malloc; define the necessary symbols. */
+
+# ifdef YYSTACK_USE_ALLOCA
+# if YYSTACK_USE_ALLOCA
+# ifdef __GNUC__
+# define YYSTACK_ALLOC __builtin_alloca
+# elif defined __BUILTIN_VA_ARG_INCR
+# include <alloca.h> /* INFRINGES ON USER NAME SPACE */
+# elif defined _AIX
+# define YYSTACK_ALLOC __alloca
+# elif defined _MSC_VER
+# include <malloc.h> /* INFRINGES ON USER NAME SPACE */
+# define alloca _alloca
+# else
+# define YYSTACK_ALLOC alloca
+# if ! defined _ALLOCA_H && ! defined _STDLIB_H && (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+# include <stdlib.h> /* INFRINGES ON USER NAME SPACE */
+# ifndef _STDLIB_H
+# define _STDLIB_H 1
+# endif
+# endif
+# endif
+# endif
+# endif
+
+# ifdef YYSTACK_ALLOC
+ /* Pacify GCC's `empty if-body' warning. */
+# define YYSTACK_FREE(Ptr) do { /* empty */; } while (YYID (0))
+# ifndef YYSTACK_ALLOC_MAXIMUM
+ /* The OS might guarantee only one guard page at the bottom of the stack,
+ and a page size can be as small as 4096 bytes. So we cannot safely
+ invoke alloca (N) if N exceeds 4096. Use a slightly smaller number
+ to allow for a few compiler-allocated temporary stack slots. */
+# define YYSTACK_ALLOC_MAXIMUM 4032 /* reasonable circa 2006 */
+# endif
+# else
+# define YYSTACK_ALLOC YYMALLOC
+# define YYSTACK_FREE YYFREE
+# ifndef YYSTACK_ALLOC_MAXIMUM
+# define YYSTACK_ALLOC_MAXIMUM YYSIZE_MAXIMUM
+# endif
+# if (defined __cplusplus && ! defined _STDLIB_H \
+ && ! ((defined YYMALLOC || defined malloc) \
+ && (defined YYFREE || defined free)))
+# include <stdlib.h> /* INFRINGES ON USER NAME SPACE */
+# ifndef _STDLIB_H
+# define _STDLIB_H 1
+# endif
+# endif
+# ifndef YYMALLOC
+# define YYMALLOC malloc
+# if ! defined malloc && ! defined _STDLIB_H && (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+void *malloc (YYSIZE_T); /* INFRINGES ON USER NAME SPACE */
+# endif
+# endif
+# ifndef YYFREE
+# define YYFREE free
+# if ! defined free && ! defined _STDLIB_H && (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+void free (void *); /* INFRINGES ON USER NAME SPACE */
+# endif
+# endif
+# endif
+#endif /* ! defined yyoverflow || YYERROR_VERBOSE */
+
+
+#if (! defined yyoverflow \
+ && (! defined __cplusplus \
+ || (defined YYSTYPE_IS_TRIVIAL && YYSTYPE_IS_TRIVIAL)))
+
+/* A type that is properly aligned for any stack member. */
+union yyalloc
+{
+ yytype_int16 yyss;
+ YYSTYPE yyvs;
+ };
+
+/* The size of the maximum gap between one aligned stack and the next. */
+# define YYSTACK_GAP_MAXIMUM (sizeof (union yyalloc) - 1)
+
+/* The size of an array large to enough to hold all stacks, each with
+ N elements. */
+# define YYSTACK_BYTES(N) \
+ ((N) * (sizeof (yytype_int16) + sizeof (YYSTYPE)) \
+ + YYSTACK_GAP_MAXIMUM)
+
+/* Copy COUNT objects from FROM to TO. The source and destination do
+ not overlap. */
+# ifndef YYCOPY
+# if defined __GNUC__ && 1 < __GNUC__
+# define YYCOPY(To, From, Count) \
+ __builtin_memcpy (To, From, (Count) * sizeof (*(From)))
+# else
+# define YYCOPY(To, From, Count) \
+ do \
+ { \
+ YYSIZE_T yyi; \
+ for (yyi = 0; yyi < (Count); yyi++) \
+ (To)[yyi] = (From)[yyi]; \
+ } \
+ while (YYID (0))
+# endif
+# endif
+
+/* Relocate STACK from its old location to the new one. The
+ local variables YYSIZE and YYSTACKSIZE give the old and new number of
+ elements in the stack, and YYPTR gives the new location of the
+ stack. Advance YYPTR to a properly aligned location for the next
+ stack. */
+# define YYSTACK_RELOCATE(Stack) \
+ do \
+ { \
+ YYSIZE_T yynewbytes; \
+ YYCOPY (&yyptr->Stack, Stack, yysize); \
+ Stack = &yyptr->Stack; \
+ yynewbytes = yystacksize * sizeof (*Stack) + YYSTACK_GAP_MAXIMUM; \
+ yyptr += yynewbytes / sizeof (*yyptr); \
+ } \
+ while (YYID (0))
+
+#endif
+
+/* YYFINAL -- State number of the termination state. */
+#define YYFINAL 77
+/* YYLAST -- Last index in YYTABLE. */
+#define YYLAST 712
+
+/* YYNTOKENS -- Number of terminals. */
+#define YYNTOKENS 77
+/* YYNNTS -- Number of nonterminals. */
+#define YYNNTS 34
+/* YYNRULES -- Number of rules. */
+#define YYNRULES 118
+/* YYNRULES -- Number of states. */
+#define YYNSTATES 254
+
+/* YYTRANSLATE(YYLEX) -- Bison symbol number corresponding to YYLEX. */
+#define YYUNDEFTOK 2
+#define YYMAXUTOK 318
+
+#define YYTRANSLATE(YYX) \
+ ((unsigned int) (YYX) <= YYMAXUTOK ? yytranslate[YYX] : YYUNDEFTOK)
+
+/* YYTRANSLATE[YYLEX] -- Bison symbol number corresponding to YYLEX. */
+static const yytype_uint8 yytranslate[] =
+{
+ 0, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 70, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 69, 75, 2,
+ 71, 72, 67, 65, 76, 66, 2, 68, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 74, 64, 73, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 1, 2, 3, 4,
+ 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
+ 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
+ 25, 26, 27, 28, 29, 30, 31, 32, 33, 34,
+ 35, 36, 37, 38, 39, 40, 41, 42, 43, 44,
+ 45, 46, 47, 48, 49, 50, 51, 52, 53, 54,
+ 55, 56, 57, 58, 59, 60, 61, 62, 63
+};
+
+#if YYDEBUG
+/* YYPRHS[YYN] -- Index of the first RHS symbol of rule number YYN in
+ YYRHS. */
+static const yytype_uint16 yyprhs[] =
+{
+ 0, 0, 3, 7, 9, 12, 14, 15, 17, 19,
+ 21, 23, 25, 30, 35, 40, 46, 51, 56, 62,
+ 64, 66, 68, 70, 79, 91, 104, 109, 118, 130,
+ 142, 149, 160, 171, 172, 176, 179, 181, 184, 186,
+ 193, 195, 201, 203, 207, 211, 214, 218, 220, 222,
+ 223, 224, 225, 228, 231, 233, 235, 237, 239, 244,
+ 246, 248, 251, 253, 257, 261, 265, 269, 273, 277,
+ 281, 285, 289, 293, 297, 300, 304, 308, 312, 316,
+ 319, 322, 326, 331, 336, 339, 342, 345, 347, 349,
+ 352, 354, 358, 361, 364, 367, 370, 374, 377, 381,
+ 384, 387, 389, 393, 396, 400, 401, 410, 413, 414,
+ 423, 424, 426, 430, 435, 436, 440, 441, 443
+};
+
+/* YYRHS -- A `-1'-separated list of the rules' RHS. */
+static const yytype_int8 yyrhs[] =
+{
+ 78, 0, -1, 78, 79, 80, -1, 80, -1, 1,
+ 70, -1, 70, -1, -1, 104, -1, 99, -1, 109,
+ -1, 81, -1, 83, -1, 38, 98, 29, 17, -1,
+ 40, 17, 64, 98, -1, 40, 12, 64, 98, -1,
+ 40, 13, 98, 64, 98, -1, 40, 17, 42, 98,
+ -1, 40, 12, 42, 98, -1, 40, 13, 98, 42,
+ 98, -1, 98, -1, 99, -1, 82, -1, 84, -1,
+ 91, 71, 90, 72, 97, 96, 23, 39, -1, 92,
+ 64, 98, 96, 42, 98, 96, 97, 96, 23, 39,
+ -1, 92, 64, 98, 96, 20, 42, 98, 96, 97,
+ 96, 23, 39, -1, 43, 17, 41, 98, -1, 93,
+ 90, 41, 79, 97, 96, 23, 28, -1, 93, 90,
+ 41, 79, 97, 96, 46, 97, 96, 23, 28, -1,
+ 93, 90, 41, 79, 97, 96, 95, 86, 96, 23,
+ 28, -1, 93, 90, 41, 95, 82, 96, -1, 93,
+ 90, 41, 95, 82, 96, 46, 95, 82, 96, -1,
+ 93, 90, 41, 95, 82, 96, 87, 96, 85, 96,
+ -1, -1, 46, 95, 82, -1, 86, 89, -1, 89,
+ -1, 87, 88, -1, 88, -1, 94, 90, 41, 95,
+ 83, 96, -1, 87, -1, 94, 90, 41, 97, 96,
+ -1, 98, -1, 98, 64, 98, -1, 71, 90, 72,
+ -1, 39, 45, -1, 39, 44, 17, -1, 28, -1,
+ 22, -1, -1, -1, -1, 97, 79, -1, 97, 83,
+ -1, 11, -1, 14, -1, 18, -1, 16, -1, 17,
+ 71, 110, 72, -1, 17, -1, 12, -1, 13, 98,
+ -1, 81, -1, 98, 65, 98, -1, 98, 66, 98,
+ -1, 98, 67, 98, -1, 98, 68, 98, -1, 98,
+ 73, 98, -1, 98, 74, 98, -1, 98, 54, 98,
+ -1, 98, 49, 98, -1, 98, 50, 98, -1, 98,
+ 55, 98, -1, 98, 56, 98, -1, 57, 98, -1,
+ 98, 75, 98, -1, 98, 58, 98, -1, 98, 59,
+ 98, -1, 98, 60, 98, -1, 65, 98, -1, 66,
+ 98, -1, 71, 98, 72, -1, 61, 98, 62, 98,
+ -1, 61, 98, 63, 98, -1, 32, 18, -1, 33,
+ 17, -1, 38, 98, -1, 101, -1, 24, -1, 26,
+ 100, -1, 17, -1, 100, 76, 17, -1, 27, 30,
+ -1, 27, 35, -1, 27, 37, -1, 27, 102, -1,
+ 27, 102, 103, -1, 27, 103, -1, 42, 25, 18,
+ -1, 25, 18, -1, 42, 18, -1, 18, -1, 36,
+ 34, 18, -1, 34, 18, -1, 42, 34, 18, -1,
+ -1, 31, 17, 105, 95, 107, 79, 108, 97, -1,
+ 47, 17, -1, -1, 48, 17, 106, 95, 107, 79,
+ 108, 97, -1, -1, 17, -1, 107, 76, 17, -1,
+ 107, 79, 76, 17, -1, -1, 17, 95, 110, -1,
+ -1, 98, -1, 110, 76, 98, -1
+};
+
+/* YYRLINE[YYN] -- source line where rule number YYN was defined. */
+static const yytype_uint16 yyrline[] =
+{
+ 0, 102, 102, 103, 104, 107, 112, 113, 114, 115,
+ 116, 117, 120, 126, 132, 140, 148, 154, 162, 171,
+ 172, 174, 175, 180, 191, 207, 219, 224, 231, 240,
+ 249, 259, 269, 280, 281, 284, 285, 288, 289, 292,
+ 300, 301, 309, 310, 311, 313, 315, 321, 327, 334,
+ 336, 338, 339, 340, 343, 348, 351, 354, 358, 361,
+ 365, 372, 378, 379, 380, 381, 382, 383, 384, 385,
+ 386, 387, 388, 389, 390, 391, 392, 393, 394, 395,
+ 396, 397, 398, 399, 402, 403, 404, 405, 406, 408,
+ 411, 412, 423, 424, 425, 426, 431, 437, 444, 445,
+ 446, 447, 450, 451, 452, 480, 480, 486, 489, 489,
+ 495, 496, 497, 498, 500, 504, 512, 513, 514
+};
+#endif
+
+#if YYDEBUG || YYERROR_VERBOSE || YYTOKEN_TABLE
+/* YYTNAME[SYMBOL-NUM] -- String name of the symbol SYMBOL-NUM.
+ First, the terminals, then, starting at YYNTOKENS, nonterminals. */
+static const char *const yytname[] =
+{
+ "$end", "error", "$undefined", "CASTREF", "UNARY", "VOID", "VAR",
+ "POINT", "RECT", "ARRAY", "SYMBOL", "INT", "THEENTITY",
+ "THEENTITYWITHID", "FLOAT", "BLTIN", "BLTINNOARGS", "ID", "STRING",
+ "HANDLER", "tDOWN", "tELSE", "tNLELSIF", "tEND", "tEXIT", "tFRAME",
+ "tGLOBAL", "tGO", "tIF", "tINTO", "tLOOP", "tMACRO", "tMCI", "tMCIWAIT",
+ "tMOVIE", "tNEXT", "tOF", "tPREVIOUS", "tPUT", "tREPEAT", "tSET",
+ "tTHEN", "tTO", "tWHEN", "tWITH", "tWHILE", "tNLELSE", "tFACTORY",
+ "tMETHOD", "tGE", "tLE", "tGT", "tLT", "tEQ", "tNEQ", "tAND", "tOR",
+ "tNOT", "tCONCAT", "tCONTAINS", "tSTARTS", "tSPRITE", "tINTERSECTS",
+ "tWITHIN", "'='", "'+'", "'-'", "'*'", "'/'", "'%'", "'\\n'", "'('",
+ "')'", "'>'", "'<'", "'&'", "','", "$accept", "program", "nl",
+ "programline", "asgn", "stmtoneliner", "stmt", "ifstmt",
+ "elsestmtoneliner", "elseifstmt", "elseifstmtoneliner",
+ "elseifstmtoneliner1", "elseifstmt1", "cond", "repeatwhile",
+ "repeatwith", "if", "elseif", "begin", "end", "stmtlist", "expr", "func",
+ "globallist", "gotofunc", "gotoframe", "gotomovie", "defn", "@1", "@2",
+ "argdef", "argstore", "macro", "arglist", 0
+};
+#endif
+
+# ifdef YYPRINT
+/* YYTOKNUM[YYLEX-NUM] -- Internal token number corresponding to
+ token YYLEX-NUM. */
+static const yytype_uint16 yytoknum[] =
+{
+ 0, 256, 257, 258, 259, 260, 261, 262, 263, 264,
+ 265, 266, 267, 268, 269, 270, 271, 272, 273, 274,
+ 275, 276, 277, 278, 279, 280, 281, 282, 283, 284,
+ 285, 286, 287, 288, 289, 290, 291, 292, 293, 294,
+ 295, 296, 297, 298, 299, 300, 301, 302, 303, 304,
+ 305, 306, 307, 308, 309, 310, 311, 312, 313, 314,
+ 315, 316, 317, 318, 61, 43, 45, 42, 47, 37,
+ 10, 40, 41, 62, 60, 38, 44
+};
+# endif
+
+/* YYR1[YYN] -- Symbol number of symbol that rule YYN derives. */
+static const yytype_uint8 yyr1[] =
+{
+ 0, 77, 78, 78, 78, 79, 80, 80, 80, 80,
+ 80, 80, 81, 81, 81, 81, 81, 81, 81, 82,
+ 82, 83, 83, 83, 83, 83, 83, 84, 84, 84,
+ 84, 84, 84, 85, 85, 86, 86, 87, 87, 88,
+ 89, 89, 90, 90, 90, 91, 92, 93, 94, 95,
+ 96, 97, 97, 97, 98, 98, 98, 98, 98, 98,
+ 98, 98, 98, 98, 98, 98, 98, 98, 98, 98,
+ 98, 98, 98, 98, 98, 98, 98, 98, 98, 98,
+ 98, 98, 98, 98, 99, 99, 99, 99, 99, 99,
+ 100, 100, 101, 101, 101, 101, 101, 101, 102, 102,
+ 102, 102, 103, 103, 103, 105, 104, 104, 106, 104,
+ 107, 107, 107, 107, 108, 109, 110, 110, 110
+};
+
+/* YYR2[YYN] -- Number of symbols composing right hand side of rule YYN. */
+static const yytype_uint8 yyr2[] =
+{
+ 0, 2, 3, 1, 2, 1, 0, 1, 1, 1,
+ 1, 1, 4, 4, 4, 5, 4, 4, 5, 1,
+ 1, 1, 1, 8, 11, 12, 4, 8, 11, 11,
+ 6, 10, 10, 0, 3, 2, 1, 2, 1, 6,
+ 1, 5, 1, 3, 3, 2, 3, 1, 1, 0,
+ 0, 0, 2, 2, 1, 1, 1, 1, 4, 1,
+ 1, 2, 1, 3, 3, 3, 3, 3, 3, 3,
+ 3, 3, 3, 3, 2, 3, 3, 3, 3, 2,
+ 2, 3, 4, 4, 2, 2, 2, 1, 1, 2,
+ 1, 3, 2, 2, 2, 2, 3, 2, 3, 2,
+ 2, 1, 3, 2, 3, 0, 8, 2, 0, 8,
+ 0, 1, 3, 4, 0, 3, 0, 1, 3
+};
+
+/* YYDEFACT[STATE-NAME] -- Default rule to reduce with in state
+ STATE-NUM when YYTABLE doesn't specify something else to do. Zero
+ means the default is an error. */
+static const yytype_uint8 yydefact[] =
+{
+ 0, 0, 54, 60, 0, 55, 57, 49, 56, 88,
+ 0, 0, 47, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 3, 62,
+ 21, 11, 22, 0, 0, 0, 19, 8, 87, 7,
+ 9, 4, 59, 0, 62, 61, 116, 116, 90, 89,
+ 101, 0, 92, 0, 93, 0, 94, 0, 95, 97,
+ 105, 84, 85, 86, 0, 45, 0, 0, 0, 0,
+ 107, 108, 74, 0, 79, 80, 0, 1, 5, 6,
+ 0, 0, 0, 0, 42, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 117, 0, 115, 0, 99, 103, 0, 100, 0,
+ 0, 0, 96, 49, 0, 46, 0, 0, 0, 0,
+ 0, 0, 49, 0, 0, 81, 2, 0, 50, 0,
+ 0, 49, 0, 70, 71, 69, 72, 73, 76, 77,
+ 78, 63, 64, 65, 66, 67, 68, 75, 58, 0,
+ 91, 102, 98, 104, 110, 12, 17, 14, 0, 0,
+ 16, 13, 26, 110, 82, 83, 51, 0, 44, 51,
+ 0, 43, 118, 111, 0, 18, 15, 0, 50, 0,
+ 0, 50, 50, 20, 0, 114, 114, 52, 53, 0,
+ 0, 50, 49, 30, 112, 0, 51, 51, 0, 50,
+ 51, 0, 51, 0, 48, 49, 50, 38, 0, 113,
+ 106, 109, 23, 51, 50, 27, 50, 50, 40, 36,
+ 0, 0, 37, 33, 0, 50, 0, 0, 35, 0,
+ 0, 50, 49, 50, 49, 0, 0, 0, 0, 49,
+ 31, 0, 32, 0, 0, 24, 28, 29, 50, 34,
+ 50, 25, 41, 39
+};
+
+/* YYDEFGOTO[NTERM-NUM]. */
+static const yytype_int16 yydefgoto[] =
+{
+ -1, 27, 187, 28, 44, 30, 188, 32, 233, 217,
+ 218, 207, 219, 83, 33, 34, 35, 208, 243, 167,
+ 178, 36, 183, 49, 38, 58, 59, 39, 113, 122,
+ 174, 196, 40, 102
+};
+
+/* YYPACT[STATE-NUM] -- Index in YYTABLE of the portion describing
+ STATE-NUM. */
+#define YYPACT_NINF -189
+static const yytype_int16 yypact[] =
+{
+ 201, -49, -189, -189, 420, -189, -189, 610, -189, -189,
+ 6, 17, -189, 15, 18, 48, 420, 29, 59, 50,
+ 52, 58, 420, 420, 420, 420, 420, 10, -189, 16,
+ -189, -189, -189, 7, 39, 432, 588, -189, -189, -189,
+ -189, -189, 35, 420, -189, 588, 420, 420, -189, 36,
+ -189, 99, -189, 100, -189, 87, -189, 30, 24, -189,
+ -189, -189, -189, 446, 115, -189, -27, 420, -20, 92,
+ -189, -189, 55, 490, 55, 55, 539, -189, -189, 262,
+ 432, 420, 432, 93, 566, 420, 420, 420, 420, 420,
+ 420, 420, 420, 420, 420, 420, 420, 420, 420, 420,
+ 446, 588, -52, 60, 121, -189, -189, 124, -189, 126,
+ 127, 105, -189, -189, 129, -189, 420, 420, 468, 420,
+ 420, 420, -189, 420, 420, -189, -189, 75, 588, 76,
+ 512, 79, 420, 588, 588, 588, 588, 588, 588, 588,
+ 588, 637, 637, 55, 55, 588, 588, 588, -189, 420,
+ -189, -189, -189, -189, 136, -189, 588, 588, 420, 420,
+ 588, 588, 588, 136, 588, 588, -189, 21, -189, -189,
+ 390, 588, 588, -189, -8, 588, 588, -8, 318, 113,
+ 420, 318, -189, -189, 139, 81, 81, -189, -189, 137,
+ 420, 588, -12, -13, -189, 142, -189, -189, 125, 588,
+ -189, 135, -189, 144, -189, -189, 144, -189, 432, -189,
+ 318, 318, -189, -189, 318, -189, 318, 144, 144, -189,
+ 432, 390, -189, 122, 128, 318, 147, 148, -189, 149,
+ 133, -189, -189, -189, -189, 154, 140, 150, 152, -9,
+ -189, 390, -189, 354, 143, -189, -189, -189, 318, -189,
+ -189, -189, -189, -189
+};
+
+/* YYPGOTO[NTERM-NUM]. */
+static const yytype_int16 yypgoto[] =
+{
+ -189, -189, -23, 88, 2, -164, 0, -189, -189, -189,
+ -5, -188, -36, -77, -189, -189, -189, -186, -6, -41,
+ -157, 3, 8, -189, -189, -189, 131, -189, -189, -189,
+ 22, 1, -189, 151
+};
+
+/* YYTABLE[YYPACT[STATE-NUM]]. What to do in state STATE-NUM. If
+ positive, shift that token. If negative, reduce the rule which
+ number is the opposite. If zero, do what YYDEFACT says.
+ If YYTABLE_NINF, syntax error. */
+#define YYTABLE_NINF -60
+static const yytype_int16 yytable[] =
+{
+ 31, 47, 29, 127, 79, 129, 182, 45, 37, 204,
+ 77, 201, 181, -51, -51, 116, -10, 220, 222, 63,
+ 148, 41, 119, 48, 149, 72, 73, 74, 75, 76,
+ 222, 220, 60, 205, 202, 50, 61, 117, 84, 210,
+ 211, 179, 51, 214, 120, 216, 100, 52, 108, 101,
+ 101, 53, 54, 55, 56, 109, 225, 231, 53, 57,
+ 55, -51, 78, 180, 110, 62, 111, 69, 184, 70,
+ 118, 66, 67, 64, 65, 71, 68, 249, 80, 31,
+ 78, 29, 248, 84, 128, 130, -10, 37, 133, 134,
+ 135, 136, 137, 138, 139, 140, 141, 142, 143, 144,
+ 145, 146, 147, 81, 85, 86, 46, 154, 169, 87,
+ 88, 89, 104, 90, 91, 92, 163, 105, 106, 156,
+ 157, 107, 160, 161, 162, 170, 164, 165, 97, 98,
+ 99, 224, 115, 121, 131, 171, 149, 189, 150, 110,
+ 192, 193, 151, 230, 152, 153, 155, 166, 168, 78,
+ 200, 185, 172, 173, 186, 190, 194, 195, 213, 209,
+ 198, 175, 176, 215, 212, 223, 204, 126, 232, 234,
+ 236, 237, 238, 226, 239, 227, 229, 244, 246, 245,
+ 247, 228, 251, 191, 235, 177, 203, 197, 206, 112,
+ 240, 0, 242, 199, 0, 0, 0, 0, 103, 221,
+ 0, -6, 1, 0, 0, 0, 0, 252, 0, 253,
+ 0, 84, 2, 3, 4, 5, 0, 6, 7, 8,
+ 0, 0, 0, 84, 0, 9, 241, 10, 11, 12,
+ 0, 0, 13, 14, 15, 0, 0, 0, 0, 16,
+ 17, 18, 0, 250, 19, 0, 0, 0, 20, 21,
+ 0, 0, 0, 0, 0, 0, 0, 0, 22, 0,
+ 0, 0, 23, 0, 0, 0, 24, 25, 0, 0,
+ 0, -6, 26, 2, 3, 4, 5, 0, 6, 7,
+ 8, 0, 0, 0, 0, 0, 9, 0, 10, 11,
+ 12, 0, 0, 13, 14, 15, 0, 0, 0, 0,
+ 16, 17, 18, 0, 0, 19, 0, 0, 0, 20,
+ 21, 0, 0, 0, 0, 0, 0, 0, 0, 22,
+ 0, 0, 0, 23, 0, 0, 0, 24, 25, 2,
+ 3, 4, 5, 26, 6, 42, 8, 0, 0, 0,
+ 0, 0, 9, 0, 10, 11, 12, 0, 0, 0,
+ 14, 15, 0, 0, 0, 0, 16, 17, 18, 0,
+ 0, 19, 0, 0, 0, 2, 3, 4, 5, 0,
+ 6, 42, 8, 0, 0, 22, 0, 0, 9, 23,
+ 10, 11, 12, 24, 25, 0, 14, 15, 78, 26,
+ 0, 0, 16, 17, 18, 0, 0, 19, 0, 0,
+ 0, 2, 3, 4, 5, 0, 6, 42, 8, 0,
+ 0, 22, 0, 0, 9, 23, 10, 11, 0, 24,
+ 25, 0, 14, 15, 0, 26, 0, 0, 16, 0,
+ 18, 2, 3, 4, 5, 0, 6, 42, 8, 0,
+ 0, 0, 0, 2, 3, 4, 5, 22, 6, 42,
+ 8, 23, 0, 0, 0, 24, 25, 0, 43, 0,
+ 18, 26, 0, 0, 0, 0, 0, 0, 0, 0,
+ 43, 0, 18, 0, 0, 114, 0, 22, 0, 0,
+ 0, 23, 0, 0, 0, 24, 25, 0, 0, 22,
+ 0, 26, 0, 23, 0, 85, 86, 24, 25, 0,
+ 87, 88, 89, 82, 90, 91, 92, 0, 0, 0,
+ 158, 93, 94, 95, 96, 0, 0, 85, 86, 97,
+ 98, 99, 87, 88, 89, 0, 90, 91, 92, 0,
+ 0, 0, 159, 93, 94, 95, 96, 0, 0, 85,
+ 86, 97, 98, 99, 87, 88, 89, 0, 90, 91,
+ 92, 0, 123, 124, 0, 93, 94, 95, 96, 0,
+ 0, 85, 86, 97, 98, 99, 87, 88, 89, 0,
+ 90, 91, 92, 0, 0, 0, 132, 93, 94, 95,
+ 96, 0, 0, 0, 125, 97, 98, 99, 85, 86,
+ 0, 0, 0, 87, 88, 89, 0, 90, 91, 92,
+ 0, 0, 0, 0, 93, 94, 95, 96, 0, 0,
+ 0, 125, 97, 98, 99, 85, 86, 0, 0, 0,
+ 87, 88, 89, 0, 90, 91, 92, 0, 0, 0,
+ 132, 93, 94, 95, 96, 0, 0, 85, 86, 97,
+ 98, 99, 87, 88, 89, 0, 90, 91, 92, 0,
+ 0, 0, 0, 93, 94, 95, 96, 0, 0, -59,
+ -59, 97, 98, 99, -59, -59, -59, 0, -59, -59,
+ -59, 0, 0, 0, 0, 0, 0, -59, -59, 0,
+ 0, 46, 0, -59, -59, -59, 85, 86, 0, 0,
+ 0, 87, 88, 89, 0, 90, 91, 92, 0, 0,
+ 0, 0, 0, 0, 95, 96, 0, 0, 0, 0,
+ 97, 98, 99
+};
+
+static const yytype_int16 yycheck[] =
+{
+ 0, 7, 0, 80, 27, 82, 170, 4, 0, 22,
+ 0, 23, 169, 22, 23, 42, 0, 203, 206, 16,
+ 72, 70, 42, 17, 76, 22, 23, 24, 25, 26,
+ 218, 217, 17, 46, 46, 18, 18, 64, 35, 196,
+ 197, 20, 25, 200, 64, 202, 43, 30, 18, 46,
+ 47, 34, 35, 36, 37, 25, 213, 221, 34, 42,
+ 36, 70, 70, 42, 34, 17, 42, 17, 76, 17,
+ 67, 12, 13, 44, 45, 17, 17, 241, 71, 79,
+ 70, 79, 239, 80, 81, 82, 70, 79, 85, 86,
+ 87, 88, 89, 90, 91, 92, 93, 94, 95, 96,
+ 97, 98, 99, 64, 49, 50, 71, 113, 131, 54,
+ 55, 56, 76, 58, 59, 60, 122, 18, 18, 116,
+ 117, 34, 119, 120, 121, 131, 123, 124, 73, 74,
+ 75, 208, 17, 41, 41, 132, 76, 178, 17, 34,
+ 181, 182, 18, 220, 18, 18, 17, 72, 72, 70,
+ 191, 174, 149, 17, 177, 42, 17, 76, 199, 17,
+ 23, 158, 159, 28, 39, 206, 22, 79, 46, 41,
+ 23, 23, 23, 214, 41, 216, 217, 23, 28, 39,
+ 28, 217, 39, 180, 225, 163, 192, 186, 193, 58,
+ 231, -1, 233, 190, -1, -1, -1, -1, 47, 205,
+ -1, 0, 1, -1, -1, -1, -1, 248, -1, 250,
+ -1, 208, 11, 12, 13, 14, -1, 16, 17, 18,
+ -1, -1, -1, 220, -1, 24, 232, 26, 27, 28,
+ -1, -1, 31, 32, 33, -1, -1, -1, -1, 38,
+ 39, 40, -1, 243, 43, -1, -1, -1, 47, 48,
+ -1, -1, -1, -1, -1, -1, -1, -1, 57, -1,
+ -1, -1, 61, -1, -1, -1, 65, 66, -1, -1,
+ -1, 70, 71, 11, 12, 13, 14, -1, 16, 17,
+ 18, -1, -1, -1, -1, -1, 24, -1, 26, 27,
+ 28, -1, -1, 31, 32, 33, -1, -1, -1, -1,
+ 38, 39, 40, -1, -1, 43, -1, -1, -1, 47,
+ 48, -1, -1, -1, -1, -1, -1, -1, -1, 57,
+ -1, -1, -1, 61, -1, -1, -1, 65, 66, 11,
+ 12, 13, 14, 71, 16, 17, 18, -1, -1, -1,
+ -1, -1, 24, -1, 26, 27, 28, -1, -1, -1,
+ 32, 33, -1, -1, -1, -1, 38, 39, 40, -1,
+ -1, 43, -1, -1, -1, 11, 12, 13, 14, -1,
+ 16, 17, 18, -1, -1, 57, -1, -1, 24, 61,
+ 26, 27, 28, 65, 66, -1, 32, 33, 70, 71,
+ -1, -1, 38, 39, 40, -1, -1, 43, -1, -1,
+ -1, 11, 12, 13, 14, -1, 16, 17, 18, -1,
+ -1, 57, -1, -1, 24, 61, 26, 27, -1, 65,
+ 66, -1, 32, 33, -1, 71, -1, -1, 38, -1,
+ 40, 11, 12, 13, 14, -1, 16, 17, 18, -1,
+ -1, -1, -1, 11, 12, 13, 14, 57, 16, 17,
+ 18, 61, -1, -1, -1, 65, 66, -1, 38, -1,
+ 40, 71, -1, -1, -1, -1, -1, -1, -1, -1,
+ 38, -1, 40, -1, -1, 29, -1, 57, -1, -1,
+ -1, 61, -1, -1, -1, 65, 66, -1, -1, 57,
+ -1, 71, -1, 61, -1, 49, 50, 65, 66, -1,
+ 54, 55, 56, 71, 58, 59, 60, -1, -1, -1,
+ 42, 65, 66, 67, 68, -1, -1, 49, 50, 73,
+ 74, 75, 54, 55, 56, -1, 58, 59, 60, -1,
+ -1, -1, 64, 65, 66, 67, 68, -1, -1, 49,
+ 50, 73, 74, 75, 54, 55, 56, -1, 58, 59,
+ 60, -1, 62, 63, -1, 65, 66, 67, 68, -1,
+ -1, 49, 50, 73, 74, 75, 54, 55, 56, -1,
+ 58, 59, 60, -1, -1, -1, 64, 65, 66, 67,
+ 68, -1, -1, -1, 72, 73, 74, 75, 49, 50,
+ -1, -1, -1, 54, 55, 56, -1, 58, 59, 60,
+ -1, -1, -1, -1, 65, 66, 67, 68, -1, -1,
+ -1, 72, 73, 74, 75, 49, 50, -1, -1, -1,
+ 54, 55, 56, -1, 58, 59, 60, -1, -1, -1,
+ 64, 65, 66, 67, 68, -1, -1, 49, 50, 73,
+ 74, 75, 54, 55, 56, -1, 58, 59, 60, -1,
+ -1, -1, -1, 65, 66, 67, 68, -1, -1, 49,
+ 50, 73, 74, 75, 54, 55, 56, -1, 58, 59,
+ 60, -1, -1, -1, -1, -1, -1, 67, 68, -1,
+ -1, 71, -1, 73, 74, 75, 49, 50, -1, -1,
+ -1, 54, 55, 56, -1, 58, 59, 60, -1, -1,
+ -1, -1, -1, -1, 67, 68, -1, -1, -1, -1,
+ 73, 74, 75
+};
+
+/* YYSTOS[STATE-NUM] -- The (internal number of the) accessing
+ symbol of state STATE-NUM. */
+static const yytype_uint8 yystos[] =
+{
+ 0, 1, 11, 12, 13, 14, 16, 17, 18, 24,
+ 26, 27, 28, 31, 32, 33, 38, 39, 40, 43,
+ 47, 48, 57, 61, 65, 66, 71, 78, 80, 81,
+ 82, 83, 84, 91, 92, 93, 98, 99, 101, 104,
+ 109, 70, 17, 38, 81, 98, 71, 95, 17, 100,
+ 18, 25, 30, 34, 35, 36, 37, 42, 102, 103,
+ 17, 18, 17, 98, 44, 45, 12, 13, 17, 17,
+ 17, 17, 98, 98, 98, 98, 98, 0, 70, 79,
+ 71, 64, 71, 90, 98, 49, 50, 54, 55, 56,
+ 58, 59, 60, 65, 66, 67, 68, 73, 74, 75,
+ 98, 98, 110, 110, 76, 18, 18, 34, 18, 25,
+ 34, 42, 103, 105, 29, 17, 42, 64, 98, 42,
+ 64, 41, 106, 62, 63, 72, 80, 90, 98, 90,
+ 98, 41, 64, 98, 98, 98, 98, 98, 98, 98,
+ 98, 98, 98, 98, 98, 98, 98, 98, 72, 76,
+ 17, 18, 18, 18, 95, 17, 98, 98, 42, 64,
+ 98, 98, 98, 95, 98, 98, 72, 96, 72, 79,
+ 95, 98, 98, 17, 107, 98, 98, 107, 97, 20,
+ 42, 97, 82, 99, 76, 79, 79, 79, 83, 96,
+ 42, 98, 96, 96, 17, 76, 108, 108, 23, 98,
+ 96, 23, 46, 95, 22, 46, 87, 88, 94, 17,
+ 97, 97, 39, 96, 97, 28, 97, 86, 87, 89,
+ 94, 95, 88, 96, 90, 97, 96, 96, 89, 96,
+ 90, 82, 46, 85, 41, 96, 23, 23, 23, 41,
+ 96, 95, 96, 95, 23, 39, 28, 28, 97, 82,
+ 83, 39, 96, 96
+};
+
+#define yyerrok (yyerrstatus = 0)
+#define yyclearin (yychar = YYEMPTY)
+#define YYEMPTY (-2)
+#define YYEOF 0
+
+#define YYACCEPT goto yyacceptlab
+#define YYABORT goto yyabortlab
+#define YYERROR goto yyerrorlab
+
+
+/* Like YYERROR except do call yyerror. This remains here temporarily
+ to ease the transition to the new meaning of YYERROR, for GCC.
+ Once GCC version 2 has supplanted version 1, this can go. */
+
+#define YYFAIL goto yyerrlab
+
+#define YYRECOVERING() (!!yyerrstatus)
+
+#define YYBACKUP(Token, Value) \
+do \
+ if (yychar == YYEMPTY && yylen == 1) \
+ { \
+ yychar = (Token); \
+ yylval = (Value); \
+ yytoken = YYTRANSLATE (yychar); \
+ YYPOPSTACK (1); \
+ goto yybackup; \
+ } \
+ else \
+ { \
+ yyerror (YY_("syntax error: cannot back up")); \
+ YYERROR; \
+ } \
+while (YYID (0))
+
+
+#define YYTERROR 1
+#define YYERRCODE 256
+
+
+/* YYLLOC_DEFAULT -- Set CURRENT to span from RHS[1] to RHS[N].
+ If N is 0, then set CURRENT to the empty location which ends
+ the previous symbol: RHS[0] (always defined). */
+
+#define YYRHSLOC(Rhs, K) ((Rhs)[K])
+#ifndef YYLLOC_DEFAULT
+# define YYLLOC_DEFAULT(Current, Rhs, N) \
+ do \
+ if (YYID (N)) \
+ { \
+ (Current).first_line = YYRHSLOC (Rhs, 1).first_line; \
+ (Current).first_column = YYRHSLOC (Rhs, 1).first_column; \
+ (Current).last_line = YYRHSLOC (Rhs, N).last_line; \
+ (Current).last_column = YYRHSLOC (Rhs, N).last_column; \
+ } \
+ else \
+ { \
+ (Current).first_line = (Current).last_line = \
+ YYRHSLOC (Rhs, 0).last_line; \
+ (Current).first_column = (Current).last_column = \
+ YYRHSLOC (Rhs, 0).last_column; \
+ } \
+ while (YYID (0))
+#endif
+
+
+/* YY_LOCATION_PRINT -- Print the location on the stream.
+ This macro was not mandated originally: define only if we know
+ we won't break user code: when these are the locations we know. */
+
+#ifndef YY_LOCATION_PRINT
+# if defined YYLTYPE_IS_TRIVIAL && YYLTYPE_IS_TRIVIAL
+# define YY_LOCATION_PRINT(File, Loc) \
+ fprintf (File, "%d.%d-%d.%d", \
+ (Loc).first_line, (Loc).first_column, \
+ (Loc).last_line, (Loc).last_column)
+# else
+# define YY_LOCATION_PRINT(File, Loc) ((void) 0)
+# endif
+#endif
+
+
+/* YYLEX -- calling `yylex' with the right arguments. */
+
+#ifdef YYLEX_PARAM
+# define YYLEX yylex (YYLEX_PARAM)
+#else
+# define YYLEX yylex ()
+#endif
+
+/* Enable debugging if requested. */
+#if YYDEBUG
+
+# ifndef YYFPRINTF
+# include <stdio.h> /* INFRINGES ON USER NAME SPACE */
+# define YYFPRINTF fprintf
+# endif
+
+# define YYDPRINTF(Args) \
+do { \
+ if (yydebug) \
+ YYFPRINTF Args; \
+} while (YYID (0))
+
+# define YY_SYMBOL_PRINT(Title, Type, Value, Location) \
+do { \
+ if (yydebug) \
+ { \
+ YYFPRINTF (stderr, "%s ", Title); \
+ yy_symbol_print (stderr, \
+ Type, Value); \
+ YYFPRINTF (stderr, "\n"); \
+ } \
+} while (YYID (0))
+
+
+/*--------------------------------.
+| Print this symbol on YYOUTPUT. |
+`--------------------------------*/
+
+/*ARGSUSED*/
+#if (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+static void
+yy_symbol_value_print (FILE *yyoutput, int yytype, YYSTYPE const * const yyvaluep)
+#else
+static void
+yy_symbol_value_print (yyoutput, yytype, yyvaluep)
+ FILE *yyoutput;
+ int yytype;
+ YYSTYPE const * const yyvaluep;
+#endif
+{
+ if (!yyvaluep)
+ return;
+# ifdef YYPRINT
+ if (yytype < YYNTOKENS)
+ YYPRINT (yyoutput, yytoknum[yytype], *yyvaluep);
+# else
+ YYUSE (yyoutput);
+# endif
+ switch (yytype)
+ {
+ default:
+ break;
+ }
+}
+
+
+/*--------------------------------.
+| Print this symbol on YYOUTPUT. |
+`--------------------------------*/
+
+#if (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+static void
+yy_symbol_print (FILE *yyoutput, int yytype, YYSTYPE const * const yyvaluep)
+#else
+static void
+yy_symbol_print (yyoutput, yytype, yyvaluep)
+ FILE *yyoutput;
+ int yytype;
+ YYSTYPE const * const yyvaluep;
+#endif
+{
+ if (yytype < YYNTOKENS)
+ YYFPRINTF (yyoutput, "token %s (", yytname[yytype]);
+ else
+ YYFPRINTF (yyoutput, "nterm %s (", yytname[yytype]);
+
+ yy_symbol_value_print (yyoutput, yytype, yyvaluep);
+ YYFPRINTF (yyoutput, ")");
+}
+
+/*------------------------------------------------------------------.
+| yy_stack_print -- Print the state stack from its BOTTOM up to its |
+| TOP (included). |
+`------------------------------------------------------------------*/
+
+#if (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+static void
+yy_stack_print (yytype_int16 *bottom, yytype_int16 *top)
+#else
+static void
+yy_stack_print (bottom, top)
+ yytype_int16 *bottom;
+ yytype_int16 *top;
+#endif
+{
+ YYFPRINTF (stderr, "Stack now");
+ for (; bottom <= top; ++bottom)
+ YYFPRINTF (stderr, " %d", *bottom);
+ YYFPRINTF (stderr, "\n");
+}
+
+# define YY_STACK_PRINT(Bottom, Top) \
+do { \
+ if (yydebug) \
+ yy_stack_print ((Bottom), (Top)); \
+} while (YYID (0))
+
+
+/*------------------------------------------------.
+| Report that the YYRULE is going to be reduced. |
+`------------------------------------------------*/
+
+#if (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+static void
+yy_reduce_print (YYSTYPE *yyvsp, int yyrule)
+#else
+static void
+yy_reduce_print (yyvsp, yyrule)
+ YYSTYPE *yyvsp;
+ int yyrule;
+#endif
+{
+ int yynrhs = yyr2[yyrule];
+ int yyi;
+ unsigned long int yylno = yyrline[yyrule];
+ YYFPRINTF (stderr, "Reducing stack by rule %d (line %lu):\n",
+ yyrule - 1, yylno);
+ /* The symbols being reduced. */
+ for (yyi = 0; yyi < yynrhs; yyi++)
+ {
+ fprintf (stderr, " $%d = ", yyi + 1);
+ yy_symbol_print (stderr, yyrhs[yyprhs[yyrule] + yyi],
+ &(yyvsp[(yyi + 1) - (yynrhs)])
+ );
+ fprintf (stderr, "\n");
+ }
+}
+
+# define YY_REDUCE_PRINT(Rule) \
+do { \
+ if (yydebug) \
+ yy_reduce_print (yyvsp, Rule); \
+} while (YYID (0))
+
+/* Nonzero means print parse trace. It is left uninitialized so that
+ multiple parsers can coexist. */
+int yydebug;
+#else /* !YYDEBUG */
+# define YYDPRINTF(Args)
+# define YY_SYMBOL_PRINT(Title, Type, Value, Location)
+# define YY_STACK_PRINT(Bottom, Top)
+# define YY_REDUCE_PRINT(Rule)
+#endif /* !YYDEBUG */
+
+
+/* YYINITDEPTH -- initial size of the parser's stacks. */
+#ifndef YYINITDEPTH
+# define YYINITDEPTH 200
+#endif
+
+/* YYMAXDEPTH -- maximum size the stacks can grow to (effective only
+ if the built-in stack extension method is used).
+
+ Do not make this value too large; the results are undefined if
+ YYSTACK_ALLOC_MAXIMUM < YYSTACK_BYTES (YYMAXDEPTH)
+ evaluated with infinite-precision integer arithmetic. */
+
+#ifndef YYMAXDEPTH
+# define YYMAXDEPTH 10000
+#endif
+
+
+
+#if YYERROR_VERBOSE
+
+# ifndef yystrlen
+# if defined __GLIBC__ && defined _STRING_H
+# define yystrlen strlen
+# else
+/* Return the length of YYSTR. */
+#if (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+static YYSIZE_T
+yystrlen (const char *yystr)
+#else
+static YYSIZE_T
+yystrlen (yystr)
+ const char *yystr;
+#endif
+{
+ YYSIZE_T yylen;
+ for (yylen = 0; yystr[yylen]; yylen++)
+ continue;
+ return yylen;
+}
+# endif
+# endif
+
+# ifndef yystpcpy
+# if defined __GLIBC__ && defined _STRING_H && defined _GNU_SOURCE
+# define yystpcpy stpcpy
+# else
+/* Copy YYSRC to YYDEST, returning the address of the terminating '\0' in
+ YYDEST. */
+#if (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+static char *
+yystpcpy (char *yydest, const char *yysrc)
+#else
+static char *
+yystpcpy (yydest, yysrc)
+ char *yydest;
+ const char *yysrc;
+#endif
+{
+ char *yyd = yydest;
+ const char *yys = yysrc;
+
+ while ((*yyd++ = *yys++) != '\0')
+ continue;
+
+ return yyd - 1;
+}
+# endif
+# endif
+
+# ifndef yytnamerr
+/* Copy to YYRES the contents of YYSTR after stripping away unnecessary
+ quotes and backslashes, so that it's suitable for yyerror. The
+ heuristic is that double-quoting is unnecessary unless the string
+ contains an apostrophe, a comma, or backslash (other than
+ backslash-backslash). YYSTR is taken from yytname. If YYRES is
+ null, do not copy; instead, return the length of what the result
+ would have been. */
+static YYSIZE_T
+yytnamerr (char *yyres, const char *yystr)
+{
+ if (*yystr == '"')
+ {
+ YYSIZE_T yyn = 0;
+ char const *yyp = yystr;
+
+ for (;;)
+ switch (*++yyp)
+ {
+ case '\'':
+ case ',':
+ goto do_not_strip_quotes;
+
+ case '\\':
+ if (*++yyp != '\\')
+ goto do_not_strip_quotes;
+ /* Fall through. */
+ default:
+ if (yyres)
+ yyres[yyn] = *yyp;
+ yyn++;
+ break;
+
+ case '"':
+ if (yyres)
+ yyres[yyn] = '\0';
+ return yyn;
+ }
+ do_not_strip_quotes: ;
+ }
+
+ if (! yyres)
+ return yystrlen (yystr);
+
+ return yystpcpy (yyres, yystr) - yyres;
+}
+# endif
+
+/* Copy into YYRESULT an error message about the unexpected token
+ YYCHAR while in state YYSTATE. Return the number of bytes copied,
+ including the terminating null byte. If YYRESULT is null, do not
+ copy anything; just return the number of bytes that would be
+ copied. As a special case, return 0 if an ordinary "syntax error"
+ message will do. Return YYSIZE_MAXIMUM if overflow occurs during
+ size calculation. */
+static YYSIZE_T
+yysyntax_error (char *yyresult, int yystate, int yychar)
+{
+ int yyn = yypact[yystate];
+
+ if (! (YYPACT_NINF < yyn && yyn <= YYLAST))
+ return 0;
+ else
+ {
+ int yytype = YYTRANSLATE (yychar);
+ YYSIZE_T yysize0 = yytnamerr (0, yytname[yytype]);
+ YYSIZE_T yysize = yysize0;
+ YYSIZE_T yysize1;
+ int yysize_overflow = 0;
+ enum { YYERROR_VERBOSE_ARGS_MAXIMUM = 5 };
+ char const *yyarg[YYERROR_VERBOSE_ARGS_MAXIMUM];
+ int yyx;
+
+# if 0
+ /* This is so xgettext sees the translatable formats that are
+ constructed on the fly. */
+ YY_("syntax error, unexpected %s");
+ YY_("syntax error, unexpected %s, expecting %s");
+ YY_("syntax error, unexpected %s, expecting %s or %s");
+ YY_("syntax error, unexpected %s, expecting %s or %s or %s");
+ YY_("syntax error, unexpected %s, expecting %s or %s or %s or %s");
+# endif
+ char *yyfmt;
+ char const *yyf;
+ static char const yyunexpected[] = "syntax error, unexpected %s";
+ static char const yyexpecting[] = ", expecting %s";
+ static char const yyor[] = " or %s";
+ char yyformat[sizeof yyunexpected
+ + sizeof yyexpecting - 1
+ + ((YYERROR_VERBOSE_ARGS_MAXIMUM - 2)
+ * (sizeof yyor - 1))];
+ char const *yyprefix = yyexpecting;
+
+ /* Start YYX at -YYN if negative to avoid negative indexes in
+ YYCHECK. */
+ int yyxbegin = yyn < 0 ? -yyn : 0;
+
+ /* Stay within bounds of both yycheck and yytname. */
+ int yychecklim = YYLAST - yyn + 1;
+ int yyxend = yychecklim < YYNTOKENS ? yychecklim : YYNTOKENS;
+ int yycount = 1;
+
+ yyarg[0] = yytname[yytype];
+ yyfmt = yystpcpy (yyformat, yyunexpected);
+
+ for (yyx = yyxbegin; yyx < yyxend; ++yyx)
+ if (yycheck[yyx + yyn] == yyx && yyx != YYTERROR)
+ {
+ if (yycount == YYERROR_VERBOSE_ARGS_MAXIMUM)
+ {
+ yycount = 1;
+ yysize = yysize0;
+ yyformat[sizeof yyunexpected - 1] = '\0';
+ break;
+ }
+ yyarg[yycount++] = yytname[yyx];
+ yysize1 = yysize + yytnamerr (0, yytname[yyx]);
+ yysize_overflow |= (yysize1 < yysize);
+ yysize = yysize1;
+ yyfmt = yystpcpy (yyfmt, yyprefix);
+ yyprefix = yyor;
+ }
+
+ yyf = YY_(yyformat);
+ yysize1 = yysize + yystrlen (yyf);
+ yysize_overflow |= (yysize1 < yysize);
+ yysize = yysize1;
+
+ if (yysize_overflow)
+ return YYSIZE_MAXIMUM;
+
+ if (yyresult)
+ {
+ /* Avoid sprintf, as that infringes on the user's name space.
+ Don't have undefined behavior even if the translation
+ produced a string with the wrong number of "%s"s. */
+ char *yyp = yyresult;
+ int yyi = 0;
+ while ((*yyp = *yyf) != '\0')
+ {
+ if (*yyp == '%' && yyf[1] == 's' && yyi < yycount)
+ {
+ yyp += yytnamerr (yyp, yyarg[yyi++]);
+ yyf += 2;
+ }
+ else
+ {
+ yyp++;
+ yyf++;
+ }
+ }
+ }
+ return yysize;
+ }
+}
+#endif /* YYERROR_VERBOSE */
+
+
+/*-----------------------------------------------.
+| Release the memory associated to this symbol. |
+`-----------------------------------------------*/
+
+/*ARGSUSED*/
+#if (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+static void
+yydestruct (const char *yymsg, int yytype, YYSTYPE *yyvaluep)
+#else
+static void
+yydestruct (yymsg, yytype, yyvaluep)
+ const char *yymsg;
+ int yytype;
+ YYSTYPE *yyvaluep;
+#endif
+{
+ YYUSE (yyvaluep);
+
+ if (!yymsg)
+ yymsg = "Deleting";
+ YY_SYMBOL_PRINT (yymsg, yytype, yyvaluep, yylocationp);
+
+ switch (yytype)
+ {
+
+ default:
+ break;
+ }
+}
+
+
+/* Prevent warnings from -Wmissing-prototypes. */
+
+#ifdef YYPARSE_PARAM
+#if defined __STDC__ || defined __cplusplus
+int yyparse (void *YYPARSE_PARAM);
+#else
+int yyparse ();
+#endif
+#else /* ! YYPARSE_PARAM */
+#if defined __STDC__ || defined __cplusplus
+int yyparse (void);
+#else
+int yyparse ();
+#endif
+#endif /* ! YYPARSE_PARAM */
+
+
+
+/* The look-ahead symbol. */
+int yychar;
+
+/* The semantic value of the look-ahead symbol. */
+YYSTYPE yylval;
+
+/* Number of syntax errors so far. */
+int yynerrs;
+
+
+
+/*----------.
+| yyparse. |
+`----------*/
+
+#ifdef YYPARSE_PARAM
+#if (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+int
+yyparse (void *YYPARSE_PARAM)
+#else
+int
+yyparse (YYPARSE_PARAM)
+ void *YYPARSE_PARAM;
+#endif
+#else /* ! YYPARSE_PARAM */
+#if (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+int
+yyparse (void)
+#else
+int
+yyparse ()
+
+#endif
+#endif
+{
+
+ int yystate;
+ int yyn;
+ int yyresult;
+ /* Number of tokens to shift before error messages enabled. */
+ int yyerrstatus;
+ /* Look-ahead token as an internal (translated) token number. */
+ int yytoken = 0;
+#if YYERROR_VERBOSE
+ /* Buffer for error messages, and its allocated size. */
+ char yymsgbuf[128];
+ char *yymsg = yymsgbuf;
+ YYSIZE_T yymsg_alloc = sizeof yymsgbuf;
+#endif
+
+ /* Three stacks and their tools:
+ `yyss': related to states,
+ `yyvs': related to semantic values,
+ `yyls': related to locations.
+
+ Refer to the stacks thru separate pointers, to allow yyoverflow
+ to reallocate them elsewhere. */
+
+ /* The state stack. */
+ yytype_int16 yyssa[YYINITDEPTH];
+ yytype_int16 *yyss = yyssa;
+ yytype_int16 *yyssp;
+
+ /* The semantic value stack. */
+ YYSTYPE yyvsa[YYINITDEPTH];
+ YYSTYPE *yyvs = yyvsa;
+ YYSTYPE *yyvsp;
+
+
+
+#define YYPOPSTACK(N) (yyvsp -= (N), yyssp -= (N))
+
+ YYSIZE_T yystacksize = YYINITDEPTH;
+
+ /* The variables used to return semantic value and location from the
+ action routines. */
+ YYSTYPE yyval;
+
+
+ /* The number of symbols on the RHS of the reduced rule.
+ Keep to zero when no symbol should be popped. */
+ int yylen = 0;
+
+ YYDPRINTF ((stderr, "Starting parse\n"));
+
+ yystate = 0;
+ yyerrstatus = 0;
+ yynerrs = 0;
+ yychar = YYEMPTY; /* Cause a token to be read. */
+
+ /* Initialize stack pointers.
+ Waste one element of value and location stack
+ so that they stay on the same level as the state stack.
+ The wasted elements are never initialized. */
+
+ yyssp = yyss;
+ yyvsp = yyvs;
+
+ goto yysetstate;
+
+/*------------------------------------------------------------.
+| yynewstate -- Push a new state, which is found in yystate. |
+`------------------------------------------------------------*/
+ yynewstate:
+ /* In all cases, when you get here, the value and location stacks
+ have just been pushed. So pushing a state here evens the stacks. */
+ yyssp++;
+
+ yysetstate:
+ *yyssp = yystate;
+
+ if (yyss + yystacksize - 1 <= yyssp)
+ {
+ /* Get the current used size of the three stacks, in elements. */
+ YYSIZE_T yysize = yyssp - yyss + 1;
+
+#ifdef yyoverflow
+ {
+ /* Give user a chance to reallocate the stack. Use copies of
+ these so that the &'s don't force the real ones into
+ memory. */
+ YYSTYPE *yyvs1 = yyvs;
+ yytype_int16 *yyss1 = yyss;
+
+
+ /* Each stack pointer address is followed by the size of the
+ data in use in that stack, in bytes. This used to be a
+ conditional around just the two extra args, but that might
+ be undefined if yyoverflow is a macro. */
+ yyoverflow (YY_("memory exhausted"),
+ &yyss1, yysize * sizeof (*yyssp),
+ &yyvs1, yysize * sizeof (*yyvsp),
+
+ &yystacksize);
+
+ yyss = yyss1;
+ yyvs = yyvs1;
+ }
+#else /* no yyoverflow */
+# ifndef YYSTACK_RELOCATE
+ goto yyexhaustedlab;
+# else
+ /* Extend the stack our own way. */
+ if (YYMAXDEPTH <= yystacksize)
+ goto yyexhaustedlab;
+ yystacksize *= 2;
+ if (YYMAXDEPTH < yystacksize)
+ yystacksize = YYMAXDEPTH;
+
+ {
+ yytype_int16 *yyss1 = yyss;
+ union yyalloc *yyptr =
+ (union yyalloc *) YYSTACK_ALLOC (YYSTACK_BYTES (yystacksize));
+ if (! yyptr)
+ goto yyexhaustedlab;
+ YYSTACK_RELOCATE (yyss);
+ YYSTACK_RELOCATE (yyvs);
+
+# undef YYSTACK_RELOCATE
+ if (yyss1 != yyssa)
+ YYSTACK_FREE (yyss1);
+ }
+# endif
+#endif /* no yyoverflow */
+
+ yyssp = yyss + yysize - 1;
+ yyvsp = yyvs + yysize - 1;
+
+
+ YYDPRINTF ((stderr, "Stack size increased to %lu\n",
+ (unsigned long int) yystacksize));
+
+ if (yyss + yystacksize - 1 <= yyssp)
+ YYABORT;
+ }
+
+ YYDPRINTF ((stderr, "Entering state %d\n", yystate));
+
+ goto yybackup;
+
+/*-----------.
+| yybackup. |
+`-----------*/
+yybackup:
+
+ /* Do appropriate processing given the current state. Read a
+ look-ahead token if we need one and don't already have one. */
+
+ /* First try to decide what to do without reference to look-ahead token. */
+ yyn = yypact[yystate];
+ if (yyn == YYPACT_NINF)
+ goto yydefault;
+
+ /* Not known => get a look-ahead token if don't already have one. */
+
+ /* YYCHAR is either YYEMPTY or YYEOF or a valid look-ahead symbol. */
+ if (yychar == YYEMPTY)
+ {
+ YYDPRINTF ((stderr, "Reading a token: "));
+ yychar = YYLEX;
+ }
+
+ if (yychar <= YYEOF)
+ {
+ yychar = yytoken = YYEOF;
+ YYDPRINTF ((stderr, "Now at end of input.\n"));
+ }
+ else
+ {
+ yytoken = YYTRANSLATE (yychar);
+ YY_SYMBOL_PRINT ("Next token is", yytoken, &yylval, &yylloc);
+ }
+
+ /* If the proper action on seeing token YYTOKEN is to reduce or to
+ detect an error, take that action. */
+ yyn += yytoken;
+ if (yyn < 0 || YYLAST < yyn || yycheck[yyn] != yytoken)
+ goto yydefault;
+ yyn = yytable[yyn];
+ if (yyn <= 0)
+ {
+ if (yyn == 0 || yyn == YYTABLE_NINF)
+ goto yyerrlab;
+ yyn = -yyn;
+ goto yyreduce;
+ }
+
+ if (yyn == YYFINAL)
+ YYACCEPT;
+
+ /* Count tokens shifted since error; after three, turn off error
+ status. */
+ if (yyerrstatus)
+ yyerrstatus--;
+
+ /* Shift the look-ahead token. */
+ YY_SYMBOL_PRINT ("Shifting", yytoken, &yylval, &yylloc);
+
+ /* Discard the shifted token unless it is eof. */
+ if (yychar != YYEOF)
+ yychar = YYEMPTY;
+
+ yystate = yyn;
+ *++yyvsp = yylval;
+
+ goto yynewstate;
+
+
+/*-----------------------------------------------------------.
+| yydefault -- do the default action for the current state. |
+`-----------------------------------------------------------*/
+yydefault:
+ yyn = yydefact[yystate];
+ if (yyn == 0)
+ goto yyerrlab;
+ goto yyreduce;
+
+
+/*-----------------------------.
+| yyreduce -- Do a reduction. |
+`-----------------------------*/
+yyreduce:
+ /* yyn is the number of a rule to reduce with. */
+ yylen = yyr2[yyn];
+
+ /* If YYLEN is nonzero, implement the default value of the action:
+ `$$ = $1'.
+
+ Otherwise, the following line sets YYVAL to garbage.
+ This behavior is undocumented and Bison
+ users should not rely upon it. Assigning to YYVAL
+ unconditionally makes the parser a bit smaller, and it avoids a
+ GCC warning that YYVAL may be used uninitialized. */
+ yyval = yyvsp[1-yylen];
+
+
+ YY_REDUCE_PRINT (yyn);
+ switch (yyn)
+ {
+ case 4:
+#line 104 "engines/director/lingo/lingo-gr.y"
+ { yyerrok; ;}
+ break;
+
+ case 5:
+#line 107 "engines/director/lingo/lingo-gr.y"
+ {
+ g_lingo->_linenumber++;
+ g_lingo->_colnumber = 1;
+ ;}
+ break;
+
+ case 10:
+#line 116 "engines/director/lingo/lingo-gr.y"
+ { g_lingo->code1(g_lingo->c_xpop); ;}
+ break;
+
+ case 12:
+#line 120 "engines/director/lingo/lingo-gr.y"
+ {
+ g_lingo->code1(g_lingo->c_varpush);
+ g_lingo->codeString((yyvsp[(4) - (4)].s)->c_str());
+ g_lingo->code1(g_lingo->c_assign);
+ (yyval.code) = (yyvsp[(2) - (4)].code);
+ delete (yyvsp[(4) - (4)].s); ;}
+ break;
+
+ case 13:
+#line 126 "engines/director/lingo/lingo-gr.y"
+ {
+ g_lingo->code1(g_lingo->c_varpush);
+ g_lingo->codeString((yyvsp[(2) - (4)].s)->c_str());
+ g_lingo->code1(g_lingo->c_assign);
+ (yyval.code) = (yyvsp[(4) - (4)].code);
+ delete (yyvsp[(2) - (4)].s); ;}
+ break;
+
+ case 14:
+#line 132 "engines/director/lingo/lingo-gr.y"
+ {
+ g_lingo->code2(g_lingo->c_constpush, 0); // Put dummy id
+ g_lingo->code1(g_lingo->c_theentityassign);
+ inst e = 0, f = 0;
+ WRITE_UINT32(&e, (yyvsp[(2) - (4)].e)[0]);
+ WRITE_UINT32(&f, (yyvsp[(2) - (4)].e)[1]);
+ g_lingo->code2(e, f);
+ (yyval.code) = (yyvsp[(4) - (4)].code); ;}
+ break;
+
+ case 15:
+#line 140 "engines/director/lingo/lingo-gr.y"
+ {
+ g_lingo->code1(g_lingo->c_swap);
+ g_lingo->code1(g_lingo->c_theentityassign);
+ inst e = 0, f = 0;
+ WRITE_UINT32(&e, (yyvsp[(2) - (5)].e)[0]);
+ WRITE_UINT32(&f, (yyvsp[(2) - (5)].e)[1]);
+ g_lingo->code2(e, f);
+ (yyval.code) = (yyvsp[(5) - (5)].code); ;}
+ break;
+
+ case 16:
+#line 148 "engines/director/lingo/lingo-gr.y"
+ {
+ g_lingo->code1(g_lingo->c_varpush);
+ g_lingo->codeString((yyvsp[(2) - (4)].s)->c_str());
+ g_lingo->code1(g_lingo->c_assign);
+ (yyval.code) = (yyvsp[(4) - (4)].code);
+ delete (yyvsp[(2) - (4)].s); ;}
+ break;
+
+ case 17:
+#line 154 "engines/director/lingo/lingo-gr.y"
+ {
+ g_lingo->code2(g_lingo->c_constpush, 0); // Put dummy id
+ g_lingo->code1(g_lingo->c_theentityassign);
+ inst e = 0, f = 0;
+ WRITE_UINT32(&e, (yyvsp[(2) - (4)].e)[0]);
+ WRITE_UINT32(&f, (yyvsp[(2) - (4)].e)[1]);
+ g_lingo->code2(e, f);
+ (yyval.code) = (yyvsp[(4) - (4)].code); ;}
+ break;
+
+ case 18:
+#line 162 "engines/director/lingo/lingo-gr.y"
+ {
+ g_lingo->code1(g_lingo->c_swap);
+ g_lingo->code1(g_lingo->c_theentityassign);
+ inst e = 0, f = 0;
+ WRITE_UINT32(&e, (yyvsp[(2) - (5)].e)[0]);
+ WRITE_UINT32(&f, (yyvsp[(2) - (5)].e)[1]);
+ g_lingo->code2(e, f);
+ (yyval.code) = (yyvsp[(5) - (5)].code); ;}
+ break;
+
+ case 19:
+#line 171 "engines/director/lingo/lingo-gr.y"
+ { g_lingo->code1(g_lingo->c_xpop); ;}
+ break;
+
+ case 23:
+#line 180 "engines/director/lingo/lingo-gr.y"
+ {
+ inst body = 0, end = 0;
+ WRITE_UINT32(&body, (yyvsp[(5) - (8)].code));
+ WRITE_UINT32(&end, (yyvsp[(6) - (8)].code));
+ (*g_lingo->_currentScript)[(yyvsp[(1) - (8)].code) + 1] = body; /* body of loop */
+ (*g_lingo->_currentScript)[(yyvsp[(1) - (8)].code) + 2] = end; ;}
+ break;
+
+ case 24:
+#line 191 "engines/director/lingo/lingo-gr.y"
+ {
+ inst init = 0, finish = 0, body = 0, end = 0, inc = 0;
+ WRITE_UINT32(&init, (yyvsp[(3) - (11)].code));
+ WRITE_UINT32(&finish, (yyvsp[(6) - (11)].code));
+ WRITE_UINT32(&body, (yyvsp[(8) - (11)].code));
+ WRITE_UINT32(&end, (yyvsp[(9) - (11)].code));
+ WRITE_UINT32(&inc, 1);
+ (*g_lingo->_currentScript)[(yyvsp[(1) - (11)].code) + 1] = init; /* initial count value */
+ (*g_lingo->_currentScript)[(yyvsp[(1) - (11)].code) + 2] = finish;/* final count value */
+ (*g_lingo->_currentScript)[(yyvsp[(1) - (11)].code) + 3] = body; /* body of loop */
+ (*g_lingo->_currentScript)[(yyvsp[(1) - (11)].code) + 4] = inc; /* increment */
+ (*g_lingo->_currentScript)[(yyvsp[(1) - (11)].code) + 5] = end; ;}
+ break;
+
+ case 25:
+#line 207 "engines/director/lingo/lingo-gr.y"
+ {
+ inst init = 0, finish = 0, body = 0, end = 0, inc = 0;
+ WRITE_UINT32(&init, (yyvsp[(3) - (12)].code));
+ WRITE_UINT32(&finish, (yyvsp[(7) - (12)].code));
+ WRITE_UINT32(&body, (yyvsp[(9) - (12)].code));
+ WRITE_UINT32(&end, (yyvsp[(10) - (12)].code));
+ WRITE_UINT32(&inc, -1);
+ (*g_lingo->_currentScript)[(yyvsp[(1) - (12)].code) + 1] = init; /* initial count value */
+ (*g_lingo->_currentScript)[(yyvsp[(1) - (12)].code) + 2] = finish;/* final count value */
+ (*g_lingo->_currentScript)[(yyvsp[(1) - (12)].code) + 3] = body; /* body of loop */
+ (*g_lingo->_currentScript)[(yyvsp[(1) - (12)].code) + 4] = inc; /* increment */
+ (*g_lingo->_currentScript)[(yyvsp[(1) - (12)].code) + 5] = end; ;}
+ break;
+
+ case 26:
+#line 219 "engines/director/lingo/lingo-gr.y"
+ {
+ g_lingo->code1(g_lingo->c_ifcode);
+ ;}
+ break;
+
+ case 27:
+#line 224 "engines/director/lingo/lingo-gr.y"
+ {
+ inst then = 0, end = 0;
+ WRITE_UINT32(&then, (yyvsp[(5) - (8)].code));
+ WRITE_UINT32(&end, (yyvsp[(6) - (8)].code));
+ (*g_lingo->_currentScript)[(yyvsp[(1) - (8)].code) + 1] = then; /* thenpart */
+ (*g_lingo->_currentScript)[(yyvsp[(1) - (8)].code) + 3] = end; /* end, if cond fails */
+ g_lingo->processIf(0, 0); ;}
+ break;
+
+ case 28:
+#line 231 "engines/director/lingo/lingo-gr.y"
+ {
+ inst then = 0, else1 = 0, end = 0;
+ WRITE_UINT32(&then, (yyvsp[(5) - (11)].code));
+ WRITE_UINT32(&else1, (yyvsp[(8) - (11)].code));
+ WRITE_UINT32(&end, (yyvsp[(9) - (11)].code));
+ (*g_lingo->_currentScript)[(yyvsp[(1) - (11)].code) + 1] = then; /* thenpart */
+ (*g_lingo->_currentScript)[(yyvsp[(1) - (11)].code) + 2] = else1; /* elsepart */
+ (*g_lingo->_currentScript)[(yyvsp[(1) - (11)].code) + 3] = end; /* end, if cond fails */
+ g_lingo->processIf(0, 0); ;}
+ break;
+
+ case 29:
+#line 240 "engines/director/lingo/lingo-gr.y"
+ {
+ inst then = 0, else1 = 0, end = 0;
+ WRITE_UINT32(&then, (yyvsp[(5) - (11)].code));
+ WRITE_UINT32(&else1, (yyvsp[(7) - (11)].code));
+ WRITE_UINT32(&end, (yyvsp[(9) - (11)].code));
+ (*g_lingo->_currentScript)[(yyvsp[(1) - (11)].code) + 1] = then; /* thenpart */
+ (*g_lingo->_currentScript)[(yyvsp[(1) - (11)].code) + 2] = else1; /* elsepart */
+ (*g_lingo->_currentScript)[(yyvsp[(1) - (11)].code) + 3] = end; /* end, if cond fails */
+ g_lingo->processIf(0, (yyvsp[(9) - (11)].code)); ;}
+ break;
+
+ case 30:
+#line 249 "engines/director/lingo/lingo-gr.y"
+ {
+ inst then = 0, else1 = 0, end = 0;
+ WRITE_UINT32(&then, (yyvsp[(4) - (6)].code));
+ WRITE_UINT32(&else1, 0);
+ WRITE_UINT32(&end, (yyvsp[(6) - (6)].code));
+ (*g_lingo->_currentScript)[(yyvsp[(1) - (6)].code) + 1] = then; /* thenpart */
+ (*g_lingo->_currentScript)[(yyvsp[(1) - (6)].code) + 2] = else1; /* elsepart */
+ (*g_lingo->_currentScript)[(yyvsp[(1) - (6)].code) + 3] = end; /* end, if cond fails */
+
+ g_lingo->processIf(0, 0); ;}
+ break;
+
+ case 31:
+#line 259 "engines/director/lingo/lingo-gr.y"
+ {
+ inst then = 0, else1 = 0, end = 0;
+ WRITE_UINT32(&then, (yyvsp[(4) - (10)].code));
+ WRITE_UINT32(&else1, (yyvsp[(8) - (10)].code));
+ WRITE_UINT32(&end, (yyvsp[(10) - (10)].code));
+ (*g_lingo->_currentScript)[(yyvsp[(1) - (10)].code) + 1] = then; /* thenpart */
+ (*g_lingo->_currentScript)[(yyvsp[(1) - (10)].code) + 2] = else1; /* elsepart */
+ (*g_lingo->_currentScript)[(yyvsp[(1) - (10)].code) + 3] = end; /* end, if cond fails */
+
+ g_lingo->processIf(0, 0); ;}
+ break;
+
+ case 32:
+#line 269 "engines/director/lingo/lingo-gr.y"
+ {
+ inst then = 0, else1 = 0, end = 0;
+ WRITE_UINT32(&then, (yyvsp[(4) - (10)].code));
+ WRITE_UINT32(&else1, (yyvsp[(6) - (10)].code));
+ WRITE_UINT32(&end, (yyvsp[(10) - (10)].code));
+ (*g_lingo->_currentScript)[(yyvsp[(1) - (10)].code) + 1] = then; /* thenpart */
+ (*g_lingo->_currentScript)[(yyvsp[(1) - (10)].code) + 2] = else1; /* elsepart */
+ (*g_lingo->_currentScript)[(yyvsp[(1) - (10)].code) + 3] = end; /* end, if cond fails */
+
+ g_lingo->processIf(0, (yyvsp[(10) - (10)].code)); ;}
+ break;
+
+ case 33:
+#line 280 "engines/director/lingo/lingo-gr.y"
+ { (yyval.code) = 0; ;}
+ break;
+
+ case 34:
+#line 281 "engines/director/lingo/lingo-gr.y"
+ { (yyval.code) = (yyvsp[(2) - (3)].code); ;}
+ break;
+
+ case 39:
+#line 292 "engines/director/lingo/lingo-gr.y"
+ {
+ inst then = 0;
+ WRITE_UINT32(&then, (yyvsp[(4) - (6)].code));
+ (*g_lingo->_currentScript)[(yyvsp[(1) - (6)].code) + 1] = then; /* thenpart */
+
+ g_lingo->codeLabel((yyvsp[(1) - (6)].code)); ;}
+ break;
+
+ case 41:
+#line 301 "engines/director/lingo/lingo-gr.y"
+ {
+ inst then = 0;
+ WRITE_UINT32(&then, (yyvsp[(4) - (5)].code));
+ (*g_lingo->_currentScript)[(yyvsp[(1) - (5)].code) + 1] = then; /* thenpart */
+
+ g_lingo->codeLabel((yyvsp[(1) - (5)].code)); ;}
+ break;
+
+ case 42:
+#line 309 "engines/director/lingo/lingo-gr.y"
+ { g_lingo->code1(STOP); ;}
+ break;
+
+ case 43:
+#line 310 "engines/director/lingo/lingo-gr.y"
+ { g_lingo->code2(g_lingo->c_eq, STOP); ;}
+ break;
+
+ case 45:
+#line 313 "engines/director/lingo/lingo-gr.y"
+ { (yyval.code) = g_lingo->code3(g_lingo->c_repeatwhilecode, STOP, STOP); ;}
+ break;
+
+ case 46:
+#line 315 "engines/director/lingo/lingo-gr.y"
+ {
+ (yyval.code) = g_lingo->code3(g_lingo->c_repeatwithcode, STOP, STOP);
+ g_lingo->code3(STOP, STOP, STOP);
+ g_lingo->codeString((yyvsp[(3) - (3)].s)->c_str());
+ delete (yyvsp[(3) - (3)].s); ;}
+ break;
+
+ case 47:
+#line 321 "engines/director/lingo/lingo-gr.y"
+ {
+ (yyval.code) = g_lingo->code1(g_lingo->c_ifcode);
+ g_lingo->code3(STOP, STOP, STOP);
+ g_lingo->code1(0); // Do not skip end
+ g_lingo->codeLabel(0); ;}
+ break;
+
+ case 48:
+#line 327 "engines/director/lingo/lingo-gr.y"
+ {
+ inst skipEnd;
+ WRITE_UINT32(&skipEnd, 1); // We have to skip end to avoid multiple executions
+ (yyval.code) = g_lingo->code1(g_lingo->c_ifcode);
+ g_lingo->code3(STOP, STOP, STOP);
+ g_lingo->code1(skipEnd); ;}
+ break;
+
+ case 49:
+#line 334 "engines/director/lingo/lingo-gr.y"
+ { (yyval.code) = g_lingo->_currentScript->size(); ;}
+ break;
+
+ case 50:
+#line 336 "engines/director/lingo/lingo-gr.y"
+ { g_lingo->code1(STOP); (yyval.code) = g_lingo->_currentScript->size(); ;}
+ break;
+
+ case 51:
+#line 338 "engines/director/lingo/lingo-gr.y"
+ { (yyval.code) = g_lingo->_currentScript->size(); ;}
+ break;
+
+ case 54:
+#line 343 "engines/director/lingo/lingo-gr.y"
+ {
+ (yyval.code) = g_lingo->code1(g_lingo->c_constpush);
+ inst i = 0;
+ WRITE_UINT32(&i, (yyvsp[(1) - (1)].i));
+ g_lingo->code1(i); ;}
+ break;
+
+ case 55:
+#line 348 "engines/director/lingo/lingo-gr.y"
+ {
+ (yyval.code) = g_lingo->code1(g_lingo->c_fconstpush);
+ g_lingo->codeFloat((yyvsp[(1) - (1)].f)); ;}
+ break;
+
+ case 56:
+#line 351 "engines/director/lingo/lingo-gr.y"
+ {
+ (yyval.code) = g_lingo->code1(g_lingo->c_stringpush);
+ g_lingo->codeString((yyvsp[(1) - (1)].s)->c_str()); ;}
+ break;
+
+ case 57:
+#line 354 "engines/director/lingo/lingo-gr.y"
+ {
+ (yyval.code) = g_lingo->code1(g_lingo->_handlers[*(yyvsp[(1) - (1)].s)]->u.func);
+ (yyval.code) = g_lingo->code2(g_lingo->c_constpush, 0); // Put dummy value
+ delete (yyvsp[(1) - (1)].s); ;}
+ break;
+
+ case 58:
+#line 358 "engines/director/lingo/lingo-gr.y"
+ {
+ (yyval.code) = g_lingo->codeFunc((yyvsp[(1) - (4)].s), (yyvsp[(3) - (4)].narg));
+ delete (yyvsp[(1) - (4)].s); ;}
+ break;
+
+ case 59:
+#line 361 "engines/director/lingo/lingo-gr.y"
+ {
+ (yyval.code) = g_lingo->code1(g_lingo->c_eval);
+ g_lingo->codeString((yyvsp[(1) - (1)].s)->c_str());
+ delete (yyvsp[(1) - (1)].s); ;}
+ break;
+
+ case 60:
+#line 365 "engines/director/lingo/lingo-gr.y"
+ {
+ (yyval.code) = g_lingo->code2(g_lingo->c_constpush, 0); // Put dummy id
+ g_lingo->code1(g_lingo->c_theentitypush);
+ inst e = 0, f = 0;
+ WRITE_UINT32(&e, (yyvsp[(1) - (1)].e)[0]);
+ WRITE_UINT32(&f, (yyvsp[(1) - (1)].e)[1]);
+ g_lingo->code2(e, f); ;}
+ break;
+
+ case 61:
+#line 372 "engines/director/lingo/lingo-gr.y"
+ {
+ (yyval.code) = g_lingo->code1(g_lingo->c_theentitypush);
+ inst e = 0, f = 0;
+ WRITE_UINT32(&e, (yyvsp[(1) - (2)].e)[0]);
+ WRITE_UINT32(&f, (yyvsp[(1) - (2)].e)[1]);
+ g_lingo->code2(e, f); ;}
+ break;
+
+ case 63:
+#line 379 "engines/director/lingo/lingo-gr.y"
+ { g_lingo->code1(g_lingo->c_add); ;}
+ break;
+
+ case 64:
+#line 380 "engines/director/lingo/lingo-gr.y"
+ { g_lingo->code1(g_lingo->c_sub); ;}
+ break;
+
+ case 65:
+#line 381 "engines/director/lingo/lingo-gr.y"
+ { g_lingo->code1(g_lingo->c_mul); ;}
+ break;
+
+ case 66:
+#line 382 "engines/director/lingo/lingo-gr.y"
+ { g_lingo->code1(g_lingo->c_div); ;}
+ break;
+
+ case 67:
+#line 383 "engines/director/lingo/lingo-gr.y"
+ { g_lingo->code1(g_lingo->c_gt); ;}
+ break;
+
+ case 68:
+#line 384 "engines/director/lingo/lingo-gr.y"
+ { g_lingo->code1(g_lingo->c_lt); ;}
+ break;
+
+ case 69:
+#line 385 "engines/director/lingo/lingo-gr.y"
+ { g_lingo->code1(g_lingo->c_neq); ;}
+ break;
+
+ case 70:
+#line 386 "engines/director/lingo/lingo-gr.y"
+ { g_lingo->code1(g_lingo->c_ge); ;}
+ break;
+
+ case 71:
+#line 387 "engines/director/lingo/lingo-gr.y"
+ { g_lingo->code1(g_lingo->c_le); ;}
+ break;
+
+ case 72:
+#line 388 "engines/director/lingo/lingo-gr.y"
+ { g_lingo->code1(g_lingo->c_and); ;}
+ break;
+
+ case 73:
+#line 389 "engines/director/lingo/lingo-gr.y"
+ { g_lingo->code1(g_lingo->c_or); ;}
+ break;
+
+ case 74:
+#line 390 "engines/director/lingo/lingo-gr.y"
+ { g_lingo->code1(g_lingo->c_not); ;}
+ break;
+
+ case 75:
+#line 391 "engines/director/lingo/lingo-gr.y"
+ { g_lingo->code1(g_lingo->c_ampersand); ;}
+ break;
+
+ case 76:
+#line 392 "engines/director/lingo/lingo-gr.y"
+ { g_lingo->code1(g_lingo->c_concat); ;}
+ break;
+
+ case 77:
+#line 393 "engines/director/lingo/lingo-gr.y"
+ { g_lingo->code1(g_lingo->c_contains); ;}
+ break;
+
+ case 78:
+#line 394 "engines/director/lingo/lingo-gr.y"
+ { g_lingo->code1(g_lingo->c_starts); ;}
+ break;
+
+ case 79:
+#line 395 "engines/director/lingo/lingo-gr.y"
+ { (yyval.code) = (yyvsp[(2) - (2)].code); ;}
+ break;
+
+ case 80:
+#line 396 "engines/director/lingo/lingo-gr.y"
+ { (yyval.code) = (yyvsp[(2) - (2)].code); g_lingo->code1(g_lingo->c_negate); ;}
+ break;
+
+ case 81:
+#line 397 "engines/director/lingo/lingo-gr.y"
+ { (yyval.code) = (yyvsp[(2) - (3)].code); ;}
+ break;
+
+ case 82:
+#line 398 "engines/director/lingo/lingo-gr.y"
+ { g_lingo->code1(g_lingo->c_intersects); ;}
+ break;
+
+ case 83:
+#line 399 "engines/director/lingo/lingo-gr.y"
+ { g_lingo->code1(g_lingo->c_within); ;}
+ break;
+
+ case 84:
+#line 402 "engines/director/lingo/lingo-gr.y"
+ { g_lingo->code1(g_lingo->c_mci); g_lingo->codeString((yyvsp[(2) - (2)].s)->c_str()); delete (yyvsp[(2) - (2)].s); ;}
+ break;
+
+ case 85:
+#line 403 "engines/director/lingo/lingo-gr.y"
+ { g_lingo->code1(g_lingo->c_mciwait); g_lingo->codeString((yyvsp[(2) - (2)].s)->c_str()); delete (yyvsp[(2) - (2)].s); ;}
+ break;
+
+ case 86:
+#line 404 "engines/director/lingo/lingo-gr.y"
+ { g_lingo->code1(g_lingo->c_printtop); ;}
+ break;
+
+ case 88:
+#line 406 "engines/director/lingo/lingo-gr.y"
+ { g_lingo->code2(g_lingo->c_constpush, (inst)0); // Push fake value on stack
+ g_lingo->code1(g_lingo->c_procret); ;}
+ break;
+
+ case 90:
+#line 411 "engines/director/lingo/lingo-gr.y"
+ { g_lingo->code1(g_lingo->c_global); g_lingo->codeString((yyvsp[(1) - (1)].s)->c_str()); delete (yyvsp[(1) - (1)].s); ;}
+ break;
+
+ case 91:
+#line 412 "engines/director/lingo/lingo-gr.y"
+ { g_lingo->code1(g_lingo->c_global); g_lingo->codeString((yyvsp[(3) - (3)].s)->c_str()); delete (yyvsp[(3) - (3)].s); ;}
+ break;
+
+ case 92:
+#line 423 "engines/director/lingo/lingo-gr.y"
+ { g_lingo->code1(g_lingo->c_gotoloop); ;}
+ break;
+
+ case 93:
+#line 424 "engines/director/lingo/lingo-gr.y"
+ { g_lingo->code1(g_lingo->c_gotonext); ;}
+ break;
+
+ case 94:
+#line 425 "engines/director/lingo/lingo-gr.y"
+ { g_lingo->code1(g_lingo->c_gotoprevious); ;}
+ break;
+
+ case 95:
+#line 426 "engines/director/lingo/lingo-gr.y"
+ {
+ g_lingo->code1(g_lingo->c_goto);
+ g_lingo->codeString((yyvsp[(2) - (2)].s)->c_str());
+ g_lingo->codeString("");
+ delete (yyvsp[(2) - (2)].s); ;}
+ break;
+
+ case 96:
+#line 431 "engines/director/lingo/lingo-gr.y"
+ {
+ g_lingo->code1(g_lingo->c_goto);
+ g_lingo->codeString((yyvsp[(2) - (3)].s)->c_str());
+ g_lingo->codeString((yyvsp[(3) - (3)].s)->c_str());
+ delete (yyvsp[(2) - (3)].s);
+ delete (yyvsp[(3) - (3)].s); ;}
+ break;
+
+ case 97:
+#line 437 "engines/director/lingo/lingo-gr.y"
+ {
+ g_lingo->code1(g_lingo->c_goto);
+ g_lingo->codeString("");
+ g_lingo->codeString((yyvsp[(2) - (2)].s)->c_str());
+ delete (yyvsp[(2) - (2)].s); ;}
+ break;
+
+ case 98:
+#line 444 "engines/director/lingo/lingo-gr.y"
+ { (yyval.s) = (yyvsp[(3) - (3)].s); ;}
+ break;
+
+ case 99:
+#line 445 "engines/director/lingo/lingo-gr.y"
+ { (yyval.s) = (yyvsp[(2) - (2)].s); ;}
+ break;
+
+ case 100:
+#line 446 "engines/director/lingo/lingo-gr.y"
+ { (yyval.s) = (yyvsp[(2) - (2)].s); ;}
+ break;
+
+ case 101:
+#line 447 "engines/director/lingo/lingo-gr.y"
+ { (yyval.s) = (yyvsp[(1) - (1)].s); ;}
+ break;
+
+ case 102:
+#line 450 "engines/director/lingo/lingo-gr.y"
+ { (yyval.s) = (yyvsp[(3) - (3)].s); ;}
+ break;
+
+ case 103:
+#line 451 "engines/director/lingo/lingo-gr.y"
+ { (yyval.s) = (yyvsp[(2) - (2)].s); ;}
+ break;
+
+ case 104:
+#line 452 "engines/director/lingo/lingo-gr.y"
+ { (yyval.s) = (yyvsp[(3) - (3)].s); ;}
+ break;
+
+ case 105:
+#line 480 "engines/director/lingo/lingo-gr.y"
+ { g_lingo->_indef = true; g_lingo->_currentFactory.clear(); ;}
+ break;
+
+ case 106:
+#line 481 "engines/director/lingo/lingo-gr.y"
+ {
+ g_lingo->code2(g_lingo->c_constpush, (inst)0); // Push fake value on stack
+ g_lingo->code1(g_lingo->c_procret);
+ g_lingo->define(*(yyvsp[(2) - (8)].s), (yyvsp[(4) - (8)].code), (yyvsp[(5) - (8)].narg));
+ g_lingo->_indef = false; ;}
+ break;
+
+ case 107:
+#line 486 "engines/director/lingo/lingo-gr.y"
+ {
+ g_lingo->codeFactory(*(yyvsp[(2) - (2)].s));
+ ;}
+ break;
+
+ case 108:
+#line 489 "engines/director/lingo/lingo-gr.y"
+ { g_lingo->_indef = true; ;}
+ break;
+
+ case 109:
+#line 490 "engines/director/lingo/lingo-gr.y"
+ {
+ g_lingo->code2(g_lingo->c_constpush, (inst)0); // Push fake value on stack
+ g_lingo->code1(g_lingo->c_procret);
+ g_lingo->define(*(yyvsp[(2) - (8)].s), (yyvsp[(4) - (8)].code), (yyvsp[(5) - (8)].narg), &g_lingo->_currentFactory);
+ g_lingo->_indef = false; ;}
+ break;
+
+ case 110:
+#line 495 "engines/director/lingo/lingo-gr.y"
+ { (yyval.narg) = 0; ;}
+ break;
+
+ case 111:
+#line 496 "engines/director/lingo/lingo-gr.y"
+ { g_lingo->codeArg((yyvsp[(1) - (1)].s)); (yyval.narg) = 1; ;}
+ break;
+
+ case 112:
+#line 497 "engines/director/lingo/lingo-gr.y"
+ { g_lingo->codeArg((yyvsp[(3) - (3)].s)); (yyval.narg) = (yyvsp[(1) - (3)].narg) + 1; ;}
+ break;
+
+ case 113:
+#line 498 "engines/director/lingo/lingo-gr.y"
+ { g_lingo->codeArg((yyvsp[(4) - (4)].s)); (yyval.narg) = (yyvsp[(1) - (4)].narg) + 1; ;}
+ break;
+
+ case 114:
+#line 500 "engines/director/lingo/lingo-gr.y"
+ { g_lingo->codeArgStore(); ;}
+ break;
+
+ case 115:
+#line 504 "engines/director/lingo/lingo-gr.y"
+ {
+ g_lingo->code1(g_lingo->c_call);
+ g_lingo->codeString((yyvsp[(1) - (3)].s)->c_str());
+ inst numpar = 0;
+ WRITE_UINT32(&numpar, (yyvsp[(3) - (3)].narg));
+ g_lingo->code1(numpar); ;}
+ break;
+
+ case 116:
+#line 512 "engines/director/lingo/lingo-gr.y"
+ { (yyval.narg) = 0; ;}
+ break;
+
+ case 117:
+#line 513 "engines/director/lingo/lingo-gr.y"
+ { (yyval.narg) = 1; ;}
+ break;
+
+ case 118:
+#line 514 "engines/director/lingo/lingo-gr.y"
+ { (yyval.narg) = (yyvsp[(1) - (3)].narg) + 1; ;}
+ break;
+
+
+/* Line 1267 of yacc.c. */
+#line 2466 "engines/director/lingo/lingo-gr.cpp"
+ default: break;
+ }
+ YY_SYMBOL_PRINT ("-> $$ =", yyr1[yyn], &yyval, &yyloc);
+
+ YYPOPSTACK (yylen);
+ yylen = 0;
+ YY_STACK_PRINT (yyss, yyssp);
+
+ *++yyvsp = yyval;
+
+
+ /* Now `shift' the result of the reduction. Determine what state
+ that goes to, based on the state we popped back to and the rule
+ number reduced by. */
+
+ yyn = yyr1[yyn];
+
+ yystate = yypgoto[yyn - YYNTOKENS] + *yyssp;
+ if (0 <= yystate && yystate <= YYLAST && yycheck[yystate] == *yyssp)
+ yystate = yytable[yystate];
+ else
+ yystate = yydefgoto[yyn - YYNTOKENS];
+
+ goto yynewstate;
+
+
+/*------------------------------------.
+| yyerrlab -- here on detecting error |
+`------------------------------------*/
+yyerrlab:
+ /* If not already recovering from an error, report this error. */
+ if (!yyerrstatus)
+ {
+ ++yynerrs;
+#if ! YYERROR_VERBOSE
+ yyerror (YY_("syntax error"));
+#else
+ {
+ YYSIZE_T yysize = yysyntax_error (0, yystate, yychar);
+ if (yymsg_alloc < yysize && yymsg_alloc < YYSTACK_ALLOC_MAXIMUM)
+ {
+ YYSIZE_T yyalloc = 2 * yysize;
+ if (! (yysize <= yyalloc && yyalloc <= YYSTACK_ALLOC_MAXIMUM))
+ yyalloc = YYSTACK_ALLOC_MAXIMUM;
+ if (yymsg != yymsgbuf)
+ YYSTACK_FREE (yymsg);
+ yymsg = (char *) YYSTACK_ALLOC (yyalloc);
+ if (yymsg)
+ yymsg_alloc = yyalloc;
+ else
+ {
+ yymsg = yymsgbuf;
+ yymsg_alloc = sizeof yymsgbuf;
+ }
+ }
+
+ if (0 < yysize && yysize <= yymsg_alloc)
+ {
+ (void) yysyntax_error (yymsg, yystate, yychar);
+ yyerror (yymsg);
+ }
+ else
+ {
+ yyerror (YY_("syntax error"));
+ if (yysize != 0)
+ goto yyexhaustedlab;
+ }
+ }
+#endif
+ }
+
+
+
+ if (yyerrstatus == 3)
+ {
+ /* If just tried and failed to reuse look-ahead token after an
+ error, discard it. */
+
+ if (yychar <= YYEOF)
+ {
+ /* Return failure if at end of input. */
+ if (yychar == YYEOF)
+ YYABORT;
+ }
+ else
+ {
+ yydestruct ("Error: discarding",
+ yytoken, &yylval);
+ yychar = YYEMPTY;
+ }
+ }
+
+ /* Else will try to reuse look-ahead token after shifting the error
+ token. */
+ goto yyerrlab1;
+
+
+/*---------------------------------------------------.
+| yyerrorlab -- error raised explicitly by YYERROR. |
+`---------------------------------------------------*/
+yyerrorlab:
+
+ /* Pacify compilers like GCC when the user code never invokes
+ YYERROR and the label yyerrorlab therefore never appears in user
+ code. */
+ if (/*CONSTCOND*/ 0)
+ goto yyerrorlab;
+
+ /* Do not reclaim the symbols of the rule which action triggered
+ this YYERROR. */
+ YYPOPSTACK (yylen);
+ yylen = 0;
+ YY_STACK_PRINT (yyss, yyssp);
+ yystate = *yyssp;
+ goto yyerrlab1;
+
+
+/*-------------------------------------------------------------.
+| yyerrlab1 -- common code for both syntax error and YYERROR. |
+`-------------------------------------------------------------*/
+yyerrlab1:
+ yyerrstatus = 3; /* Each real token shifted decrements this. */
+
+ for (;;)
+ {
+ yyn = yypact[yystate];
+ if (yyn != YYPACT_NINF)
+ {
+ yyn += YYTERROR;
+ if (0 <= yyn && yyn <= YYLAST && yycheck[yyn] == YYTERROR)
+ {
+ yyn = yytable[yyn];
+ if (0 < yyn)
+ break;
+ }
+ }
+
+ /* Pop the current state because it cannot handle the error token. */
+ if (yyssp == yyss)
+ YYABORT;
+
+
+ yydestruct ("Error: popping",
+ yystos[yystate], yyvsp);
+ YYPOPSTACK (1);
+ yystate = *yyssp;
+ YY_STACK_PRINT (yyss, yyssp);
+ }
+
+ if (yyn == YYFINAL)
+ YYACCEPT;
+
+ *++yyvsp = yylval;
+
+
+ /* Shift the error token. */
+ YY_SYMBOL_PRINT ("Shifting", yystos[yyn], yyvsp, yylsp);
+
+ yystate = yyn;
+ goto yynewstate;
+
+
+/*-------------------------------------.
+| yyacceptlab -- YYACCEPT comes here. |
+`-------------------------------------*/
+yyacceptlab:
+ yyresult = 0;
+ goto yyreturn;
+
+/*-----------------------------------.
+| yyabortlab -- YYABORT comes here. |
+`-----------------------------------*/
+yyabortlab:
+ yyresult = 1;
+ goto yyreturn;
+
+#ifndef yyoverflow
+/*-------------------------------------------------.
+| yyexhaustedlab -- memory exhaustion comes here. |
+`-------------------------------------------------*/
+yyexhaustedlab:
+ yyerror (YY_("memory exhausted"));
+ yyresult = 2;
+ /* Fall through. */
+#endif
+
+yyreturn:
+ if (yychar != YYEOF && yychar != YYEMPTY)
+ yydestruct ("Cleanup: discarding lookahead",
+ yytoken, &yylval);
+ /* Do not reclaim the symbols of the rule which action triggered
+ this YYABORT or YYACCEPT. */
+ YYPOPSTACK (yylen);
+ YY_STACK_PRINT (yyss, yyssp);
+ while (yyssp != yyss)
+ {
+ yydestruct ("Cleanup: popping",
+ yystos[*yyssp], yyvsp);
+ YYPOPSTACK (1);
+ }
+#ifndef yyoverflow
+ if (yyss != yyssa)
+ YYSTACK_FREE (yyss);
+#endif
+#if YYERROR_VERBOSE
+ if (yymsg != yymsgbuf)
+ YYSTACK_FREE (yymsg);
+#endif
+ /* Make sure YYID is used. */
+ return YYID (yyresult);
+}
+
+
+#line 517 "engines/director/lingo/lingo-gr.y"
+
+
diff --git a/engines/director/lingo/lingo-gr.h b/engines/director/lingo/lingo-gr.h
new file mode 100644
index 0000000000..a806533d2f
--- /dev/null
+++ b/engines/director/lingo/lingo-gr.h
@@ -0,0 +1,192 @@
+/* A Bison parser, made by GNU Bison 2.3. */
+
+/* Skeleton interface for Bison's Yacc-like parsers in C
+
+ Copyright (C) 1984, 1989, 1990, 2000, 2001, 2002, 2003, 2004, 2005, 2006
+ Free Software Foundation, Inc.
+
+ 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 2, 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, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA. */
+
+/* As a special exception, you may create a larger work that contains
+ part or all of the Bison parser skeleton and distribute that work
+ under terms of your choice, so long as that work isn't itself a
+ parser generator using the skeleton or a modified version thereof
+ as a parser skeleton. Alternatively, if you modify or redistribute
+ the parser skeleton itself, you may (at your option) remove this
+ special exception, which will cause the skeleton and the resulting
+ Bison output files to be licensed under the GNU General Public
+ License without this special exception.
+
+ This special exception was added by the Free Software Foundation in
+ version 2.2 of Bison. */
+
+/* Tokens. */
+#ifndef YYTOKENTYPE
+# define YYTOKENTYPE
+ /* Put the tokens into the symbol table, so that GDB and other debuggers
+ know about them. */
+ enum yytokentype {
+ CASTREF = 258,
+ UNARY = 259,
+ VOID = 260,
+ VAR = 261,
+ POINT = 262,
+ RECT = 263,
+ ARRAY = 264,
+ SYMBOL = 265,
+ INT = 266,
+ THEENTITY = 267,
+ THEENTITYWITHID = 268,
+ FLOAT = 269,
+ BLTIN = 270,
+ BLTINNOARGS = 271,
+ ID = 272,
+ STRING = 273,
+ HANDLER = 274,
+ tDOWN = 275,
+ tELSE = 276,
+ tNLELSIF = 277,
+ tEND = 278,
+ tEXIT = 279,
+ tFRAME = 280,
+ tGLOBAL = 281,
+ tGO = 282,
+ tIF = 283,
+ tINTO = 284,
+ tLOOP = 285,
+ tMACRO = 286,
+ tMCI = 287,
+ tMCIWAIT = 288,
+ tMOVIE = 289,
+ tNEXT = 290,
+ tOF = 291,
+ tPREVIOUS = 292,
+ tPUT = 293,
+ tREPEAT = 294,
+ tSET = 295,
+ tTHEN = 296,
+ tTO = 297,
+ tWHEN = 298,
+ tWITH = 299,
+ tWHILE = 300,
+ tNLELSE = 301,
+ tFACTORY = 302,
+ tMETHOD = 303,
+ tGE = 304,
+ tLE = 305,
+ tGT = 306,
+ tLT = 307,
+ tEQ = 308,
+ tNEQ = 309,
+ tAND = 310,
+ tOR = 311,
+ tNOT = 312,
+ tCONCAT = 313,
+ tCONTAINS = 314,
+ tSTARTS = 315,
+ tSPRITE = 316,
+ tINTERSECTS = 317,
+ tWITHIN = 318
+ };
+#endif
+/* Tokens. */
+#define CASTREF 258
+#define UNARY 259
+#define VOID 260
+#define VAR 261
+#define POINT 262
+#define RECT 263
+#define ARRAY 264
+#define SYMBOL 265
+#define INT 266
+#define THEENTITY 267
+#define THEENTITYWITHID 268
+#define FLOAT 269
+#define BLTIN 270
+#define BLTINNOARGS 271
+#define ID 272
+#define STRING 273
+#define HANDLER 274
+#define tDOWN 275
+#define tELSE 276
+#define tNLELSIF 277
+#define tEND 278
+#define tEXIT 279
+#define tFRAME 280
+#define tGLOBAL 281
+#define tGO 282
+#define tIF 283
+#define tINTO 284
+#define tLOOP 285
+#define tMACRO 286
+#define tMCI 287
+#define tMCIWAIT 288
+#define tMOVIE 289
+#define tNEXT 290
+#define tOF 291
+#define tPREVIOUS 292
+#define tPUT 293
+#define tREPEAT 294
+#define tSET 295
+#define tTHEN 296
+#define tTO 297
+#define tWHEN 298
+#define tWITH 299
+#define tWHILE 300
+#define tNLELSE 301
+#define tFACTORY 302
+#define tMETHOD 303
+#define tGE 304
+#define tLE 305
+#define tGT 306
+#define tLT 307
+#define tEQ 308
+#define tNEQ 309
+#define tAND 310
+#define tOR 311
+#define tNOT 312
+#define tCONCAT 313
+#define tCONTAINS 314
+#define tSTARTS 315
+#define tSPRITE 316
+#define tINTERSECTS 317
+#define tWITHIN 318
+
+
+
+
+#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED
+typedef union YYSTYPE
+#line 69 "engines/director/lingo/lingo-gr.y"
+{
+ Common::String *s;
+ int i;
+ double f;
+ int e[2]; // Entity + field
+ int code;
+ int narg; /* number of arguments */
+ Common::Array<double> *arr;
+}
+/* Line 1529 of yacc.c. */
+#line 185 "engines/director/lingo/lingo-gr.hpp"
+ YYSTYPE;
+# define yystype YYSTYPE /* obsolescent; will be withdrawn */
+# define YYSTYPE_IS_DECLARED 1
+# define YYSTYPE_IS_TRIVIAL 1
+#endif
+
+extern YYSTYPE yylval;
+
diff --git a/engines/director/lingo/lingo-gr.y b/engines/director/lingo/lingo-gr.y
new file mode 100644
index 0000000000..9a10916048
--- /dev/null
+++ b/engines/director/lingo/lingo-gr.y
@@ -0,0 +1,517 @@
+/* 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 2
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+// Heavily inspired by hoc
+// Copyright (C) AT&T 1995
+// All Rights Reserved
+//
+// Permission to use, copy, modify, and distribute this software and
+// its documentation for any purpose and without fee is hereby
+// granted, provided that the above copyright notice appear in all
+// copies and that both that the copyright notice and this
+// permission notice and warranty disclaimer appear in supporting
+// documentation, and that the name of AT&T or any of its entities
+// not be used in advertising or publicity pertaining to
+// distribution of the software without specific, written prior
+// permission.
+//
+// AT&T DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
+// INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS.
+// IN NO EVENT SHALL AT&T OR ANY OF ITS ENTITIES BE LIABLE FOR ANY
+// SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
+// IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
+// ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
+// THIS SOFTWARE.
+
+
+%debug
+
+%{
+#define FORBIDDEN_SYMBOL_ALLOW_ALL
+
+#include "common/hash-str.h"
+
+#include "director/lingo/lingo.h"
+#include "director/lingo/lingo-gr.h"
+
+extern int yylex();
+extern int yyparse();
+
+using namespace Director;
+void yyerror(char *s) {
+ g_lingo->_hadError = true;
+ warning("%s at line %d col %d", s, g_lingo->_linenumber, g_lingo->_colnumber);
+}
+
+
+%}
+
+%union {
+ Common::String *s;
+ int i;
+ double f;
+ int e[2]; // Entity + field
+ int code;
+ int narg; /* number of arguments */
+ Common::Array<double> *arr;
+}
+
+%token CASTREF UNARY VOID VAR POINT RECT ARRAY SYMBOL
+%token<i> INT
+%token<e> THEENTITY THEENTITYWITHID
+%token<f> FLOAT
+%token<s> BLTIN BLTINNOARGS ID STRING HANDLER
+%token tDOWN tELSE tNLELSIF tEND tEXIT tFRAME tGLOBAL tGO tIF tINTO tLOOP tMACRO
+%token tMCI tMCIWAIT tMOVIE tNEXT tOF tPREVIOUS tPUT tREPEAT tSET tTHEN tTO tWHEN
+%token tWITH tWHILE tNLELSE tFACTORY tMETHOD
+%token tGE tLE tGT tLT tEQ tNEQ tAND tOR tNOT
+%token tCONCAT tCONTAINS tSTARTS
+%token tSPRITE tINTERSECTS tWITHIN
+
+%type<code> asgn begin elseif elsestmtoneliner end expr if repeatwhile repeatwith stmtlist
+%type<s> gotoframe gotomovie
+%type<narg> argdef arglist
+
+%right '='
+%left '+' '-'
+%left '*' '/' '%'
+%right UNARY
+
+%%
+
+program: program nl programline
+ | programline
+ | error '\n' { yyerrok; }
+ ;
+
+nl: '\n' {
+ g_lingo->_linenumber++;
+ g_lingo->_colnumber = 1;
+ }
+
+programline: /* empty */
+ | defn
+ | func
+ | macro
+ | asgn { g_lingo->code1(g_lingo->c_xpop); }
+ | stmt
+ ;
+
+asgn: tPUT expr tINTO ID {
+ g_lingo->code1(g_lingo->c_varpush);
+ g_lingo->codeString($4->c_str());
+ g_lingo->code1(g_lingo->c_assign);
+ $$ = $2;
+ delete $4; }
+ | tSET ID '=' expr {
+ g_lingo->code1(g_lingo->c_varpush);
+ g_lingo->codeString($2->c_str());
+ g_lingo->code1(g_lingo->c_assign);
+ $$ = $4;
+ delete $2; }
+ | tSET THEENTITY '=' expr {
+ g_lingo->code2(g_lingo->c_constpush, 0); // Put dummy id
+ g_lingo->code1(g_lingo->c_theentityassign);
+ inst e = 0, f = 0;
+ WRITE_UINT32(&e, $2[0]);
+ WRITE_UINT32(&f, $2[1]);
+ g_lingo->code2(e, f);
+ $$ = $4; }
+ | tSET THEENTITYWITHID expr '=' expr {
+ g_lingo->code1(g_lingo->c_swap);
+ g_lingo->code1(g_lingo->c_theentityassign);
+ inst e = 0, f = 0;
+ WRITE_UINT32(&e, $2[0]);
+ WRITE_UINT32(&f, $2[1]);
+ g_lingo->code2(e, f);
+ $$ = $5; }
+ | tSET ID tTO expr {
+ g_lingo->code1(g_lingo->c_varpush);
+ g_lingo->codeString($2->c_str());
+ g_lingo->code1(g_lingo->c_assign);
+ $$ = $4;
+ delete $2; }
+ | tSET THEENTITY tTO expr {
+ g_lingo->code2(g_lingo->c_constpush, 0); // Put dummy id
+ g_lingo->code1(g_lingo->c_theentityassign);
+ inst e = 0, f = 0;
+ WRITE_UINT32(&e, $2[0]);
+ WRITE_UINT32(&f, $2[1]);
+ g_lingo->code2(e, f);
+ $$ = $4; }
+ | tSET THEENTITYWITHID expr tTO expr {
+ g_lingo->code1(g_lingo->c_swap);
+ g_lingo->code1(g_lingo->c_theentityassign);
+ inst e = 0, f = 0;
+ WRITE_UINT32(&e, $2[0]);
+ WRITE_UINT32(&f, $2[1]);
+ g_lingo->code2(e, f);
+ $$ = $5; }
+ ;
+stmtoneliner: expr { g_lingo->code1(g_lingo->c_xpop); }
+ | func
+ ;
+stmt: stmtoneliner
+ | ifstmt
+ // repeat while (expression = TRUE)
+ // statements
+ // end repeat
+ //
+ | repeatwhile '(' cond ')' stmtlist end tEND tREPEAT {
+ inst body = 0, end = 0;
+ WRITE_UINT32(&body, $5);
+ WRITE_UINT32(&end, $6);
+ (*g_lingo->_currentScript)[$1 + 1] = body; /* body of loop */
+ (*g_lingo->_currentScript)[$1 + 2] = end; } /* end, if cond fails */
+ ;
+ // repeat with index = start to end
+ // statements
+ // end repeat
+ //
+ | repeatwith '=' expr end tTO expr end stmtlist end tEND tREPEAT {
+ inst init = 0, finish = 0, body = 0, end = 0, inc = 0;
+ WRITE_UINT32(&init, $3);
+ WRITE_UINT32(&finish, $6);
+ WRITE_UINT32(&body, $8);
+ WRITE_UINT32(&end, $9);
+ WRITE_UINT32(&inc, 1);
+ (*g_lingo->_currentScript)[$1 + 1] = init; /* initial count value */
+ (*g_lingo->_currentScript)[$1 + 2] = finish;/* final count value */
+ (*g_lingo->_currentScript)[$1 + 3] = body; /* body of loop */
+ (*g_lingo->_currentScript)[$1 + 4] = inc; /* increment */
+ (*g_lingo->_currentScript)[$1 + 5] = end; } /* end, if cond fails */
+ // repeat with index = high down to low
+ // statements
+ // end repeat
+ //
+ | repeatwith '=' expr end tDOWN tTO expr end stmtlist end tEND tREPEAT {
+ inst init = 0, finish = 0, body = 0, end = 0, inc = 0;
+ WRITE_UINT32(&init, $3);
+ WRITE_UINT32(&finish, $7);
+ WRITE_UINT32(&body, $9);
+ WRITE_UINT32(&end, $10);
+ WRITE_UINT32(&inc, -1);
+ (*g_lingo->_currentScript)[$1 + 1] = init; /* initial count value */
+ (*g_lingo->_currentScript)[$1 + 2] = finish;/* final count value */
+ (*g_lingo->_currentScript)[$1 + 3] = body; /* body of loop */
+ (*g_lingo->_currentScript)[$1 + 4] = inc; /* increment */
+ (*g_lingo->_currentScript)[$1 + 5] = end; } /* end, if cond fails */
+ | tWHEN ID tTHEN expr {
+ g_lingo->code1(g_lingo->c_ifcode);
+ }
+ ;
+
+ifstmt: if cond tTHEN nl stmtlist end tEND tIF {
+ inst then = 0, end = 0;
+ WRITE_UINT32(&then, $5);
+ WRITE_UINT32(&end, $6);
+ (*g_lingo->_currentScript)[$1 + 1] = then; /* thenpart */
+ (*g_lingo->_currentScript)[$1 + 3] = end; /* end, if cond fails */
+ g_lingo->processIf(0, 0); }
+ | if cond tTHEN nl stmtlist end tNLELSE stmtlist end tEND tIF {
+ inst then = 0, else1 = 0, end = 0;
+ WRITE_UINT32(&then, $5);
+ WRITE_UINT32(&else1, $8);
+ WRITE_UINT32(&end, $9);
+ (*g_lingo->_currentScript)[$1 + 1] = then; /* thenpart */
+ (*g_lingo->_currentScript)[$1 + 2] = else1; /* elsepart */
+ (*g_lingo->_currentScript)[$1 + 3] = end; /* end, if cond fails */
+ g_lingo->processIf(0, 0); }
+ | if cond tTHEN nl stmtlist end begin elseifstmt end tEND tIF {
+ inst then = 0, else1 = 0, end = 0;
+ WRITE_UINT32(&then, $5);
+ WRITE_UINT32(&else1, $7);
+ WRITE_UINT32(&end, $9);
+ (*g_lingo->_currentScript)[$1 + 1] = then; /* thenpart */
+ (*g_lingo->_currentScript)[$1 + 2] = else1; /* elsepart */
+ (*g_lingo->_currentScript)[$1 + 3] = end; /* end, if cond fails */
+ g_lingo->processIf(0, $9); }
+ | if cond tTHEN begin stmtoneliner end {
+ inst then = 0, else1 = 0, end = 0;
+ WRITE_UINT32(&then, $4);
+ WRITE_UINT32(&else1, 0);
+ WRITE_UINT32(&end, $6);
+ (*g_lingo->_currentScript)[$1 + 1] = then; /* thenpart */
+ (*g_lingo->_currentScript)[$1 + 2] = else1; /* elsepart */
+ (*g_lingo->_currentScript)[$1 + 3] = end; /* end, if cond fails */
+
+ g_lingo->processIf(0, 0); }
+ | if cond tTHEN begin stmtoneliner end tNLELSE begin stmtoneliner end {
+ inst then = 0, else1 = 0, end = 0;
+ WRITE_UINT32(&then, $4);
+ WRITE_UINT32(&else1, $8);
+ WRITE_UINT32(&end, $10);
+ (*g_lingo->_currentScript)[$1 + 1] = then; /* thenpart */
+ (*g_lingo->_currentScript)[$1 + 2] = else1; /* elsepart */
+ (*g_lingo->_currentScript)[$1 + 3] = end; /* end, if cond fails */
+
+ g_lingo->processIf(0, 0); }
+ | if cond tTHEN begin stmtoneliner end elseifstmtoneliner end elsestmtoneliner end {
+ inst then = 0, else1 = 0, end = 0;
+ WRITE_UINT32(&then, $4);
+ WRITE_UINT32(&else1, $6);
+ WRITE_UINT32(&end, $10);
+ (*g_lingo->_currentScript)[$1 + 1] = then; /* thenpart */
+ (*g_lingo->_currentScript)[$1 + 2] = else1; /* elsepart */
+ (*g_lingo->_currentScript)[$1 + 3] = end; /* end, if cond fails */
+
+ g_lingo->processIf(0, $10); }
+ ;
+elsestmtoneliner: /* nothing */ { $$ = 0; }
+ | tNLELSE begin stmtoneliner { $$ = $2; }
+ ;
+
+elseifstmt: elseifstmt elseifstmt1
+ | elseifstmt1
+ ;
+
+elseifstmtoneliner: elseifstmtoneliner elseifstmtoneliner1
+ | elseifstmtoneliner1
+ ;
+
+elseifstmtoneliner1: elseif cond tTHEN begin stmt end {
+ inst then = 0;
+ WRITE_UINT32(&then, $4);
+ (*g_lingo->_currentScript)[$1 + 1] = then; /* thenpart */
+
+ g_lingo->codeLabel($1); }
+ ;
+
+elseifstmt1: elseifstmtoneliner
+ | elseif cond tTHEN stmtlist end {
+ inst then = 0;
+ WRITE_UINT32(&then, $4);
+ (*g_lingo->_currentScript)[$1 + 1] = then; /* thenpart */
+
+ g_lingo->codeLabel($1); }
+ ;
+
+cond: expr { g_lingo->code1(STOP); }
+ | expr '=' expr { g_lingo->code2(g_lingo->c_eq, STOP); }
+ | '(' cond ')'
+ ;
+repeatwhile: tREPEAT tWHILE { $$ = g_lingo->code3(g_lingo->c_repeatwhilecode, STOP, STOP); }
+ ;
+repeatwith: tREPEAT tWITH ID {
+ $$ = g_lingo->code3(g_lingo->c_repeatwithcode, STOP, STOP);
+ g_lingo->code3(STOP, STOP, STOP);
+ g_lingo->codeString($3->c_str());
+ delete $3; }
+ ;
+if: tIF {
+ $$ = g_lingo->code1(g_lingo->c_ifcode);
+ g_lingo->code3(STOP, STOP, STOP);
+ g_lingo->code1(0); // Do not skip end
+ g_lingo->codeLabel(0); } // Mark beginning of the if() statement
+ ;
+elseif: tNLELSIF {
+ inst skipEnd;
+ WRITE_UINT32(&skipEnd, 1); // We have to skip end to avoid multiple executions
+ $$ = g_lingo->code1(g_lingo->c_ifcode);
+ g_lingo->code3(STOP, STOP, STOP);
+ g_lingo->code1(skipEnd); }
+ ;
+begin: /* nothing */ { $$ = g_lingo->_currentScript->size(); }
+ ;
+end: /* nothing */ { g_lingo->code1(STOP); $$ = g_lingo->_currentScript->size(); }
+ ;
+stmtlist: /* nothing */ { $$ = g_lingo->_currentScript->size(); }
+ | stmtlist nl
+ | stmtlist stmt
+ ;
+
+expr: INT {
+ $$ = g_lingo->code1(g_lingo->c_constpush);
+ inst i = 0;
+ WRITE_UINT32(&i, $1);
+ g_lingo->code1(i); }
+ | FLOAT {
+ $$ = g_lingo->code1(g_lingo->c_fconstpush);
+ g_lingo->codeFloat($1); }
+ | STRING {
+ $$ = g_lingo->code1(g_lingo->c_stringpush);
+ g_lingo->codeString($1->c_str()); }
+ | BLTINNOARGS {
+ $$ = g_lingo->code1(g_lingo->_handlers[*$1]->u.func);
+ $$ = g_lingo->code2(g_lingo->c_constpush, 0); // Put dummy value
+ delete $1; }
+ | ID '(' arglist ')' {
+ $$ = g_lingo->codeFunc($1, $3);
+ delete $1; }
+ | ID {
+ $$ = g_lingo->code1(g_lingo->c_eval);
+ g_lingo->codeString($1->c_str());
+ delete $1; }
+ | THEENTITY {
+ $$ = g_lingo->code2(g_lingo->c_constpush, 0); // Put dummy id
+ g_lingo->code1(g_lingo->c_theentitypush);
+ inst e = 0, f = 0;
+ WRITE_UINT32(&e, $1[0]);
+ WRITE_UINT32(&f, $1[1]);
+ g_lingo->code2(e, f); }
+ | THEENTITYWITHID expr {
+ $$ = g_lingo->code1(g_lingo->c_theentitypush);
+ inst e = 0, f = 0;
+ WRITE_UINT32(&e, $1[0]);
+ WRITE_UINT32(&f, $1[1]);
+ g_lingo->code2(e, f); }
+ | asgn
+ | expr '+' expr { g_lingo->code1(g_lingo->c_add); }
+ | expr '-' expr { g_lingo->code1(g_lingo->c_sub); }
+ | expr '*' expr { g_lingo->code1(g_lingo->c_mul); }
+ | expr '/' expr { g_lingo->code1(g_lingo->c_div); }
+ | expr '>' expr { g_lingo->code1(g_lingo->c_gt); }
+ | expr '<' expr { g_lingo->code1(g_lingo->c_lt); }
+ | expr tNEQ expr { g_lingo->code1(g_lingo->c_neq); }
+ | expr tGE expr { g_lingo->code1(g_lingo->c_ge); }
+ | expr tLE expr { g_lingo->code1(g_lingo->c_le); }
+ | expr tAND expr { g_lingo->code1(g_lingo->c_and); }
+ | expr tOR expr { g_lingo->code1(g_lingo->c_or); }
+ | tNOT expr %prec UNARY { g_lingo->code1(g_lingo->c_not); }
+ | expr '&' expr { g_lingo->code1(g_lingo->c_ampersand); }
+ | expr tCONCAT expr { g_lingo->code1(g_lingo->c_concat); }
+ | expr tCONTAINS expr { g_lingo->code1(g_lingo->c_contains); }
+ | expr tSTARTS expr { g_lingo->code1(g_lingo->c_starts); }
+ | '+' expr %prec UNARY { $$ = $2; }
+ | '-' expr %prec UNARY { $$ = $2; g_lingo->code1(g_lingo->c_negate); }
+ | '(' expr ')' { $$ = $2; }
+ | tSPRITE expr tINTERSECTS expr { g_lingo->code1(g_lingo->c_intersects); }
+ | tSPRITE expr tWITHIN expr { g_lingo->code1(g_lingo->c_within); }
+ ;
+
+func: tMCI STRING { g_lingo->code1(g_lingo->c_mci); g_lingo->codeString($2->c_str()); delete $2; }
+ | tMCIWAIT ID { g_lingo->code1(g_lingo->c_mciwait); g_lingo->codeString($2->c_str()); delete $2; }
+ | tPUT expr { g_lingo->code1(g_lingo->c_printtop); }
+ | gotofunc
+ | tEXIT { g_lingo->code2(g_lingo->c_constpush, (inst)0); // Push fake value on stack
+ g_lingo->code1(g_lingo->c_procret); }
+ | tGLOBAL globallist
+ ;
+
+globallist: ID { g_lingo->code1(g_lingo->c_global); g_lingo->codeString($1->c_str()); delete $1; }
+ | globallist ',' ID { g_lingo->code1(g_lingo->c_global); g_lingo->codeString($3->c_str()); delete $3; }
+ ;
+
+// go {to} {frame} whichFrame {of movie whichMovie}
+// go {to} {frame "Open23" of} movie whichMovie
+// go loop
+// go next
+// go previous
+// go to {frame} whichFrame {of movie whichMovie}
+// go to {frame whichFrame of} movie whichMovie
+
+gotofunc: tGO tLOOP { g_lingo->code1(g_lingo->c_gotoloop); }
+ | tGO tNEXT { g_lingo->code1(g_lingo->c_gotonext); }
+ | tGO tPREVIOUS { g_lingo->code1(g_lingo->c_gotoprevious); }
+ | tGO gotoframe {
+ g_lingo->code1(g_lingo->c_goto);
+ g_lingo->codeString($2->c_str());
+ g_lingo->codeString("");
+ delete $2; }
+ | tGO gotoframe gotomovie {
+ g_lingo->code1(g_lingo->c_goto);
+ g_lingo->codeString($2->c_str());
+ g_lingo->codeString($3->c_str());
+ delete $2;
+ delete $3; }
+ | tGO gotomovie {
+ g_lingo->code1(g_lingo->c_goto);
+ g_lingo->codeString("");
+ g_lingo->codeString($2->c_str());
+ delete $2; }
+ ;
+
+gotoframe: tTO tFRAME STRING { $$ = $3; }
+ | tFRAME STRING { $$ = $2; }
+ | tTO STRING { $$ = $2; }
+ | STRING { $$ = $1; }
+ ;
+
+gotomovie: tOF tMOVIE STRING { $$ = $3; }
+ | tMOVIE STRING { $$ = $2; }
+ | tTO tMOVIE STRING { $$ = $3; }
+ ;
+
+// macro
+//
+// Special Note The macro keyword is retained in Director 3.0 to maintain compatibility
+// with scripts developed under Version 2.0. When writing new scripts, or editing old
+// scripts, you should use handlers instead of macros. (Handlers are defined with the on keyword.)
+//
+// Syntax:
+//
+// -- [comment]
+// macro macroName [argument1] [, argument2]
+// [, argument3]
+// [statement1]
+// [statement2]
+//
+// Keyword. Defines a macro. A macro is a multiple-line script defined
+// in the Text window. Macros can accept arguments (inputs) and
+// optionally return a result. Macros can call other macros and can be
+// called from any other script or factory.
+//
+// The first line of a castmember in the Text window that contains a macro must be
+// a comment (--). You can define more than one macro in a given text castmember.
+// The macro definition ends where the next macro (or factory) begins.
+//
+// See also:
+// on keyword
+defn: tMACRO ID { g_lingo->_indef = true; g_lingo->_currentFactory.clear(); }
+ begin argdef nl argstore stmtlist {
+ g_lingo->code2(g_lingo->c_constpush, (inst)0); // Push fake value on stack
+ g_lingo->code1(g_lingo->c_procret);
+ g_lingo->define(*$2, $4, $5);
+ g_lingo->_indef = false; }
+ | tFACTORY ID {
+ g_lingo->codeFactory(*$2);
+ }
+ | tMETHOD ID { g_lingo->_indef = true; }
+ begin argdef nl argstore stmtlist {
+ g_lingo->code2(g_lingo->c_constpush, (inst)0); // Push fake value on stack
+ g_lingo->code1(g_lingo->c_procret);
+ g_lingo->define(*$2, $4, $5, &g_lingo->_currentFactory);
+ g_lingo->_indef = false; } ;
+argdef: /* nothing */ { $$ = 0; }
+ | ID { g_lingo->codeArg($1); $$ = 1; }
+ | argdef ',' ID { g_lingo->codeArg($3); $$ = $1 + 1; }
+ | argdef nl ',' ID { g_lingo->codeArg($4); $$ = $1 + 1; }
+ ;
+argstore: /* nothing */ { g_lingo->codeArgStore(); }
+ ;
+
+
+macro: ID begin arglist {
+ g_lingo->code1(g_lingo->c_call);
+ g_lingo->codeString($1->c_str());
+ inst numpar = 0;
+ WRITE_UINT32(&numpar, $3);
+ g_lingo->code1(numpar); }
+ ;
+
+arglist: /* nothing */ { $$ = 0; }
+ | expr { $$ = 1; }
+ | arglist ',' expr { $$ = $1 + 1; }
+ ;
+
+%%
diff --git a/engines/director/lingo/lingo-lex.cpp b/engines/director/lingo/lingo-lex.cpp
new file mode 100644
index 0000000000..a7f672d363
--- /dev/null
+++ b/engines/director/lingo/lingo-lex.cpp
@@ -0,0 +1,2308 @@
+#line 2 "engines/director/lingo/lingo-lex.cpp"
+
+#line 4 "engines/director/lingo/lingo-lex.cpp"
+
+#define YY_INT_ALIGNED short int
+
+/* A lexical scanner generated by flex */
+
+#define FLEX_SCANNER
+#define YY_FLEX_MAJOR_VERSION 2
+#define YY_FLEX_MINOR_VERSION 5
+#define YY_FLEX_SUBMINOR_VERSION 35
+#if YY_FLEX_SUBMINOR_VERSION > 0
+#define FLEX_BETA
+#endif
+
+/* First, we deal with platform-specific or compiler-specific issues. */
+
+/* begin standard C headers. */
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <stdlib.h>
+
+/* end standard C headers. */
+
+/* flex integer type definitions */
+
+#ifndef FLEXINT_H
+#define FLEXINT_H
+
+/* C99 systems have <inttypes.h>. Non-C99 systems may or may not. */
+
+#if defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L
+
+/* C99 says to define __STDC_LIMIT_MACROS before including stdint.h,
+ * if you want the limit (max/min) macros for int types.
+ */
+#ifndef __STDC_LIMIT_MACROS
+#define __STDC_LIMIT_MACROS 1
+#endif
+
+#include <inttypes.h>
+typedef int8_t flex_int8_t;
+typedef uint8_t flex_uint8_t;
+typedef int16_t flex_int16_t;
+typedef uint16_t flex_uint16_t;
+typedef int32_t flex_int32_t;
+typedef uint32_t flex_uint32_t;
+typedef uint64_t flex_uint64_t;
+#else
+typedef signed char flex_int8_t;
+typedef short int flex_int16_t;
+typedef int flex_int32_t;
+typedef unsigned char flex_uint8_t;
+typedef unsigned short int flex_uint16_t;
+typedef unsigned int flex_uint32_t;
+#endif /* ! C99 */
+
+/* Limits of integral types. */
+#ifndef INT8_MIN
+#define INT8_MIN (-128)
+#endif
+#ifndef INT16_MIN
+#define INT16_MIN (-32767-1)
+#endif
+#ifndef INT32_MIN
+#define INT32_MIN (-2147483647-1)
+#endif
+#ifndef INT8_MAX
+#define INT8_MAX (127)
+#endif
+#ifndef INT16_MAX
+#define INT16_MAX (32767)
+#endif
+#ifndef INT32_MAX
+#define INT32_MAX (2147483647)
+#endif
+#ifndef UINT8_MAX
+#define UINT8_MAX (255U)
+#endif
+#ifndef UINT16_MAX
+#define UINT16_MAX (65535U)
+#endif
+#ifndef UINT32_MAX
+#define UINT32_MAX (4294967295U)
+#endif
+
+#endif /* ! FLEXINT_H */
+
+#ifdef __cplusplus
+
+/* The "const" storage-class-modifier is valid. */
+#define YY_USE_CONST
+
+#else /* ! __cplusplus */
+
+/* C99 requires __STDC__ to be defined as 1. */
+#if defined (__STDC__)
+
+#define YY_USE_CONST
+
+#endif /* defined (__STDC__) */
+#endif /* ! __cplusplus */
+
+#ifdef YY_USE_CONST
+#define yyconst const
+#else
+#define yyconst
+#endif
+
+/* Returned upon end-of-file. */
+#define YY_NULL 0
+
+/* Promotes a possibly negative, possibly signed char to an unsigned
+ * integer for use as an array index. If the signed char is negative,
+ * we want to instead treat it as an 8-bit unsigned char, hence the
+ * double cast.
+ */
+#define YY_SC_TO_UI(c) ((unsigned int) (unsigned char) c)
+
+/* Enter a start condition. This macro really ought to take a parameter,
+ * but we do it the disgusting crufty way forced on us by the ()-less
+ * definition of BEGIN.
+ */
+#define BEGIN (yy_start) = 1 + 2 *
+
+/* Translate the current start state into a value that can be later handed
+ * to BEGIN to return to the state. The YYSTATE alias is for lex
+ * compatibility.
+ */
+#define YY_START (((yy_start) - 1) / 2)
+#define YYSTATE YY_START
+
+/* Action number for EOF rule of a given start state. */
+#define YY_STATE_EOF(state) (YY_END_OF_BUFFER + state + 1)
+
+/* Special action meaning "start processing a new file". */
+#define YY_NEW_FILE yyrestart(yyin )
+
+#define YY_END_OF_BUFFER_CHAR 0
+
+/* Size of default input buffer. */
+#ifndef YY_BUF_SIZE
+#define YY_BUF_SIZE 16384
+#endif
+
+/* The state buf must be large enough to hold one state per character in the main buffer.
+ */
+#define YY_STATE_BUF_SIZE ((YY_BUF_SIZE + 2) * sizeof(yy_state_type))
+
+#ifndef YY_TYPEDEF_YY_BUFFER_STATE
+#define YY_TYPEDEF_YY_BUFFER_STATE
+typedef struct yy_buffer_state *YY_BUFFER_STATE;
+#endif
+
+#ifndef YY_TYPEDEF_YY_SIZE_T
+#define YY_TYPEDEF_YY_SIZE_T
+typedef size_t yy_size_t;
+#endif
+
+extern yy_size_t yyleng;
+
+extern FILE *yyin, *yyout;
+
+#define EOB_ACT_CONTINUE_SCAN 0
+#define EOB_ACT_END_OF_FILE 1
+#define EOB_ACT_LAST_MATCH 2
+
+ #define YY_LESS_LINENO(n)
+
+/* Return all but the first "n" matched characters back to the input stream. */
+#define yyless(n) \
+ do \
+ { \
+ /* Undo effects of setting up yytext. */ \
+ int yyless_macro_arg = (n); \
+ YY_LESS_LINENO(yyless_macro_arg);\
+ *yy_cp = (yy_hold_char); \
+ YY_RESTORE_YY_MORE_OFFSET \
+ (yy_c_buf_p) = yy_cp = yy_bp + yyless_macro_arg - YY_MORE_ADJ; \
+ YY_DO_BEFORE_ACTION; /* set up yytext again */ \
+ } \
+ while ( 0 )
+
+#define unput(c) yyunput( c, (yytext_ptr) )
+
+#ifndef YY_STRUCT_YY_BUFFER_STATE
+#define YY_STRUCT_YY_BUFFER_STATE
+struct yy_buffer_state
+ {
+ FILE *yy_input_file;
+
+ char *yy_ch_buf; /* input buffer */
+ char *yy_buf_pos; /* current position in input buffer */
+
+ /* Size of input buffer in bytes, not including room for EOB
+ * characters.
+ */
+ yy_size_t yy_buf_size;
+
+ /* Number of characters read into yy_ch_buf, not including EOB
+ * characters.
+ */
+ yy_size_t yy_n_chars;
+
+ /* Whether we "own" the buffer - i.e., we know we created it,
+ * and can realloc() it to grow it, and should free() it to
+ * delete it.
+ */
+ int yy_is_our_buffer;
+
+ /* Whether this is an "interactive" input source; if so, and
+ * if we're using stdio for input, then we want to use getc()
+ * instead of fread(), to make sure we stop fetching input after
+ * each newline.
+ */
+ int yy_is_interactive;
+
+ /* Whether we're considered to be at the beginning of a line.
+ * If so, '^' rules will be active on the next match, otherwise
+ * not.
+ */
+ int yy_at_bol;
+
+ int yy_bs_lineno; /**< The line count. */
+ int yy_bs_column; /**< The column count. */
+
+ /* Whether to try to fill the input buffer when we reach the
+ * end of it.
+ */
+ int yy_fill_buffer;
+
+ int yy_buffer_status;
+
+#define YY_BUFFER_NEW 0
+#define YY_BUFFER_NORMAL 1
+ /* When an EOF's been seen but there's still some text to process
+ * then we mark the buffer as YY_EOF_PENDING, to indicate that we
+ * shouldn't try reading from the input source any more. We might
+ * still have a bunch of tokens to match, though, because of
+ * possible backing-up.
+ *
+ * When we actually see the EOF, we change the status to "new"
+ * (via yyrestart()), so that the user can continue scanning by
+ * just pointing yyin at a new input file.
+ */
+#define YY_BUFFER_EOF_PENDING 2
+
+ };
+#endif /* !YY_STRUCT_YY_BUFFER_STATE */
+
+/* Stack of input buffers. */
+static size_t yy_buffer_stack_top = 0; /**< index of top of stack. */
+static size_t yy_buffer_stack_max = 0; /**< capacity of stack. */
+static YY_BUFFER_STATE * yy_buffer_stack = 0; /**< Stack as an array. */
+
+/* We provide macros for accessing buffer states in case in the
+ * future we want to put the buffer states in a more general
+ * "scanner state".
+ *
+ * Returns the top of the stack, or NULL.
+ */
+#define YY_CURRENT_BUFFER ( (yy_buffer_stack) \
+ ? (yy_buffer_stack)[(yy_buffer_stack_top)] \
+ : NULL)
+
+/* Same as previous macro, but useful when we know that the buffer stack is not
+ * NULL or when we need an lvalue. For internal use only.
+ */
+#define YY_CURRENT_BUFFER_LVALUE (yy_buffer_stack)[(yy_buffer_stack_top)]
+
+/* yy_hold_char holds the character lost when yytext is formed. */
+static char yy_hold_char;
+static yy_size_t yy_n_chars; /* number of characters read into yy_ch_buf */
+yy_size_t yyleng;
+
+/* Points to current character in buffer. */
+static char *yy_c_buf_p = (char *) 0;
+static int yy_init = 0; /* whether we need to initialize */
+static int yy_start = 0; /* start state number */
+
+/* Flag which is used to allow yywrap()'s to do buffer switches
+ * instead of setting up a fresh yyin. A bit of a hack ...
+ */
+static int yy_did_buffer_switch_on_eof;
+
+void yyrestart (FILE *input_file );
+void yy_switch_to_buffer (YY_BUFFER_STATE new_buffer );
+YY_BUFFER_STATE yy_create_buffer (FILE *file,int size );
+void yy_delete_buffer (YY_BUFFER_STATE b );
+void yy_flush_buffer (YY_BUFFER_STATE b );
+void yypush_buffer_state (YY_BUFFER_STATE new_buffer );
+void yypop_buffer_state (void );
+
+static void yyensure_buffer_stack (void );
+static void yy_load_buffer_state (void );
+static void yy_init_buffer (YY_BUFFER_STATE b,FILE *file );
+
+#define YY_FLUSH_BUFFER yy_flush_buffer(YY_CURRENT_BUFFER )
+
+YY_BUFFER_STATE yy_scan_buffer (char *base,yy_size_t size );
+YY_BUFFER_STATE yy_scan_string (yyconst char *yy_str );
+YY_BUFFER_STATE yy_scan_bytes (yyconst char *bytes,yy_size_t len );
+
+void *yyalloc (yy_size_t );
+void *yyrealloc (void *,yy_size_t );
+void yyfree (void * );
+
+#define yy_new_buffer yy_create_buffer
+
+#define yy_set_interactive(is_interactive) \
+ { \
+ if ( ! YY_CURRENT_BUFFER ){ \
+ yyensure_buffer_stack (); \
+ YY_CURRENT_BUFFER_LVALUE = \
+ yy_create_buffer(yyin,YY_BUF_SIZE ); \
+ } \
+ YY_CURRENT_BUFFER_LVALUE->yy_is_interactive = is_interactive; \
+ }
+
+#define yy_set_bol(at_bol) \
+ { \
+ if ( ! YY_CURRENT_BUFFER ){\
+ yyensure_buffer_stack (); \
+ YY_CURRENT_BUFFER_LVALUE = \
+ yy_create_buffer(yyin,YY_BUF_SIZE ); \
+ } \
+ YY_CURRENT_BUFFER_LVALUE->yy_at_bol = at_bol; \
+ }
+
+#define YY_AT_BOL() (YY_CURRENT_BUFFER_LVALUE->yy_at_bol)
+
+/* Begin user sect3 */
+
+#define yywrap(n) 1
+#define YY_SKIP_YYWRAP
+
+typedef unsigned char YY_CHAR;
+
+FILE *yyin = (FILE *) 0, *yyout = (FILE *) 0;
+
+typedef int yy_state_type;
+
+extern int yylineno;
+
+int yylineno = 1;
+
+extern char *yytext;
+#define yytext_ptr yytext
+
+static yy_state_type yy_get_previous_state (void );
+static yy_state_type yy_try_NUL_trans (yy_state_type current_state );
+static int yy_get_next_buffer (void );
+static void yy_fatal_error (yyconst char msg[] );
+
+/* Done after the current pattern has been matched and before the
+ * corresponding action - sets up yytext.
+ */
+#define YY_DO_BEFORE_ACTION \
+ (yytext_ptr) = yy_bp; \
+ yyleng = (yy_size_t) (yy_cp - yy_bp); \
+ (yy_hold_char) = *yy_cp; \
+ *yy_cp = '\0'; \
+ (yy_c_buf_p) = yy_cp;
+
+#define YY_NUM_RULES 54
+#define YY_END_OF_BUFFER 55
+/* This struct is not used in this scanner,
+ but its presence is necessary. */
+struct yy_trans_info
+ {
+ flex_int32_t yy_verify;
+ flex_int32_t yy_nxt;
+ };
+static yyconst flex_int16_t yy_accept[186] =
+ { 0,
+ 0, 0, 55, 53, 3, 51, 51, 53, 53, 50,
+ 50, 50, 49, 50, 50, 47, 47, 47, 47, 47,
+ 47, 47, 47, 47, 47, 47, 47, 47, 47, 47,
+ 47, 47, 2, 2, 3, 51, 0, 0, 51, 0,
+ 0, 52, 46, 1, 48, 49, 45, 43, 44, 47,
+ 47, 47, 47, 47, 47, 47, 47, 47, 47, 16,
+ 7, 47, 47, 47, 47, 47, 47, 47, 47, 27,
+ 28, 47, 47, 47, 47, 47, 47, 47, 37, 47,
+ 47, 2, 2, 0, 1, 48, 4, 47, 47, 47,
+ 11, 47, 47, 47, 47, 47, 47, 47, 21, 47,
+
+ 47, 47, 26, 47, 30, 47, 32, 47, 47, 47,
+ 47, 47, 47, 0, 47, 6, 10, 13, 47, 47,
+ 47, 47, 18, 19, 47, 47, 47, 47, 25, 47,
+ 47, 47, 47, 0, 36, 41, 47, 39, 9, 47,
+ 47, 14, 47, 47, 20, 47, 47, 24, 47, 47,
+ 47, 47, 35, 42, 47, 0, 47, 47, 15, 47,
+ 47, 23, 47, 31, 38, 33, 0, 40, 0, 47,
+ 12, 47, 22, 47, 0, 8, 5, 47, 29, 0,
+ 47, 0, 17, 34, 0
+ } ;
+
+static yyconst flex_int32_t yy_ec[256] =
+ { 0,
+ 1, 1, 1, 1, 1, 1, 1, 1, 2, 3,
+ 1, 1, 4, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 5, 1, 6, 1, 1, 7, 8, 1, 7,
+ 7, 7, 7, 7, 9, 10, 7, 11, 11, 11,
+ 11, 11, 11, 11, 11, 11, 11, 7, 1, 12,
+ 13, 14, 1, 1, 15, 16, 17, 18, 19, 20,
+ 21, 22, 23, 24, 24, 25, 26, 27, 28, 29,
+ 24, 30, 31, 32, 33, 34, 35, 36, 37, 24,
+ 1, 1, 1, 7, 38, 1, 39, 40, 41, 42,
+
+ 43, 44, 45, 46, 47, 24, 24, 48, 49, 50,
+ 51, 52, 24, 53, 54, 55, 56, 57, 58, 59,
+ 60, 24, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1
+ } ;
+
+static yyconst flex_int32_t yy_meta[61] =
+ { 0,
+ 1, 2, 3, 4, 2, 1, 1, 1, 1, 1,
+ 5, 1, 1, 1, 6, 6, 6, 6, 6, 6,
+ 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
+ 6, 6, 6, 6, 6, 6, 6, 5, 6, 6,
+ 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
+ 6, 6, 6, 6, 6, 6, 6, 6, 6, 6
+ } ;
+
+static yyconst flex_int16_t yy_base[191] =
+ { 0,
+ 0, 59, 210, 446, 63, 67, 71, 75, 192, 446,
+ 150, 148, 52, 68, 137, 56, 0, 56, 57, 67,
+ 72, 68, 68, 69, 112, 70, 79, 103, 81, 109,
+ 120, 121, 170, 174, 178, 101, 182, 186, 190, 97,
+ 143, 446, 446, 0, 90, 124, 446, 446, 446, 0,
+ 112, 110, 104, 115, 147, 167, 179, 182, 119, 0,
+ 0, 167, 172, 184, 179, 172, 173, 172, 179, 0,
+ 0, 193, 183, 187, 185, 188, 198, 200, 0, 205,
+ 200, 242, 254, 211, 0, 80, 0, 217, 223, 232,
+ 0, 221, 222, 234, 245, 243, 235, 236, 232, 246,
+
+ 246, 242, 0, 244, 0, 260, 0, 257, 252, 279,
+ 261, 266, 273, 277, 283, 0, 0, 0, 272, 283,
+ 292, 279, 0, 0, 282, 297, 287, 294, 0, 293,
+ 302, 289, 292, 201, 0, 0, 299, 302, 325, 305,
+ 304, 0, 310, 308, 0, 320, 327, 0, 320, 318,
+ 327, 320, 351, 0, 327, 358, 328, 322, 0, 342,
+ 332, 0, 332, 0, 0, 0, 370, 0, 346, 337,
+ 0, 359, 0, 348, 360, 446, 0, 351, 0, 384,
+ 353, 390, 0, 391, 446, 421, 423, 429, 434, 439
+ } ;
+
+static yyconst flex_int16_t yy_def[191] =
+ { 0,
+ 185, 1, 185, 185, 185, 185, 185, 185, 186, 185,
+ 185, 185, 185, 185, 185, 187, 187, 187, 187, 187,
+ 187, 187, 187, 187, 187, 187, 187, 187, 187, 187,
+ 187, 187, 185, 185, 185, 185, 185, 185, 185, 185,
+ 186, 185, 185, 188, 185, 185, 185, 185, 185, 187,
+ 187, 187, 187, 187, 187, 187, 187, 187, 187, 187,
+ 187, 187, 187, 187, 187, 187, 187, 187, 187, 187,
+ 187, 187, 187, 187, 187, 187, 187, 187, 187, 187,
+ 187, 185, 185, 185, 188, 185, 187, 187, 187, 187,
+ 187, 187, 187, 187, 187, 187, 187, 187, 187, 187,
+
+ 187, 187, 187, 187, 187, 187, 187, 187, 187, 187,
+ 187, 187, 187, 185, 187, 187, 187, 187, 187, 187,
+ 187, 187, 187, 187, 187, 187, 187, 187, 187, 187,
+ 187, 187, 187, 189, 187, 187, 187, 187, 185, 187,
+ 187, 187, 187, 187, 187, 187, 187, 187, 187, 187,
+ 187, 187, 189, 187, 187, 185, 187, 187, 187, 187,
+ 187, 187, 187, 187, 187, 187, 185, 187, 185, 187,
+ 187, 187, 187, 187, 185, 185, 187, 187, 187, 185,
+ 187, 190, 187, 190, 0, 185, 185, 185, 185, 185
+ } ;
+
+static yyconst flex_int16_t yy_nxt[507] =
+ { 0,
+ 4, 5, 6, 7, 8, 9, 10, 11, 12, 4,
+ 13, 14, 10, 15, 16, 17, 18, 19, 20, 21,
+ 22, 17, 23, 17, 24, 25, 26, 27, 28, 29,
+ 30, 31, 17, 17, 32, 17, 17, 17, 16, 17,
+ 18, 19, 20, 21, 22, 17, 23, 24, 25, 26,
+ 27, 28, 29, 30, 31, 17, 17, 32, 17, 17,
+ 33, 45, 46, 34, 35, 36, 36, 37, 38, 39,
+ 39, 38, 38, 39, 39, 38, 37, 36, 36, 37,
+ 47, 48, 51, 52, 53, 40, 57, 61, 68, 40,
+ 86, 54, 59, 55, 62, 60, 63, 69, 70, 74,
+
+ 86, 58, 56, 36, 36, 51, 52, 53, 71, 40,
+ 57, 61, 68, 40, 54, 59, 55, 62, 60, 63,
+ 69, 84, 70, 74, 58, 56, 64, 75, 65, 87,
+ 66, 71, 72, 45, 46, 73, 88, 76, 89, 67,
+ 77, 78, 80, 81, 84, 90, 95, 79, 42, 49,
+ 64, 75, 65, 87, 66, 72, 44, 43, 73, 88,
+ 76, 89, 67, 77, 91, 78, 80, 81, 90, 95,
+ 79, 82, 36, 36, 83, 83, 36, 36, 83, 35,
+ 36, 36, 37, 37, 36, 36, 37, 38, 91, 92,
+ 38, 38, 39, 39, 38, 93, 94, 42, 96, 97,
+
+ 98, 99, 134, 100, 40, 134, 101, 102, 40, 185,
+ 103, 104, 109, 92, 105, 106, 107, 108, 110, 93,
+ 94, 96, 97, 111, 98, 99, 100, 112, 40, 101,
+ 102, 113, 40, 103, 185, 104, 109, 105, 106, 107,
+ 108, 114, 110, 82, 36, 36, 83, 111, 115, 116,
+ 117, 112, 118, 119, 113, 83, 36, 36, 83, 120,
+ 121, 122, 185, 124, 114, 125, 126, 127, 128, 185,
+ 123, 115, 116, 129, 117, 118, 119, 130, 131, 132,
+ 134, 133, 120, 134, 121, 122, 124, 136, 125, 126,
+ 137, 127, 128, 123, 138, 139, 129, 140, 185, 141,
+
+ 130, 142, 131, 132, 133, 135, 143, 185, 144, 145,
+ 136, 146, 148, 137, 147, 149, 150, 154, 138, 139,
+ 151, 140, 141, 152, 155, 142, 156, 157, 135, 156,
+ 143, 144, 145, 158, 159, 146, 148, 147, 160, 149,
+ 150, 154, 161, 151, 162, 165, 152, 163, 155, 164,
+ 166, 157, 167, 168, 170, 167, 158, 159, 171, 156,
+ 172, 160, 156, 173, 174, 176, 161, 177, 162, 165,
+ 163, 167, 164, 166, 167, 178, 168, 170, 179, 180,
+ 169, 171, 181, 183, 172, 182, 173, 174, 182, 176,
+ 177, 182, 185, 185, 182, 185, 185, 175, 185, 178,
+
+ 185, 179, 185, 180, 169, 181, 183, 185, 185, 185,
+ 185, 185, 185, 185, 185, 185, 185, 185, 185, 185,
+ 175, 41, 41, 185, 41, 41, 41, 50, 50, 85,
+ 85, 185, 185, 85, 85, 153, 185, 185, 185, 153,
+ 184, 185, 185, 185, 184, 3, 185, 185, 185, 185,
+ 185, 185, 185, 185, 185, 185, 185, 185, 185, 185,
+ 185, 185, 185, 185, 185, 185, 185, 185, 185, 185,
+ 185, 185, 185, 185, 185, 185, 185, 185, 185, 185,
+ 185, 185, 185, 185, 185, 185, 185, 185, 185, 185,
+ 185, 185, 185, 185, 185, 185, 185, 185, 185, 185,
+
+ 185, 185, 185, 185, 185, 185
+ } ;
+
+static yyconst flex_int16_t yy_chk[507] =
+ { 0,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 2, 13, 13, 2, 5, 5, 5, 5, 6, 6,
+ 6, 6, 7, 7, 7, 7, 8, 8, 8, 8,
+ 14, 14, 16, 18, 19, 6, 21, 23, 26, 7,
+ 86, 20, 22, 20, 23, 22, 24, 26, 27, 29,
+
+ 45, 21, 20, 36, 36, 16, 18, 19, 27, 6,
+ 21, 23, 26, 7, 20, 22, 20, 23, 22, 24,
+ 26, 40, 27, 29, 21, 20, 25, 30, 25, 51,
+ 25, 27, 28, 46, 46, 28, 52, 30, 53, 25,
+ 30, 31, 32, 32, 40, 54, 59, 31, 41, 15,
+ 25, 30, 25, 51, 25, 28, 12, 11, 28, 52,
+ 30, 53, 25, 30, 55, 31, 32, 32, 54, 59,
+ 31, 33, 33, 33, 33, 34, 34, 34, 34, 35,
+ 35, 35, 35, 37, 37, 37, 37, 38, 55, 56,
+ 38, 39, 39, 39, 39, 57, 58, 9, 62, 63,
+
+ 64, 65, 134, 66, 38, 134, 67, 68, 39, 3,
+ 69, 72, 77, 56, 73, 74, 75, 76, 78, 57,
+ 58, 62, 63, 80, 64, 65, 66, 80, 38, 67,
+ 68, 81, 39, 69, 0, 72, 77, 73, 74, 75,
+ 76, 84, 78, 82, 82, 82, 82, 80, 88, 89,
+ 90, 80, 92, 93, 81, 83, 83, 83, 83, 94,
+ 95, 96, 0, 97, 84, 98, 99, 100, 101, 0,
+ 96, 88, 89, 102, 90, 92, 93, 104, 106, 108,
+ 110, 109, 94, 110, 95, 96, 97, 111, 98, 99,
+ 112, 100, 101, 96, 113, 114, 102, 115, 0, 119,
+
+ 104, 120, 106, 108, 109, 110, 121, 0, 122, 125,
+ 111, 126, 128, 112, 127, 130, 131, 137, 113, 114,
+ 132, 115, 119, 133, 138, 120, 139, 140, 110, 139,
+ 121, 122, 125, 141, 143, 126, 128, 127, 144, 130,
+ 131, 137, 146, 132, 147, 151, 133, 149, 138, 150,
+ 152, 140, 153, 155, 157, 153, 141, 143, 158, 156,
+ 160, 144, 156, 161, 163, 169, 146, 170, 147, 151,
+ 149, 167, 150, 152, 167, 172, 155, 157, 174, 175,
+ 156, 158, 178, 181, 160, 180, 161, 163, 180, 169,
+ 170, 182, 184, 0, 182, 184, 0, 167, 0, 172,
+
+ 0, 174, 0, 175, 156, 178, 181, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 167, 186, 186, 0, 186, 186, 186, 187, 187, 188,
+ 188, 0, 0, 188, 188, 189, 0, 0, 0, 189,
+ 190, 0, 0, 0, 190, 185, 185, 185, 185, 185,
+ 185, 185, 185, 185, 185, 185, 185, 185, 185, 185,
+ 185, 185, 185, 185, 185, 185, 185, 185, 185, 185,
+ 185, 185, 185, 185, 185, 185, 185, 185, 185, 185,
+ 185, 185, 185, 185, 185, 185, 185, 185, 185, 185,
+ 185, 185, 185, 185, 185, 185, 185, 185, 185, 185,
+
+ 185, 185, 185, 185, 185, 185
+ } ;
+
+static yy_state_type yy_last_accepting_state;
+static char *yy_last_accepting_cpos;
+
+extern int yy_flex_debug;
+int yy_flex_debug = 0;
+
+/* The intent behind this definition is that it'll catch
+ * any uses of REJECT which flex missed.
+ */
+#define REJECT reject_used_but_not_detected
+#define yymore() yymore_used_but_not_detected
+#define YY_MORE_ADJ 0
+#define YY_RESTORE_YY_MORE_OFFSET
+char *yytext;
+#line 1 "engines/director/lingo/lingo-lex.l"
+/* 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 2
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+#line 25 "engines/director/lingo/lingo-lex.l"
+
+#define FORBIDDEN_SYMBOL_ALLOW_ALL
+
+#include "common/str.h"
+
+#include "director/lingo/lingo.h"
+#include "director/lingo/lingo-gr.h"
+
+using namespace Director;
+
+int yyparse();
+static void count() {
+ g_lingo->_colnumber += strlen(yytext);
+}
+
+static void countnl() {
+ char *p = yytext;
+
+ while(*p == '\n' || *p == '\r')
+ p++;
+
+ g_lingo->_linenumber++;
+ g_lingo->_colnumber = strlen(p);
+}
+
+#line 673 "engines/director/lingo/lingo-lex.cpp"
+
+#define INITIAL 0
+
+#ifndef YY_NO_UNISTD_H
+/* Special case for "unistd.h", since it is non-ANSI. We include it way
+ * down here because we want the user's section 1 to have been scanned first.
+ * The user has a chance to override it with an option.
+ */
+#include <unistd.h>
+#endif
+
+#ifndef YY_EXTRA_TYPE
+#define YY_EXTRA_TYPE void *
+#endif
+
+static int yy_init_globals (void );
+
+/* Accessor methods to globals.
+ These are made visible to non-reentrant scanners for convenience. */
+
+int yylex_destroy (void );
+
+int yyget_debug (void );
+
+void yyset_debug (int debug_flag );
+
+YY_EXTRA_TYPE yyget_extra (void );
+
+void yyset_extra (YY_EXTRA_TYPE user_defined );
+
+FILE *yyget_in (void );
+
+void yyset_in (FILE * in_str );
+
+FILE *yyget_out (void );
+
+void yyset_out (FILE * out_str );
+
+yy_size_t yyget_leng (void );
+
+char *yyget_text (void );
+
+int yyget_lineno (void );
+
+void yyset_lineno (int line_number );
+
+/* Macros after this point can all be overridden by user definitions in
+ * section 1.
+ */
+
+#ifndef YY_SKIP_YYWRAP
+#ifdef __cplusplus
+extern "C" int yywrap (void );
+#else
+extern int yywrap (void );
+#endif
+#endif
+
+ static void yyunput (int c,char *buf_ptr );
+
+#ifndef yytext_ptr
+static void yy_flex_strncpy (char *,yyconst char *,int );
+#endif
+
+#ifdef YY_NEED_STRLEN
+static int yy_flex_strlen (yyconst char * );
+#endif
+
+#ifndef YY_NO_INPUT
+
+#ifdef __cplusplus
+static int yyinput (void );
+#else
+static int input (void );
+#endif
+
+#endif
+
+/* Amount of stuff to slurp up with each read. */
+#ifndef YY_READ_BUF_SIZE
+#define YY_READ_BUF_SIZE 8192
+#endif
+
+/* Copy whatever the last rule matched to the standard output. */
+#ifndef ECHO
+/* This used to be an fputs(), but since the string might contain NUL's,
+ * we now use fwrite().
+ */
+#define ECHO fwrite( yytext, yyleng, 1, yyout )
+#endif
+
+/* Gets input and stuffs it into "buf". number of characters read, or YY_NULL,
+ * is returned in "result".
+ */
+#ifndef YY_INPUT
+#define YY_INPUT(buf,result,max_size) \
+ if ( YY_CURRENT_BUFFER_LVALUE->yy_is_interactive ) \
+ { \
+ int c = '*'; \
+ yy_size_t n; \
+ for ( n = 0; n < max_size && \
+ (c = getc( yyin )) != EOF && c != '\n'; ++n ) \
+ buf[n] = (char) c; \
+ if ( c == '\n' ) \
+ buf[n++] = (char) c; \
+ if ( c == EOF && ferror( yyin ) ) \
+ YY_FATAL_ERROR( "input in flex scanner failed" ); \
+ result = n; \
+ } \
+ else \
+ { \
+ errno=0; \
+ while ( (result = fread(buf, 1, max_size, yyin))==0 && ferror(yyin)) \
+ { \
+ if( errno != EINTR) \
+ { \
+ YY_FATAL_ERROR( "input in flex scanner failed" ); \
+ break; \
+ } \
+ errno=0; \
+ clearerr(yyin); \
+ } \
+ }\
+\
+
+#endif
+
+/* No semi-colon after return; correct usage is to write "yyterminate();" -
+ * we don't want an extra ';' after the "return" because that will cause
+ * some compilers to complain about unreachable statements.
+ */
+#ifndef yyterminate
+#define yyterminate() return YY_NULL
+#endif
+
+/* Number of entries by which start-condition stack grows. */
+#ifndef YY_START_STACK_INCR
+#define YY_START_STACK_INCR 25
+#endif
+
+/* Report a fatal error. */
+#ifndef YY_FATAL_ERROR
+#define YY_FATAL_ERROR(msg) yy_fatal_error( msg )
+#endif
+
+/* end tables serialization structures and prototypes */
+
+/* Default declaration of generated scanner - a define so the user can
+ * easily add parameters.
+ */
+#ifndef YY_DECL
+#define YY_DECL_IS_OURS 1
+
+extern int yylex (void);
+
+#define YY_DECL int yylex (void)
+#endif /* !YY_DECL */
+
+/* Code executed at the beginning of each rule, after yytext and yyleng
+ * have been set up.
+ */
+#ifndef YY_USER_ACTION
+#define YY_USER_ACTION
+#endif
+
+/* Code executed at the end of each rule. */
+#ifndef YY_BREAK
+#define YY_BREAK break;
+#endif
+
+#define YY_RULE_SETUP \
+ if ( yyleng > 0 ) \
+ YY_CURRENT_BUFFER_LVALUE->yy_at_bol = \
+ (yytext[yyleng - 1] == '\n'); \
+ YY_USER_ACTION
+
+/** The main scanner function which does all the work.
+ */
+YY_DECL
+{
+ register yy_state_type yy_current_state;
+ register char *yy_cp, *yy_bp;
+ register int yy_act;
+
+#line 60 "engines/director/lingo/lingo-lex.l"
+
+
+#line 861 "engines/director/lingo/lingo-lex.cpp"
+
+ if ( !(yy_init) )
+ {
+ (yy_init) = 1;
+
+#ifdef YY_USER_INIT
+ YY_USER_INIT;
+#endif
+
+ if ( ! (yy_start) )
+ (yy_start) = 1; /* first start state */
+
+ if ( ! yyin )
+ yyin = stdin;
+
+ if ( ! yyout )
+ yyout = stdout;
+
+ if ( ! YY_CURRENT_BUFFER ) {
+ yyensure_buffer_stack ();
+ YY_CURRENT_BUFFER_LVALUE =
+ yy_create_buffer(yyin,YY_BUF_SIZE );
+ }
+
+ yy_load_buffer_state( );
+ }
+
+ while ( 1 ) /* loops until end-of-file is reached */
+ {
+ yy_cp = (yy_c_buf_p);
+
+ /* Support of yytext. */
+ *yy_cp = (yy_hold_char);
+
+ /* yy_bp points to the position in yy_ch_buf of the start of
+ * the current run.
+ */
+ yy_bp = yy_cp;
+
+ yy_current_state = (yy_start);
+ yy_current_state += YY_AT_BOL();
+yy_match:
+ do
+ {
+ register YY_CHAR yy_c = yy_ec[YY_SC_TO_UI(*yy_cp)];
+ if ( yy_accept[yy_current_state] )
+ {
+ (yy_last_accepting_state) = yy_current_state;
+ (yy_last_accepting_cpos) = yy_cp;
+ }
+ while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state )
+ {
+ yy_current_state = (int) yy_def[yy_current_state];
+ if ( yy_current_state >= 186 )
+ yy_c = yy_meta[(unsigned int) yy_c];
+ }
+ yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c];
+ ++yy_cp;
+ }
+ while ( yy_base[yy_current_state] != 446 );
+
+yy_find_action:
+ yy_act = yy_accept[yy_current_state];
+ if ( yy_act == 0 )
+ { /* have to back up */
+ yy_cp = (yy_last_accepting_cpos);
+ yy_current_state = (yy_last_accepting_state);
+ yy_act = yy_accept[yy_current_state];
+ }
+
+ YY_DO_BEFORE_ACTION;
+
+do_action: /* This label is used only to access EOF actions. */
+
+ switch ( yy_act )
+ { /* beginning of action switch */
+ case 0: /* must back up */
+ /* undo the effects of YY_DO_BEFORE_ACTION */
+ *yy_cp = (yy_hold_char);
+ yy_cp = (yy_last_accepting_cpos);
+ yy_current_state = (yy_last_accepting_state);
+ goto yy_find_action;
+
+case 1:
+YY_RULE_SETUP
+#line 62 "engines/director/lingo/lingo-lex.l"
+
+ YY_BREAK
+case 2:
+YY_RULE_SETUP
+#line 63 "engines/director/lingo/lingo-lex.l"
+{ count(); }
+ YY_BREAK
+case 3:
+YY_RULE_SETUP
+#line 64 "engines/director/lingo/lingo-lex.l"
+{ count(); return ' '; }
+ YY_BREAK
+case 4:
+YY_RULE_SETUP
+#line 66 "engines/director/lingo/lingo-lex.l"
+{ count(); return tAND; }
+ YY_BREAK
+case 5:
+YY_RULE_SETUP
+#line 67 "engines/director/lingo/lingo-lex.l"
+{ count(); return tCONTAINS; }
+ YY_BREAK
+case 6:
+YY_RULE_SETUP
+#line 68 "engines/director/lingo/lingo-lex.l"
+{ count(); return tDOWN; }
+ YY_BREAK
+case 7:
+YY_RULE_SETUP
+#line 69 "engines/director/lingo/lingo-lex.l"
+{ count(); return tIF; }
+ YY_BREAK
+case 8:
+/* rule 8 can match eol */
+YY_RULE_SETUP
+#line 70 "engines/director/lingo/lingo-lex.l"
+{ countnl(); return tNLELSIF; }
+ YY_BREAK
+case 9:
+/* rule 9 can match eol */
+YY_RULE_SETUP
+#line 71 "engines/director/lingo/lingo-lex.l"
+{ countnl(); return tNLELSE; }
+ YY_BREAK
+case 10:
+YY_RULE_SETUP
+#line 72 "engines/director/lingo/lingo-lex.l"
+{ count(); return tELSE; }
+ YY_BREAK
+case 11:
+YY_RULE_SETUP
+#line 73 "engines/director/lingo/lingo-lex.l"
+{ count(); return tEND; }
+ YY_BREAK
+case 12:
+YY_RULE_SETUP
+#line 74 "engines/director/lingo/lingo-lex.l"
+{ count(); return tFACTORY; }
+ YY_BREAK
+case 13:
+YY_RULE_SETUP
+#line 75 "engines/director/lingo/lingo-lex.l"
+{ count(); return tEXIT; }
+ YY_BREAK
+case 14:
+YY_RULE_SETUP
+#line 76 "engines/director/lingo/lingo-lex.l"
+{ count(); return tFRAME; }
+ YY_BREAK
+case 15:
+YY_RULE_SETUP
+#line 77 "engines/director/lingo/lingo-lex.l"
+{ count(); return tGLOBAL; }
+ YY_BREAK
+case 16:
+YY_RULE_SETUP
+#line 78 "engines/director/lingo/lingo-lex.l"
+{ count(); return tGO; }
+ YY_BREAK
+case 17:
+YY_RULE_SETUP
+#line 79 "engines/director/lingo/lingo-lex.l"
+{ count(); return tINTERSECTS; }
+ YY_BREAK
+case 18:
+YY_RULE_SETUP
+#line 80 "engines/director/lingo/lingo-lex.l"
+{ count(); return tINTO; }
+ YY_BREAK
+case 19:
+YY_RULE_SETUP
+#line 81 "engines/director/lingo/lingo-lex.l"
+{ count(); return tLOOP; }
+ YY_BREAK
+case 20:
+YY_RULE_SETUP
+#line 82 "engines/director/lingo/lingo-lex.l"
+{ count(); return tMACRO; }
+ YY_BREAK
+case 21:
+YY_RULE_SETUP
+#line 83 "engines/director/lingo/lingo-lex.l"
+{ count(); return tMCI; }
+ YY_BREAK
+case 22:
+YY_RULE_SETUP
+#line 84 "engines/director/lingo/lingo-lex.l"
+{ count(); return tMCIWAIT; }
+ YY_BREAK
+case 23:
+YY_RULE_SETUP
+#line 85 "engines/director/lingo/lingo-lex.l"
+{ count(); return tMETHOD; }
+ YY_BREAK
+case 24:
+YY_RULE_SETUP
+#line 86 "engines/director/lingo/lingo-lex.l"
+{ count(); return tMOVIE; }
+ YY_BREAK
+case 25:
+YY_RULE_SETUP
+#line 87 "engines/director/lingo/lingo-lex.l"
+{ count(); return tNEXT; }
+ YY_BREAK
+case 26:
+YY_RULE_SETUP
+#line 88 "engines/director/lingo/lingo-lex.l"
+{ count(); return tNOT; }
+ YY_BREAK
+case 27:
+YY_RULE_SETUP
+#line 89 "engines/director/lingo/lingo-lex.l"
+{ count(); return tOF; }
+ YY_BREAK
+case 28:
+YY_RULE_SETUP
+#line 90 "engines/director/lingo/lingo-lex.l"
+{ count(); return tOR; }
+ YY_BREAK
+case 29:
+YY_RULE_SETUP
+#line 91 "engines/director/lingo/lingo-lex.l"
+{ count(); return tPREVIOUS; }
+ YY_BREAK
+case 30:
+YY_RULE_SETUP
+#line 92 "engines/director/lingo/lingo-lex.l"
+{ count(); return tPUT; }
+ YY_BREAK
+case 31:
+YY_RULE_SETUP
+#line 93 "engines/director/lingo/lingo-lex.l"
+{ count(); return tREPEAT; }
+ YY_BREAK
+case 32:
+YY_RULE_SETUP
+#line 94 "engines/director/lingo/lingo-lex.l"
+{ count(); return tSET; }
+ YY_BREAK
+case 33:
+YY_RULE_SETUP
+#line 95 "engines/director/lingo/lingo-lex.l"
+{ count(); return tSTARTS; }
+ YY_BREAK
+case 34:
+YY_RULE_SETUP
+#line 96 "engines/director/lingo/lingo-lex.l"
+{
+ count();
+
+ const char *ptr = &yytext[4]; // Skip 'the '
+ while (*ptr == ' ' || *ptr == '\t')
+ ptr++;
+
+ Common::String field;
+ while (*ptr != ' ' && *ptr != '\t')
+ field += *ptr++;
+
+ while (*ptr == ' ' || *ptr == '\t')
+ ptr++;
+
+ ptr += 3; // Skip 'of '
+
+ while (*ptr == ' ' || *ptr == '\t')
+ ptr++;
+
+ if (g_lingo->_theEntities.contains(ptr)) {
+ field = Common::String::format("%d%s", g_lingo->_theEntities[ptr]->entity, field.c_str());
+
+ if (!g_lingo->_theEntityFields.contains(field)) {
+ error("Unhandled the field %s", ptr);
+ }
+
+ if (g_lingo->_theEntityFields[field]->entity != g_lingo->_theEntities[ptr]->entity)
+ error("Unsupported field '%s' for entity '%s'", field.c_str(), ptr);
+
+ yylval.e[0] = g_lingo->_theEntities[ptr]->entity;
+ yylval.e[1] = g_lingo->_theEntityFields[field]->field;
+
+ if (g_lingo->_theEntities[ptr]->hasId)
+ return THEENTITYWITHID;
+ else
+ return THEENTITY;
+ }
+
+ warning("Unhandled the entity %s", ptr);
+ }
+ YY_BREAK
+case 35:
+YY_RULE_SETUP
+#line 136 "engines/director/lingo/lingo-lex.l"
+{
+ count();
+
+ const char *ptr = &yytext[4]; // Skip 'the '
+ while (*ptr == ' ' || *ptr == '\t')
+ ptr++;
+
+ if (g_lingo->_theEntities.contains(ptr)) {
+ yylval.e[0] = g_lingo->_theEntities[ptr]->entity;
+ yylval.e[1] = 0; // No field
+
+ if (g_lingo->_theEntities[ptr]->hasId)
+ return THEENTITYWITHID;
+ else
+ return THEENTITY;
+ }
+
+ warning("Unhandled the entity %s", ptr);
+ }
+ YY_BREAK
+case 36:
+YY_RULE_SETUP
+#line 155 "engines/director/lingo/lingo-lex.l"
+{ count(); return tTHEN; }
+ YY_BREAK
+case 37:
+YY_RULE_SETUP
+#line 156 "engines/director/lingo/lingo-lex.l"
+{ count(); return tTO; }
+ YY_BREAK
+case 38:
+YY_RULE_SETUP
+#line 157 "engines/director/lingo/lingo-lex.l"
+{ count(); return tSPRITE; }
+ YY_BREAK
+case 39:
+YY_RULE_SETUP
+#line 158 "engines/director/lingo/lingo-lex.l"
+{ count(); return tWITH; }
+ YY_BREAK
+case 40:
+YY_RULE_SETUP
+#line 159 "engines/director/lingo/lingo-lex.l"
+{ count(); return tWITHIN; }
+ YY_BREAK
+case 41:
+YY_RULE_SETUP
+#line 160 "engines/director/lingo/lingo-lex.l"
+{ count(); return tWHEN; }
+ YY_BREAK
+case 42:
+YY_RULE_SETUP
+#line 161 "engines/director/lingo/lingo-lex.l"
+{ count(); return tWHILE; }
+ YY_BREAK
+case 43:
+YY_RULE_SETUP
+#line 163 "engines/director/lingo/lingo-lex.l"
+{ count(); return tNEQ; }
+ YY_BREAK
+case 44:
+YY_RULE_SETUP
+#line 164 "engines/director/lingo/lingo-lex.l"
+{ count(); return tGE; }
+ YY_BREAK
+case 45:
+YY_RULE_SETUP
+#line 165 "engines/director/lingo/lingo-lex.l"
+{ count(); return tLE; }
+ YY_BREAK
+case 46:
+YY_RULE_SETUP
+#line 166 "engines/director/lingo/lingo-lex.l"
+{ count(); return tCONCAT; }
+ YY_BREAK
+case 47:
+YY_RULE_SETUP
+#line 168 "engines/director/lingo/lingo-lex.l"
+{
+ count();
+ yylval.s = new Common::String(yytext);
+
+ if (g_lingo->_handlers.contains(yytext)) {
+ if (g_lingo->_handlers[yytext]->type == BLTIN && g_lingo->_handlers[yytext]->nargs == -1)
+ return BLTINNOARGS;
+ }
+
+ return ID;
+ }
+ YY_BREAK
+case 48:
+YY_RULE_SETUP
+#line 179 "engines/director/lingo/lingo-lex.l"
+{ count(); yylval.f = atof(yytext); return FLOAT; }
+ YY_BREAK
+case 49:
+YY_RULE_SETUP
+#line 180 "engines/director/lingo/lingo-lex.l"
+{ count(); yylval.i = strtol(yytext, NULL, 10); return INT; }
+ YY_BREAK
+case 50:
+YY_RULE_SETUP
+#line 181 "engines/director/lingo/lingo-lex.l"
+{ count(); return *yytext; }
+ YY_BREAK
+case 51:
+/* rule 51 can match eol */
+YY_RULE_SETUP
+#line 182 "engines/director/lingo/lingo-lex.l"
+{ return '\n'; }
+ YY_BREAK
+case 52:
+YY_RULE_SETUP
+#line 183 "engines/director/lingo/lingo-lex.l"
+{ count(); yylval.s = new Common::String(&yytext[1]); yylval.s->deleteLastChar(); return STRING; }
+ YY_BREAK
+case 53:
+YY_RULE_SETUP
+#line 184 "engines/director/lingo/lingo-lex.l"
+
+ YY_BREAK
+case 54:
+YY_RULE_SETUP
+#line 186 "engines/director/lingo/lingo-lex.l"
+ECHO;
+ YY_BREAK
+#line 1285 "engines/director/lingo/lingo-lex.cpp"
+case YY_STATE_EOF(INITIAL):
+ yyterminate();
+
+ case YY_END_OF_BUFFER:
+ {
+ /* Amount of text matched not including the EOB char. */
+ int yy_amount_of_matched_text = (int) (yy_cp - (yytext_ptr)) - 1;
+
+ /* Undo the effects of YY_DO_BEFORE_ACTION. */
+ *yy_cp = (yy_hold_char);
+ YY_RESTORE_YY_MORE_OFFSET
+
+ if ( YY_CURRENT_BUFFER_LVALUE->yy_buffer_status == YY_BUFFER_NEW )
+ {
+ /* We're scanning a new file or input source. It's
+ * possible that this happened because the user
+ * just pointed yyin at a new source and called
+ * yylex(). If so, then we have to assure
+ * consistency between YY_CURRENT_BUFFER and our
+ * globals. Here is the right place to do so, because
+ * this is the first action (other than possibly a
+ * back-up) that will match for the new input source.
+ */
+ (yy_n_chars) = YY_CURRENT_BUFFER_LVALUE->yy_n_chars;
+ YY_CURRENT_BUFFER_LVALUE->yy_input_file = yyin;
+ YY_CURRENT_BUFFER_LVALUE->yy_buffer_status = YY_BUFFER_NORMAL;
+ }
+
+ /* Note that here we test for yy_c_buf_p "<=" to the position
+ * of the first EOB in the buffer, since yy_c_buf_p will
+ * already have been incremented past the NUL character
+ * (since all states make transitions on EOB to the
+ * end-of-buffer state). Contrast this with the test
+ * in input().
+ */
+ if ( (yy_c_buf_p) <= &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)] )
+ { /* This was really a NUL. */
+ yy_state_type yy_next_state;
+
+ (yy_c_buf_p) = (yytext_ptr) + yy_amount_of_matched_text;
+
+ yy_current_state = yy_get_previous_state( );
+
+ /* Okay, we're now positioned to make the NUL
+ * transition. We couldn't have
+ * yy_get_previous_state() go ahead and do it
+ * for us because it doesn't know how to deal
+ * with the possibility of jamming (and we don't
+ * want to build jamming into it because then it
+ * will run more slowly).
+ */
+
+ yy_next_state = yy_try_NUL_trans( yy_current_state );
+
+ yy_bp = (yytext_ptr) + YY_MORE_ADJ;
+
+ if ( yy_next_state )
+ {
+ /* Consume the NUL. */
+ yy_cp = ++(yy_c_buf_p);
+ yy_current_state = yy_next_state;
+ goto yy_match;
+ }
+
+ else
+ {
+ yy_cp = (yy_c_buf_p);
+ goto yy_find_action;
+ }
+ }
+
+ else switch ( yy_get_next_buffer( ) )
+ {
+ case EOB_ACT_END_OF_FILE:
+ {
+ (yy_did_buffer_switch_on_eof) = 0;
+
+ if ( yywrap( ) )
+ {
+ /* Note: because we've taken care in
+ * yy_get_next_buffer() to have set up
+ * yytext, we can now set up
+ * yy_c_buf_p so that if some total
+ * hoser (like flex itself) wants to
+ * call the scanner after we return the
+ * YY_NULL, it'll still work - another
+ * YY_NULL will get returned.
+ */
+ (yy_c_buf_p) = (yytext_ptr) + YY_MORE_ADJ;
+
+ yy_act = YY_STATE_EOF(YY_START);
+ goto do_action;
+ }
+
+ else
+ {
+ if ( ! (yy_did_buffer_switch_on_eof) )
+ YY_NEW_FILE;
+ }
+ break;
+ }
+
+ case EOB_ACT_CONTINUE_SCAN:
+ (yy_c_buf_p) =
+ (yytext_ptr) + yy_amount_of_matched_text;
+
+ yy_current_state = yy_get_previous_state( );
+
+ yy_cp = (yy_c_buf_p);
+ yy_bp = (yytext_ptr) + YY_MORE_ADJ;
+ goto yy_match;
+
+ case EOB_ACT_LAST_MATCH:
+ (yy_c_buf_p) =
+ &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)];
+
+ yy_current_state = yy_get_previous_state( );
+
+ yy_cp = (yy_c_buf_p);
+ yy_bp = (yytext_ptr) + YY_MORE_ADJ;
+ goto yy_find_action;
+ }
+ break;
+ }
+
+ default:
+ YY_FATAL_ERROR(
+ "fatal flex scanner internal error--no action found" );
+ } /* end of action switch */
+ } /* end of scanning one token */
+} /* end of yylex */
+
+/* yy_get_next_buffer - try to read in a new buffer
+ *
+ * Returns a code representing an action:
+ * EOB_ACT_LAST_MATCH -
+ * EOB_ACT_CONTINUE_SCAN - continue scanning from current position
+ * EOB_ACT_END_OF_FILE - end of file
+ */
+static int yy_get_next_buffer (void)
+{
+ register char *dest = YY_CURRENT_BUFFER_LVALUE->yy_ch_buf;
+ register char *source = (yytext_ptr);
+ register int number_to_move, i;
+ int ret_val;
+
+ if ( (yy_c_buf_p) > &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars) + 1] )
+ YY_FATAL_ERROR(
+ "fatal flex scanner internal error--end of buffer missed" );
+
+ if ( YY_CURRENT_BUFFER_LVALUE->yy_fill_buffer == 0 )
+ { /* Don't try to fill the buffer, so this is an EOF. */
+ if ( (yy_c_buf_p) - (yytext_ptr) - YY_MORE_ADJ == 1 )
+ {
+ /* We matched a single character, the EOB, so
+ * treat this as a final EOF.
+ */
+ return EOB_ACT_END_OF_FILE;
+ }
+
+ else
+ {
+ /* We matched some text prior to the EOB, first
+ * process it.
+ */
+ return EOB_ACT_LAST_MATCH;
+ }
+ }
+
+ /* Try to read more data. */
+
+ /* First move last chars to start of buffer. */
+ number_to_move = (int) ((yy_c_buf_p) - (yytext_ptr)) - 1;
+
+ for ( i = 0; i < number_to_move; ++i )
+ *(dest++) = *(source++);
+
+ if ( YY_CURRENT_BUFFER_LVALUE->yy_buffer_status == YY_BUFFER_EOF_PENDING )
+ /* don't do the read, it's not guaranteed to return an EOF,
+ * just force an EOF
+ */
+ YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars) = 0;
+
+ else
+ {
+ yy_size_t num_to_read =
+ YY_CURRENT_BUFFER_LVALUE->yy_buf_size - number_to_move - 1;
+
+ while ( num_to_read <= 0 )
+ { /* Not enough room in the buffer - grow it. */
+
+ /* just a shorter name for the current buffer */
+ YY_BUFFER_STATE b = YY_CURRENT_BUFFER;
+
+ int yy_c_buf_p_offset =
+ (int) ((yy_c_buf_p) - b->yy_ch_buf);
+
+ if ( b->yy_is_our_buffer )
+ {
+ yy_size_t new_size = b->yy_buf_size * 2;
+
+ if ( new_size <= 0 )
+ b->yy_buf_size += b->yy_buf_size / 8;
+ else
+ b->yy_buf_size *= 2;
+
+ b->yy_ch_buf = (char *)
+ /* Include room in for 2 EOB chars. */
+ yyrealloc((void *) b->yy_ch_buf,b->yy_buf_size + 2 );
+ }
+ else
+ /* Can't grow it, we don't own it. */
+ b->yy_ch_buf = 0;
+
+ if ( ! b->yy_ch_buf )
+ YY_FATAL_ERROR(
+ "fatal error - scanner input buffer overflow" );
+
+ (yy_c_buf_p) = &b->yy_ch_buf[yy_c_buf_p_offset];
+
+ num_to_read = YY_CURRENT_BUFFER_LVALUE->yy_buf_size -
+ number_to_move - 1;
+
+ }
+
+ if ( num_to_read > YY_READ_BUF_SIZE )
+ num_to_read = YY_READ_BUF_SIZE;
+
+ /* Read in more data. */
+ YY_INPUT( (&YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[number_to_move]),
+ (yy_n_chars), num_to_read );
+
+ YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars);
+ }
+
+ if ( (yy_n_chars) == 0 )
+ {
+ if ( number_to_move == YY_MORE_ADJ )
+ {
+ ret_val = EOB_ACT_END_OF_FILE;
+ yyrestart(yyin );
+ }
+
+ else
+ {
+ ret_val = EOB_ACT_LAST_MATCH;
+ YY_CURRENT_BUFFER_LVALUE->yy_buffer_status =
+ YY_BUFFER_EOF_PENDING;
+ }
+ }
+
+ else
+ ret_val = EOB_ACT_CONTINUE_SCAN;
+
+ if ((yy_size_t) ((yy_n_chars) + number_to_move) > YY_CURRENT_BUFFER_LVALUE->yy_buf_size) {
+ /* Extend the array by 50%, plus the number we really need. */
+ yy_size_t new_size = (yy_n_chars) + number_to_move + ((yy_n_chars) >> 1);
+ YY_CURRENT_BUFFER_LVALUE->yy_ch_buf = (char *) yyrealloc((void *) YY_CURRENT_BUFFER_LVALUE->yy_ch_buf,new_size );
+ if ( ! YY_CURRENT_BUFFER_LVALUE->yy_ch_buf )
+ YY_FATAL_ERROR( "out of dynamic memory in yy_get_next_buffer()" );
+ }
+
+ (yy_n_chars) += number_to_move;
+ YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)] = YY_END_OF_BUFFER_CHAR;
+ YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars) + 1] = YY_END_OF_BUFFER_CHAR;
+
+ (yytext_ptr) = &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[0];
+
+ return ret_val;
+}
+
+/* yy_get_previous_state - get the state just before the EOB char was reached */
+
+ static yy_state_type yy_get_previous_state (void)
+{
+ register yy_state_type yy_current_state;
+ register char *yy_cp;
+
+ yy_current_state = (yy_start);
+ yy_current_state += YY_AT_BOL();
+
+ for ( yy_cp = (yytext_ptr) + YY_MORE_ADJ; yy_cp < (yy_c_buf_p); ++yy_cp )
+ {
+ register YY_CHAR yy_c = (*yy_cp ? yy_ec[YY_SC_TO_UI(*yy_cp)] : 1);
+ if ( yy_accept[yy_current_state] )
+ {
+ (yy_last_accepting_state) = yy_current_state;
+ (yy_last_accepting_cpos) = yy_cp;
+ }
+ while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state )
+ {
+ yy_current_state = (int) yy_def[yy_current_state];
+ if ( yy_current_state >= 186 )
+ yy_c = yy_meta[(unsigned int) yy_c];
+ }
+ yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c];
+ }
+
+ return yy_current_state;
+}
+
+/* yy_try_NUL_trans - try to make a transition on the NUL character
+ *
+ * synopsis
+ * next_state = yy_try_NUL_trans( current_state );
+ */
+ static yy_state_type yy_try_NUL_trans (yy_state_type yy_current_state )
+{
+ register int yy_is_jam;
+ register char *yy_cp = (yy_c_buf_p);
+
+ register YY_CHAR yy_c = 1;
+ if ( yy_accept[yy_current_state] )
+ {
+ (yy_last_accepting_state) = yy_current_state;
+ (yy_last_accepting_cpos) = yy_cp;
+ }
+ while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state )
+ {
+ yy_current_state = (int) yy_def[yy_current_state];
+ if ( yy_current_state >= 186 )
+ yy_c = yy_meta[(unsigned int) yy_c];
+ }
+ yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c];
+ yy_is_jam = (yy_current_state == 185);
+
+ return yy_is_jam ? 0 : yy_current_state;
+}
+
+ static void yyunput (int c, register char * yy_bp )
+{
+ register char *yy_cp;
+
+ yy_cp = (yy_c_buf_p);
+
+ /* undo effects of setting up yytext */
+ *yy_cp = (yy_hold_char);
+
+ if ( yy_cp < YY_CURRENT_BUFFER_LVALUE->yy_ch_buf + 2 )
+ { /* need to shift things up to make room */
+ /* +2 for EOB chars. */
+ register yy_size_t number_to_move = (yy_n_chars) + 2;
+ register char *dest = &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[
+ YY_CURRENT_BUFFER_LVALUE->yy_buf_size + 2];
+ register char *source =
+ &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[number_to_move];
+
+ while ( source > YY_CURRENT_BUFFER_LVALUE->yy_ch_buf )
+ *--dest = *--source;
+
+ yy_cp += (int) (dest - source);
+ yy_bp += (int) (dest - source);
+ YY_CURRENT_BUFFER_LVALUE->yy_n_chars =
+ (yy_n_chars) = YY_CURRENT_BUFFER_LVALUE->yy_buf_size;
+
+ if ( yy_cp < YY_CURRENT_BUFFER_LVALUE->yy_ch_buf + 2 )
+ YY_FATAL_ERROR( "flex scanner push-back overflow" );
+ }
+
+ *--yy_cp = (char) c;
+
+ (yytext_ptr) = yy_bp;
+ (yy_hold_char) = *yy_cp;
+ (yy_c_buf_p) = yy_cp;
+}
+
+#ifndef YY_NO_INPUT
+#ifdef __cplusplus
+ static int yyinput (void)
+#else
+ static int input (void)
+#endif
+
+{
+ int c;
+
+ *(yy_c_buf_p) = (yy_hold_char);
+
+ if ( *(yy_c_buf_p) == YY_END_OF_BUFFER_CHAR )
+ {
+ /* yy_c_buf_p now points to the character we want to return.
+ * If this occurs *before* the EOB characters, then it's a
+ * valid NUL; if not, then we've hit the end of the buffer.
+ */
+ if ( (yy_c_buf_p) < &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)] )
+ /* This was really a NUL. */
+ *(yy_c_buf_p) = '\0';
+
+ else
+ { /* need more input */
+ yy_size_t offset = (yy_c_buf_p) - (yytext_ptr);
+ ++(yy_c_buf_p);
+
+ switch ( yy_get_next_buffer( ) )
+ {
+ case EOB_ACT_LAST_MATCH:
+ /* This happens because yy_g_n_b()
+ * sees that we've accumulated a
+ * token and flags that we need to
+ * try matching the token before
+ * proceeding. But for input(),
+ * there's no matching to consider.
+ * So convert the EOB_ACT_LAST_MATCH
+ * to EOB_ACT_END_OF_FILE.
+ */
+
+ /* Reset buffer status. */
+ yyrestart(yyin );
+
+ /*FALLTHROUGH*/
+
+ case EOB_ACT_END_OF_FILE:
+ {
+ if ( yywrap( ) )
+ return 0;
+
+ if ( ! (yy_did_buffer_switch_on_eof) )
+ YY_NEW_FILE;
+#ifdef __cplusplus
+ return yyinput();
+#else
+ return input();
+#endif
+ }
+
+ case EOB_ACT_CONTINUE_SCAN:
+ (yy_c_buf_p) = (yytext_ptr) + offset;
+ break;
+ }
+ }
+ }
+
+ c = *(unsigned char *) (yy_c_buf_p); /* cast for 8-bit char's */
+ *(yy_c_buf_p) = '\0'; /* preserve yytext */
+ (yy_hold_char) = *++(yy_c_buf_p);
+
+ YY_CURRENT_BUFFER_LVALUE->yy_at_bol = (c == '\n');
+
+ return c;
+}
+#endif /* ifndef YY_NO_INPUT */
+
+/** Immediately switch to a different input stream.
+ * @param input_file A readable stream.
+ *
+ * @note This function does not reset the start condition to @c INITIAL .
+ */
+ void yyrestart (FILE * input_file )
+{
+
+ if ( ! YY_CURRENT_BUFFER ){
+ yyensure_buffer_stack ();
+ YY_CURRENT_BUFFER_LVALUE =
+ yy_create_buffer(yyin,YY_BUF_SIZE );
+ }
+
+ yy_init_buffer(YY_CURRENT_BUFFER,input_file );
+ yy_load_buffer_state( );
+}
+
+/** Switch to a different input buffer.
+ * @param new_buffer The new input buffer.
+ *
+ */
+ void yy_switch_to_buffer (YY_BUFFER_STATE new_buffer )
+{
+
+ /* TODO. We should be able to replace this entire function body
+ * with
+ * yypop_buffer_state();
+ * yypush_buffer_state(new_buffer);
+ */
+ yyensure_buffer_stack ();
+ if ( YY_CURRENT_BUFFER == new_buffer )
+ return;
+
+ if ( YY_CURRENT_BUFFER )
+ {
+ /* Flush out information for old buffer. */
+ *(yy_c_buf_p) = (yy_hold_char);
+ YY_CURRENT_BUFFER_LVALUE->yy_buf_pos = (yy_c_buf_p);
+ YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars);
+ }
+
+ YY_CURRENT_BUFFER_LVALUE = new_buffer;
+ yy_load_buffer_state( );
+
+ /* We don't actually know whether we did this switch during
+ * EOF (yywrap()) processing, but the only time this flag
+ * is looked at is after yywrap() is called, so it's safe
+ * to go ahead and always set it.
+ */
+ (yy_did_buffer_switch_on_eof) = 1;
+}
+
+static void yy_load_buffer_state (void)
+{
+ (yy_n_chars) = YY_CURRENT_BUFFER_LVALUE->yy_n_chars;
+ (yytext_ptr) = (yy_c_buf_p) = YY_CURRENT_BUFFER_LVALUE->yy_buf_pos;
+ yyin = YY_CURRENT_BUFFER_LVALUE->yy_input_file;
+ (yy_hold_char) = *(yy_c_buf_p);
+}
+
+/** Allocate and initialize an input buffer state.
+ * @param file A readable stream.
+ * @param size The character buffer size in bytes. When in doubt, use @c YY_BUF_SIZE.
+ *
+ * @return the allocated buffer state.
+ */
+ YY_BUFFER_STATE yy_create_buffer (FILE * file, int size )
+{
+ YY_BUFFER_STATE b;
+
+ b = (YY_BUFFER_STATE) yyalloc(sizeof( struct yy_buffer_state ) );
+ if ( ! b )
+ YY_FATAL_ERROR( "out of dynamic memory in yy_create_buffer()" );
+
+ b->yy_buf_size = size;
+
+ /* yy_ch_buf has to be 2 characters longer than the size given because
+ * we need to put in 2 end-of-buffer characters.
+ */
+ b->yy_ch_buf = (char *) yyalloc(b->yy_buf_size + 2 );
+ if ( ! b->yy_ch_buf )
+ YY_FATAL_ERROR( "out of dynamic memory in yy_create_buffer()" );
+
+ b->yy_is_our_buffer = 1;
+
+ yy_init_buffer(b,file );
+
+ return b;
+}
+
+/** Destroy the buffer.
+ * @param b a buffer created with yy_create_buffer()
+ *
+ */
+ void yy_delete_buffer (YY_BUFFER_STATE b )
+{
+
+ if ( ! b )
+ return;
+
+ if ( b == YY_CURRENT_BUFFER ) /* Not sure if we should pop here. */
+ YY_CURRENT_BUFFER_LVALUE = (YY_BUFFER_STATE) 0;
+
+ if ( b->yy_is_our_buffer )
+ yyfree((void *) b->yy_ch_buf );
+
+ yyfree((void *) b );
+}
+
+#ifndef __cplusplus
+extern int isatty (int );
+#endif /* __cplusplus */
+
+/* Initializes or reinitializes a buffer.
+ * This function is sometimes called more than once on the same buffer,
+ * such as during a yyrestart() or at EOF.
+ */
+ static void yy_init_buffer (YY_BUFFER_STATE b, FILE * file )
+
+{
+ int oerrno = errno;
+
+ yy_flush_buffer(b );
+
+ b->yy_input_file = file;
+ b->yy_fill_buffer = 1;
+
+ /* If b is the current buffer, then yy_init_buffer was _probably_
+ * called from yyrestart() or through yy_get_next_buffer.
+ * In that case, we don't want to reset the lineno or column.
+ */
+ if (b != YY_CURRENT_BUFFER){
+ b->yy_bs_lineno = 1;
+ b->yy_bs_column = 0;
+ }
+
+ b->yy_is_interactive = file ? (isatty( fileno(file) ) > 0) : 0;
+
+ errno = oerrno;
+}
+
+/** Discard all buffered characters. On the next scan, YY_INPUT will be called.
+ * @param b the buffer state to be flushed, usually @c YY_CURRENT_BUFFER.
+ *
+ */
+ void yy_flush_buffer (YY_BUFFER_STATE b )
+{
+ if ( ! b )
+ return;
+
+ b->yy_n_chars = 0;
+
+ /* We always need two end-of-buffer characters. The first causes
+ * a transition to the end-of-buffer state. The second causes
+ * a jam in that state.
+ */
+ b->yy_ch_buf[0] = YY_END_OF_BUFFER_CHAR;
+ b->yy_ch_buf[1] = YY_END_OF_BUFFER_CHAR;
+
+ b->yy_buf_pos = &b->yy_ch_buf[0];
+
+ b->yy_at_bol = 1;
+ b->yy_buffer_status = YY_BUFFER_NEW;
+
+ if ( b == YY_CURRENT_BUFFER )
+ yy_load_buffer_state( );
+}
+
+/** Pushes the new state onto the stack. The new state becomes
+ * the current state. This function will allocate the stack
+ * if necessary.
+ * @param new_buffer The new state.
+ *
+ */
+void yypush_buffer_state (YY_BUFFER_STATE new_buffer )
+{
+ if (new_buffer == NULL)
+ return;
+
+ yyensure_buffer_stack();
+
+ /* This block is copied from yy_switch_to_buffer. */
+ if ( YY_CURRENT_BUFFER )
+ {
+ /* Flush out information for old buffer. */
+ *(yy_c_buf_p) = (yy_hold_char);
+ YY_CURRENT_BUFFER_LVALUE->yy_buf_pos = (yy_c_buf_p);
+ YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars);
+ }
+
+ /* Only push if top exists. Otherwise, replace top. */
+ if (YY_CURRENT_BUFFER)
+ (yy_buffer_stack_top)++;
+ YY_CURRENT_BUFFER_LVALUE = new_buffer;
+
+ /* copied from yy_switch_to_buffer. */
+ yy_load_buffer_state( );
+ (yy_did_buffer_switch_on_eof) = 1;
+}
+
+/** Removes and deletes the top of the stack, if present.
+ * The next element becomes the new top.
+ *
+ */
+void yypop_buffer_state (void)
+{
+ if (!YY_CURRENT_BUFFER)
+ return;
+
+ yy_delete_buffer(YY_CURRENT_BUFFER );
+ YY_CURRENT_BUFFER_LVALUE = NULL;
+ if ((yy_buffer_stack_top) > 0)
+ --(yy_buffer_stack_top);
+
+ if (YY_CURRENT_BUFFER) {
+ yy_load_buffer_state( );
+ (yy_did_buffer_switch_on_eof) = 1;
+ }
+}
+
+/* Allocates the stack if it does not exist.
+ * Guarantees space for at least one push.
+ */
+static void yyensure_buffer_stack (void)
+{
+ yy_size_t num_to_alloc;
+
+ if (!(yy_buffer_stack)) {
+
+ /* First allocation is just for 2 elements, since we don't know if this
+ * scanner will even need a stack. We use 2 instead of 1 to avoid an
+ * immediate realloc on the next call.
+ */
+ num_to_alloc = 1;
+ (yy_buffer_stack) = (struct yy_buffer_state**)yyalloc
+ (num_to_alloc * sizeof(struct yy_buffer_state*)
+ );
+ if ( ! (yy_buffer_stack) )
+ YY_FATAL_ERROR( "out of dynamic memory in yyensure_buffer_stack()" );
+
+ memset((yy_buffer_stack), 0, num_to_alloc * sizeof(struct yy_buffer_state*));
+
+ (yy_buffer_stack_max) = num_to_alloc;
+ (yy_buffer_stack_top) = 0;
+ return;
+ }
+
+ if ((yy_buffer_stack_top) >= ((yy_buffer_stack_max)) - 1){
+
+ /* Increase the buffer to prepare for a possible push. */
+ int grow_size = 8 /* arbitrary grow size */;
+
+ num_to_alloc = (yy_buffer_stack_max) + grow_size;
+ (yy_buffer_stack) = (struct yy_buffer_state**)yyrealloc
+ ((yy_buffer_stack),
+ num_to_alloc * sizeof(struct yy_buffer_state*)
+ );
+ if ( ! (yy_buffer_stack) )
+ YY_FATAL_ERROR( "out of dynamic memory in yyensure_buffer_stack()" );
+
+ /* zero only the new slots.*/
+ memset((yy_buffer_stack) + (yy_buffer_stack_max), 0, grow_size * sizeof(struct yy_buffer_state*));
+ (yy_buffer_stack_max) = num_to_alloc;
+ }
+}
+
+/** Setup the input buffer state to scan directly from a user-specified character buffer.
+ * @param base the character buffer
+ * @param size the size in bytes of the character buffer
+ *
+ * @return the newly allocated buffer state object.
+ */
+YY_BUFFER_STATE yy_scan_buffer (char * base, yy_size_t size )
+{
+ YY_BUFFER_STATE b;
+
+ if ( size < 2 ||
+ base[size-2] != YY_END_OF_BUFFER_CHAR ||
+ base[size-1] != YY_END_OF_BUFFER_CHAR )
+ /* They forgot to leave room for the EOB's. */
+ return 0;
+
+ b = (YY_BUFFER_STATE) yyalloc(sizeof( struct yy_buffer_state ) );
+ if ( ! b )
+ YY_FATAL_ERROR( "out of dynamic memory in yy_scan_buffer()" );
+
+ b->yy_buf_size = size - 2; /* "- 2" to take care of EOB's */
+ b->yy_buf_pos = b->yy_ch_buf = base;
+ b->yy_is_our_buffer = 0;
+ b->yy_input_file = 0;
+ b->yy_n_chars = b->yy_buf_size;
+ b->yy_is_interactive = 0;
+ b->yy_at_bol = 1;
+ b->yy_fill_buffer = 0;
+ b->yy_buffer_status = YY_BUFFER_NEW;
+
+ yy_switch_to_buffer(b );
+
+ return b;
+}
+
+/** Setup the input buffer state to scan a string. The next call to yylex() will
+ * scan from a @e copy of @a str.
+ * @param yystr a NUL-terminated string to scan
+ *
+ * @return the newly allocated buffer state object.
+ * @note If you want to scan bytes that may contain NUL values, then use
+ * yy_scan_bytes() instead.
+ */
+YY_BUFFER_STATE yy_scan_string (yyconst char * yystr )
+{
+
+ return yy_scan_bytes(yystr,strlen(yystr) );
+}
+
+/** Setup the input buffer state to scan the given bytes. The next call to yylex() will
+ * scan from a @e copy of @a bytes.
+ * @param bytes the byte buffer to scan
+ * @param len the number of bytes in the buffer pointed to by @a bytes.
+ *
+ * @return the newly allocated buffer state object.
+ */
+YY_BUFFER_STATE yy_scan_bytes (yyconst char * yybytes, yy_size_t _yybytes_len )
+{
+ YY_BUFFER_STATE b;
+ char *buf;
+ yy_size_t n, i;
+
+ /* Get memory for full buffer, including space for trailing EOB's. */
+ n = _yybytes_len + 2;
+ buf = (char *) yyalloc(n );
+ if ( ! buf )
+ YY_FATAL_ERROR( "out of dynamic memory in yy_scan_bytes()" );
+
+ for ( i = 0; i < _yybytes_len; ++i )
+ buf[i] = yybytes[i];
+
+ buf[_yybytes_len] = buf[_yybytes_len+1] = YY_END_OF_BUFFER_CHAR;
+
+ b = yy_scan_buffer(buf,n );
+ if ( ! b )
+ YY_FATAL_ERROR( "bad buffer in yy_scan_bytes()" );
+
+ /* It's okay to grow etc. this buffer, and we should throw it
+ * away when we're done.
+ */
+ b->yy_is_our_buffer = 1;
+
+ return b;
+}
+
+#ifndef YY_EXIT_FAILURE
+#define YY_EXIT_FAILURE 2
+#endif
+
+static void yy_fatal_error (yyconst char* msg )
+{
+ (void) fprintf( stderr, "%s\n", msg );
+ exit( YY_EXIT_FAILURE );
+}
+
+/* Redefine yyless() so it works in section 3 code. */
+
+#undef yyless
+#define yyless(n) \
+ do \
+ { \
+ /* Undo effects of setting up yytext. */ \
+ int yyless_macro_arg = (n); \
+ YY_LESS_LINENO(yyless_macro_arg);\
+ yytext[yyleng] = (yy_hold_char); \
+ (yy_c_buf_p) = yytext + yyless_macro_arg; \
+ (yy_hold_char) = *(yy_c_buf_p); \
+ *(yy_c_buf_p) = '\0'; \
+ yyleng = yyless_macro_arg; \
+ } \
+ while ( 0 )
+
+/* Accessor methods (get/set functions) to struct members. */
+
+/** Get the current line number.
+ *
+ */
+int yyget_lineno (void)
+{
+
+ return yylineno;
+}
+
+/** Get the input stream.
+ *
+ */
+FILE *yyget_in (void)
+{
+ return yyin;
+}
+
+/** Get the output stream.
+ *
+ */
+FILE *yyget_out (void)
+{
+ return yyout;
+}
+
+/** Get the length of the current token.
+ *
+ */
+yy_size_t yyget_leng (void)
+{
+ return yyleng;
+}
+
+/** Get the current token.
+ *
+ */
+
+char *yyget_text (void)
+{
+ return yytext;
+}
+
+/** Set the current line number.
+ * @param line_number
+ *
+ */
+void yyset_lineno (int line_number )
+{
+
+ yylineno = line_number;
+}
+
+/** Set the input stream. This does not discard the current
+ * input buffer.
+ * @param in_str A readable stream.
+ *
+ * @see yy_switch_to_buffer
+ */
+void yyset_in (FILE * in_str )
+{
+ yyin = in_str ;
+}
+
+void yyset_out (FILE * out_str )
+{
+ yyout = out_str ;
+}
+
+int yyget_debug (void)
+{
+ return yy_flex_debug;
+}
+
+void yyset_debug (int bdebug )
+{
+ yy_flex_debug = bdebug ;
+}
+
+static int yy_init_globals (void)
+{
+ /* Initialization is the same as for the non-reentrant scanner.
+ * This function is called from yylex_destroy(), so don't allocate here.
+ */
+
+ (yy_buffer_stack) = 0;
+ (yy_buffer_stack_top) = 0;
+ (yy_buffer_stack_max) = 0;
+ (yy_c_buf_p) = (char *) 0;
+ (yy_init) = 0;
+ (yy_start) = 0;
+
+/* Defined in main.c */
+#ifdef YY_STDINIT
+ yyin = stdin;
+ yyout = stdout;
+#else
+ yyin = (FILE *) 0;
+ yyout = (FILE *) 0;
+#endif
+
+ /* For future reference: Set errno on error, since we are called by
+ * yylex_init()
+ */
+ return 0;
+}
+
+/* yylex_destroy is for both reentrant and non-reentrant scanners. */
+int yylex_destroy (void)
+{
+
+ /* Pop the buffer stack, destroying each element. */
+ while(YY_CURRENT_BUFFER){
+ yy_delete_buffer(YY_CURRENT_BUFFER );
+ YY_CURRENT_BUFFER_LVALUE = NULL;
+ yypop_buffer_state();
+ }
+
+ /* Destroy the stack itself. */
+ yyfree((yy_buffer_stack) );
+ (yy_buffer_stack) = NULL;
+
+ /* Reset the globals. This is important in a non-reentrant scanner so the next time
+ * yylex() is called, initialization will occur. */
+ yy_init_globals( );
+
+ return 0;
+}
+
+/*
+ * Internal utility routines.
+ */
+
+#ifndef yytext_ptr
+static void yy_flex_strncpy (char* s1, yyconst char * s2, int n )
+{
+ register int i;
+ for ( i = 0; i < n; ++i )
+ s1[i] = s2[i];
+}
+#endif
+
+#ifdef YY_NEED_STRLEN
+static int yy_flex_strlen (yyconst char * s )
+{
+ register int n;
+ for ( n = 0; s[n]; ++n )
+ ;
+
+ return n;
+}
+#endif
+
+void *yyalloc (yy_size_t size )
+{
+ return (void *) malloc( size );
+}
+
+void *yyrealloc (void * ptr, yy_size_t size )
+{
+ /* The cast to (char *) in the following accommodates both
+ * implementations that use char* generic pointers, and those
+ * that use void* generic pointers. It works with the latter
+ * because both ANSI C and C++ allow castless assignment from
+ * any pointer type to void*, and deal with argument conversions
+ * as though doing an assignment.
+ */
+ return (void *) realloc( (char *) ptr, size );
+}
+
+void yyfree (void * ptr )
+{
+ free( (char *) ptr ); /* see yyrealloc() for (char *) cast */
+}
+
+#define YYTABLES_NAME "yytables"
+
+#line 186 "engines/director/lingo/lingo-lex.l"
+
+
+
+extern int yydebug;
+
+namespace Director {
+
+int Lingo::parse(const char *code) {
+ YY_BUFFER_STATE bp;
+
+ yydebug = 0;
+
+ yy_delete_buffer(YY_CURRENT_BUFFER);
+
+ bp = yy_scan_string(code);
+ yy_switch_to_buffer(bp);
+ yyparse();
+ yy_delete_buffer(bp);
+
+ return 0;
+}
+
+}
+
diff --git a/engines/director/lingo/lingo-lex.l b/engines/director/lingo/lingo-lex.l
new file mode 100644
index 0000000000..50ad8fa61a
--- /dev/null
+++ b/engines/director/lingo/lingo-lex.l
@@ -0,0 +1,207 @@
+/* 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 2
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+%option noyywrap
+%{
+
+#define FORBIDDEN_SYMBOL_ALLOW_ALL
+
+#include "common/str.h"
+
+#include "director/lingo/lingo.h"
+#include "director/lingo/lingo-gr.h"
+
+using namespace Director;
+
+int yyparse();
+static void count() {
+ g_lingo->_colnumber += strlen(yytext);
+}
+
+static void countnl() {
+ char *p = yytext;
+
+ while(*p == '\n' || *p == '\r')
+ p++;
+
+ g_lingo->_linenumber++;
+ g_lingo->_colnumber = strlen(p);
+}
+
+%}
+
+identifier [_[:alpha:]][_[:alnum:]]*
+constfloat [[:digit:]]+\.[[:digit:]]*
+constinteger [[:digit:]]+
+conststring \"[^\"\n]*\"
+operator [-+*/%=^:,()><&]
+newline [ \t]*[\n\r]+
+whitespace [\t ]
+
+%%
+
+--[^\r\n]*
+^{whitespace}+ { count(); }
+[\t]+ { count(); return ' '; }
+
+(?i:and) { count(); return tAND; }
+(?i:contains) { count(); return tCONTAINS; }
+(?i:down) { count(); return tDOWN; }
+(?i:if) { count(); return tIF; }
+(?i:[\n\r]+[\t ]*else[\t ]+if) { countnl(); return tNLELSIF; }
+(?i:[\n\r]+[\t ]*else) { countnl(); return tNLELSE; }
+(?i:else) { count(); return tELSE; }
+(?i:end) { count(); return tEND; }
+(?i:factory) { count(); return tFACTORY; }
+(?i:exit) { count(); return tEXIT; }
+(?i:frame) { count(); return tFRAME; }
+(?i:global) { count(); return tGLOBAL; }
+(?i:go) { count(); return tGO; }
+(?i:intersects) { count(); return tINTERSECTS; }
+(?i:into) { count(); return tINTO; }
+(?i:loop) { count(); return tLOOP; }
+(?i:macro) { count(); return tMACRO; }
+(?i:mci) { count(); return tMCI; }
+(?i:mciwait) { count(); return tMCIWAIT; }
+(?i:method) { count(); return tMETHOD; }
+(?i:movie) { count(); return tMOVIE; }
+(?i:next) { count(); return tNEXT; }
+(?i:not) { count(); return tNOT; }
+(?i:of) { count(); return tOF; }
+(?i:or) { count(); return tOR; }
+(?i:previous) { count(); return tPREVIOUS; }
+(?i:put) { count(); return tPUT; }
+(?i:repeat) { count(); return tREPEAT; }
+(?i:set) { count(); return tSET; }
+(?i:starts) { count(); return tSTARTS; }
+(?i:the[ \t]+[[:alpha:]]+[\t ]+of[\t ]+[[:alpha:]]+) {
+ count();
+
+ const char *ptr = &yytext[4]; // Skip 'the '
+ while (*ptr == ' ' || *ptr == '\t')
+ ptr++;
+
+ Common::String field;
+ while (*ptr != ' ' && *ptr != '\t')
+ field += *ptr++;
+
+ while (*ptr == ' ' || *ptr == '\t')
+ ptr++;
+
+ ptr += 3; // Skip 'of '
+
+ while (*ptr == ' ' || *ptr == '\t')
+ ptr++;
+
+ if (g_lingo->_theEntities.contains(ptr)) {
+ field = Common::String::format("%d%s", g_lingo->_theEntities[ptr]->entity, field.c_str());
+
+ if (!g_lingo->_theEntityFields.contains(field)) {
+ error("Unhandled the field %s", ptr);
+ }
+
+ if (g_lingo->_theEntityFields[field]->entity != g_lingo->_theEntities[ptr]->entity)
+ error("Unsupported field '%s' for entity '%s'", field.c_str(), ptr);
+
+ yylval.e[0] = g_lingo->_theEntities[ptr]->entity;
+ yylval.e[1] = g_lingo->_theEntityFields[field]->field;
+
+ if (g_lingo->_theEntities[ptr]->hasId)
+ return THEENTITYWITHID;
+ else
+ return THEENTITY;
+ }
+
+ warning("Unhandled the entity %s", ptr);
+ }
+(?i:the[ \t]+[[:alpha:]]+) {
+ count();
+
+ const char *ptr = &yytext[4]; // Skip 'the '
+ while (*ptr == ' ' || *ptr == '\t')
+ ptr++;
+
+ if (g_lingo->_theEntities.contains(ptr)) {
+ yylval.e[0] = g_lingo->_theEntities[ptr]->entity;
+ yylval.e[1] = 0; // No field
+
+ if (g_lingo->_theEntities[ptr]->hasId)
+ return THEENTITYWITHID;
+ else
+ return THEENTITY;
+ }
+
+ warning("Unhandled the entity %s", ptr);
+ }
+(?i:then) { count(); return tTHEN; }
+(?i:to) { count(); return tTO; }
+(?i:sprite) { count(); return tSPRITE; }
+(?i:with) { count(); return tWITH; }
+(?i:within) { count(); return tWITHIN; }
+(?i:when) { count(); return tWHEN; }
+(?i:while) { count(); return tWHILE; }
+
+[<][>] { count(); return tNEQ; }
+[>][=] { count(); return tGE; }
+[<][=] { count(); return tLE; }
+[&][&] { count(); return tCONCAT; }
+
+{identifier} {
+ count();
+ yylval.s = new Common::String(yytext);
+
+ if (g_lingo->_handlers.contains(yytext)) {
+ if (g_lingo->_handlers[yytext]->type == BLTIN && g_lingo->_handlers[yytext]->nargs == -1)
+ return BLTINNOARGS;
+ }
+
+ return ID;
+ }
+{constfloat} { count(); yylval.f = atof(yytext); return FLOAT; }
+{constinteger} { count(); yylval.i = strtol(yytext, NULL, 10); return INT; }
+{operator} { count(); return *yytext; }
+{newline} { return '\n'; }
+{conststring} { count(); yylval.s = new Common::String(&yytext[1]); yylval.s->deleteLastChar(); return STRING; }
+.
+
+%%
+
+extern int yydebug;
+
+namespace Director {
+
+int Lingo::parse(const char *code) {
+ YY_BUFFER_STATE bp;
+
+ yydebug = 0;
+
+ yy_delete_buffer(YY_CURRENT_BUFFER);
+
+ bp = yy_scan_string(code);
+ yy_switch_to_buffer(bp);
+ yyparse();
+ yy_delete_buffer(bp);
+
+ return 0;
+}
+
+}
diff --git a/engines/director/lingo/lingo-the.cpp b/engines/director/lingo/lingo-the.cpp
new file mode 100644
index 0000000000..2aedf32a54
--- /dev/null
+++ b/engines/director/lingo/lingo-the.cpp
@@ -0,0 +1,603 @@
+/* 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 2
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "engines/director/lingo/lingo.h"
+
+namespace Director {
+
+class Sprite;
+
+TheEntity entities[] = {
+ { kTheCast, "cast", true },
+ { kTheClickOn, "clickOn", false },
+ { kTheColorDepth, "colorDepth", false },
+ { kTheColorQD, "colorQD", false },
+ { kTheCommandDown, "commandDown", false },
+ { kTheControlDown, "controlDown", false },
+ { kTheDoubleClick, "doubleClick", false },
+ { kTheExitLock, "exitlock", false },
+ { kTheFloatPrecision, "floatPrecision", false },
+ { kTheFrame, "frame", false },
+ { kTheItemDelimiter, "itemDelimiter", false },
+ { kTheKey, "key", false },
+ { kTheKeyCode, "keycode", false },
+ { kTheLastClick, "lastClick", false },
+ { kTheLastEvent, "lastEvent", false },
+ { kTheLastFrame, "lastFrame", false },
+ { kTheMenu, "menu", true },
+ { kTheMenus, "menus", false },
+ { kTheMenuItem, "menuitem", true },
+ { kTheMenuItems, "menuitems", false },
+ { kTheMouseDown, "mouseDown", false },
+ { kTheMouseDownScript, "mouseDownScript", false },
+ { kTheMouseH, "mouseh", false },
+ { kTheMouseUp, "mouseUp", false },
+ { kTheMouseUpScript, "mouseUpScript", false },
+ { kTheMouseV, "mousev", false },
+ { kTheMovie, "movie", false },
+ { kTheMultiSound, "multiSound", false },
+ { kTheOptionDown, "optionDown", false },
+ { kThePathName, "pathname", false },
+ { kThePerFrameHook, "perframehook", false },
+ { kThePreloadEventAbort,"preloadEventAbort",false },
+ { kTheRightMouseDown, "rightMouseDown", false },
+ { kTheRightMouseUp, "rightMouseUp", false },
+ { kTheRomanLingo, "romanLingo", false },
+ { kTheShiftDown, "shiftDown", false },
+ { kTheSprite, "sprite", true },
+ { kTheStage, "stage", false },
+ { kTheStillDown, "stillDown", false },
+ { kTheTicks, "ticks", false },
+ { kTheTimeoutLength, "timeoutlength", false },
+ { kTheTimer, "timer", false },
+ { kTheWindow, "window", false },
+ { kTheNOEntity, NULL, false }
+};
+
+TheEntityField fields[] = {
+ { kTheSprite, "backColor", kTheBackColor },
+ { kTheSprite, "blend", kTheBlend },
+ { kTheSprite, "bottom", kTheBottom },
+ { kTheSprite, "castnum", kTheCastNum },
+ { kTheSprite, "constraint", kTheConstraint },
+ { kTheSprite, "cursor", kTheCursor },
+ { kTheSprite, "editableText", kTheEditableText },
+ { kTheSprite, "foreColor", kTheForeColor },
+ { kTheSprite, "height", kTheHeight },
+ { kTheSprite, "ink", kTheInk },
+ { kTheSprite, "left", kTheLeft },
+ { kTheSprite, "lineSize", kTheLineSize },
+ { kTheSprite, "loch", kTheLocH },
+ { kTheSprite, "locv", kTheLocV },
+ { kTheSprite, "moveable", kTheMoveable },
+ { kTheSprite, "movieRate", kTheMovieRate },
+ { kTheSprite, "movieTime", kTheMovieTime },
+ { kTheSprite, "right", kTheRight },
+ { kTheSprite, "scriptNum", kTheScriptNum },
+ { kTheSprite, "startTime", kTheStartTime },
+ { kTheSprite, "stretch", kTheStrech },
+ { kTheSprite, "stopTime", kTheStopTime },
+ { kTheSprite, "top", kTheTop },
+ { kTheSprite, "trails", kTheTrails },
+ { kTheSprite, "type", kTheType },
+ { kTheSprite, "visible", kTheVisible },
+ { kTheSprite, "volume", kTheVolume },
+ { kTheSprite, "width", kTheWidth },
+
+ // Common cast fields
+ { kTheCast, "castType", kTheCastType },
+ { kTheCast, "filename", kTheFilename },
+ { kTheCast, "height", kTheHeight },
+ { kTheCast, "loaded", kTheLoaded },
+ { kTheCast, "modified", kTheModified },
+ { kTheCast, "name", kTheName },
+ { kTheCast, "number", kTheNumber },
+ { kTheCast, "rect", kTheRect },
+ { kTheCast, "purgePriority",kThePurgePriority }, // 0 Never purge, 1 Purge Last, 2 Purge next, 2 Purge normal
+ { kTheCast, "scriptText", kTheScriptText },
+ { kTheCast, "width", kTheWidth },
+
+ // Shape fields
+ { kTheCast, "backColor", kTheBackColor },
+ { kTheCast, "foreColor", kTheForeColor },
+
+ // Digital video fields
+ { kTheCast, "controller", kTheController },
+ { kTheCast, "directToStage",kTheDirectToStage },
+ { kTheCast, "frameRate", kTheFrameRate },
+ { kTheCast, "loop", kTheLoop },
+ { kTheCast, "pausedAtStart",kThePausedAtStart },
+ { kTheCast, "preload", kThePreload },
+ { kTheCast, "sound", kTheSound }, // 0-1 off-on
+
+ // Bitmap fields
+ { kTheCast, "depth", kTheDepth },
+ { kTheCast, "regPoint", kTheRegPoint },
+ { kTheCast, "palette", kThePalette },
+ { kTheCast, "picture", kThePicture },
+
+ // TextCast fields
+ { kTheCast, "hilite", kTheHilite },
+ { kTheCast, "size", kTheSize },
+ { kTheCast, "text", kTheText },
+
+ { kTheWindow, "drawRect", kTheDrawRect },
+ { kTheWindow, "filename", kTheFilename },
+ { kTheWindow, "sourceRect", kTheSourceRect },
+ { kTheWindow, "visible", kTheVisible },
+
+ { kTheMenuItem, "checkmark", kTheCheckMark },
+ { kTheMenuItem, "enabled", kTheEnabled },
+ { kTheMenuItem, "name", kTheName },
+ { kTheMenuItem, "script", kTheScript },
+
+ { kTheMenu, "name", kTheName },
+
+ { kTheMenuItems,"number", kTheNumber },
+ { kTheMenus, "number", kTheNumber },
+
+ { kTheNOEntity, NULL, kTheNOField }
+};
+
+void Lingo::initTheEntities() {
+ TheEntity *e = entities;
+
+ while (e->entity != kTheNOEntity) {
+ _theEntities[e->name] = e;
+ e++;
+ }
+
+ TheEntityField *f = fields;
+
+ while (f->entity != kTheNOEntity) {
+ _theEntityFields[Common::String::format("%d%s", f->entity, f->name)] = f;
+ f++;
+ }
+}
+
+void Lingo::setTheEntity(int entity, Datum &id, int field, Datum &d) {
+ switch (entity) {
+ case kTheSprite:
+ setTheSprite(id, field, d);
+ break;
+ case kThePerFrameHook:
+ warning("STUB: setting the perframehook");
+ break;
+ case kTheFloatPrecision:
+ _floatPrecision = d.toInt();
+ _floatPrecision = MAX(0, MIN(_floatPrecision, 19)); // 0 to 19
+ _floatPrecisionFormat = Common::String::format("%%.%df", _floatPrecision);
+ warning("set to %d: %s", _floatPrecision, _floatPrecisionFormat.c_str());
+ break;
+ default:
+ warning("Unprocessed setting field %d of entity %d", field, entity);
+ }
+}
+
+void Lingo::setTheSprite(Datum &id1, int field, Datum &d) {
+ int id = 0;
+
+ if (id1.type == INT) {
+ id = id1.u.i;
+ } else {
+ warning("Unknown the sprite id type: %s", id1.type2str());
+ return;
+ }
+
+ d.toInt(); // Enforce Integer
+
+ if (!_vm->_currentScore) {
+ warning("The sprite %d field %d setting over non-active score", id, field);
+ return;
+ }
+
+ Sprite *sprite = _vm->_currentScore->getSpriteById(id);
+
+ if (!sprite)
+ return;
+
+ switch (field) {
+ case kTheCastNum:
+ if (_vm->_currentScore->_casts.contains(d.u.i)) {
+ sprite->_cast = _vm->_currentScore->_casts[d.u.i];
+ sprite->_castId = d.u.i;
+ }
+ break;
+ case kTheWidth:
+ sprite->_width = d.u.i;
+ break;
+ case kTheHeight:
+ sprite->_height = d.u.i;
+ break;
+ case kTheTrails:
+ sprite->_trails = d.u.i;
+ break;
+ case kTheInk:
+ sprite->_ink = static_cast<InkType>(d.u.i);
+ break;
+ case kTheLocH:
+ sprite->_startPoint.x = d.u.i;
+ break;
+ case kTheLocV:
+ sprite->_startPoint.y = d.u.i;
+ break;
+ case kTheConstraint:
+ sprite->_constraint = d.u.i;
+ break;
+ case kTheMoveable:
+ sprite->_moveable = d.u.i;
+ break;
+ case kTheBackColor:
+ sprite->_backColor = d.u.i;
+ break;
+ case kTheForeColor:
+ sprite->_foreColor = d.u.i;
+ break;
+ case kTheLeft:
+ sprite->_left = d.u.i;
+ break;
+ case kTheRight:
+ sprite->_right = d.u.i;
+ break;
+ case kTheTop:
+ sprite->_top = d.u.i;
+ break;
+ case kTheBottom:
+ sprite->_bottom = d.u.i;
+ break;
+ case kTheBlend:
+ sprite->_blend = d.u.i;
+ break;
+ case kTheVisible:
+ sprite->_visible = (d.u.i == 0 ? false : true);
+ break;
+ case kTheType:
+ sprite->_type = d.u.i;
+ break;
+ case kTheMovieRate:
+ sprite->_movieRate = d.u.i;
+ break;
+ case kTheMovieTime:
+ sprite->_movieTime = d.u.i;
+ break;
+ case kTheStopTime:
+ sprite->_stopTime = d.u.i;
+ break;
+ case kTheStartTime:
+ sprite->_startTime = d.u.i;
+ break;
+ case kTheStretch:
+ sprite->_stretch = d.u.i;
+ break;
+ case kTheVolume:
+ sprite->_volume = d.u.i;
+ break;
+ case kTheLineSize:
+ sprite->_lineSize = d.u.i;
+ break;
+ case kTheEditableText:
+ sprite->_editableText = *d.toString();
+ break;
+ default:
+ warning("Unprocessed setting field %d of sprite", field);
+ }
+}
+
+Datum Lingo::getTheEntity(int entity, Datum &id, int field) {
+ Datum d;
+
+ switch (entity) {
+ case kTheSprite:
+ d = getTheSprite(id, field);
+ break;
+ case kTheCast:
+ d = getTheCast(id, field);
+ case kThePerFrameHook:
+ warning("STUB: getting the perframehook");
+ break;
+ case kTheFloatPrecision:
+ d.type = INT;
+ d.u.i = _floatPrecision;
+ break;
+ default:
+ warning("Unprocessed getting field %d of entity %d", field, entity);
+ d.type = VOID;
+ }
+
+ return d;
+}
+
+Datum Lingo::getTheSprite(Datum &id1, int field) {
+ Datum d;
+ int id = 0;
+
+ if (id1.type == INT) {
+ id = id1.u.i;
+ } else {
+ warning("Unknown the sprite id type: %s", id1.type2str());
+ return d;
+ }
+
+ if (!_vm->_currentScore) {
+ warning("The sprite %d field %d setting over non-active score", id, field);
+ return d;
+ }
+
+ Sprite *sprite = _vm->_currentScore->getSpriteById(id);
+
+ if (!sprite)
+ return d;
+
+ d.type = INT;
+
+ switch (field) {
+ case kTheCastNum:
+ d.u.i = sprite->_castId;
+ break;
+ case kTheWidth:
+ d.u.i = sprite->_width;
+ break;
+ case kTheHeight:
+ d.u.i = sprite->_height;
+ break;
+ case kTheTrails:
+ d.u.i = sprite->_trails;
+ break;
+ case kTheInk:
+ d.u.i = sprite->_ink;
+ break;
+ case kTheLocH:
+ d.u.i = sprite->_startPoint.x;
+ break;
+ case kTheLocV:
+ d.u.i = sprite->_startPoint.y;
+ break;
+ case kTheConstraint:
+ d.u.i = sprite->_constraint;
+ break;
+ case kTheMoveable:
+ d.u.i = sprite->_moveable;
+ break;
+ case kTheBackColor:
+ d.u.i = sprite->_backColor;
+ break;
+ case kTheForeColor:
+ d.u.i = sprite->_foreColor;
+ break;
+ case kTheLeft:
+ d.u.i = sprite->_left;
+ break;
+ case kTheRight:
+ d.u.i = sprite->_right;
+ break;
+ case kTheBottom:
+ d.u.i = sprite->_bottom;
+ break;
+ case kTheTop:
+ d.u.i = sprite->_top;
+ break;
+ case kTheBlend:
+ d.u.i = sprite->_blend;
+ break;
+ case kTheVisible:
+ d.u.i = (sprite->_visible ? 1 : 0);
+ break;
+ case kTheType:
+ d.u.i = sprite->_type;
+ break;
+ case kTheMovieRate:
+ d.u.i = sprite->_movieRate;
+ break;
+ case kTheMovieTime:
+ d.u.i = sprite->_movieTime;
+ break;
+ case kTheStopTime:
+ d.u.i = sprite->_stopTime;
+ break;
+ case kTheStartTime:
+ d.u.i = sprite->_startTime;
+ break;
+ case kTheVolume:
+ d.u.i = sprite->_volume;
+ break;
+ case kTheStretch:
+ d.u.i = sprite->_stretch;
+ break;
+ case kTheLineSize:
+ d.u.i = sprite->_lineSize;
+ break;
+ case kTheEditableText:
+ d.toString();
+ d.u.s = &sprite->_editableText;
+ break;
+ default:
+ warning("Unprocessed getting field %d of sprite", field);
+ d.type = VOID;
+ }
+
+ return d;
+}
+
+Datum Lingo::getTheCast(Datum &id1, int field) {
+ Datum d;
+ int id = 0;
+
+ if (id1.type == INT) {
+ id = id1.u.i;
+ } else {
+ warning("Unknown the cast id type: %s", id1.type2str());
+ return d;
+ }
+
+ if (!_vm->_currentScore) {
+ warning("The cast %d field %d setting over non-active score", id, field);
+ return d;
+ }
+
+ Cast *cast;
+ CastInfo *castInfo;
+ if (!_vm->_currentScore->_casts.contains(id)) {
+ if (field == kTheLoaded) {
+ d.type = INT;
+ d.u.i = 0;
+ }
+
+ return d;
+ } else {
+ warning("The cast %d found", id);
+ return d;
+ }
+ cast = _vm->_currentScore->_casts[id];
+ castInfo = _vm->_currentScore->_castsInfo[id];
+
+ d.type = INT;
+
+ switch (field) {
+ case kTheCastType:
+ d.u.i = cast->type;
+ break;
+ case kTheFilename:
+ d.toString();
+ d.u.s = &castInfo->fileName;
+ break;
+ case kTheName:
+ d.toString();
+ d.u.s = &castInfo->name;
+ break;
+ case kTheScriptText:
+ d.toString();
+ d.u.s = &castInfo->script;
+ break;
+ case kTheWidth:
+ d.u.i = cast->initialRect.width();
+ break;
+ case kTheHeight:
+ d.u.i = cast->initialRect.height();
+ break;
+ case kTheBackColor:
+ {
+ if (cast->type != kCastShape) {
+ warning("Field %d of cast %d not found", field, id);
+ d.type = VOID;
+ return d;
+ }
+
+ ShapeCast *shape = static_cast<ShapeCast *>(_vm->_currentScore->_casts[id]);
+ d.u.i = shape->bgCol;
+ }
+ break;
+ case kTheForeColor:
+ {
+ if (cast->type != kCastShape) {
+ warning("Field %d of cast %d not found", field, id);
+ d.type = VOID;
+ return d;
+ }
+
+ ShapeCast *shape = static_cast<ShapeCast *>(_vm->_currentScore->_casts[id]);
+ d.u.i = shape->fgCol;
+ }
+ break;
+ case kTheLoaded:
+ d.u.i = 1; //Not loaded handled above
+ break;
+ default:
+ warning("Unprocessed getting field %d of cast %d", field, id);
+ d.type = VOID;
+ //TODO find out about String fields
+ }
+
+ return d;
+}
+
+void Lingo::setTheCast(Datum &id1, int field, Datum &d) {
+ int id = 0;
+
+ if (id1.type == INT) {
+ id = id1.u.i;
+ } else {
+ warning("Unknown the cast id type: %s", id1.type2str());
+ return;
+ }
+
+ if (!_vm->_currentScore) {
+ warning("The cast %d field %d setting over non-active score", id, field);
+ return;
+ }
+
+ Cast *cast = _vm->_currentScore->_casts[id];
+ CastInfo *castInfo = _vm->_currentScore->_castsInfo[id];
+
+ if (!cast) {
+ warning("The cast %d found", id);
+ return;
+ }
+
+ switch (field) {
+ case kTheCastType:
+ cast->type = static_cast<CastType>(d.u.i);
+ cast->modified = 1;
+ break;
+ case kTheFilename:
+ castInfo->fileName = *d.u.s;
+ break;
+ case kTheName:
+ castInfo->name = *d.u.s;
+ break;
+ case kTheScriptText:
+ castInfo->script = *d.u.s;
+ break;
+ case kTheWidth:
+ cast->initialRect.setWidth(d.u.i);
+ cast->modified = 1;
+ break;
+ case kTheHeight:
+ cast->initialRect.setHeight(d.u.i);
+ cast->modified = 1;
+ break;
+ case kTheBackColor:
+ {
+ if (cast->type != kCastShape) {
+ warning("Field %d of cast %d not found", field, id);
+ }
+ ShapeCast *shape = static_cast<ShapeCast *>(_vm->_currentScore->_casts[id]);
+ shape->bgCol = d.u.i;
+ shape->modified = 1;
+ }
+ break;
+ case kTheForeColor:
+ {
+ if (cast->type != kCastShape) {
+ warning("Field %d of cast %d not found", field, id);
+ return;
+ }
+ ShapeCast *shape = static_cast<ShapeCast *>(_vm->_currentScore->_casts[id]);
+ shape->fgCol = d.u.i;
+ shape->modified = 1;
+ }
+ break;
+ default:
+ warning("Unprocessed getting field %d of cast %d", field, id);
+ }
+}
+
+} // End of namespace Director
diff --git a/engines/director/lingo/lingo-the.h b/engines/director/lingo/lingo-the.h
new file mode 100644
index 0000000000..5fea4ba009
--- /dev/null
+++ b/engines/director/lingo/lingo-the.h
@@ -0,0 +1,153 @@
+/* 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 2
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+ #ifndef DIRECTOR_LINGO_LINGO_THE_H
+ #define DIRECTOR_LINGO_LINGO_THE_H
+
+namespace Director {
+
+enum TheEntityType {
+ kTheNOEntity = 0,
+ kTheFrame = 1,
+ kThePathName,
+ kTheMenu,
+ kTheMenuItem,
+ kTheMenuItems,
+ kTheMenus,
+ kTheMovie,
+ kTheMouseH,
+ kTheMouseV,
+ kTheMouseDownScript,
+ kTheMouseUpScript,
+
+ kTheSprite,
+ kTheCast,
+ kThePerFrameHook,
+ kTheTicks,
+ kTheTimer,
+ kTheTimeoutLength,
+ kTheWindow,
+
+ kTheClickOn,
+ kTheDoubleClick,
+ kTheLastClick,
+ kTheLastFrame,
+ kTheLastEvent,
+ kTheMouseDown,
+ kTheMouseUp,
+ kTheRightMouseUp,
+ kTheRightMouseDown,
+ kTheStillDown,
+ kTheKey,
+ kTheKeyCode,
+ kTheControlDown,
+ kTheCommandDown,
+ kTheShiftDown,
+ kTheOptionDown,
+
+ kTheColorDepth,
+ kTheColorQD,
+ kTheExitLock,
+ kTheFloatPrecision,
+ kTheItemDelimiter,
+ kTheMultiSound,
+ kThePreloadEventAbort,
+ kTheRomanLingo,
+ kTheStage
+};
+
+enum TheFieldType {
+ kTheNOField = 0,
+ kTheCastNum = 1,
+ kTheCastType,
+ kTheCheckMark,
+ kTheController,
+ kTheCursor,
+ kTheDepth,
+ kTheDirectToStage,
+ kTheDrawRect,
+ kTheLocH,
+ kTheLocV,
+ kTheBackColor,
+ kTheBlend,
+ kTheBottom,
+ kTheConstraint,
+ kTheEditableText,
+ kTheEnabled,
+ kTheForeColor,
+ kTheFrameRate,
+ kTheFilename,
+ kTheHeight,
+ kTheHilite,
+ kTheInk,
+ kTheLeft,
+ kTheLineSize,
+ kTheLoop,
+ kTheLoaded,
+ kTheModified,
+ kTheMoveable,
+ kTheMovieRate,
+ kTheMovieTime,
+ kTheNumber,
+ kTheName,
+ kThePalette,
+ kThePausedAtStart,
+ kThePicture,
+ kThePreload,
+ kThePurgePriority,
+ kTheRect,
+ kTheRegPoint,
+ kTheRight,
+ kTheStopTime,
+ kTheStretch,
+ kTheStartTime,
+ kTheScript,
+ kTheScriptNum,
+ kTheScriptText,
+ kTheSize,
+ kTheStrech,
+ kTheSound,
+ kTheSourceRect,
+ kTheText,
+ kTheTop,
+ kTheTrails,
+ kTheType,
+ kTheVisible,
+ kTheVolume,
+ kTheWidth
+};
+
+struct TheEntity {
+ TheEntityType entity;
+ const char *name;
+ bool hasId;
+};
+
+struct TheEntityField {
+ TheEntityType entity;
+ const char *name;
+ TheFieldType field;
+};
+
+} // End of namespace Director
+
+#endif
diff --git a/engines/director/lingo/lingo.cpp b/engines/director/lingo/lingo.cpp
new file mode 100644
index 0000000000..0bb43092f3
--- /dev/null
+++ b/engines/director/lingo/lingo.cpp
@@ -0,0 +1,395 @@
+/* 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 2
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "director/lingo/lingo.h"
+#include "director/lingo/lingo-gr.h"
+
+namespace Director {
+
+Lingo *g_lingo;
+
+struct EventHandlerType {
+ LEvent handler;
+ const char *name;
+} static const eventHanlerDescs[] = {
+ { kEventPrepareMovie, "prepareMovie" },
+ { kEventStartMovie, "startMovie" },
+ { kEventStopMovie, "stopMovie" },
+
+ { kEventNew, "newSprite" },
+ { kEventBeginSprite, "beginSprite" },
+ { kEventEndSprite, "endSprite" },
+
+ { kEventEnterFrame, "enterFrame" },
+ { kEventPrepareFrame, "prepareFrame" },
+ { kEventIdle, "idle" },
+ { kEventStepFrame, "stepFrame"},
+ { kEventExitFrame, "exitFrame" },
+
+ { kEventActivateWindow, "activateWindow" },
+ { kEventDeactivateWindow, "deactivateWindow" },
+ { kEventMoveWindow, "moveWindow" },
+ { kEventResizeWindow, "resizeWindow" },
+ { kEventOpenWindow, "openWindow" },
+ { kEventCloseWindow, "closeWindow" },
+ { kEventStart, "start" },
+
+ { kEventKeyUp, "keyUp" },
+ { kEventKeyDown, "keyDown" },
+ { kEventMouseUp, "mouseUp" },
+ { kEventMouseDown, "mouseDown" },
+ { kEventRightMouseDown, "rightMouseDown" },
+ { kEventRightMouseUp, "rightMouseUp" },
+ { kEventMouseEnter, "mouseEnter" },
+ { kEventMouseLeave, "mouseLeave" },
+ { kEventMouseUpOutSide, "mouseUpOutSide" },
+ { kEventMouseWithin, "mouseWithin" },
+
+ { kEventNone, 0 },
+};
+
+Symbol::Symbol() {
+ name = NULL;
+ type = VOID;
+ u.s = NULL;
+ nargs = 0;
+ global = false;
+}
+
+Lingo::Lingo(DirectorEngine *vm) : _vm(vm) {
+ g_lingo = this;
+
+ for (const EventHandlerType *t = &eventHanlerDescs[0]; t->handler != kEventNone; ++t)
+ _eventHandlerTypes[t->handler] = t->name;
+
+ initBuiltIns();
+ initTheEntities();
+
+ _currentScript = 0;
+ _currentScriptType = kMovieScript;
+ _pc = 0;
+ _returning = false;
+ _indef = false;
+
+ _linenumber = _colnumber = 0;
+
+ _hadError = false;
+
+ _inFactory = false;
+
+ _floatPrecision = 4;
+ _floatPrecisionFormat = "%.4f";
+
+ warning("Lingo Inited");
+}
+
+Lingo::~Lingo() {
+}
+
+const char *Lingo::findNextDefinition(const char *s) {
+ const char *res = s;
+
+ while (*res) {
+ while (*res && (*res == ' ' || *res == '\t' || *res == '\n'))
+ res++;
+
+ if (!*res)
+ return NULL;
+
+ if (!strncmp(res, "macro ", 6)) {
+ warning("See macro");
+ return res;
+ }
+
+ if (!strncmp(res, "factory ", 8)) {
+ warning("See factory");
+ return res;
+ }
+
+ if (!strncmp(res, "method ", 7)) {
+ warning("See method");
+ return res;
+ }
+
+ while (*res && *res != '\n')
+ res++;
+ }
+
+ return NULL;
+}
+
+void Lingo::addCode(const char *code, ScriptType type, uint16 id) {
+ debug(2, "Add code \"%s\" for type %d with id %d", code, type, id);
+
+ if (_scripts[type].contains(id)) {
+ delete _scripts[type][id];
+ }
+
+ _currentScript = new ScriptData;
+ _currentScriptType = type;
+ _scripts[type][id] = _currentScript;
+
+ _linenumber = _colnumber = 1;
+ _hadError = false;
+
+ const char *begin, *end;
+
+ // macros and factories have conflicting grammar. Thus we ease life for the parser.
+ if ((begin = findNextDefinition(code))) {
+ bool first = true;
+
+ while ((end = findNextDefinition(begin + 1))) {
+
+ if (first) {
+ begin = code;
+ first = false;
+ }
+ Common::String chunk(begin, end);
+
+ if (chunk.hasPrefix("factory") || chunk.hasPrefix("method"))
+ _inFactory = true;
+ else if (chunk.hasPrefix("macro"))
+ _inFactory = false;
+ else
+ _inFactory = false;
+
+ debug(2, "Code chunk:\n#####\n%s#####", chunk.c_str());
+
+ parse(chunk.c_str());
+
+ _currentScript->clear();
+
+ begin = end;
+ }
+
+ _hadError = true; // HACK: This is for preventing test execution
+
+ debug(2, "Code chunk:\n#####\n%s#####", begin);
+ parse(begin);
+ } else {
+ parse(code);
+
+ code1(STOP);
+ }
+
+ _inFactory = false;
+
+ if (_currentScript->size() && !_hadError)
+ Common::hexdump((byte *)&_currentScript->front(), _currentScript->size() * sizeof(inst));
+}
+
+void Lingo::executeScript(ScriptType type, uint16 id) {
+ if (!_scripts[type].contains(id)) {
+ warning("Request to execute non-existant script type %d id %d", type, id);
+ return;
+ }
+
+ debug(2, "Executing script type: %d, id: %d", type, id);
+
+ _currentScript = _scripts[type][id];
+ _pc = 0;
+ _returning = false;
+
+ _localvars = new SymbolHash;
+
+ execute(_pc);
+
+ cleanLocalVars();
+}
+
+void Lingo::processEvent(LEvent event, int entityId) {
+ if (!_eventHandlerTypes.contains(event))
+ error("processEvent: Unknown event %d for entity %d", event, entityId);
+
+ debug(2, "processEvent(%s) for %d", _eventHandlerTypes[event], entityId);
+}
+
+int Lingo::alignTypes(Datum &d1, Datum &d2) {
+ int opType = INT;
+
+ if (d1.type == FLOAT || d2.type == FLOAT) {
+ opType = FLOAT;
+ d1.toFloat();
+ d2.toFloat();
+ }
+
+ return opType;
+}
+
+int Datum::toInt() {
+ switch (type) {
+ case INT:
+ // no-op
+ break;
+ case FLOAT:
+ u.i = (int)u.f;
+ type = INT;
+ break;
+ default:
+ warning("Incorrect operation toInt() for type: %s", type2str());
+ }
+
+ return u.i;
+}
+
+double Datum::toFloat() {
+ switch (type) {
+ case INT:
+ u.f = (double)u.i;
+ type = FLOAT;
+ break;
+ case FLOAT:
+ // no-op
+ break;
+ default:
+ warning("Incorrect operation toFloat() for type: %s", type2str());
+ }
+
+ return u.f;
+}
+
+Common::String *Datum::toString() {
+ Common::String *s = new Common::String;
+ switch (type) {
+ case INT:
+ s->format("%d", u.i);
+ break;
+ case FLOAT:
+ s->format(g_lingo->_floatPrecisionFormat.c_str(), u.f);
+ break;
+ case STRING:
+ delete s;
+ s = u.s;
+ break;
+ default:
+ warning("Incorrect operation toString() for type: %s", type2str());
+ }
+
+ u.s = s;
+ type = STRING;
+
+ return u.s;
+}
+
+const char *Datum::type2str(bool isk) {
+ static char res[20];
+
+ switch (isk ? u.i : type) {
+ case INT:
+ return isk ? "#integer" : "INT";
+ case FLOAT:
+ return isk ? "#float" : "FLOAT";
+ case STRING:
+ return isk ? "#string" : "STRING";
+ case CASTREF:
+ return "CASTREF";
+ case VOID:
+ return isk ? "#void" : "VOID";
+ case POINT:
+ return isk ? "#point" : "POINT";
+ case SYMBOL:
+ return isk ? "#symbol" : "SYMBOL";
+ default:
+ snprintf(res, 20, "-- (%d) --", type);
+ return res;
+ }
+}
+
+// This is table for built-in Macintosh font lowercasing.
+// '.' means that the symbol should be not changed, rest
+// of the symbols are stripping the diacritics
+// The table starts from 0x80
+//
+// TODO: Check it for correctness.
+static char lowerCaseConvert[] =
+"aacenoua" // 80
+"aaaaacee" // 88
+"eeiiiino" // 90
+"oooouuuu" // 98
+"........" // a0
+".......o" // a8
+"........" // b0
+".......o" // b8
+"........" // c0
+".. aao.." // c8
+"--.....y";// d0-d8
+
+Common::String *Lingo::toLowercaseMac(Common::String *s) {
+ Common::String *res = new Common::String;
+ const unsigned char *p = (const unsigned char *)s->c_str();
+
+ while (*p) {
+ if (*p >= 0x80 && *p <= 0xd8) {
+ if (lowerCaseConvert[*p - 0x80] != '.')
+ *res += lowerCaseConvert[*p - 0x80];
+ else
+ *res += *p;
+ } else if (*p < 0x80) {
+ *res += tolower(*p);
+ } else {
+ warning("Unacceptable symbol in toLowercaseMac: %c", *p);
+
+ *res += *p;
+ }
+ p++;
+ }
+
+ return res;
+}
+
+void Lingo::runTests() {
+ Common::File inFile;
+ Common::ArchiveMemberList fileList;
+ SearchMan.listMatchingMembers(fileList, "*.lingo");
+
+ int counter = 1;
+
+ for (Common::ArchiveMemberList::iterator it = fileList.begin(); it != fileList.end(); ++it) {
+ Common::ArchiveMember const &m = **it;
+ Common::SeekableReadStream *const stream = m.createReadStream();
+ if (stream) {
+ uint size = stream->size();
+
+ char *script = (char *)calloc(size + 1, 1);
+
+ stream->read(script, size);
+
+ warning("Compiling file %s of size %d, id: %d", m.getName().c_str(), size, counter);
+
+ _hadError = false;
+ addCode(script, kMovieScript, counter);
+
+ if (!_hadError)
+ executeScript(kMovieScript, counter);
+ else
+ warning("Skipping execution");
+
+ free(script);
+
+ counter++;
+ }
+
+ inFile.close();
+ }
+}
+
+} // End of namespace Director
diff --git a/engines/director/lingo/lingo.h b/engines/director/lingo/lingo.h
new file mode 100644
index 0000000000..3bec29832e
--- /dev/null
+++ b/engines/director/lingo/lingo.h
@@ -0,0 +1,335 @@
+/* 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 2
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef DIRECTOR_LINGO_LINGO_H
+#define DIRECTOR_LINGO_LINGO_H
+
+#include "common/debug.h"
+#include "common/hashmap.h"
+#include "common/hash-str.h"
+#include "audio/audiostream.h"
+#include "common/str.h"
+#include "engines/director/director.h"
+#include "engines/director/score.h"
+#include "director/lingo/lingo-gr.h"
+#include "director/lingo/lingo-the.h"
+
+namespace Director {
+
+enum LEvent {
+ kEventPrepareMovie,
+ kEventStartMovie,
+ kEventStopMovie,
+
+ kEventNew,
+ kEventBeginSprite,
+ kEventEndSprite,
+
+ kEventNone,
+ kEventEnterFrame,
+ kEventPrepareFrame,
+ kEventIdle,
+ kEventStepFrame,
+ kEventExitFrame,
+
+ kEventActivateWindow,
+ kEventDeactivateWindow,
+ kEventMoveWindow,
+ kEventResizeWindow,
+ kEventOpenWindow,
+ kEventCloseWindow,
+
+ kEventKeyUp,
+ kEventKeyDown,
+ kEventMouseUp,
+ kEventMouseDown,
+ kEventRightMouseUp,
+ kEventRightMouseDown,
+ kEventMouseEnter,
+ kEventMouseLeave,
+ kEventMouseUpOutSide,
+ kEventMouseWithin,
+
+ kEventStart
+};
+
+typedef void (*inst)(void);
+#define STOP (inst)0
+
+typedef Common::Array<inst> ScriptData;
+typedef Common::Array<double> FloatArray;
+
+struct Symbol { /* symbol table entry */
+ char *name;
+ int type;
+ union {
+ int i; /* VAR */
+ double f; /* FLOAT */
+ ScriptData *defn; /* FUNCTION, PROCEDURE */
+ void (*func)(void); /* BUILTIN */
+ Common::String *s; /* STRING */
+ FloatArray *arr; /* ARRAY, POINT, RECT */
+ } u;
+ int nargs;
+ bool global;
+
+ Symbol();
+};
+
+struct Datum { /* interpreter stack type */
+ int type;
+
+ union {
+ int i;
+ double f;
+ Common::String *s;
+ Symbol *sym;
+ FloatArray *arr; /* ARRAY, POINT, RECT */
+ } u;
+
+ Datum() { u.sym = NULL; type = VOID; }
+
+ double toFloat();
+ int toInt();
+ Common::String *toString();
+
+ const char *type2str(bool isk = false);
+};
+
+struct Builtin {
+ void (*func)(void);
+ int nargs;
+
+ Builtin(void (*func1)(void), int nargs1) : func(func1), nargs(nargs1) {}
+};
+
+typedef Common::HashMap<int32, ScriptData *> ScriptHash;
+typedef Common::Array<Datum> StackData;
+typedef Common::HashMap<Common::String, Symbol *, Common::IgnoreCase_Hash, Common::IgnoreCase_EqualTo> SymbolHash;
+typedef Common::HashMap<Common::String, Builtin *, Common::IgnoreCase_Hash, Common::IgnoreCase_EqualTo> BuiltinHash;
+
+typedef Common::HashMap<Common::String, TheEntity *, Common::IgnoreCase_Hash, Common::IgnoreCase_EqualTo> TheEntityHash;
+typedef Common::HashMap<Common::String, TheEntityField *, Common::IgnoreCase_Hash, Common::IgnoreCase_EqualTo> TheEntityFieldHash;
+
+struct CFrame { /* proc/func call stack frame */
+ Symbol *sp; /* symbol table entry */
+ int retpc; /* where to resume after return */
+ ScriptData *retscript; /* which script to resume after return */
+ SymbolHash *localvars;
+};
+
+class Lingo {
+public:
+ Lingo(DirectorEngine *vm);
+ ~Lingo();
+
+ void addCode(const char *code, ScriptType type, uint16 id);
+ void executeScript(ScriptType type, uint16 id);
+
+ void processEvent(LEvent event, int entityId);
+
+ void initBuiltIns();
+ void initTheEntities();
+
+ Common::String *toLowercaseMac(Common::String *s);
+
+ void runTests();
+
+private:
+ const char *findNextDefinition(const char *s);
+
+public:
+ void execute(int pc);
+ void pushContext();
+ void popContext();
+ Symbol *lookupVar(const char *name, bool create = true, bool putInGlobalList = false);
+ void cleanLocalVars();
+ void define(Common::String &s, int start, int nargs, Common::String *prefix = NULL);
+ void processIf(int elselabel, int endlabel);
+
+ int alignTypes(Datum &d1, Datum &d2);
+
+ int code1(inst code) { _currentScript->push_back(code); return _currentScript->size() - 1; }
+ int code2(inst code_1, inst code_2) { int o = code1(code_1); code1(code_2); return o; }
+ int code3(inst code_1, inst code_2, inst code_3) { int o = code1(code_1); code1(code_2); code1(code_3); return o; }
+ int codeString(const char *s);
+ void codeLabel(int label);
+
+ int calcStringAlignment(const char *s) {
+ return calcCodeAlignment(strlen(s) + 1);
+ }
+ int calcCodeAlignment(int l) {
+ int instLen = sizeof(inst);
+ return (l + instLen - 1) / instLen;
+ }
+
+ void codeArg(Common::String *s);
+ void codeArgStore();
+ int codeFunc(Common::String *s, int numpar);
+ int codeFloat(double f);
+ void codeFactory(Common::String &s);
+
+ void pushVoid();
+
+ static void c_xpop();
+ static void c_printtop();
+
+ static void c_add();
+ static void c_sub();
+ static void c_mul();
+ static void c_div();
+ static void c_negate();
+
+ static void c_and();
+ static void c_or();
+ static void c_not();
+
+ static void c_ampersand();
+ static void c_concat();
+ static void c_contains();
+ static void c_starts();
+
+ static void c_intersects();
+ static void c_within();
+
+ static void c_constpush();
+ static void c_fconstpush();
+ static void c_stringpush();
+ static void c_varpush();
+ static void c_assign();
+ bool verify(Symbol *s);
+ static void c_eval();
+
+ static void c_swap();
+
+ static void c_theentitypush();
+ static void c_theentityassign();
+
+ static void c_repeatwhilecode();
+ static void c_repeatwithcode();
+ static void c_ifcode();
+ static void c_eq();
+ static void c_neq();
+ static void c_gt();
+ static void c_lt();
+ static void c_ge();
+ static void c_le();
+ static void c_call();
+ static void c_procret();
+
+ static void c_mci();
+ static void c_mciwait();
+ static void c_goto();
+ static void c_gotoloop();
+ static void c_gotonext();
+ static void c_gotoprevious();
+ static void c_global();
+
+ static void b_abs();
+ static void b_atan();
+ static void b_chars();
+ static void b_cos();
+ static void b_exp();
+ static void b_float();
+ static void b_integer();
+ static void b_length();
+ static void b_log();
+ static void b_pi();
+ static void b_power();
+ static void b_random();
+ static void b_sin();
+ static void b_sqrt();
+ static void b_string();
+ static void b_tan();
+
+ static void b_dontpassevent();
+ static void b_updatestage();
+ static void b_ilk();
+
+ static void b_point();
+
+ void func_mci(Common::String &s);
+ void func_mciwait(Common::String &s);
+ void func_goto(Common::String &frame, Common::String &movie);
+ void func_gotoloop();
+ void func_gotonext();
+ void func_gotoprevious();
+
+public:
+ void setTheEntity(int entity, Datum &id, int field, Datum &d);
+ void setTheSprite(Datum &id, int field, Datum &d);
+ void setTheCast(Datum &id, int field, Datum &d);
+ Datum getTheEntity(int entity, Datum &id, int field);
+ Datum getTheSprite(Datum &id, int field);
+ Datum getTheCast(Datum &id, int field);
+
+public:
+ ScriptData *_currentScript;
+ ScriptType _currentScriptType;
+ bool _returning;
+ bool _indef;
+
+ Common::Array<CFrame *> _callstack;
+ Common::Array<Common::String *> _argstack;
+ TheEntityHash _theEntities;
+ TheEntityFieldHash _theEntityFields;
+ Common::Array<int> _labelstack;
+
+ SymbolHash _handlers;
+
+ int _linenumber;
+ int _colnumber;
+
+ Common::String _floatPrecisionFormat;
+
+ bool _hadError;
+
+ bool _inFactory;
+ Common::String _currentFactory;
+
+private:
+ int parse(const char *code);
+ void push(Datum d);
+ Datum pop(void);
+
+ Common::HashMap<uint32, const char *> _eventHandlerTypes;
+ Common::HashMap<Common::String, Audio::AudioStream *> _audioAliases;
+
+ ScriptHash _scripts[kMaxScriptType + 1];
+
+ SymbolHash _globalvars;
+ SymbolHash *_localvars;
+
+ int _pc;
+
+ StackData _stack;
+
+ DirectorEngine *_vm;
+
+ int _floatPrecision;
+};
+
+extern Lingo *g_lingo;
+
+} // End of namespace Director
+
+#endif
diff --git a/engines/director/lingo/tests/Lingo bytecode.html b/engines/director/lingo/tests/Lingo bytecode.html
new file mode 100644
index 0000000000..1fcb4a31fc
--- /dev/null
+++ b/engines/director/lingo/tests/Lingo bytecode.html
@@ -0,0 +1,2360 @@
+<!DOCTYPE html>
+<!-- saved from url=(0054)http://fileformats.archiveteam.org/wiki/Lingo_bytecode -->
+<html lang="en" dir="ltr" class="client-js gr__fileformats_archiveteam_org"><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+<title>Lingo bytecode - Just Solve the File Format Problem</title>
+
+<meta name="generator" content="MediaWiki 1.19.2">
+<link rel="shortcut icon" href="http://fileformats.archiveteam.org/favicon.ico">
+<link rel="search" type="application/opensearchdescription+xml" href="http://fileformats.archiveteam.org/opensearch_desc.php" title="Just Solve the File Format Problem (en)">
+<link rel="EditURI" type="application/rsd+xml" href="http://fileformats.archiveteam.org/api.php?action=rsd">
+<link rel="copyright" href="http://creativecommons.org/publicdomain/zero/1.0/">
+<link rel="alternate" type="application/atom+xml" title="Just Solve the File Format Problem Atom feed" href="http://fileformats.archiveteam.org/index.php?title=Special:RecentChanges&amp;feed=atom">
+<link rel="stylesheet" href="./Lingo bytecode - Just Solve the File Format Problem_files/load.php">
+<style type="text/css" media="all">.js-messagebox{margin:1em 5%;padding:0.5em 2.5%;border:1px solid #ccc;background-color:#fcfcfc;font-size:0.8em}.js-messagebox .js-messagebox-group{margin:1px;padding:0.5em 2.5%;border-bottom:1px solid #ddd}.js-messagebox .js-messagebox-group:last-child{border-bottom:thin none transparent}
+
+/* cache key: justsolve:resourceloader:filter:minify-css:7:8b08bdc91c52a9ffba396dccfb5b473c */
+
+
+.mw-collapsible-toggle{float:right} li .mw-collapsible-toggle{float:none} .mw-collapsible-toggle-li{list-style:none}
+
+/* cache key: justsolve:resourceloader:filter:minify-css:7:4250852ed2349a0d4d0fc6509a3e7d4c */
+</style><meta name="ResourceLoaderDynamicStyles" content="">
+<style>a:lang(ar),a:lang(ckb),a:lang(fa),a:lang(kk-arab),a:lang(mzn),a:lang(ps),a:lang(ur){text-decoration:none}a.new,#quickbar a.new{color:#ba0000}
+
+/* cache key: justsolve:resourceloader:filter:minify-css:7:c88e2bcd56513749bec09a7e29cb3ffa */
+</style>
+
+<script src="./Lingo bytecode - Just Solve the File Format Problem_files/load(1).php"></script><script src="./Lingo bytecode - Just Solve the File Format Problem_files/load(2).php"></script>
+<script>if(window.mw){
+mw.config.set({"wgCanonicalNamespace":"","wgCanonicalSpecialPageName":false,"wgNamespaceNumber":0,"wgPageName":"Lingo_bytecode","wgTitle":"Lingo bytecode","wgCurRevisionId":25053,"wgArticleId":6263,"wgIsArticle":true,"wgAction":"view","wgUserName":null,"wgUserGroups":["*"],"wgCategories":["File Formats","Electronic File Formats","Development","FormatInfo without extensions","FormatInfo without mimetypes"],"wgBreakFrames":false,"wgPageContentLanguage":"en","wgSeparatorTransformTable":["",""],"wgDigitTransformTable":["",""],"wgRelevantPageName":"Lingo_bytecode","wgRestrictionEdit":[],"wgRestrictionMove":[]});
+}</script><script>if(window.mw){
+mw.loader.implement("user.options",function($){mw.user.options.set({"ccmeonemails":0,"cols":80,"date":"default","diffonly":0,"disablemail":0,"disablesuggest":0,"editfont":"default","editondblclick":0,"editsection":1,"editsectiononrightclick":0,"enotifminoredits":0,"enotifrevealaddr":0,"enotifusertalkpages":1,"enotifwatchlistpages":0,"extendwatchlist":0,"externaldiff":0,"externaleditor":0,"fancysig":0,"forceeditsummary":0,"gender":"unknown","hideminor":0,"hidepatrolled":0,"highlightbroken":1,"imagesize":2,"justify":0,"math":1,"minordefault":0,"newpageshidepatrolled":0,"nocache":0,"noconvertlink":0,"norollbackdiff":0,"numberheadings":0,"previewonfirst":0,"previewontop":1,"quickbar":5,"rcdays":7,"rclimit":50,"rememberpassword":0,"rows":25,"searchlimit":20,"showhiddencats":0,"showjumplinks":1,"shownumberswatching":1,"showtoc":1,"showtoolbar":1,"skin":"vector","stubthreshold":0,"thumbsize":2,"underline":2,"uselivepreview":0,"usenewrc":0,"watchcreations":0,"watchdefault":0,"watchdeletion":0,
+"watchlistdays":3,"watchlisthideanons":0,"watchlisthidebots":0,"watchlisthideliu":0,"watchlisthideminor":0,"watchlisthideown":0,"watchlisthidepatrolled":0,"watchmoves":0,"wllimit":250,"variant":"en","language":"en","searchNs0":true,"searchNs1":false,"searchNs2":false,"searchNs3":false,"searchNs4":false,"searchNs5":false,"searchNs6":false,"searchNs7":false,"searchNs8":false,"searchNs9":false,"searchNs10":false,"searchNs11":false,"searchNs12":false,"searchNs13":false,"searchNs14":false,"searchNs15":false});;},{},{});mw.loader.implement("user.tokens",function($){mw.user.tokens.set({"editToken":"+\\","watchToken":false});;},{},{});
+
+/* cache key: justsolve:resourceloader:filter:minify-js:7:9983699ab6150ffa89a90653b2338ac8 */
+}</script>
+<script>if(window.mw){
+mw.loader.load(["mediawiki.page.startup","mediawiki.legacy.wikibits","mediawiki.legacy.ajax"]);
+}</script><script type="text/javascript" src="./Lingo bytecode - Just Solve the File Format Problem_files/load(3).php"></script>
+<!--[if lt IE 7]><style type="text/css">body{behavior:url("/skins/vector/csshover.min.htc")}</style><![endif]--><meta name="chromesniffer" id="chromesniffer_meta" content="{&quot;MediaWiki&quot;:-1,&quot;jQuery&quot;:&quot;1.7.1&quot;}"><script type="text/javascript" src="chrome-extension://homgcnaoacgigpkkljjjekpignblkeae/detector.js"></script></head>
+<body class="mediawiki ltr sitedir-ltr ns-0 ns-subject page-Lingo_bytecode skin-vector action-view" data-gr-c-s-loaded="true">
+ <div id="mw-page-base" class="noprint"></div>
+ <div id="mw-head-base" class="noprint"></div>
+ <!-- content -->
+ <div id="content" class="mw-body">
+ <a id="top"></a>
+ <div id="mw-js-message" style="display:none;" class="js-messagebox"></div>
+ <!-- firstHeading -->
+ <h1 id="firstHeading" class="firstHeading">
+ <span dir="auto">Lingo bytecode</span>
+ </h1>
+ <!-- /firstHeading -->
+ <!-- bodyContent -->
+ <div id="bodyContent">
+ <!-- tagline -->
+ <div id="siteSub">From Just Solve the File Format Problem</div>
+ <!-- /tagline -->
+ <!-- subtitle -->
+ <div id="contentSub"></div>
+ <!-- /subtitle -->
+ <!-- jumpto -->
+ <div id="jump-to-nav" class="mw-jump">
+ Jump to: <a href="http://fileformats.archiveteam.org/wiki/Lingo_bytecode#mw-head">navigation</a>,
+ <a href="http://fileformats.archiveteam.org/wiki/Lingo_bytecode#p-search">search</a>
+ </div>
+ <!-- /jumpto -->
+ <!-- bodycontent -->
+ <div id="mw-content-text" lang="en" dir="ltr" class="mw-content-ltr"><table class="infobox formatinfo" border="0" style="float: right; border: 1px solid #666666; max-width: 25%; overflow: hidden; background-color: #F8E0F7; padding: 0.25em; margin: 0.25em 1em;">
+<tbody><tr>
+<th colspan="2"><a href="http://fileformats.archiveteam.org/wiki/File_Formats" title="File Formats">File Format</a></th>
+</tr>
+
+<tr>
+<th>Name</th>
+<td>Lingo bytecode</td>
+</tr>
+
+
+<tr>
+<th> Ontology
+</th>
+<td>
+<ul><li>
+<a href="http://fileformats.archiveteam.org/wiki/Electronic_File_Formats" title="Electronic File Formats">Electronic File Formats</a>
+<ul><li>
+<a href="http://fileformats.archiveteam.org/wiki/Development" title="Development">Development</a>
+<ul><li>
+<strong class="selflink">Lingo bytecode</strong>
+</li></ul>
+</li></ul>
+</li></ul>
+</td>
+</tr>
+
+
+
+</tbody></table>
+<p>This is a partial, work-in-progress examination of the bytecode created when Lingo code is compiled in Macromedia Director 4.0. It describes instructions for a stack-based virtual machine. This virtual machine is sometimes known as the IML, or Idealized Machine Layer.
+</p><p>Each instruction is one, two or three bytes.
+</p>
+<ul><li> If the first byte is in the range 0x00-0x3F, then the full instruction is one byte.
+</li><li> If the first byte is in the range 0x40-0x7F, then the full instruction is two bytes.
+</li><li> If the first byte is in the range 0x80-0xFF, then the full instruction is three bytes.
+</li></ul>
+<p>Constant blobs like string literals are stored after the bytecode, and referred to by records that are six bytes long regardless of the actual length of the data. This means the first constant will be referred to as 0x00, the second constant as 0x06, the third as 0x0C, and so on. Integer literals over 32767 and floating-point number literals are also stored as constants.
+</p><p>There is also a namelist for referring to external identifiers, stored separately from the bytecode. This is a simple array of strings.
+</p>
+<table id="toc" class="toc"><tbody><tr><td><div id="toctitle"><h2>Contents</h2><span class="toctoggle">&nbsp;[<a href="http://fileformats.archiveteam.org/wiki/Lingo_bytecode#" class="internal" id="togglelink">hide</a>]&nbsp;</span></div>
+<ul>
+<li class="toclevel-1 tocsection-1"><a href="http://fileformats.archiveteam.org/wiki/Lingo_bytecode#One-Byte_Instructions"><span class="tocnumber">1</span> <span class="toctext">One-Byte Instructions</span></a></li>
+<li class="toclevel-1 tocsection-2"><a href="http://fileformats.archiveteam.org/wiki/Lingo_bytecode#Two-Byte_Instructions"><span class="tocnumber">2</span> <span class="toctext">Two-Byte Instructions</span></a></li>
+<li class="toclevel-1 tocsection-3"><a href="http://fileformats.archiveteam.org/wiki/Lingo_bytecode#Three_Byte_Instructions"><span class="tocnumber">3</span> <span class="toctext">Three Byte Instructions</span></a></li>
+<li class="toclevel-1 tocsection-4"><a href="http://fileformats.archiveteam.org/wiki/Lingo_bytecode#Syntactic_Sugar"><span class="tocnumber">4</span> <span class="toctext">Syntactic Sugar</span></a></li>
+<li class="toclevel-1 tocsection-5"><a href="http://fileformats.archiveteam.org/wiki/Lingo_bytecode#Lscr_bytecode-container_chunk_layout"><span class="tocnumber">5</span> <span class="toctext">Lscr bytecode-container chunk layout</span></a>
+<ul>
+<li class="toclevel-2 tocsection-6"><a href="http://fileformats.archiveteam.org/wiki/Lingo_bytecode#Header"><span class="tocnumber">5.1</span> <span class="toctext">Header</span></a></li>
+<li class="toclevel-2 tocsection-7"><a href="http://fileformats.archiveteam.org/wiki/Lingo_bytecode#Function_Record"><span class="tocnumber">5.2</span> <span class="toctext">Function Record</span></a></li>
+<li class="toclevel-2 tocsection-8"><a href="http://fileformats.archiveteam.org/wiki/Lingo_bytecode#Bytecode_Trailer"><span class="tocnumber">5.3</span> <span class="toctext">Bytecode Trailer</span></a></li>
+<li class="toclevel-2 tocsection-9"><a href="http://fileformats.archiveteam.org/wiki/Lingo_bytecode#Constants"><span class="tocnumber">5.4</span> <span class="toctext">Constants</span></a></li>
+</ul>
+</li>
+<li class="toclevel-1 tocsection-10"><a href="http://fileformats.archiveteam.org/wiki/Lingo_bytecode#Projector_File_.28Windows.29"><span class="tocnumber">6</span> <span class="toctext">Projector File (Windows)</span></a>
+<ul>
+<li class="toclevel-2 tocsection-11"><a href="http://fileformats.archiveteam.org/wiki/Lingo_bytecode#Director_3.0"><span class="tocnumber">6.1</span> <span class="toctext">Director 3.0</span></a></li>
+<li class="toclevel-2 tocsection-12"><a href="http://fileformats.archiveteam.org/wiki/Lingo_bytecode#Director_4.0"><span class="tocnumber">6.2</span> <span class="toctext">Director 4.0</span></a></li>
+</ul>
+</li>
+</ul>
+</td></tr></tbody></table>
+<h1> <span class="mw-headline" id="One-Byte_Instructions"> One-Byte Instructions </span></h1>
+<table class="wikitable">
+
+<tbody><tr>
+<th> 01
+</th>
+<td> <code>exit</code>
+</td>
+<td>
+</td>
+<td>
+</td>
+<td> Leave the current function immediately and return to its caller. Automatically added as the final step of a function.
+</td></tr>
+<tr>
+<th> 02
+</th></tr>
+<tr>
+<th> 03
+</th>
+<td>
+<p><code>0</code>
+</p><p><code>FALSE</code>
+</p>
+</td>
+<td>
+</td>
+<td> +1
+</td>
+<td>
+<p>Push zero onto the stack.
+</p>
+</td></tr>
+<tr>
+<th> 04
+</th>
+<td> <code>(a&nbsp;*&nbsp;b)</code>
+</td>
+<td> -2
+</td>
+<td> +1
+</td>
+<td> Pop two values from the stack, multiply them together and push the result.
+</td></tr>
+<tr>
+<th> 05
+</th>
+<td> <code>(a&nbsp;+&nbsp;b)</code>
+</td>
+<td> -2
+</td>
+<td> +1
+</td>
+<td> Pop two values from the stack, add them together and push the result.
+</td></tr>
+<tr>
+<th> 06
+</th>
+<td> <code>(a&nbsp;-&nbsp;b)</code>
+</td>
+<td> -2
+</td>
+<td> +1
+</td>
+<td> Pop two values from the stack, subtract the second from the first and push the result.
+</td></tr>
+<tr>
+<th> 07
+</th>
+<td> <code>(a&nbsp;/&nbsp;b)</code>
+</td>
+<td> -2
+</td>
+<td> +1
+</td>
+<td> Pop two values from the stack, divide the first by the second and push the result.
+</td></tr>
+<tr>
+<th> 08
+</th>
+<td> <code>(a&nbsp;mod&nbsp;b)</code>
+</td>
+<td> -2
+</td>
+<td> +1
+</td>
+<td> Pop two values from the stack, perform a modulo operation and push the result.
+</td></tr>
+<tr>
+<th> 09
+</th>
+<td> <code>(-a)</code>
+</td>
+<td> -1
+</td>
+<td> +1
+</td>
+<td> Pop one value from the stack, negate it and push the result.
+</td></tr>
+<tr>
+<th> 0A
+</th>
+<td> <code>(a&nbsp;&amp;&nbsp;b)</code>
+</td>
+<td> -2
+</td>
+<td> +1
+</td>
+<td> Pop two values from the stack, concatenate them and push the resulting string.
+</td></tr>
+<tr>
+<th> 0B
+</th>
+<td> <code>(a&nbsp;&amp;&amp;&nbsp;b)</code>
+</td>
+<td> -2
+</td>
+<td> +1
+</td>
+<td> Pop two values from the stack, concatenate them with one space character added in between, and push the resulting string.
+</td></tr>
+<tr>
+<th> 0C
+</th>
+<td> <code>(a&nbsp;&lt;&nbsp;b)</code>
+</td>
+<td> -2
+</td>
+<td> +1
+</td>
+<td> Pop two values from the stack, push 1 if the first is less than the second and 0 if not.
+</td></tr>
+<tr>
+<th> 0D
+</th>
+<td> <code>(a&nbsp;&lt;=&nbsp;b)</code>
+</td>
+<td> -2
+</td>
+<td> +1
+</td>
+<td> Pop two values from the stack, push 1 if the first is less than or equal to the second and 0 if not.
+</td></tr>
+<tr>
+<th> 0E
+</th>
+<td> <code>(a&nbsp;&lt;&gt;&nbsp;b)</code>
+</td>
+<td> -2
+</td>
+<td> +1
+</td>
+<td> Pop two values from the stack, push 0 if the two values are the same and 1 if they are not.
+</td></tr>
+<tr>
+<th> 0F
+</th>
+<td> <code>(a&nbsp;=&nbsp;b)</code>
+</td>
+<td> -2
+</td>
+<td> +1
+</td>
+<td> Pop two values from the stack, push 1 if the two values are the same and 0 if they are not.
+</td></tr>
+<tr>
+<th> 10
+</th>
+<td> <code>(a&nbsp;&gt;&nbsp;b)</code>
+</td>
+<td> -2
+</td>
+<td> +1
+</td>
+<td> Pop two values from the stack, push 1 if the first is greater than the second and 0 if not.
+</td></tr>
+<tr>
+<th> 11
+</th>
+<td> <code>(a&nbsp;&gt;=&nbsp;b)</code>
+</td>
+<td> -2
+</td>
+<td> +1
+</td>
+<td> Pop two values from the stack, push 1 if the first is greater than or equal to the sceond and 0 if not.
+</td></tr>
+<tr>
+<th> 12
+</th>
+<td> <code>(a&nbsp;and&nbsp;b)</code>
+</td>
+<td> -2
+</td>
+<td> +1
+</td>
+<td> Pop two values from the stack, push 1 if both are logically true and 0 if not.
+</td></tr>
+<tr>
+<th> 13
+</th>
+<td> <code>(a&nbsp;or&nbsp;b)</code>
+</td>
+<td> -2
+</td>
+<td> +1
+</td>
+<td> Pop two values from the stack, push 1 if either are logically true and 0 if not.
+</td></tr>
+<tr>
+<th> 14
+</th>
+<td> <code>(not&nbsp;a)</code>
+</td>
+<td> -1
+</td>
+<td> +1
+</td>
+<td> Pop one value from the stack, push 0 if it is logically true and 1 if not.
+</td></tr>
+<tr>
+<th> 15
+</th>
+<td> <code>(a&nbsp;contains&nbsp;b)</code>
+</td>
+<td> -2
+</td>
+<td> +1
+</td>
+<td>
+<p>Pop two values from the stack, push 1 if the first is a string that contains the second and 0 if not.
+</p><p>The text comparison is case-insensitive and ignores diacritic marks, e.g. "a" and "Ã…" are treated the same.
+</p>
+</td></tr>
+<tr>
+<th> 16
+</th>
+<td> <code>(a&nbsp;starts&nbsp;b)</code>
+</td>
+<td> -2
+</td>
+<td> +1
+</td>
+<td>
+<p>Pop two values from the stack, push 1 if the first is a string that begins with second string and 0 if not.
+</p><p>The text comparison is case-insensitive and ignores diacritic marks, e.g. "a" and "Ã…" are treated the same.
+</p>
+</td></tr>
+<tr>
+<th> 17
+</th>
+<td> <code>(char&nbsp;a of&nbsp;c)</code>
+<p><code>(char&nbsp;a&nbsp;to&nbsp;b of&nbsp;c)</code>
+</p><p><code>(item&nbsp;1&nbsp;to&nbsp;3 of&nbsp;someItems)</code>
+</p>
+</td>
+<td> -9
+</td>
+<td> +1
+</td>
+<td> String slice/split operation. It takes nine arguments from the stack:
+<table class="wikitable">
+
+<tbody><tr>
+<th> -9
+</th>
+<td> First char position
+</td></tr>
+<tr>
+<th> -8
+</th>
+<td> Last char position
+</td></tr>
+<tr>
+<th> -7
+</th>
+<td> First word position
+</td></tr>
+<tr>
+<th> -6
+</th>
+<td> Last word position
+</td></tr>
+<tr>
+<th> -5
+</th>
+<td> First item position <i>(items separated by</i> <code>the itemDelimiter</code><i>, which is a comma by default)</i>
+</td></tr>
+<tr>
+<th> -4
+</th>
+<td> Last item position
+</td></tr>
+<tr>
+<th> -3
+</th>
+<td> First line position
+</td></tr>
+<tr>
+<th> -2
+</th>
+<td> Last line position
+</td></tr>
+<tr>
+<th> -1
+</th>
+<td> The string to slice
+</td></tr></tbody></table>
+<p>The positions used here are one-based, so zero is invalid as a position and is instead used to indicate unused parameters. Only one "first X position" can be set, the rest must be zero. The corresponding "last X position" may either be set too, or it can be zero, in which case the first position will also be used as the last.
+</p>
+</td></tr>
+<tr>
+<th> 18
+</th>
+<td> <code>hilite&nbsp;word&nbsp;1 of&nbsp;field&nbsp;10</code>
+</td>
+<td> -9
+</td>
+<td>
+</td>
+<td> Highlight (select) some text. The nine arguments taken from the stack are:
+<table class="wikitable">
+
+<tbody><tr>
+<th> -9
+</th>
+<td> First char position
+</td></tr>
+<tr>
+<th> -8
+</th>
+<td> Last char position
+</td></tr>
+<tr>
+<th> -7
+</th>
+<td> First word position
+</td></tr>
+<tr>
+<th> -6
+</th>
+<td> Last word position
+</td></tr>
+<tr>
+<th> -5
+</th>
+<td> First item position
+</td></tr>
+<tr>
+<th> -4
+</th>
+<td> Last item position
+</td></tr>
+<tr>
+<th> -3
+</th>
+<td> First line position
+</td></tr>
+<tr>
+<th> -2
+</th>
+<td> Last line position
+</td></tr>
+<tr>
+<th> -1
+</th>
+<td> Field number (cast ID)
+</td></tr></tbody></table>
+<p>The positions used here are one-based, so zero is invalid as a position and is instead used to indicate unused parameters. Only one "first X position" can be set, the rest must be zero. The corresponding "last X position" may either be set too, or it can be zero, in which case the first position will also be used as the last.
+</p>
+</td></tr>
+<tr>
+<th> 19
+</th>
+<td> <code>(sprite 1 intersects 2)</code>
+</td>
+<td> -2
+</td>
+<td> +1
+</td>
+<td> Pop two sprite IDs and push 1 if the bounding rectangles of the two sprites touch at all, or 0 if they do not.
+</td></tr>
+<tr>
+<th> 1A
+</th>
+<td> <code>(sprite 1 within 2)</code>
+</td>
+<td> -2
+</td>
+<td> +1
+</td>
+<td> Pop two sprite IDs and push 1 if the bounding rectangle of the first is entirely inside the bounding rectangle of the second, or 0 if not.
+</td></tr>
+<tr>
+<th> 1B
+</th>
+<td> <code>(field 1)</code>
+</td>
+<td> -1
+</td>
+<td> +1
+</td>
+<td> Pop a cast ID (name or number), push the value of that cast member's <code>text</code> property.
+</td></tr>
+<tr>
+<th> 1C
+</th>
+<td> <code>tell&nbsp;someObject to&nbsp;go&nbsp;to&nbsp;frame&nbsp;1</code>
+</td>
+<td> -1
+</td>
+<td>
+</td>
+<td> Pop an object from the stack and begin running subsequent bytecodes in the context of that object, until code 1D is encountered.
+</td></tr>
+<tr>
+<th> 1D
+</th>
+<td> <code>tell&nbsp;someObject to&nbsp;go&nbsp;to&nbsp;frame&nbsp;1</code>
+</td>
+<td>
+</td>
+<td>
+</td>
+<td> Marker for the end of a sequence of bytecodes started by 1C. Similar to 01 except for nested bytecode chunks instead of the main one.
+</td></tr>
+<tr>
+<th> 1E
+</th>
+<td>
+</td>
+<td> -1
+</td>
+<td> +1
+</td>
+<td> Some kind of list transformation or check, seen used just before setting the actorList to []. More research is needed to know exactly what is happening there.
+</td></tr>
+<tr>
+<th> 1F
+</th>
+<td> <code>[#key: value]</code>
+</td>
+<td> -1
+</td>
+<td> +1
+</td>
+<td> Pops a list that must be in the form [#symbol1, val1, #symbol2, val2 ...] to transform into [#symbol1: val1, #symbol2: val2 ...]
+</td></tr></tbody></table>
+<h1> <span class="mw-headline" id="Two-Byte_Instructions"> Two-Byte Instructions </span></h1>
+<table class="wikitable">
+
+<tbody><tr>
+<th> 41&nbsp;XX
+</th>
+<td> <code>1</code> .. <code>127</code>
+</td>
+<td>
+</td>
+<td> +1
+</td>
+<td> Push integer of value XX, which must be between 1 and 127, inclusive. To push zero, use 03. To push larger integers, use 81 XX YY.
+</td></tr>
+<tr>
+<th> 42&nbsp;XX
+</th>
+<td> <code>a,&nbsp;b,&nbsp;c</code>
+</td>
+<td> -XX
+</td>
+<td> +1
+</td>
+<td> Pop the specified number of values off the top of the stack, create an unparenthesized argument list containing them (i.e. for a call statement like <code>myFunction 1, 2, 3</code>), and push that to the stack.
+</td></tr>
+<tr>
+<th> 43&nbsp;XX
+</th>
+<td> <code>[a,&nbsp;b,&nbsp;c]</code>
+</td>
+<td> -XX
+</td>
+<td> +1
+</td>
+<td> Pop the specified number of values off the top of the stack, create a list for them (which can also be used for a parenthesized call expression like <code>set result = myFunction(1, 2, 3)</code>), and push that to the stack.
+</td></tr>
+<tr>
+<th> 44&nbsp;XX
+</th>
+<td>
+<p><code>"literal"</code>
+</p><p><code>0.5</code>
+</p><p><code>32768</code>
+</p>
+</td>
+<td>
+</td>
+<td> +1
+</td>
+<td> Push a constant from local constant records onto the stack. These records seem to be six bytes long (regardless of the actual size of the constant value), so pushing the first one is <code>44 00</code>, the second is <code>44 06</code>, the third is <code>44 0C</code>, etc.
+</td></tr>
+<tr>
+<th> 45&nbsp;XX
+</th>
+<td> <code>#symbol</code>
+</td>
+<td>
+</td>
+<td> +1
+</td>
+<td> Push a symbol with a name from namelist[XX]. Note that the name will be stored as "name", not "#name".
+</td></tr>
+<tr>
+<th> 46&nbsp;XX
+</th></tr>
+<tr>
+<th> 47&nbsp;XX
+</th></tr>
+<tr>
+<th> 48&nbsp;XX
+</th></tr>
+<tr>
+<th> 49&nbsp;XX
+</th>
+<td>
+<p><code>(someGlobal)</code>
+</p><p><i>where previously declared:</i>
+</p><p><code>global someGlobal</code>
+</p>
+</td>
+<td>
+</td>
+<td> +1
+</td>
+<td> Push the value of a global variable with a name from namelist[XX].
+</td></tr>
+<tr>
+<th> 4A XX
+</th></tr>
+<tr>
+<th> 4B XX
+</th>
+<td> <code>(someParam)</code>
+</td>
+<td>
+</td>
+<td> +1
+</td>
+<td> Push the value of a function call parameter. The parameter records seem to be 6 bytes long, so the first is pushed with 4B 00, the second with 4B 06, etc.
+</td></tr>
+<tr>
+<th> 4C&nbsp;XX
+</th>
+<td> <code>(someLocal)</code>
+</td>
+<td>
+</td>
+<td> +1
+</td>
+<td> Push the value of a local variable. The local variable records seem to be 6 bytes long, so the first is pushed with <code>4C 00</code>, the second with <code>4C 06</code>, etc.
+</td></tr>
+<tr>
+<th> 4D&nbsp;XX
+</th></tr>
+<tr>
+<th> 4E&nbsp;XX
+</th></tr>
+
+<tr>
+<th> 4F&nbsp;XX
+</th>
+<td> <code>set&nbsp;someGlobal&nbsp;=&nbsp;0</code>
+</td>
+<td> -1
+</td>
+<td>
+</td>
+<td> Pop one value and use it to set the global variable with name from namelist[XX].
+</td></tr>
+<tr>
+<th> 50&nbsp;XX
+</th></tr>
+<tr>
+<th> 51&nbsp;XX
+</th></tr>
+<tr>
+<th> 52&nbsp;XX
+</th>
+<td> <code>set&nbsp;someLocal&nbsp;=&nbsp;0</code>
+</td>
+<td> -1
+</td>
+<td>
+</td>
+<td> Pop one value and use it to set a local variable. See code 4C 00 for a note about local variable records.
+</td></tr>
+<tr>
+<th> 53&nbsp;XX
+</th></tr>
+<tr>
+<th> 54&nbsp;XX
+</th>
+<td> <code>end repeat</code>
+</td>
+<td>
+</td>
+<td>
+</td>
+<td> Unconditional backwards jump by XX bytes, relative to the first byte of this instruction.
+</td></tr>
+<tr>
+<th> 55&nbsp;XX
+</th></tr>
+<tr>
+<th> 56&nbsp;XX
+</th>
+<td> <code>localFunction(1,2,3)</code>
+</td>
+<td> -1
+</td>
+<td> +1 or +0
+</td>
+<td> Call a function defined in this script with the name at namelist[XX]. The top value on the stack must be an argument list. If the argument list was created with code 43 XX, one return value will be pushed to the stack. If the argument list was created with code 42 XX, no return value will be pushed.
+</td></tr>
+<tr>
+<th> 57&nbsp;XX
+</th>
+<td> <code>someFunction&nbsp;1,2,3</code>
+<p><code>(someFunction(1,2,3))</code>
+</p>
+</td>
+<td> -1
+</td>
+<td> +1 OR +0
+</td>
+<td> Call the external function with name from namelist[XX]. The top value on the stack must be an argument list. If the argument list was created with code 43 XX, one return value will be pushed to the stack. If the argument list was created with code 42 XX, no return value will be pushed.
+</td></tr>
+<tr>
+<th> 58&nbsp;XX
+</th>
+<td> <code>someObject(mSomeMethod, 1,2,3)</code>
+</td>
+<td> -2
+</td>
+<td> +1 OR +0
+</td>
+<td> Pop [argument list, call target] to make a method call. If the call target is a literal number, this indicates a local variable is the target. It must be divided by six to get the actual local variable number. The first argument of the argument list will be a symbol with the name of the method. Note: It is still unclear what difference the value of XX makes. It has been seen as 0x01 and 0x05. More research is needed to know more. (Possibly local variables vs. call parameters?)
+</td></tr>
+<tr>
+<th> 59&nbsp;16
+</th>
+<td> <code>put&nbsp;"extra" into&nbsp;textVar</code>
+</td>
+<td> -1
+</td>
+<td>
+</td>
+<td> (Not sure how the target value is specified, needs more research)
+</td></tr>
+<tr>
+<th> 59&nbsp;25
+</th>
+<td> <code>put&nbsp;"extra" after&nbsp;textVar</code>
+</td>
+<td> -1
+</td>
+<td>
+</td>
+<td> (See above)
+</td></tr>
+<tr>
+<th> 59&nbsp;35
+</th>
+<td> <code>put&nbsp;"extra" before&nbsp;textVar</code>
+</td>
+<td> -1
+</td>
+<td>
+</td>
+<td> (See above)
+</td></tr>
+<tr>
+<th> 5A&nbsp;XX
+</th></tr>
+<tr>
+<th> 5B&nbsp;05
+</th>
+<td> <code>delete&nbsp;word&nbsp;3 of&nbsp;textVar</code>
+</td>
+<td> -1
+</td>
+<td>
+</td>
+<td> (See above)
+</td></tr>
+<tr>
+<th> 5C&nbsp;00
+</th>
+<td>
+<p><code>(the&nbsp;abbr&nbsp;time)</code>
+</p><p><code>(the&nbsp;short&nbsp;date)</code>
+</p><p><code>(the last word in someText)</code>
+</p>
+</td>
+<td> -1 OR -2
+</td>
+<td> +1
+</td>
+<td>
+<p>If the top value is one of the following setting IDs, pop it from the stack, and push the current value of the setting:
+</p>
+<table class="wikitable">
+
+<tbody><tr>
+<th> 00
+</th>
+<td> <code>the floatPrecision</code>
+</td></tr>
+<tr>
+<th> 01
+</th>
+<td> <code>the mouseDownScript</code>
+</td></tr>
+<tr>
+<th> 02
+</th>
+<td> <code>the mouseUpScript</code>
+</td></tr>
+<tr>
+<th> 03
+</th>
+<td> <code>the keyDownScript</code>
+</td></tr>
+<tr>
+<th> 04
+</th>
+<td> <code>the keyUpScript</code>
+</td></tr>
+<tr>
+<th> 05
+</th>
+<td> <code>the timeoutScript</code>
+</td></tr></tbody></table>
+<p>If the top value is a time-formatting ID, pop it from the stack, and push the current time formatted using it:
+</p>
+<table class="wikitable">
+
+<tbody><tr>
+<th> 06
+</th>
+<td> <code>the&nbsp;short&nbsp;time</code>
+</td>
+<td> 1:09 AM
+</td></tr>
+<tr>
+<th> 07
+</th>
+<td> <code>the&nbsp;abbreviated&nbsp;time</code>, <code>the&nbsp;abbrev&nbsp;time</code>, <code>the&nbsp;abbr&nbsp;time</code>
+</td>
+<td> 1:09 AM
+</td></tr>
+<tr>
+<th> 08
+</th>
+<td> <code>the&nbsp;long&nbsp;time</code>
+</td>
+<td> 1:09:38 AM
+</td></tr></tbody></table>
+<p>If the top value is a date-formatting ID, pop it from the stack, and push the current date formatted using it:
+</p>
+<table class="wikitable">
+
+<tbody><tr>
+<th> 09
+</th>
+<td> <code>the&nbsp;short&nbsp;date</code>
+</td>
+<td> 3/4/16
+</td></tr>
+<tr>
+<th> 0A
+</th>
+<td> <code>the&nbsp;abbreviated&nbsp;date</code>, <code>the&nbsp;abbrev&nbsp;date</code>, <code>the&nbsp;abbr&nbsp;date</code>
+</td>
+<td> Mon, Mar 14, 2016
+</td></tr>
+<tr>
+<th> 0B
+</th>
+<td> <code>the&nbsp;long&nbsp;date</code>
+</td>
+<td> Monday, March 14, 2016
+</td></tr></tbody></table>
+<p>If the top value is a slice type ID, pop both it and the previous value from the stack. The previous value will be a string, slice the last "bit" of it according to the slice type, and push the sliced value:
+</p>
+<table class="wikitable">
+
+<tbody><tr>
+<th> 0C
+</th>
+<td> <code>the last char</code>
+</td></tr>
+<tr>
+<th> 0D
+</th>
+<td> <code>the last word</code>
+</td></tr>
+<tr>
+<th> 0E
+</th>
+<td> <code>the last item</code>
+</td></tr>
+<tr>
+<th> 0F
+</th>
+<td> <code>the last line</code>
+</td></tr></tbody></table>
+</td></tr>
+<tr>
+<th> 5C&nbsp;01
+</th>
+<td> <code>(the number of chars in someText)</code>
+</td>
+<td> -2
+</td>
+<td> +1
+</td>
+<td>
+<p>Pop [text, stat ID] and push the stat value for the given text, using these stat IDs:
+</p>
+<table class="wikitable">
+
+<tbody><tr>
+<th> 01
+</th>
+<td> chars
+</td></tr>
+<tr>
+<th> 02
+</th>
+<td> words
+</td></tr>
+<tr>
+<th> 03
+</th>
+<td> items <i>(separated by</i> <code>the itemDelimiter</code><i>, which is a comma by default)</i>
+</td></tr>
+<tr>
+<th> 04
+</th>
+<td> lines
+</td></tr></tbody></table>
+</td></tr>
+<tr>
+<th> 5C&nbsp;02
+</th>
+<td> <code>(the name of menu 1)</code>
+</td>
+<td> -2
+</td>
+<td> +1
+</td>
+<td>
+<p>Pop [menu ID, property ID] and push the value of the specified menu property, using these property IDs:
+</p>
+<table class="wikitable">
+
+<tbody><tr>
+<th> 01
+</th>
+<td> <code>name</code>
+</td></tr>
+<tr>
+<th> 02
+</th>
+<td> <code>number of menuItems</code>
+</td></tr></tbody></table>
+</td></tr>
+<tr>
+<th> 5C&nbsp;03
+</th>
+<td> <code>(the&nbsp;name of&nbsp;menuItem&nbsp;3 of&nbsp;menu&nbsp;1)</code>
+</td>
+<td> -3
+</td>
+<td> +1
+</td>
+<td>
+<p>Pop [item ID, menu ID, property ID] and push the value of the specified menu item property, using these property IDs:
+</p>
+<table class="wikitable">
+
+<tbody><tr>
+<th> 01
+</th>
+<td> <code>name</code>
+</td></tr>
+<tr>
+<th> 02
+</th>
+<td> <code>checkMark</code>
+</td></tr>
+<tr>
+<th> 03
+</th>
+<td> <code>enabled</code>
+</td></tr>
+<tr>
+<th> 04
+</th>
+<td> <code>script</code>
+</td></tr></tbody></table>
+</td></tr>
+<tr>
+<th> 5C&nbsp;04
+</th>
+<td> <code>(the&nbsp;volume of&nbsp;sound&nbsp;1)</code>
+</td>
+<td> -2
+</td>
+<td> +1
+</td>
+<td>
+<p>Pop [sound ID, property ID] and push the value of the specified sound property, using these property IDs:
+</p>
+<table class="wikitable">
+
+<tbody><tr>
+<th> 01
+</th>
+<td> <code>volume</code> (0 to 255)
+</td></tr></tbody></table>
+</td></tr>
+<tr>
+<th> 5C&nbsp;06
+</th>
+<td> <code>(the&nbsp;cursor of&nbsp;sprite&nbsp;3)</code>
+</td>
+<td> -2
+</td>
+<td> +1
+</td>
+<td>
+<p>Pop [sprite ID, property ID] and push the value of the specified sprite property, using these property IDs:
+</p>
+<table class="wikitable">
+
+<tbody><tr>
+<th> 01
+</th>
+<td> <code>type</code>
+</td></tr>
+<tr>
+<th> 02
+</th>
+<td> <code>backColor</code>
+</td></tr>
+<tr>
+<th> 03
+</th>
+<td> <code>bottom</code>
+</td></tr>
+<tr>
+<th> 04
+</th>
+<td> <code>castNum</code>
+</td></tr>
+<tr>
+<th> 05
+</th>
+<td> <code>constraint</code>
+</td></tr>
+<tr>
+<th> 06
+</th>
+<td> <code>cursor</code>
+</td></tr>
+<tr>
+<th> 07
+</th>
+<td> <code>foreColor</code>
+</td></tr>
+<tr>
+<th> 08
+</th>
+<td> <code>height</code>
+</td></tr>
+<tr>
+<th> 0A
+</th>
+<td> <code>ink</code>
+</td></tr>
+<tr>
+<th> 0B
+</th>
+<td> <code>left</code>
+</td></tr>
+<tr>
+<th> 0C
+</th>
+<td> <code>lineSize</code>
+</td></tr>
+<tr>
+<th> 0D
+</th>
+<td> <code>locH</code>
+</td></tr>
+<tr>
+<th> 0E
+</th>
+<td> <code>locV</code>
+</td></tr>
+<tr>
+<th> 0F
+</th>
+<td> <code>movieRate</code>
+</td></tr>
+<tr>
+<th> 10
+</th>
+<td> <code>movieTime</code>
+</td></tr>
+<tr>
+<th> 12
+</th>
+<td> <code>puppet</code>
+</td></tr>
+<tr>
+<th> 13
+</th>
+<td> <code>right</code>
+</td></tr>
+<tr>
+<th> 14
+</th>
+<td> <code>startTime</code>
+</td></tr>
+<tr>
+<th> 15
+</th>
+<td> <code>stopTime</code>
+</td></tr>
+<tr>
+<th> 16
+</th>
+<td> <code>stretch</code>
+</td></tr>
+<tr>
+<th> 17
+</th>
+<td> <code>top</code>
+</td></tr>
+<tr>
+<th> 18
+</th>
+<td> <code>trails</code>
+</td></tr>
+<tr>
+<th> 19
+</th>
+<td> <code>visible</code>
+</td></tr>
+<tr>
+<th> 1A
+</th>
+<td> <code>volume</code> (-256 to 256, &lt;= 0 is silent)
+</td></tr>
+<tr>
+<th> 1B
+</th>
+<td> <code>width</code>
+</td></tr>
+<tr>
+<th> 1D
+</th>
+<td> <code>scriptNum</code>
+</td></tr>
+<tr>
+<th> 1E
+</th>
+<td> <code>moveableSprite</code>
+</td></tr>
+<tr>
+<th> 20
+</th>
+<td> <code>scoreColor</code>
+</td></tr></tbody></table>
+<p>The values for <code>type</code> are:
+</p>
+<table class="wikitable">
+
+<tbody><tr>
+<th> 0
+</th>
+<td> inactive
+</td></tr>
+<tr>
+<th> 1
+</th>
+<td> bitmap
+</td></tr>
+<tr>
+<th> 2
+</th>
+<td> rectangle
+</td></tr>
+<tr>
+<th> 3
+</th>
+<td> rounded rectangle
+</td></tr>
+<tr>
+<th> 4
+</th>
+<td> oval
+</td></tr>
+<tr>
+<th> 5
+</th>
+<td> line top-left to bottom-right
+</td></tr>
+<tr>
+<th> 6
+</th>
+<td> line bottom-left to top-right
+</td></tr>
+<tr>
+<th> 7
+</th>
+<td> text
+</td></tr>
+<tr>
+<th> 8
+</th>
+<td> button
+</td></tr>
+<tr>
+<th> 9
+</th>
+<td> checkbox
+</td></tr>
+<tr>
+<th> 10
+</th>
+<td> radio button
+</td></tr>
+<tr>
+<th> 16
+</th>
+<td> undetermined (try the castType of the associated cast member)
+</td></tr></tbody></table>
+</td></tr>
+<tr>
+<th> 5C&nbsp;07
+</th>
+<td> <code>(the exitLock)</code>
+</td>
+<td> -1
+</td>
+<td> +1
+</td>
+<td>
+<p>Pop a setting ID from the stack and push its value, using these setting IDs:
+</p>
+<table class="wikitable">
+
+<tbody><tr>
+<th> 01
+</th>
+<td> <code>the beepOn</code>
+</td></tr>
+<tr>
+<th> 02
+</th>
+<td> <code>the buttonStyle</code>
+</td></tr>
+<tr>
+<th> 03
+</th>
+<td> <code>the centerStage</code>
+</td></tr>
+<tr>
+<th> 04
+</th>
+<td> <code>the checkBoxAccess</code>
+</td></tr>
+<tr>
+<th> 05
+</th>
+<td> <code>the checkBoxType</code>
+</td></tr>
+<tr>
+<th> 06
+</th>
+<td> <code>the colorDepth</code>
+</td></tr>
+<tr>
+<th> 08
+</th>
+<td> <code>the exitLock</code>
+</td></tr>
+<tr>
+<th> 09
+</th>
+<td> <code>the fixStageSize</code>
+</td></tr>
+<tr>
+<th> 13
+</th>
+<td> <code>the timeoutLapsed</code>
+</td></tr>
+<tr>
+<th> 17
+</th>
+<td> <code>the selEnd</code>
+</td></tr>
+<tr>
+<th> 18
+</th>
+<td> <code>the selStart</code>
+</td></tr>
+<tr>
+<th> 19
+</th>
+<td> <code>the soundEnabled</code>
+</td></tr>
+<tr>
+<th> 1A
+</th>
+<td> <code>the soundLevel</code>
+</td></tr>
+<tr>
+<th> 1B
+</th>
+<td> <code>the stageColor</code>
+</td></tr>
+<tr>
+<th> 1D
+</th>
+<td> <code>the stillDown</code>
+</td></tr>
+<tr>
+<th> 1E
+</th>
+<td> <code>the timeoutKeyDown</code>
+</td></tr>
+<tr>
+<th> 1F
+</th>
+<td> <code>the timeoutLength</code>
+</td></tr>
+<tr>
+<th> 20
+</th>
+<td> <code>the timeoutMouse</code>
+</td></tr>
+<tr>
+<th> 21
+</th>
+<td> <code>the timeoutPlay</code>
+</td></tr>
+<tr>
+<th> 22
+</th>
+<td> <code>the timer</code>
+</td></tr></tbody></table>
+</td></tr>
+<tr>
+<th> 5C&nbsp;08
+</th>
+<td> <code>(the&nbsp;number of&nbsp;castMembers)</code>
+</td>
+<td> -1
+</td>
+<td> +1
+</td>
+<td>
+<p>Pop a stat ID from the stack and push the stat, using these stat IDs:
+</p>
+<table class="wikitable">
+
+<tbody><tr>
+<th> 01
+</th>
+<td> <code>the perFrameHook</code>
+</td></tr>
+<tr>
+<th> 02
+</th>
+<td> <code>number of castMembers</code>
+</td></tr>
+<tr>
+<th> 03
+</th>
+<td> <code>number of menus</code>
+</td></tr></tbody></table>
+</td></tr>
+<tr>
+<th> 5C&nbsp;09
+</th>
+<td>
+<p><code>(the&nbsp;picture of&nbsp;cast&nbsp;"bob")</code>
+</p><p><code>(the&nbsp;name of&nbsp;cast&nbsp;3)</code>
+</p>
+</td>
+<td> -2
+</td>
+<td> +1
+</td>
+<td>
+<p>Pop [cast ID, property ID] from the stack and push the value of the cast property, using these property IDs:
+</p>
+<table class="wikitable">
+
+<tbody><tr>
+<th> 01
+</th>
+<td> <code>name</code>
+</td></tr>
+<tr>
+<th> 02
+</th>
+<td> <code>text</code>
+</td></tr>
+<tr>
+<th> 08
+</th>
+<td> <code>picture</code>
+</td></tr>
+<tr>
+<th> 0A
+</th>
+<td> <code>number</code>
+</td></tr>
+<tr>
+<th> 0B
+</th>
+<td> <code>size</code>
+</td></tr>
+<tr>
+<th> 11
+</th>
+<td> <code>foreColor</code>
+</td></tr>
+<tr>
+<th> 12
+</th>
+<td> <code>backColor</code>
+</td></tr></tbody></table>
+</td></tr>
+<tr>
+<th> 5C 0C
+</th>
+<td> <code>(the textSize of field 1)</code>
+</td>
+<td> -2
+</td>
+<td> +1
+</td>
+<td>
+<p>Pop [field ID, property ID] and push the value of the property for the given field according to these property IDs:
+</p>
+<table class="wikitable">
+
+<tbody><tr>
+<th> 03
+</th>
+<td> <code>textStyle</code>
+</td></tr>
+<tr>
+<th> 04
+</th>
+<td> <code>textFont</code>
+</td></tr>
+<tr>
+<th> 05
+</th>
+<td> <code>textHeight</code>
+</td></tr>
+<tr>
+<th> 06
+</th>
+<td> <code>textAlign</code>
+</td></tr>
+<tr>
+<th> 07
+</th>
+<td> <code>textSize</code>
+</td></tr></tbody></table>
+</td></tr>
+<tr>
+<th> 5C 0D
+</th>
+<td> <code>(the sound of cast 5)</code>
+</td>
+<td> -2
+</td>
+<td> +1
+</td>
+<td>
+<p>Pop [cast ID, property ID] and push the value of property for the given cast according to these property IDs:
+</p>
+<table class="wikitable">
+
+<tbody><tr>
+<th> 10
+</th>
+<td> <code>sound</code>
+</td></tr></tbody></table>
+</td></tr>
+<tr>
+<th> 5D&nbsp;00
+</th>
+<td> <code>when keyDown then beep</code>
+</td>
+<td> -2
+</td>
+<td>
+</td>
+<td>
+<p>Pop [statement, eventID] and set the statement (Lingo source code in a text string) to run on the given event:
+</p>
+<table class="wikitable">
+
+<tbody><tr>
+<th> 01
+</th>
+<td> <code>mouseDown</code>
+</td></tr>
+<tr>
+<th> 02
+</th>
+<td> <code>mouseUp</code>
+</td></tr>
+<tr>
+<th> 03
+</th>
+<td> <code>keyDown</code>
+</td></tr>
+<tr>
+<th> 04
+</th>
+<td> <code>keyUp</code>
+</td></tr>
+<tr>
+<th> 05
+</th>
+<td> <code>timeOut</code>
+</td></tr></tbody></table>
+</td></tr>
+<tr>
+<th> 5D&nbsp;03
+</th>
+<td> <code>set&nbsp;the&nbsp;enabled of&nbsp;menuItem&nbsp;3 of&nbsp;menu&nbsp;5 to&nbsp;FALSE</code>
+</td>
+<td> -4
+</td>
+<td>
+</td>
+<td> Pop [menuItem ID, menu ID, new value, property ID] and set the menu item property. See the table for code 5C 03 for menu item property IDs.
+</td></tr>
+<tr>
+<th> 5D&nbsp;04
+</th>
+<td> <code>set&nbsp;the&nbsp;volume of&nbsp;sound&nbsp;3 to&nbsp;255</code>
+</td>
+<td> -3
+</td>
+<td>
+</td>
+<td> Pop [sound ID, new value, property ID] and set the menu item property. See the table for code 5C 04 for sound property IDs.
+</td></tr>
+<tr>
+<th> 5D&nbsp;06
+</th>
+<td> <code>set&nbsp;the&nbsp;constraint of&nbsp;sprite&nbsp;3 to&nbsp;0</code>
+</td>
+<td> -3
+</td>
+<td>
+</td>
+<td> Pop [sprite ID, new value, property ID] and set the sprite property. See the table for code 5C 06 for sprite property IDs.
+</td></tr>
+<tr>
+<th> 5D 07
+</th>
+<td> <code>set the exitLock to TRUE</code>
+</td>
+<td> -2
+</td>
+<td>
+</td>
+<td> Pop [new value, setting ID] and change the relevant setting. See the table for code 5C 07 for setting IDs.
+</td></tr>
+<tr>
+<th> 5D&nbsp;09
+</th>
+<td> <code>set&nbsp;the&nbsp;backColor of&nbsp;cast&nbsp;"bob" to&nbsp;0</code>
+</td>
+<td> -3
+</td>
+<td>
+</td>
+<td> Pop [cast ID, new value, property ID] and set the cast property. See the table for code 5C 09 for cast property IDs.
+</td></tr>
+<tr>
+<th> 5D 0C
+</th>
+<td> <code>set&nbsp;the&nbsp;textAlign of&nbsp;field&nbsp;3 to&nbsp;"right"</code>
+</td>
+<td> -3
+</td>
+<td>
+</td>
+<td> Pop [field ID, new value, property ID] and set the field property. See the table for code 5C 0C for cast property IDs.
+</td></tr>
+<tr>
+<th> 5D 0D
+</th>
+<td> <code>set the sound of cast 3 to TRUE</code>
+</td>
+<td> -3
+</td>
+<td>
+</td>
+<td> Pop [cast ID, new value, property ID] and set the cast property. See the table for code 5C 0D for cast property IDs.
+</td></tr>
+<tr>
+<th> 5E&nbsp;XX
+</th></tr>
+<tr>
+<th> 5F&nbsp;XX
+</th>
+<td> <code>(the someProperty)</code>
+</td>
+<td>
+</td>
+<td> +1
+</td>
+<td> Push the value of the contextual property with the name at namelist[XX].
+</td></tr>
+<tr>
+<th> 60&nbsp;XX
+</th>
+<td> <code>set&nbsp;the&nbsp;someProperty to&nbsp;0</code>
+</td>
+<td> -1
+</td>
+<td>
+</td>
+<td> Pop a value and use it to set the contextual property with the name at namelist[XX].
+</td></tr>
+<tr>
+<th> 61&nbsp;XX
+</th>
+<td> <code>(the&nbsp;someProperty of&nbsp;someVariable)</code>
+</td>
+<td> -1
+</td>
+<td> +1
+</td>
+<td> Pop a property-owning object from the stack, and push the value of this object's property with the name at namelist[XX].
+</td></tr>
+<tr>
+<th> 62&nbsp;XX
+</th>
+<td> <code>set the&nbsp;someProperty of&nbsp;someVariable to&nbsp;1</code>
+</td>
+<td> -2
+</td>
+<td>
+</td>
+<td> Pop [property-owning object, new value] from the stack and set the property of the object with the name at namelist[XX].
+</td></tr>
+<tr>
+<th> 63&nbsp;XX
+</th>
+<td> <code>tell&nbsp;someObject to&nbsp;go&nbsp;to&nbsp;frame&nbsp;1</code>
+</td>
+<td> -1
+</td>
+<td>
+</td>
+<td> Remote call: Similar to 57 XX except runs in the context of a <code>tell ... to</code> block.
+</td></tr>
+<tr>
+<th> 64&nbsp;XX
+</th>
+<td>
+</td>
+<td>
+</td>
+<td> +1
+</td>
+<td>
+<p>Push a copy of a value already on the stack, relative to the top where 00 is the top slot, 01 is one slot beneath the top, etc.
+</p><p>This is used by the compiler when generating bytecode for the <code>repeat with i in list</code> block, to keep state in the loop without allocating new local variables.
+</p>
+</td></tr>
+<tr>
+<th> 65&nbsp;XX
+</th>
+<td>
+</td>
+<td> -XX
+</td>
+<td>
+</td>
+<td>
+<p>Pop and discard XX values from the top of the stack.
+</p><p>This is used in the bytecode generated for a <code>repeat with i in list</code> block, to clean up at the end.
+</p>
+</td></tr>
+<tr>
+<th> 66 XX
+</th>
+<td> <code>(the pathName)</code>
+</td>
+<td> -1
+</td>
+<td> +1
+</td>
+<td> Pop an empty list and push the value of a read-only property named at namelist[XX].
+</td></tr></tbody></table>
+<h1> <span class="mw-headline" id="Three_Byte_Instructions"> Three Byte Instructions </span></h1>
+<table class="wikitable">
+
+<tbody><tr>
+<th> 81&nbsp;XX&nbsp;YY
+</th>
+<td> <code>128</code> .. <code>32767</code>
+</td>
+<td> &nbsp;
+</td>
+<td> +1
+</td>
+<td> Push the integer ((XX * 0x100) + YY). Larger integers and floats are pushed using constants.
+</td></tr>
+<tr>
+<th> 82&nbsp;XX&nbsp;YY
+</th>
+<td> <code>someFunc 1, 2, 3,</code> <i>(...)</i> <code>254, 255, 256, 257</code>
+</td>
+<td> -((XX&nbsp;*&nbsp;0x100)&nbsp;+&nbsp;YY)
+</td>
+<td> +1
+</td>
+<td> Pop ((XX * 0x100) + YY) values from the stack and push a no-return-value argument list object containing these values. Similar to code 42 XX, only necessary when there are more than 255 arguments in the list.
+</td></tr>
+
+<tr>
+<th> 83&nbsp;XX&nbsp;YY
+</th>
+<td> <code>[1, 2, 3,</code> <i>(...)</i> <code>254, 255, 256, 257]</code>
+</td>
+<td> -((XX&nbsp;*&nbsp;0x100)&nbsp;+&nbsp;YY)
+</td>
+<td> +1
+</td>
+<td> Pop ((XX * 0x100) + YY) values from the stack and push a list object containing these values. Similar to code 43 XX, only necessary when there are more than 255 items in the list.
+</td></tr>
+<tr>
+<th> 93&nbsp;XX&nbsp;YY
+</th>
+<td>
+<p><code>else</code>
+</p><p><code>exit repeat</code>
+</p><p><code>next repeat</code>
+</p>
+</td>
+<td>
+</td>
+<td>
+</td>
+<td> Unconditional jump: Advance by ((XX * 0x100) + YY) bytes, relative to the first byte of this instruction (i.e. it may be 3 more than you are expecting)
+<p>(<code>next repeat</code> jumps forward to <code>end repeat</code> instead of jumping back itself)
+</p>
+</td></tr>
+<tr>
+<th> 95&nbsp;XX&nbsp;YY
+</th>
+<td>
+<p><code>if&nbsp;somethingIsTrue&nbsp;then</code>
+</p><p><code>repeat&nbsp;while&nbsp;somethingIsTrue</code>
+</p>
+</td>
+<td> -1
+</td>
+<td>
+</td>
+<td> Conditional jump: Pop a value, and if it is logically FALSE, advance by ((XX * 0x100) + YY) bytes, relative to the first byte of this instruction
+</td></tr></tbody></table>
+<h1> <span class="mw-headline" id="Syntactic_Sugar"> Syntactic Sugar </span></h1>
+<p>Some functions get special syntax when written out in source code, but under the hood, the compiler just transforms it into more regular syntax. Here is a mapping that shows the equivalent in plain, generalized Lingo that gets used for the bytecode.
+</p>
+<table class="wikitable">
+
+<tbody><tr>
+<th> Specialized Syntax
+</th>
+<th> Generalized Syntax
+</th></tr>
+<tr>
+<td><code>play frame 10 of movie "theMovie"</code>
+</td>
+<td><code>play 10, "theMovie"</code>
+</td></tr>
+<tr>
+<td><code>play frame 10</code>
+</td>
+<td><code>play 10</code>
+</td></tr>
+<tr>
+<td><code>play movie "theMovie"</code>
+</td>
+<td><code>play 1, "theMovie"</code>
+</td></tr>
+<tr>
+<td><code>play done</code>
+</td>
+<td><code>play</code>
+</td></tr>
+<tr>
+<td><pre>repeat with i = 15 to 20
+ ...
+
+end repeat</pre>
+</td>
+<td><pre>set i = 15
+repeat while i &lt;= 20
+ ...
+ set i = i + 1
+end repeat</pre>
+</td></tr>
+<tr>
+<td><pre>repeat with i = 15 down to 10
+ ...
+
+end repeat</pre>
+</td>
+<td><pre>set i = 15
+repeat while i &gt;= 10
+ ...
+ set i = i - 1
+end repeat</pre>
+</td></tr>
+<tr>
+<td><code>sound playFile 1, "Thunder"</code>
+</td>
+<td><code>sound #playFile, 1, "Thunder"</code>
+</td></tr>
+<tr>
+<td><code>sound fadeIn 5</code>
+</td>
+<td><code>sound #fadeIn, 5</code>
+</td></tr>
+<tr>
+<td><code>sound fadeIn 5, 10</code>
+</td>
+<td><code>sound #fadeIn, 5, 10</code>
+</td></tr>
+<tr>
+<td><code>sound fadeOut 5</code>
+</td>
+<td><code>sound #fadeOut, 5</code>
+</td></tr>
+<tr>
+<td><code>sound fadeOut 5, 10</code>
+</td>
+<td><code>sound #fadeOut, 5, 10</code>
+</td></tr>
+<tr>
+<td><code>sound stop 1</code>
+</td>
+<td><code>sound #stop, 1</code>
+</td></tr>
+<tr>
+<td><code>sound close 1</code>
+</td>
+<td><code>sound #close, 1</code>
+</td></tr>
+<tr>
+<td><code>go to frame 10</code>
+<p><code>go frame 10</code>
+</p><p><code>go to 10</code>
+</p>
+</td>
+<td><code>go 10</code>
+</td></tr>
+<tr>
+<td><code>go to movie "theMovie"</code>
+<p><code>go movie "theMovie"</code>
+</p>
+</td>
+<td><code>go 1, "theMovie"</code>
+</td></tr>
+<tr>
+<td><code>go to frame 10 of movie "theMovie"</code>
+<p><code>go frame 10 of movie "theMovie"</code>
+</p><p><code>go to 10 of movie "theMovie"</code>
+</p><p><code>go 10 of movie "theMovie"</code>
+</p>
+</td>
+<td><code>go 10, "theMovie"</code>
+</td></tr>
+<tr>
+<td><code>go loop</code>
+</td>
+<td><code>go #loop</code>
+</td></tr>
+<tr>
+<td><code>go next</code>
+</td>
+<td><code>go #next</code>
+</td></tr>
+<tr>
+<td><code>go previous</code>
+</td>
+<td><code>go #previous</code>
+</td></tr>
+<tr>
+<td><code>open "document" with "application"</code>
+</td>
+<td><code>open "document", "application"</code>
+</td></tr>
+<tr>
+<td><code>set obj = GetObject()<br>obj(mSomeMethod, 1, 2, 3)</code>
+</td>
+<td><code>set obj = GetObject()<br>obj(#mSomeMethod, 1, 2, 3)</code>
+</td></tr></tbody></table>
+<h1> <span class="mw-headline" id="Lscr_bytecode-container_chunk_layout"> <code>Lscr</code> bytecode-container chunk layout </span></h1>
+<h2> <span class="mw-headline" id="Header"> Header </span></h2>
+<table class="wikitable">
+
+<tbody><tr>
+<td colspan="3">...
+</td></tr>
+<tr>
+<th> $0040-$0041
+</th>
+<td> uint16
+</td>
+<td> Offset to the function records block
+</td></tr>
+<tr>
+<td colspan="3">...
+</td></tr>
+<tr>
+<th> $004E-$004F
+</th>
+<td> uint16
+</td>
+<td> Number of constants
+</td></tr>
+<tr>
+<td colspan="3">...
+</td></tr>
+<tr>
+<th> $0048-$0049
+</th>
+<td> uint16
+</td>
+<td> Number of function records
+</td></tr>
+<tr>
+<td colspan="3">...
+</td></tr>
+<tr>
+<th> $0052-$0053
+</th>
+<td> uint16
+</td>
+<td> Offset to the constant records block
+</td></tr>
+<tr>
+<td colspan="3">...
+</td></tr>
+<tr>
+<th> $005A-$005B
+</th>
+<td> uint16
+</td>
+<td> Base address for constant data
+</td></tr></tbody></table>
+<h2> <span class="mw-headline" id="Function_Record"> Function Record </span></h2>
+<p>Each function record is 42 bytes long.
+</p>
+<table class="wikitable">
+
+<tbody><tr>
+<th> $0000-$0001
+</th>
+<td> uint16
+</td>
+<td> Namelist index for the function's name, or 0xFFFF if there is no name(?)
+</td></tr>
+<tr>
+<th> $0002-$0003
+</th>
+<td> uint16
+</td>
+<td> Unknown
+</td></tr>
+<tr>
+<th> $0004-$0007
+</th>
+<td> uint32
+</td>
+<td> Length of the function bytecode in bytes
+</td></tr>
+<tr>
+<th> $0008-$000B
+</th>
+<td> uint32
+</td>
+<td> Offset to the function bytecode
+</td></tr>
+<tr>
+<th> $000C-$000D
+</th>
+<td> uint16
+</td>
+<td> Number of arguments
+</td></tr>
+<tr>
+<th> $000E-$0011
+</th>
+<td> uint32
+</td>
+<td> Unknown
+</td></tr>
+<tr>
+<th> $0012-$0013
+</th>
+<td> uint16
+</td>
+<td> Number of local variables
+</td></tr>
+<tr>
+<th> $0014-$0017
+</th>
+<td> uint32
+</td>
+<td> Unknown
+</td></tr>
+<tr>
+<th> $0018-$0019
+</th>
+<td> uint16
+</td>
+<td> Count (C)
+</td></tr>
+<tr>
+<th> $001A-$001D
+</th>
+<td> uint32
+</td>
+<td> Unknown
+</td></tr>
+<tr>
+<th> $001E-$0021
+</th>
+<td> uint32
+</td>
+<td> Unknown
+</td></tr>
+<tr>
+<th> $0022-$0023
+</th>
+<td> uint16
+</td>
+<td> Unknown
+</td></tr>
+<tr>
+<th> $0024-$0025
+</th>
+<td> uint16
+</td>
+<td> Count (D)
+</td></tr>
+<tr>
+<th> $0026-$0029
+</th>
+<td> uint32
+</td>
+<td> Unknown
+</td></tr></tbody></table>
+<h2> <span class="mw-headline" id="Bytecode_Trailer"> Bytecode Trailer </span></h2>
+<p>After the bytecode section for a function (determined using the offset and length fields from the function record), and then after an additional padding byte if there are an odd number of bytes in the bytecode, are the following values:
+</p>
+<ul><li> For each argument: uint16 namelist index for the argument's name
+</li><li> For each local variable: uint16 namelist index for the variable's name
+</li><li> Count (C) * uint16
+</li><li> Count (D) * uint8
+</li><li> A padding byte if Count (D) is an odd number
+</li></ul>
+<h2> <span class="mw-headline" id="Constants"> Constants </span></h2>
+<p>Each constant record is six bytes long and has this format:
+</p>
+<ul><li> uint16: Value type ID
+</li><li> uint32: Data address, relative to the base address given in the header
+</li></ul>
+<p>Here is how the value type IDs correspond to the data found at the given address:
+</p>
+<table class="wikitable">
+<tbody><tr>
+<th> 01
+</th>
+<td> Text string
+</td>
+<td> uint32 length (including null terminator) followed immediately by the character data
+</td></tr>
+<tr>
+<th> 04
+</th>
+<td> 32-bit unsigned integer
+</td>
+<td> "Data address" not an address, but the value itself
+</td></tr>
+<tr>
+<th> 09
+</th>
+<td> Floating point
+</td>
+<td> uint32 length followed by the floating point data. In practice, the length always seems to be 10, so the number is an 80-bit extended precision number.
+</td></tr></tbody></table>
+<h1> <span class="mw-headline" id="Projector_File_.28Windows.29"> Projector File (Windows) </span></h1>
+<h2> <span class="mw-headline" id="Director_3.0"> Director 3.0 </span></h2>
+<p>At the very end of the projector executable is a 32-bit little-endian file address.
+</p><p>At this location is found:
+</p>
+<ul><li> 7 bytes: Not sure/more research needed
+</li><li> uint32: Length of the RIFF block
+</li><li> uint8: Length of the original RIFF file's name
+</li><li> ASCII: Original RIFF file's name
+</li><li> uint8: Length of the original RIFF file's parent folder
+</li><li> ASCII: Original RIFF file's parent folder
+</li><li> RIFF block
+</li></ul>
+<h2> <span class="mw-headline" id="Director_4.0"> Director 4.0 </span></h2>
+<p>At the very end of the projector executable is a 32-bit little-endian file address.
+</p><p>At this location is found:
+</p>
+<ul><li> ASCII "PJ93"
+</li><li> The file address of the main RIFF data file
+</li><li> Six further addresses for other embedded data (more research required to know more about these)
+</li></ul>
+
+<!--
+NewPP limit report
+Preprocessor node count: 196/1000000
+Post-expand include size: 1279/2097152 bytes
+Template argument size: 338/2097152 bytes
+Expensive parser function count: 0/100
+-->
+
+<!-- Saved in parser cache with key justsolve:pcache:idhash:6263-0!*!0!!en!*!* and timestamp 20160712223455 -->
+</div> <!-- /bodycontent -->
+ <!-- printfooter -->
+ <div class="printfooter">
+ Retrieved from "<a href="http://fileformats.archiveteam.org/index.php?title=Lingo_bytecode&amp;oldid=25053">http://fileformats.archiveteam.org/index.php?title=Lingo_bytecode&amp;oldid=25053</a>" </div>
+ <!-- /printfooter -->
+ <!-- catlinks -->
+ <div id="catlinks" class="catlinks"><div id="mw-normal-catlinks" class="mw-normal-catlinks"><a href="http://fileformats.archiveteam.org/wiki/Special:Categories" title="Special:Categories">Categories</a>: <ul><li><a href="http://fileformats.archiveteam.org/wiki/Category:File_Formats" title="Category:File Formats">File Formats</a></li><li><a href="http://fileformats.archiveteam.org/wiki/Category:Electronic_File_Formats" title="Category:Electronic File Formats">Electronic File Formats</a></li><li><a href="http://fileformats.archiveteam.org/wiki/Category:Development" title="Category:Development">Development</a></li></ul></div><div id="mw-hidden-catlinks" class="mw-hidden-catlinks mw-hidden-cats-hidden">Hidden categories: <ul><li><a href="http://fileformats.archiveteam.org/wiki/Category:FormatInfo_without_extensions" title="Category:FormatInfo without extensions">FormatInfo without extensions</a></li><li><a href="http://fileformats.archiveteam.org/wiki/Category:FormatInfo_without_mimetypes" title="Category:FormatInfo without mimetypes">FormatInfo without mimetypes</a></li></ul></div></div> <!-- /catlinks -->
+ <div class="visualClear"></div>
+ <!-- debughtml -->
+ <!-- /debughtml -->
+ </div>
+ <!-- /bodyContent -->
+ </div>
+ <!-- /content -->
+ <!-- header -->
+ <div id="mw-head" class="noprint">
+
+<!-- 0 -->
+<div id="p-personal" class="">
+ <h5>Personal tools</h5>
+ <ul>
+ <li id="pt-login"><a href="http://fileformats.archiveteam.org/index.php?title=Special:UserLogin&amp;returnto=Lingo+bytecode" title="You are encouraged to log in; however, it is not mandatory [ctrl-option-o]" accesskey="o">Log in / create account</a></li>
+ </ul>
+</div>
+
+<!-- /0 -->
+ <div id="left-navigation">
+
+<!-- 0 -->
+<div id="p-namespaces" class="vectorTabs">
+ <h5>Namespaces</h5>
+ <ul>
+ <li id="ca-nstab-main" class="selected"><span><a href="http://fileformats.archiveteam.org/wiki/Lingo_bytecode" title="View the content page [ctrl-option-c]" accesskey="c">Page</a></span></li>
+ <li id="ca-talk" class="new"><span><a href="http://fileformats.archiveteam.org/index.php?title=Talk:Lingo_bytecode&amp;action=edit&amp;redlink=1" title="Discussion about the content page [ctrl-option-t]" accesskey="t">Discussion</a></span></li>
+ </ul>
+</div>
+
+<!-- /0 -->
+
+<!-- 1 -->
+<div id="p-variants" class="vectorMenu emptyPortlet">
+ <h4>
+ </h4>
+ <h5><span>Variants</span><a href="http://fileformats.archiveteam.org/wiki/Lingo_bytecode#"></a></h5>
+ <div class="menu">
+ <ul>
+ </ul>
+ </div>
+</div>
+
+<!-- /1 -->
+ </div>
+ <div id="right-navigation">
+
+<!-- 0 -->
+<div id="p-views" class="vectorTabs">
+ <h5>Views</h5>
+ <ul>
+ <li id="ca-view" class="selected"><span><a href="http://fileformats.archiveteam.org/wiki/Lingo_bytecode">Read</a></span></li>
+ <li id="ca-viewsource"><span><a href="http://fileformats.archiveteam.org/index.php?title=Lingo_bytecode&amp;action=edit" title="This page is protected.
+You can view its source [ctrl-option-e]" accesskey="e">View source</a></span></li>
+ <li id="ca-history" class="collapsible"><span><a href="http://fileformats.archiveteam.org/index.php?title=Lingo_bytecode&amp;action=history" title="Past revisions of this page [ctrl-option-h]" accesskey="h">View history</a></span></li>
+ </ul>
+</div>
+
+<!-- /0 -->
+
+<!-- 1 -->
+<div id="p-cactions" class="vectorMenu emptyPortlet">
+ <h5><span>Actions</span><a href="http://fileformats.archiveteam.org/wiki/Lingo_bytecode#"></a></h5>
+ <div class="menu">
+ <ul>
+ </ul>
+ </div>
+</div>
+
+<!-- /1 -->
+
+<!-- 2 -->
+<div id="p-search">
+ <h5><label for="searchInput">Search</label></h5>
+ <form action="http://fileformats.archiveteam.org/index.php" id="searchform">
+ <div>
+ <input type="search" name="search" title="Search Just Solve the File Format Problem [ctrl-option-f]" accesskey="f" id="searchInput"> <input type="submit" name="go" value="Go" title="Go to a page with this exact name if exists" id="searchGoButton" class="searchButton"> <input type="submit" name="fulltext" value="Search" title="Search the pages for this text" id="mw-searchButton" class="searchButton"> <input type="hidden" name="title" value="Special:Search">
+ </div>
+ </form>
+</div>
+
+<!-- /2 -->
+ </div>
+ </div>
+ <!-- /header -->
+ <!-- panel -->
+ <div id="mw-panel" class="noprint">
+ <!-- logo -->
+ <div id="p-logo"><a style="background-image: url(/thumbsup.png);" href="http://fileformats.archiveteam.org/wiki/Main_Page" title="Visit the main page"></a></div>
+ <!-- /logo -->
+
+<!-- navigation -->
+<div class="portal" id="p-navigation">
+ <h5>Navigation</h5>
+ <div class="body">
+ <ul>
+ <li id="n-mainpage-description"><a href="http://fileformats.archiveteam.org/wiki/Main_Page" title="Visit the main page [ctrl-option-z]" accesskey="z">Main page</a></li>
+ <li id="n-File-formats"><a href="http://fileformats.archiveteam.org/wiki/File_Formats">File formats</a></li>
+ <li id="n-Formats-by-extension"><a href="http://fileformats.archiveteam.org/wiki/Category:File_formats_by_extension">Formats by extension</a></li>
+ <li id="n-Still-more-extensions"><a href="http://fileformats.archiveteam.org/wiki/Category:File_Format_Extension">Still more extensions</a></li>
+ <li id="n-Software"><a href="http://fileformats.archiveteam.org/wiki/Software">Software</a></li>
+ <li id="n-Glossary"><a href="http://fileformats.archiveteam.org/wiki/Glossary">Glossary</a></li>
+ <li id="n-Library"><a href="http://fileformats.archiveteam.org/wiki/Library">Library</a></li>
+ <li id="n-Sources"><a href="http://fileformats.archiveteam.org/wiki/Sources">Sources</a></li>
+ <li id="n-Categories"><a href="http://fileformats.archiveteam.org/wiki/Category:Top_Level_Categories">Categories</a></li>
+ <li id="n-portal"><a href="http://fileformats.archiveteam.org/wiki/Just_Solve_the_File_Format_Problem:Community_portal" title="About the project, what you can do, where to find things">Community portal</a></li>
+ <li id="n-recentchanges"><a href="http://fileformats.archiveteam.org/wiki/Special:RecentChanges" title="A list of recent changes in the wiki [ctrl-option-r]" accesskey="r">Recent changes</a></li>
+ <li id="n-randompage"><a href="http://fileformats.archiveteam.org/wiki/Special:Random" title="Load a random page [ctrl-option-x]" accesskey="x">Random page</a></li>
+ </ul>
+ </div>
+</div>
+
+<!-- /navigation -->
+
+<!-- SEARCH -->
+
+<!-- /SEARCH -->
+
+<!-- TOOLBOX -->
+<div class="portal" id="p-tb">
+ <h5>Toolbox</h5>
+ <div class="body">
+ <ul>
+ <li id="t-whatlinkshere"><a href="http://fileformats.archiveteam.org/wiki/Special:WhatLinksHere/Lingo_bytecode" title="A list of all wiki pages that link here [ctrl-option-j]" accesskey="j">What links here</a></li>
+ <li id="t-recentchangeslinked"><a href="http://fileformats.archiveteam.org/wiki/Special:RecentChangesLinked/Lingo_bytecode" title="Recent changes in pages linked from this page [ctrl-option-k]" accesskey="k">Related changes</a></li>
+ <li id="t-specialpages"><a href="http://fileformats.archiveteam.org/wiki/Special:SpecialPages" title="A list of all special pages [ctrl-option-q]" accesskey="q">Special pages</a></li>
+ <li><a href="http://fileformats.archiveteam.org/index.php?title=Lingo_bytecode&amp;printable=yes" rel="alternate">Printable version</a></li>
+ <li id="t-permalink"><a href="http://fileformats.archiveteam.org/index.php?title=Lingo_bytecode&amp;oldid=25053" title="Permanent link to this revision of the page">Permanent link</a></li>
+ </ul>
+ </div>
+</div>
+
+<!-- /TOOLBOX -->
+
+<!-- LANGUAGES -->
+
+<!-- /LANGUAGES -->
+ </div>
+ <!-- /panel -->
+ <!-- footer -->
+ <div id="footer">
+ <ul id="footer-info">
+ <li id="footer-info-lastmod"> This page was last modified on 17 March 2016, at 18:25.</li>
+ <li id="footer-info-viewcount">This page has been accessed 586 times.</li>
+ <li id="footer-info-copyright">Content is available under <a class="external" href="http://creativecommons.org/publicdomain/zero/1.0/">Creative Commons 0</a>.</li>
+ </ul>
+ <ul id="footer-places">
+ <li id="footer-places-privacy"><a href="http://fileformats.archiveteam.org/wiki/Just_Solve_the_File_Format_Problem:Privacy_policy" title="Just Solve the File Format Problem:Privacy policy">Privacy policy</a></li>
+ <li id="footer-places-about"><a href="http://fileformats.archiveteam.org/wiki/Just_Solve_the_File_Format_Problem:About" title="Just Solve the File Format Problem:About">About Just Solve the File Format Problem</a></li>
+ <li id="footer-places-disclaimer"><a href="http://fileformats.archiveteam.org/wiki/Just_Solve_the_File_Format_Problem:General_disclaimer" title="Just Solve the File Format Problem:General disclaimer">Disclaimers</a></li>
+ </ul>
+ <ul id="footer-icons" class="noprint">
+ <li id="footer-copyrightico">
+ <a href="http://creativecommons.org/publicdomain/zero/1.0/"><img src="./Lingo bytecode - Just Solve the File Format Problem_files/cc-0.png" alt="Creative Commons 0" width="88" height="31"></a>
+ </li>
+ <li id="footer-poweredbyico">
+ <a href="http://www.mediawiki.org/"><img src="./Lingo bytecode - Just Solve the File Format Problem_files/poweredby_mediawiki_88x31.png" alt="Powered by MediaWiki" width="88" height="31"></a>
+ </li>
+ </ul>
+ <div style="clear:both"></div>
+ </div>
+ <!-- /footer -->
+ <script src="./Lingo bytecode - Just Solve the File Format Problem_files/load(4).php"></script>
+<script>if(window.mw){
+mw.loader.load(["mediawiki.user","mediawiki.page.ready"], null, true);
+}</script><script src="./Lingo bytecode - Just Solve the File Format Problem_files/load(5).php" type="text/javascript"></script>
+<!-- Served in 0.102 secs. -->
+
+
+<script type="text/javascript" src="chrome-extension://cmjeonfdjdekpggjkoknhhkcifnaichh/src/rules.js"></script><script type="text/javascript" src="chrome-extension://cmjeonfdjdekpggjkoknhhkcifnaichh/src/inject.js"></script></body><span class="gr__tooltip"><span class="gr__tooltip-content"></span><i class="gr__tooltip-logo"></i><span class="gr__triangle"></span></span></html> \ No newline at end of file
diff --git a/engines/director/lingo/tests/factory.lingo b/engines/director/lingo/tests/factory.lingo
new file mode 100644
index 0000000000..388d2c3a51
--- /dev/null
+++ b/engines/director/lingo/tests/factory.lingo
@@ -0,0 +1,69 @@
+--
+macro AimGun2
+global aim1
+set aim1 = aim2(mNew)
+--
+factory aim2
+method mNew
+ dontpassevent
+ global aim1
+ when mousedown then aim1(fire)
+ when keydown then aim1(mExit)
+ set the locv of sprite 24 to 540
+method mMove x, y
+ set the locH of sprite 15 to x
+ set the locV of sprite 15 to y-250
+method mAtFrame
+ dontpassevent
+ me(mMove, the mouseH, the mouseV)
+method fire
+ global fire1, targeth, targetv
+ set fire1 = fire2(mNew)
+ set the perframehook to fire1
+ me(mDispose)
+method mExit
+ set the perframehook to false
+ postfire
+ me(mDispose)
+method mDispose
+ global aim1
+ set aim1 = 1
+ when keydown then nothing
+--
+factory fire2
+method mNew
+ dontpassevent
+ when mousedown then nothing
+ set the castnum of sprite 14 to f15
+method mAtFrame
+ Global StartH, StartV, targetv, stepH, stepV, bcast
+ dontpassevent
+ set the castnum of sprite 14 to bcast
+ set the LocV of sprite 14 to (startV-stepV)
+ if sprite 14 intersects 10 and (startV-6) <= targetV then
+ set the castnum of sprite 14 to f16
+ set the perframehook to false
+ me(hit)
+ exit
+ end if
+ if startV < targetV or bcast>g17 then
+ set the perframehook to false
+ set the locV of sprite 14 to 340
+ aimgun2
+ exit
+ end if
+ set startV to (startV-stepV)
+ set bcast = bcast + 1
+method hit
+ global KillLoc
+ set killloc to the loch of sprite 3
+ go "Death"
+ set the locV of sprite 14 to 400
+ aimgun2
+method mDispose
+ global fire1
+ set fire1 = 0
+--
+macro KillIt2
+global KillLoc
+set the locH of sprite 3 to KillLoc
diff --git a/engines/director/lingo/tests/goto.lingo b/engines/director/lingo/tests/goto.lingo
new file mode 100644
index 0000000000..250ea24f8a
--- /dev/null
+++ b/engines/director/lingo/tests/goto.lingo
@@ -0,0 +1,5 @@
+go to frame "Open23" of movie "OpenCabin23"
+go "CARDBACK"
+go movie "BAR 1"
+go to "Open23" of movie "OpenCabin23"
+go to "Chair"
diff --git a/engines/director/lingo/tests/if.lingo b/engines/director/lingo/tests/if.lingo
new file mode 100644
index 0000000000..8f1e2391e6
--- /dev/null
+++ b/engines/director/lingo/tests/if.lingo
@@ -0,0 +1,27 @@
+--
+set x = 1
+if x = 5 then exit
+else put 10.0
+
+repeat with x = 1 to 6
+ if x = 3 then put 30
+ else if x = 4 then put 40
+ else if x = 5 then put 50
+ else put 10.0
+ if x = 1 then
+ put 1
+ else if x = 2 then
+ put 1232.12345678901234
+ put 2.2
+ else if x = 3 then
+ put 3
+ else if x = 4 then
+ put 4
+ else if x = 5 then
+ put 5
+ else if x = 6 then
+ put 6
+ end if
+ if x = 4 then put 40
+ else put 50
+end repeat
diff --git a/engines/director/lingo/tests/ilk.lingo b/engines/director/lingo/tests/ilk.lingo
new file mode 100644
index 0000000000..686d9a5118
--- /dev/null
+++ b/engines/director/lingo/tests/ilk.lingo
@@ -0,0 +1,7 @@
+put ilk(10)
+put ilk(20.0)
+put ilk("Macromedia")
+put ilk(point(10, 20))
+put ilk(ilk(10))
+set x = point(10, 20)
+put ilk(x)
diff --git a/engines/director/lingo/tests/lingotests.lingo b/engines/director/lingo/tests/lingotests.lingo
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/engines/director/lingo/tests/lingotests.lingo
diff --git a/engines/director/lingo/tests/loops.lingo b/engines/director/lingo/tests/loops.lingo
new file mode 100644
index 0000000000..e4c5ccefc5
--- /dev/null
+++ b/engines/director/lingo/tests/loops.lingo
@@ -0,0 +1,22 @@
+set x = 5
+if x <= 5 then set x = 6
+if (x = 5) then
+set x = 7 -- this is comment
+else
+set x = 8
+-- this is another comment
+end if
+put x
+-- this is more comment
+set y = 1
+repeat while (y < 5)
+set y = y + 1
+put y
+end repeat
+
+repeat with z = 10 to 15
+put z
+end repeat
+repeat with y = 5 down to 1
+put y
+end repeat
diff --git a/engines/director/lingo/tests/macros.lingo b/engines/director/lingo/tests/macros.lingo
new file mode 100644
index 0000000000..7ffa557cb7
--- /dev/null
+++ b/engines/director/lingo/tests/macros.lingo
@@ -0,0 +1,42 @@
+--
+macro SHIPX
+global x, y
+set x = Random(5)
+if x = 1 then
+go "Zoom"
+exit
+end if
+if x >1 then
+set y = 10
+exit
+end if
+put 100
+
+--
+macro ZIPX
+set x = Random(5)
+if x = 1 then
+go "ZIP"
+exit
+end if
+if x >1 then
+put x
+exit
+end if
+
+--
+macro check par1, par2
+, par3
+if par1 = 3 then
+put -3
+else
+put 0
+end if
+if par2 = 2 then
+put 2
+else
+put 0
+end if
+put par1
+put par2
+put par3
diff --git a/engines/director/lingo/tests/macros2.lingo b/engines/director/lingo/tests/macros2.lingo
new file mode 100644
index 0000000000..b587ae421c
--- /dev/null
+++ b/engines/director/lingo/tests/macros2.lingo
@@ -0,0 +1,11 @@
+check(2, 3)
+global x, y
+set y = 8
+shipx
+put x
+zipx
+put x
+put y
+check(1, 2, 3)
+check 4, 5, 6
+check 7, 8
diff --git a/engines/director/lingo/tests/math.lingo b/engines/director/lingo/tests/math.lingo
new file mode 100644
index 0000000000..6f8ecc374f
--- /dev/null
+++ b/engines/director/lingo/tests/math.lingo
@@ -0,0 +1,22 @@
+if random(4) > 2 then put 1000
+set z = 5.5
+set z1 = 2
+set z2 = z / z1
+put z
+put z1
+put z2
+put integer(z2)
+put cos(z2)
+
+set x = 2 + 3 * (4 / 2)
+put x
+
+put power(2, 8)
+put power(2, 8, 0)
+put power(2)
+updatestage
+
+-- Type conversion
+put (1024/4096)*100 -- 0
+put (1024/4096)*100.0 -- 0.0
+put ((1024*1.0)/4096)*100.0 -- 25.0
diff --git a/engines/director/lingo/tests/mci.lingo b/engines/director/lingo/tests/mci.lingo
new file mode 100644
index 0000000000..04130bf907
--- /dev/null
+++ b/engines/director/lingo/tests/mci.lingo
@@ -0,0 +1,2 @@
+mci "open MM\T005045a.wav type WaveAudio alias T005045a"
+mci "play T005045a from 22710 to 32872"
diff --git a/engines/director/lingo/tests/point.lingo b/engines/director/lingo/tests/point.lingo
new file mode 100644
index 0000000000..4e90ddb684
--- /dev/null
+++ b/engines/director/lingo/tests/point.lingo
@@ -0,0 +1,3 @@
+put point(10, 20)
+set x = point(20,30)
+put x
diff --git a/engines/director/lingo/tests/strings.lingo b/engines/director/lingo/tests/strings.lingo
new file mode 100644
index 0000000000..568eb74640
--- /dev/null
+++ b/engines/director/lingo/tests/strings.lingo
@@ -0,0 +1,13 @@
+set z = "foo bar baz"
+set z1 = z & " meow"
+set z1 = z1 && "woof"
+put z
+put z1
+put chars("Macromedia", 6, 6)
+put chars("Macromedia", 6, 10)
+put chars("Macromedia", -1, 15)
+if z1 contains "MeÍW" then
+ put "Contains"
+else
+ put "Doesn't contain"
+end if
diff --git a/engines/director/lingo/tests/the.lingo b/engines/director/lingo/tests/the.lingo
new file mode 100644
index 0000000000..65c0d6ea5c
--- /dev/null
+++ b/engines/director/lingo/tests/the.lingo
@@ -0,0 +1,6 @@
+put 1.0 / 3
+set the floatPrecision to 6
+put 1.0 / 3
+put the loch of sprite 4
+set the loch of sprite 5 to 10
+set the castnum of sprite 8 to the number of cast "A Blank Castmember"
diff --git a/engines/director/module.mk b/engines/director/module.mk
new file mode 100644
index 0000000000..90d27e7413
--- /dev/null
+++ b/engines/director/module.mk
@@ -0,0 +1,33 @@
+MODULE := engines/director
+
+MODULE_OBJS = \
+ detection.o \
+ dib.o \
+ director.o \
+ movie.o \
+ resource.o \
+ score.o \
+ sound.o \
+ lingo/lingo-gr.o \
+ lingo/lingo.o \
+ lingo/lingo-builtins.o \
+ lingo/lingo-code.o \
+ lingo/lingo-codegen.o \
+ lingo/lingo-funcs.o \
+ lingo/lingo-lex.o \
+ lingo/lingo-the.o
+
+engines/director/lingo/lingo-lex.cpp: engines/director/lingo/lingo-lex.l
+ flex -o engines/director/lingo/lingo-lex.cpp engines/director/lingo/lingo-lex.l
+
+engines/director/lingo/lingo-gr.cpp: engines/director/lingo/lingo-gr.y
+ bison -dv -o engines/director/lingo/lingo-gr.cpp engines/director/lingo/lingo-gr.y
+ mv engines/director/lingo/lingo-gr.hpp engines/director/lingo/lingo-gr.h
+
+# This module can be built as a plugin
+ifeq ($(ENABLE_DIRECTOR), DYNAMIC_PLUGIN)
+PLUGIN := 1
+endif
+
+# Include common rules
+include $(srcdir)/rules.mk
diff --git a/engines/director/movie.cpp b/engines/director/movie.cpp
new file mode 100644
index 0000000000..3c34e2d432
--- /dev/null
+++ b/engines/director/movie.cpp
@@ -0,0 +1,66 @@
+/* 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.
+ *
+ * Additional copyright for this file:
+ * Copyright (C) 1995-1997 Presto Studios, Inc.
+ *
+ * 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 2
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "video/qt_decoder.h"
+#include "director/movie.h"
+#include "director/score.h"
+#include "common/debug.h"
+#include "common/system.h"
+namespace Director {
+
+Movie::Movie(Common::String fileName, DirectorEngine *vm) {
+ _vm = vm;
+ _currentVideo = new Video::QuickTimeDecoder();
+ if (!_currentVideo->loadFile(fileName)) {
+ warning("Can not open file %s", fileName.c_str());
+ return;
+ }
+}
+
+void Movie::play(Common::Point dest) {
+
+ _currentVideo->start();
+
+ uint16 width = _currentVideo->getWidth();
+ uint16 height = _currentVideo->getHeight();
+
+ while (!_currentVideo->endOfVideo()) {
+ if (_currentVideo->needsUpdate()) {
+ const Graphics::Surface *frame = _currentVideo->decodeNextFrame();
+ g_system->copyRectToScreen(frame->getPixels(), frame->pitch, dest.x, dest.y, width, height);
+ g_system->updateScreen();
+ }
+ g_system->delayMillis(10);
+ _vm->getCurrentScore()->processEvents();
+ }
+}
+
+void Movie::stop() {
+ _currentVideo->stop();
+}
+
+Movie::~Movie() {
+ delete _currentVideo;
+}
+
+} //End of namespace Director
diff --git a/engines/director/movie.h b/engines/director/movie.h
new file mode 100644
index 0000000000..e26d10a7c7
--- /dev/null
+++ b/engines/director/movie.h
@@ -0,0 +1,51 @@
+/* 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.
+ *
+ * Additional copyright for this file:
+ * Copyright (C) 1995-1997 Presto Studios, Inc.
+ *
+ * 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 2
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef DIRECTOR_MOVIE_H
+#define DIRECTOR_MOVIE_H
+
+#include "common/str.h"
+#include "common/rect.h"
+#include "graphics/managed_surface.h"
+#include "director/director.h"
+
+namespace Video {
+class VideoDecoder;
+}
+
+namespace Director {
+
+class Movie {
+public:
+ Movie(Common::String fileName, DirectorEngine *vm);
+ ~Movie();
+ void play(Common::Point dest);
+ void stop();
+
+private:
+ Video::VideoDecoder *_currentVideo;
+ DirectorEngine *_vm;
+};
+} //End of namespace Director
+
+#endif
diff --git a/engines/director/resource.cpp b/engines/director/resource.cpp
new file mode 100644
index 0000000000..5a2c4bc8a6
--- /dev/null
+++ b/engines/director/resource.cpp
@@ -0,0 +1,440 @@
+/* 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 2
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "director/resource.h"
+
+#include "common/debug.h"
+#include "common/macresman.h"
+#include "common/substream.h"
+#include "common/util.h"
+#include "common/textconsole.h"
+
+namespace Director {
+
+// Base Archive code
+
+Archive::Archive() {
+ _stream = 0;
+ _isBigEndian = true;
+}
+
+Archive::~Archive() {
+ close();
+}
+
+bool Archive::openFile(const Common::String &fileName) {
+ Common::File *file = new Common::File();
+
+ if (!file->open(fileName)) {
+ delete file;
+ return false;
+ }
+
+ if (!openStream(file)) {
+ close();
+ return false;
+ }
+
+ return true;
+}
+
+void Archive::close() {
+ _types.clear();
+ delete _stream; _stream = 0;
+}
+
+bool Archive::hasResource(uint32 tag, uint16 id) const {
+ if (!_types.contains(tag))
+ return false;
+
+ return _types[tag].contains(id);
+}
+
+bool Archive::hasResource(uint32 tag, const Common::String &resName) const {
+ if (!_types.contains(tag) || resName.empty())
+ return false;
+
+ const ResourceMap &resMap = _types[tag];
+
+ for (ResourceMap::const_iterator it = resMap.begin(); it != resMap.end(); it++)
+ if (it->_value.name.matchString(resName))
+ return true;
+
+ return false;
+}
+
+Common::SeekableSubReadStreamEndian *Archive::getResource(uint32 tag, uint16 id) {
+ if (!_types.contains(tag))
+ error("Archive does not contain '%s' %04x", tag2str(tag), id);
+
+ const ResourceMap &resMap = _types[tag];
+
+ if (!resMap.contains(id))
+ error("Archive does not contain '%s' %04x", tag2str(tag), id);
+
+ const Resource &res = resMap[id];
+
+ return new Common::SeekableSubReadStreamEndian(_stream, res.offset, res.offset + res.size, _isBigEndian, DisposeAfterUse::NO);
+}
+
+uint32 Archive::getOffset(uint32 tag, uint16 id) const {
+ if (!_types.contains(tag))
+ error("Archive does not contain '%s' %04x", tag2str(tag), id);
+
+ const ResourceMap &resMap = _types[tag];
+
+ if (!resMap.contains(id))
+ error("Archive does not contain '%s' %04x", tag2str(tag), id);
+
+ return resMap[id].offset;
+}
+
+uint16 Archive::findResourceID(uint32 tag, const Common::String &resName) const {
+ if (!_types.contains(tag) || resName.empty())
+ return 0xFFFF;
+
+ const ResourceMap &resMap = _types[tag];
+
+ for (ResourceMap::const_iterator it = resMap.begin(); it != resMap.end(); it++)
+ if (it->_value.name.matchString(resName))
+ return it->_key;
+
+ return 0xFFFF;
+}
+
+Common::String Archive::getName(uint32 tag, uint16 id) const {
+ if (!_types.contains(tag))
+ error("Archive does not contain '%s' %04x", tag2str(tag), id);
+
+ const ResourceMap &resMap = _types[tag];
+
+ if (!resMap.contains(id))
+ error("Archive does not contain '%s' %04x", tag2str(tag), id);
+
+ return resMap[id].name;
+}
+
+Common::Array<uint32> Archive::getResourceTypeList() const {
+ Common::Array<uint32> typeList;
+
+ for (TypeMap::const_iterator it = _types.begin(); it != _types.end(); it++)
+ typeList.push_back(it->_key);
+
+ return typeList;
+}
+
+Common::Array<uint16> Archive::getResourceIDList(uint32 type) const {
+ Common::Array<uint16> idList;
+
+ if (!_types.contains(type))
+ return idList;
+
+ const ResourceMap &resMap = _types[type];
+
+ for (ResourceMap::const_iterator it = resMap.begin(); it != resMap.end(); it++)
+ idList.push_back(it->_key);
+
+ return idList;
+}
+
+uint32 Archive::convertTagToUppercase(uint32 tag) {
+ uint32 newTag = toupper(tag >> 24) << 24;
+ newTag |= toupper((tag >> 16) & 0xFF) << 16;
+ newTag |= toupper((tag >> 8) & 0xFF) << 8;
+
+ return newTag | toupper(tag & 0xFF);
+}
+
+// Mac Archive code
+
+MacArchive::MacArchive() : Archive(), _resFork(0) {
+}
+
+MacArchive::~MacArchive() {
+ delete _resFork;
+}
+
+void MacArchive::close() {
+ Archive::close();
+ delete _resFork;
+ _resFork = 0;
+}
+
+bool MacArchive::openFile(const Common::String &fileName) {
+ close();
+
+ _resFork = new Common::MacResManager();
+
+ if (!_resFork->open(fileName) || !_resFork->hasResFork()) {
+ close();
+ return false;
+ }
+
+ Common::MacResTagArray tagArray = _resFork->getResTagArray();
+
+ for (uint32 i = 0; i < tagArray.size(); i++) {
+ ResourceMap &resMap = _types[tagArray[i]];
+ Common::MacResIDArray idArray = _resFork->getResIDArray(tagArray[i]);
+
+ for (uint32 j = 0; j < idArray.size(); j++) {
+ Resource &res = resMap[idArray[j]];
+
+ res.offset = res.size = 0; // unused
+ res.name = _resFork->getResName(tagArray[i], idArray[j]);
+ }
+ }
+
+
+ return true;
+}
+
+bool MacArchive::openStream(Common::SeekableReadStream *stream, uint32 startOffset) {
+ // TODO: Add support for this (v4 Windows games)
+ return false;
+}
+
+Common::SeekableSubReadStreamEndian *MacArchive::getResource(uint32 tag, uint16 id) {
+ assert(_resFork);
+ Common::SeekableReadStream *stream = _resFork->getResource(tag, id);
+ return new Common::SeekableSubReadStreamEndian(stream, 0, stream->size(), true, DisposeAfterUse::NO);
+}
+
+// RIFF Archive code
+
+bool RIFFArchive::openStream(Common::SeekableReadStream *stream, uint32 startOffset) {
+ close();
+
+ stream->seek(startOffset);
+
+ if (convertTagToUppercase(stream->readUint32BE()) != MKTAG('R', 'I', 'F', 'F'))
+ return false;
+
+ stream->readUint32LE(); // size
+
+ if (convertTagToUppercase(stream->readUint32BE()) != MKTAG('R', 'M', 'M', 'P'))
+ return false;
+
+ if (convertTagToUppercase(stream->readUint32BE()) != MKTAG('C', 'F', 'T', 'C'))
+ return false;
+
+ uint32 cftcSize = stream->readUint32LE();
+ uint32 startPos = stream->pos();
+ stream->readUint32LE(); // unknown (always 0?)
+
+ while ((uint32)stream->pos() < startPos + cftcSize) {
+ uint32 tag = convertTagToUppercase(stream->readUint32BE());
+
+ uint32 size = stream->readUint32LE();
+ uint32 id = stream->readUint32LE();
+ uint32 offset = stream->readUint32LE();
+
+ if (tag == 0)
+ break;
+
+ uint16 startResPos = stream->pos();
+ stream->seek(offset + 12);
+
+ Common::String name = "";
+ byte nameSize = stream->readByte();
+
+ if (nameSize) {
+ for (uint8 i = 0; i < nameSize; i++) {
+ name += stream->readByte();
+ }
+ }
+
+ stream->seek(startResPos);
+
+ debug(3, "Found RIFF resource '%s' %d: %d @ 0x%08x", tag2str(tag), id, size, offset);
+
+ ResourceMap &resMap = _types[tag];
+ Resource &res = resMap[id];
+ res.offset = offset;
+ res.size = size;
+ res.name = name;
+ }
+
+ _stream = stream;
+ return true;
+}
+
+Common::SeekableSubReadStreamEndian *RIFFArchive::getResource(uint32 tag, uint16 id) {
+ if (!_types.contains(tag))
+ error("Archive does not contain '%s' %04x", tag2str(tag), id);
+
+ const ResourceMap &resMap = _types[tag];
+
+ if (!resMap.contains(id))
+ error("Archive does not contain '%s' %04x", tag2str(tag), id);
+
+ const Resource &res = resMap[id];
+
+ // Adjust to skip the resource header
+ uint32 offset = res.offset + 12;
+ uint32 size = res.size - 4;
+ // Skip the Pascal string
+ _stream->seek(offset);
+ byte stringSize = _stream->readByte(); // 1 for this byte
+
+ offset += stringSize + 1;
+ size -= stringSize + 1;
+
+ // Align to nearest word boundary
+ if (offset & 1) {
+ offset++;
+ size--;
+ }
+
+ return new Common::SeekableSubReadStreamEndian(_stream, offset, offset + size, true, DisposeAfterUse::NO);
+}
+
+// RIFX Archive code
+
+bool RIFXArchive::openStream(Common::SeekableReadStream *stream, uint32 startOffset) {
+ close();
+
+ stream->seek(startOffset);
+
+ uint32 headerTag = stream->readUint32BE();
+
+ if (headerTag == MKTAG('R', 'I', 'F', 'X'))
+ _isBigEndian = true;
+ else if (SWAP_BYTES_32(headerTag) == MKTAG('R', 'I', 'F', 'X'))
+ _isBigEndian = false;
+ else
+ return false;
+
+ Common::SeekableSubReadStreamEndian subStream(stream, startOffset + 4, stream->size(), _isBigEndian, DisposeAfterUse::NO);
+
+ subStream.readUint32(); // size
+
+ uint32 rifxType = subStream.readUint32();
+
+ if (rifxType != MKTAG('M', 'V', '9', '3') && rifxType != MKTAG('A', 'P', 'P', 'L'))
+ return false;
+
+ if (subStream.readUint32() != MKTAG('i', 'm', 'a', 'p'))
+ return false;
+
+ subStream.readUint32(); // imap length
+ subStream.readUint32(); // unknown
+ uint32 mmapOffset = subStream.readUint32() - startOffset - 4;
+
+ subStream.seek(mmapOffset);
+
+ if (subStream.readUint32() != MKTAG('m', 'm', 'a', 'p'))
+ return false;
+
+ subStream.readUint32(); // mmap length
+ subStream.readUint16(); // unknown
+ subStream.readUint16(); // unknown
+ subStream.readUint32(); // resCount + empty entries
+ uint32 resCount = subStream.readUint32();
+ subStream.skip(8); // all 0xFF
+ subStream.readUint32(); // unknown
+
+ Common::Array<Resource> resources;
+
+ // Need to look for these two resources
+ const Resource *keyRes = 0;
+ const Resource *casRes = 0;
+
+ for (uint32 i = 0; i < resCount; i++) {
+ uint32 tag = subStream.readUint32();
+ uint32 size = subStream.readUint32();
+ uint32 offset = subStream.readUint32();
+ /*uint16 flags = */ subStream.readUint16();
+ /*uint16 unk1 = */ subStream.readUint16();
+ /*uint32 unk2 = */ subStream.readUint32();
+
+ debug(3, "Found RIFX resource index %d: '%s', %d @ 0x%08x", i, tag2str(tag), size, offset);
+
+ Resource res;
+ res.offset = offset;
+ res.size = size;
+ resources.push_back(res);
+
+ // APPL is a special case; it has an embedded "normal" archive
+ if (rifxType == MKTAG('A', 'P', 'P', 'L') && tag == MKTAG('F', 'i', 'l', 'e'))
+ return openStream(stream, offset);
+
+ // Looking for two types here
+ if (tag == MKTAG('K', 'E', 'Y', '*'))
+ keyRes = &resources[resources.size() - 1];
+ else if (tag == MKTAG('C', 'A', 'S', '*'))
+ casRes = &resources[resources.size() - 1];
+ }
+
+ // We need to have found the 'File' resource already
+ if (rifxType == MKTAG('A', 'P', 'P', 'L')) {
+ warning("No 'File' resource present in APPL archive");
+ return false;
+ }
+
+ // A KEY* must be present
+ if (!keyRes) {
+ warning("No 'KEY*' resource present");
+ return false;
+ }
+
+ // Parse the CAS*, if present
+ Common::Array<uint32> casEntries;
+ if (casRes) {
+ Common::SeekableSubReadStreamEndian casStream(stream, casRes->offset + 8, casRes->offset + 8 + casRes->size, _isBigEndian, DisposeAfterUse::NO);
+ casEntries.resize(casRes->size / 4);
+
+ for (uint32 i = 0; i < casEntries.size(); i++)
+ casEntries[i] = casStream.readUint32();
+ }
+
+ // Parse the KEY*
+ Common::SeekableSubReadStreamEndian keyStream(stream, keyRes->offset + 8, keyRes->offset + 8 + keyRes->size, _isBigEndian, DisposeAfterUse::NO);
+ /*uint16 unk1 = */ keyStream.readUint16();
+ /*uint16 unk2 = */ keyStream.readUint16();
+ /*uint32 unk3 = */ keyStream.readUint32();
+ uint32 keyCount = keyStream.readUint32();
+
+ for (uint32 i = 0; i < keyCount; i++) {
+ uint32 index = keyStream.readUint32();
+ uint32 id = keyStream.readUint32();
+ uint32 resTag = keyStream.readUint32();
+
+ // Handle CAS*/CASt nonsense
+ if (resTag == MKTAG('C', 'A', 'S', 't')) {
+ for (uint32 j = 0; j < casEntries.size(); j++) {
+ if (casEntries[j] == index) {
+ id += j + 1;
+ break;
+ }
+ }
+ }
+
+ const Resource &res = resources[index];
+ debug(3, "Found RIFX resource: '%s' 0x%04x, %d @ 0x%08x", tag2str(resTag), id, res.size, res.offset);
+ _types[resTag][id] = res;
+ }
+
+ _stream = stream;
+ return true;
+}
+
+} // End of namespace Director
diff --git a/engines/director/resource.h b/engines/director/resource.h
new file mode 100644
index 0000000000..fda8b79d82
--- /dev/null
+++ b/engines/director/resource.h
@@ -0,0 +1,110 @@
+/* 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 2
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef DIRECTOR_RESOURCE_H
+#define DIRECTOR_RESOURCE_H
+
+#include "common/scummsys.h"
+#include "common/endian.h"
+#include "common/func.h"
+#include "common/hashmap.h"
+#include "common/file.h"
+#include "common/str.h"
+#include "common/substream.h"
+
+namespace Common {
+class MacResManager;
+}
+
+namespace Director {
+
+// Completely ripped off of Mohawk's Archive code
+
+class Archive {
+public:
+ Archive();
+ virtual ~Archive();
+
+ virtual bool openFile(const Common::String &fileName);
+ virtual bool openStream(Common::SeekableReadStream *stream, uint32 offset = 0) = 0;
+ virtual void close();
+
+ bool isOpen() const { return _stream != 0; }
+
+ bool hasResource(uint32 tag, uint16 id) const;
+ bool hasResource(uint32 tag, const Common::String &resName) const;
+ virtual Common::SeekableSubReadStreamEndian *getResource(uint32 tag, uint16 id);
+ uint32 getOffset(uint32 tag, uint16 id) const;
+ uint16 findResourceID(uint32 tag, const Common::String &resName) const;
+ Common::String getName(uint32 tag, uint16 id) const;
+
+ Common::Array<uint32> getResourceTypeList() const;
+ Common::Array<uint16> getResourceIDList(uint32 type) const;
+ bool _isBigEndian;
+ static uint32 convertTagToUppercase(uint32 tag);
+
+protected:
+ Common::SeekableReadStream *_stream;
+ struct Resource {
+ uint32 offset;
+ uint32 size;
+ Common::String name;
+ };
+ typedef Common::HashMap<uint16, Resource> ResourceMap;
+ typedef Common::HashMap<uint32, ResourceMap> TypeMap;
+ TypeMap _types;
+};
+
+class MacArchive : public Archive {
+public:
+ MacArchive();
+ ~MacArchive();
+
+ void close();
+ bool openFile(const Common::String &fileName);
+ bool openStream(Common::SeekableReadStream *stream, uint32 startOffset = 0);
+ Common::SeekableSubReadStreamEndian *getResource(uint32 tag, uint16 id);
+
+private:
+ Common::MacResManager *_resFork;
+};
+
+class RIFFArchive : public Archive {
+public:
+ RIFFArchive() : Archive() {}
+ ~RIFFArchive() {}
+
+ bool openStream(Common::SeekableReadStream *stream, uint32 startOffset = 0);
+ Common::SeekableSubReadStreamEndian *getResource(uint32 tag, uint16 id);
+};
+
+class RIFXArchive : public Archive {
+public:
+ RIFXArchive() : Archive(){ _isBigEndian = true; }
+ ~RIFXArchive() {}
+
+ bool openStream(Common::SeekableReadStream *stream, uint32 startOffset = 0);
+};
+
+} // End of namespace Director
+
+#endif
diff --git a/engines/director/score.cpp b/engines/director/score.cpp
new file mode 100644
index 0000000000..4bb4dda2e5
--- /dev/null
+++ b/engines/director/score.cpp
@@ -0,0 +1,1617 @@
+/* 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 2
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "director/score.h"
+#include "common/stream.h"
+#include "common/debug.h"
+#include "common/file.h"
+#include "common/archive.h"
+#include "common/config-manager.h"
+#include "common/unzip.h"
+
+#include "common/system.h"
+#include "director/dib.h"
+#include "director/resource.h"
+#include "director/lingo/lingo.h"
+#include "director/sound.h"
+
+#include "graphics/palette.h"
+#include "common/events.h"
+#include "engines/util.h"
+#include "graphics/managed_surface.h"
+#include "graphics/macgui/macwindowmanager.h"
+#include "image/bmp.h"
+#include "graphics/fontman.h"
+#include "graphics/fonts/bdf.h"
+
+namespace Director {
+
+static byte defaultPalette[768] = {
+ 0, 0, 0, 17, 17, 17, 34, 34, 34, 68, 68, 68,
+ 85, 85, 85, 119, 119, 119, 136, 136, 136, 170, 170, 170,
+ 187, 187, 187, 221, 221, 221, 238, 238, 238, 0, 0, 17,
+ 0, 0, 34, 0, 0, 68, 0, 0, 85, 0, 0, 119,
+ 0, 0, 136, 0, 0, 170, 0, 0, 187, 0, 0, 221,
+ 0, 0, 238, 0, 17, 0, 0, 34, 0, 0, 68, 0,
+ 0, 85, 0, 0, 119, 0, 0, 136, 0, 0, 170, 0,
+ 0, 187, 0, 0, 221, 0, 0, 238, 0, 17, 0, 0,
+ 34, 0, 0, 68, 0, 0, 85, 0, 0, 119, 0, 0,
+ 136, 0, 0, 170, 0, 0, 187, 0, 0, 221, 0, 0,
+ 238, 0, 0, 0, 0, 51, 0, 0, 102, 0, 0, 153,
+ 0, 0, 204, 0, 0, 255, 0, 51, 0, 0, 51, 51,
+ 0, 51, 102, 0, 51, 153, 0, 51, 204, 0, 51, 255,
+ 0, 102, 0, 0, 102, 51, 0, 102, 102, 0, 102, 153,
+ 0, 102, 204, 0, 102, 255, 0, 153, 0, 0, 153, 51,
+ 0, 153, 102, 0, 153, 153, 0, 153, 204, 0, 153, 255,
+ 0, 204, 0, 0, 204, 51, 0, 204, 102, 0, 204, 153,
+ 0, 204, 204, 0, 204, 255, 0, 255, 0, 0, 255, 51,
+ 0, 255, 102, 0, 255, 153, 0, 255, 204, 0, 255, 255,
+ 51, 0, 0, 51, 0, 51, 51, 0, 102, 51, 0, 153,
+ 51, 0, 204, 51, 0, 255, 51, 51, 0, 51, 51, 51,
+ 51, 51, 102, 51, 51, 153, 51, 51, 204, 51, 51, 255,
+ 51, 102, 0, 51, 102, 51, 51, 102, 102, 51, 102, 153,
+ 51, 102, 204, 51, 102, 255, 51, 153, 0, 51, 153, 51,
+ 51, 153, 102, 51, 153, 153, 51, 153, 204, 51, 153, 255,
+ 51, 204, 0, 51, 204, 51, 51, 204, 102, 51, 204, 153,
+ 51, 204, 204, 51, 204, 255, 51, 255, 0, 51, 255, 51,
+ 51, 255, 102, 51, 255, 153, 51, 255, 204, 51, 255, 255,
+ 102, 0, 0, 102, 0, 51, 102, 0, 102, 102, 0, 153,
+ 102, 0, 204, 102, 0, 255, 102, 51, 0, 102, 51, 51,
+ 102, 51, 102, 102, 51, 153, 102, 51, 204, 102, 51, 255,
+ 102, 102, 0, 102, 102, 51, 102, 102, 102, 102, 102, 153,
+ 102, 102, 204, 102, 102, 255, 102, 153, 0, 102, 153, 51,
+ 102, 153, 102, 102, 153, 153, 102, 153, 204, 102, 153, 255,
+ 102, 204, 0, 102, 204, 51, 102, 204, 102, 102, 204, 153,
+ 102, 204, 204, 102, 204, 255, 102, 255, 0, 102, 255, 51,
+ 102, 255, 102, 102, 255, 153, 102, 255, 204, 102, 255, 255,
+ 153, 0, 0, 153, 0, 51, 153, 0, 102, 153, 0, 153,
+ 153, 0, 204, 153, 0, 255, 153, 51, 0, 153, 51, 51,
+ 153, 51, 102, 153, 51, 153, 153, 51, 204, 153, 51, 255,
+ 153, 102, 0, 153, 102, 51, 153, 102, 102, 153, 102, 153,
+ 153, 102, 204, 153, 102, 255, 153, 153, 0, 153, 153, 51,
+ 153, 153, 102, 153, 153, 153, 153, 153, 204, 153, 153, 255,
+ 153, 204, 0, 153, 204, 51, 153, 204, 102, 153, 204, 153,
+ 153, 204, 204, 153, 204, 255, 153, 255, 0, 153, 255, 51,
+ 153, 255, 102, 153, 255, 153, 153, 255, 204, 153, 255, 255,
+ 204, 0, 0, 204, 0, 51, 204, 0, 102, 204, 0, 153,
+ 204, 0, 204, 204, 0, 255, 204, 51, 0, 204, 51, 51,
+ 204, 51, 102, 204, 51, 153, 204, 51, 204, 204, 51, 255,
+ 204, 102, 0, 204, 102, 51, 204, 102, 102, 204, 102, 153,
+ 204, 102, 204, 204, 102, 255, 204, 153, 0, 204, 153, 51,
+ 204, 153, 102, 204, 153, 153, 204, 153, 204, 204, 153, 255,
+ 204, 204, 0, 204, 204, 51, 204, 204, 102, 204, 204, 153,
+ 204, 204, 204, 204, 204, 255, 204, 255, 0, 204, 255, 51,
+ 204, 255, 102, 204, 255, 153, 204, 255, 204, 204, 255, 255,
+ 255, 0, 0, 255, 0, 51, 255, 0, 102, 255, 0, 153,
+ 255, 0, 204, 255, 0, 255, 255, 51, 0, 255, 51, 51,
+ 255, 51, 102, 255, 51, 153, 255, 51, 204, 255, 51, 255,
+ 255, 102, 0, 255, 102, 51, 255, 102, 102, 255, 102, 153,
+ 255, 102, 204, 255, 102, 255, 255, 153, 0, 255, 153, 51,
+ 255, 153, 102, 255, 153, 153, 255, 153, 204, 255, 153, 255,
+ 255, 204, 0, 255, 204, 51, 255, 204, 102, 255, 204, 153,
+ 255, 204, 204, 255, 204, 255, 255, 255, 0, 255, 255, 51,
+ 255, 255, 102, 255, 255, 153, 255, 255, 204, 255, 255, 255 };
+
+Score::Score(DirectorEngine *vm) {
+ _vm = vm;
+ _surface = new Graphics::ManagedSurface;
+ _trailSurface = new Graphics::ManagedSurface;
+ _movieArchive = _vm->getMainArchive();
+ _lingo = _vm->getLingo();
+ _soundManager = _vm->getSoundManager();
+ _lingo->processEvent(kEventPrepareMovie, 0);
+ _movieScriptCount = 0;
+ _labels = NULL;
+
+ if (_movieArchive->hasResource(MKTAG('M','C','N','M'), 0)) {
+ _macName = _movieArchive->getName(MKTAG('M','C','N','M'), 0).c_str();
+ }
+
+ if (_movieArchive->hasResource(MKTAG('V','W','L','B'), 1024)) {
+ loadLabels(*_movieArchive->getResource(MKTAG('V','W','L','B'), 1024));
+ }
+}
+
+void Score::loadArchive() {
+ Common::Array<uint16> clutList = _movieArchive->getResourceIDList(MKTAG('C','L','U','T'));
+
+ if (clutList.size() > 1)
+ warning("More than one palette was found (%d)", clutList.size());
+
+ if (clutList.size() == 0) {
+ warning("CLUT resource not found, using default Mac palette");
+ g_system->getPaletteManager()->setPalette(defaultPalette, 0, 256);
+ _vm->setPalette(defaultPalette, 256);
+ } else {
+ Common::SeekableSubReadStreamEndian *pal = _movieArchive->getResource(MKTAG('C', 'L', 'U', 'T'), clutList[0]);
+
+ loadPalette(*pal);
+ g_system->getPaletteManager()->setPalette(_vm->getPalette(), 0, _vm->getPaletteColorCount());
+ }
+
+ assert(_movieArchive->hasResource(MKTAG('V','W','S','C'), 1024));
+ assert(_movieArchive->hasResource(MKTAG('V','W','C','F'), 1024));
+
+ loadFrames(*_movieArchive->getResource(MKTAG('V','W','S','C'), 1024));
+ loadConfig(*_movieArchive->getResource(MKTAG('V','W','C','F'), 1024));
+
+ if (_vm->getVersion() < 4) {
+ assert(_movieArchive->hasResource(MKTAG('V','W','C','R'), 1024));
+ loadCastData(*_movieArchive->getResource(MKTAG('V','W','C','R'), 1024));
+ }
+
+ if (_movieArchive->hasResource(MKTAG('V','W','A','C'), 1024)) {
+ loadActions(*_movieArchive->getResource(MKTAG('V','W','A','C'), 1024));
+ }
+
+ if (_movieArchive->hasResource(MKTAG('V','W','F','I'), 1024)) {
+ loadFileInfo(*_movieArchive->getResource(MKTAG('V','W','F','I'), 1024));
+ }
+
+ if (_movieArchive->hasResource(MKTAG('V','W','F','M'), 1024)) {
+ loadFontMap(*_movieArchive->getResource(MKTAG('V','W','F','M'), 1024));
+ }
+
+ Common::Array<uint16> vwci = _movieArchive->getResourceIDList(MKTAG('V','W','C','I'));
+
+ if (vwci.size() > 0) {
+ Common::Array<uint16>::iterator iterator;
+
+ for (iterator = vwci.begin(); iterator != vwci.end(); ++iterator)
+ loadCastInfo(*_movieArchive->getResource(MKTAG('V','W','C','I'), *iterator), *iterator);
+ }
+
+ Common::Array<uint16> stxt = _movieArchive->getResourceIDList(MKTAG('S','T','X','T'));
+
+ if (stxt.size() > 0) {
+ Common::Array<uint16>::iterator iterator;
+
+ for (iterator = stxt.begin(); iterator != stxt.end(); ++iterator) {
+ loadScriptText(*_movieArchive->getResource(MKTAG('S','T','X','T'), *iterator));
+ }
+ }
+}
+
+Score::~Score() {
+ if (_surface)
+ _surface->free();
+
+ if (_trailSurface)
+ _trailSurface->free();
+
+ delete _surface;
+ delete _trailSurface;
+
+ if (_movieArchive)
+ _movieArchive->close();
+
+ delete _surface;
+ delete _trailSurface;
+
+ delete _font;
+ delete _movieArchive;
+
+ delete _labels;
+}
+
+void Score::loadPalette(Common::SeekableSubReadStreamEndian &stream) {
+ uint16 steps = stream.size() / 6;
+ uint16 index = (steps * 3) - 1;
+ uint16 _paletteColorCount = steps;
+ byte *_palette = new byte[index];
+
+ for (uint8 i = 0; i < steps; i++) {
+ _palette[index - 2] = stream.readByte();
+ stream.readByte();
+
+ _palette[index - 1] = stream.readByte();
+ stream.readByte();
+
+ _palette[index] = stream.readByte();
+ stream.readByte();
+ index -= 3;
+ }
+ _vm->setPalette(_palette, _paletteColorCount);
+}
+
+void Score::loadFrames(Common::SeekableSubReadStreamEndian &stream) {
+ uint32 size = stream.readUint32();
+ size -= 4;
+
+ if (_vm->getVersion() > 3) {
+ stream.skip(16);
+ //Unknown, some bytes - constant (refer to contuinity).
+ }
+
+ uint16 channelSize;
+ uint16 channelOffset;
+
+ Frame *initial = new Frame(_vm);
+ _frames.push_back(initial);
+
+ while (size != 0) {
+ uint16 frameSize = stream.readUint16();
+ size -= frameSize;
+ frameSize -= 2;
+ Frame *frame = new Frame(*_frames.back());
+
+ while (frameSize != 0) {
+ if (_vm->getVersion() < 4) {
+ channelSize = stream.readByte() * 2;
+ channelOffset = stream.readByte() * 2;
+ frameSize -= channelSize + 2;
+ } else {
+ channelSize = stream.readByte();
+ channelOffset = stream.readByte();
+ frameSize -= channelSize + 4;
+ }
+ frame->readChannel(stream, channelOffset, channelSize);
+
+ }
+
+ _frames.push_back(frame);
+ }
+
+ //remove initial frame
+ _frames.remove_at(0);
+}
+
+void Score::loadConfig(Common::SeekableSubReadStreamEndian &stream) {
+ /*uint16 unk1 = */ stream.readUint16();
+ /*ver1 = */ stream.readUint16();
+ _movieRect = Score::readRect(stream);
+
+ _castArrayStart = stream.readUint16();
+ _castArrayEnd = stream.readUint16();
+ _currentFrameRate = stream.readByte();
+ stream.skip(9);
+ _stageColor = stream.readUint16();
+}
+
+void Score::readVersion(uint32 rid) {
+ _versionMinor = rid & 0xffff;
+ _versionMajor = rid >> 16;
+
+ debug("Version: %d.%d", _versionMajor, _versionMinor);
+}
+
+void Score::loadCastData(Common::SeekableSubReadStreamEndian &stream) {
+ for (uint16 id = _castArrayStart; id <= _castArrayEnd; id++) {
+ byte size = stream.readByte();
+ if (size == 0)
+ continue;
+
+ uint8 castType = stream.readByte();
+
+ switch (castType) {
+ case kCastBitmap:
+ _casts[id] = new BitmapCast(stream);
+ _casts[id]->type = kCastBitmap;
+ break;
+ case kCastText:
+ _casts[id] = new TextCast(stream);
+ _casts[id]->type = kCastText;
+ break;
+ case kCastShape:
+ _casts[id] = new ShapeCast(stream);
+ _casts[id]->type = kCastShape;
+ break;
+ case kCastButton:
+ _casts[id] = new ButtonCast(stream);
+ _casts[id]->type = kCastButton;
+ break;
+ default:
+ warning("Unhandled cast type: %d", castType);
+ stream.skip(size - 1);
+ break;
+ }
+ }
+
+ //Set cast pointers to sprites
+ for (uint16 i = 0; i < _frames.size(); i++) {
+ for (uint16 j = 0; j < _frames[i]->_sprites.size(); j++) {
+ byte castId = _frames[i]->_sprites[j]->_castId;
+
+ if (_casts.contains(castId))
+ _frames[i]->_sprites[j]->_cast = _casts.find(castId)->_value;
+ }
+ }
+}
+
+void Score::loadLabels(Common::SeekableSubReadStreamEndian &stream) {
+ _labels = new Common::SortedArray<Label *>(compareLabels);
+ uint16 count = stream.readUint16() + 1;
+ uint16 offset = count * 4 + 2;
+
+ uint16 frame = stream.readUint16();
+ uint16 stringPos = stream.readUint16() + offset;
+
+ for (uint16 i = 0; i < count; i++) {
+ uint16 nextFrame = stream.readUint16();
+ uint16 nextStringPos = stream.readUint16() + offset;
+ uint16 streamPos = stream.pos();
+
+ stream.seek(stringPos);
+ Common::String label;
+ for (uint16 j = stringPos; j < nextStringPos; j++) {
+ label += stream.readByte();
+ }
+ _labels->insert(new Label(label, frame));
+ stream.seek(streamPos);
+
+ frame = nextFrame;
+ stringPos = nextStringPos;
+ }
+
+ Common::SortedArray<Label *>::iterator j;
+
+ for (j = _labels->begin(); j != _labels->end(); ++j) {
+ debug("Frame %d, Label %s", (*j)->number, (*j)->name.c_str());
+ }
+}
+
+int Score::compareLabels(const void *a, const void *b) {
+ return ((Label *)a)->number - ((Label *)b)->number;
+}
+
+void Score::loadActions(Common::SeekableSubReadStreamEndian &stream) {
+ uint16 count = stream.readUint16() + 1;
+ uint16 offset = count * 4 + 2;
+
+ byte id = stream.readByte();
+ /*byte subId = */ stream.readByte(); //I couldn't find how it used in continuity (except print). Frame actionId = 1 byte.
+ uint16 stringPos = stream.readUint16() + offset;
+
+ for (uint16 i = 0; i < count; i++) {
+ uint16 nextId = stream.readByte();
+ /*byte subId = */ stream.readByte();
+ uint16 nextStringPos = stream.readUint16() + offset;
+ uint16 streamPos = stream.pos();
+
+ stream.seek(stringPos);
+
+ for (uint16 j = stringPos; j < nextStringPos; j++) {
+ byte ch = stream.readByte();
+ if (ch == 0x0d) {
+ ch = '\n';
+ }
+ _actions[id] += ch;
+ }
+
+ stream.seek(streamPos);
+
+ id = nextId;
+ stringPos = nextStringPos;
+
+ if (stringPos == stream.size())
+ break;
+ }
+
+ Common::HashMap<uint16, Common::String>::iterator j;
+
+ if (ConfMan.getBool("dump_scripts"))
+ for (j = _actions.begin(); j != _actions.end(); ++j) {
+ if (!j->_value.empty())
+ dumpScript(j->_value.c_str(), kFrameScript, j->_key);
+ }
+
+ for (j = _actions.begin(); j != _actions.end(); ++j)
+ if (!j->_value.empty())
+ _lingo->addCode(j->_value.c_str(), kFrameScript, j->_key);
+}
+
+void Score::loadScriptText(Common::SeekableSubReadStreamEndian &stream) {
+ /*uint32 unk1 = */ stream.readUint32();
+ uint32 strLen = stream.readUint32();
+ /*uin32 dataLen = */ stream.readUint32();
+ Common::String script;
+
+ for (uint32 i = 0; i < strLen; i++) {
+ byte ch = stream.readByte();
+
+ if (ch == 0x0d) {
+ //in old Mac systems \r was the code for end-of-line instead.
+ ch = '\n';
+ }
+ script += ch;
+ }
+
+ if (!script.empty() && ConfMan.getBool("dump_scripts"))
+ dumpScript(script.c_str(), kMovieScript, _movieScriptCount);
+
+ if (!script.empty())
+ _lingo->addCode(script.c_str(), kMovieScript, _movieScriptCount);
+
+ _movieScriptCount++;
+}
+
+void Score::setStartToLabel(Common::String label) {
+ Common::SortedArray<Label *>::iterator i;
+
+ for (i = _labels->begin(); i != _labels->end(); ++i) {
+ if ((*i)->name == label) {
+ _currentFrame = (*i)->number;
+ return;
+ }
+ }
+ warning("Label %s not found", label.c_str());
+}
+
+void Score::dumpScript(const char *script, ScriptType type, uint16 id) {
+ Common::DumpFile out;
+ Common::String typeName;
+ char buf[256];
+
+ switch (type) {
+ case kFrameScript:
+ typeName = "frame";
+ break;
+ case kMovieScript:
+ typeName = "movie";
+ break;
+ case kSpriteScript:
+ typeName = "sprite";
+ break;
+ }
+
+ sprintf(buf, "./dumps/%s-%s-%d.txt", _macName.c_str(), typeName.c_str(), id);
+
+ if (!out.open(buf)) {
+ warning("Can not open dump file %s", buf);
+ return;
+ }
+
+ out.write(script, strlen(script));
+
+ out.flush();
+ out.close();
+}
+
+void Score::loadCastInfo(Common::SeekableSubReadStreamEndian &stream, uint16 id) {
+ uint32 entryType = 0;
+ Common::Array<Common::String> castStrings = loadStrings(stream, entryType);
+ CastInfo *ci = new CastInfo();
+
+ ci->script = castStrings[0];
+
+ if (!ci->script.empty() && ConfMan.getBool("dump_scripts"))
+ dumpScript(ci->script.c_str(), kSpriteScript, id);
+
+ if (!ci->script.empty())
+ _lingo->addCode(ci->script.c_str(), kSpriteScript, id);
+
+ ci->name = getString(castStrings[1]);
+ ci->directory = getString(castStrings[2]);
+ ci->fileName = getString(castStrings[3]);
+ ci->type = castStrings[4];
+
+ _castsInfo[id] = ci;
+}
+
+void Score::gotoloop() {
+ //This command has the playback head contonuously return to the first marker to to the left and then loop back.
+ //If no marker are to the left of the playback head, the playback head continues to the right.
+ Common::SortedArray<Label *>::iterator i;
+
+ for (i = _labels->begin(); i != _labels->end(); ++i) {
+ if ((*i)->name == _currentLabel) {
+ _currentFrame = (*i)->number;
+ return;
+ }
+ }
+}
+
+void Score::gotonext() {
+ Common::SortedArray<Label *>::iterator i;
+
+ for (i = _labels->begin(); i != _labels->end(); ++i) {
+ if ((*i)->name == _currentLabel) {
+ if (i != _labels->end()) {
+ //return to the first marker to to the right
+ ++i;
+ _currentFrame = (*i)->number;
+ return;
+ } else {
+ //if no markers are to the right of the playback head,
+ //the playback head goes to the first marker to the left
+ _currentFrame = (*i)->number;
+ return;
+ }
+ }
+ }
+ //If there are not markers to the left,
+ //the playback head goes to frame 1, (Director frame array start from 1, engine from 0)
+ _currentFrame = 0;
+}
+
+void Score::gotoprevious() {
+ //One label
+ if (_labels->begin() == _labels->end()) {
+ _currentFrame = (*_labels->begin())->number;
+ return;
+ }
+
+ Common::SortedArray<Label *>::iterator previous = _labels->begin();
+ Common::SortedArray<Label *>::iterator i = previous++;
+
+ for (i = _labels->begin(); i != _labels->end(); ++i, ++previous) {
+ if ((*i)->name == _currentLabel) {
+ _currentFrame = (*previous)->number;
+ return;
+ } else {
+ _currentFrame = (*i)->number;
+ return;
+ }
+ }
+ _currentFrame = 0;
+}
+
+Common::String Score::getString(Common::String str) {
+ if (str.size() == 0) {
+ return str;
+ }
+
+ uint8 f = static_cast<uint8>(str.firstChar());
+
+ if (f == 0) {
+ return "";
+ }
+
+ str.deleteChar(0);
+
+ if (str.lastChar() == '\x00') {
+ str.deleteLastChar();
+ }
+
+ return str;
+}
+
+void Score::loadFileInfo(Common::SeekableSubReadStreamEndian &stream) {
+ Common::Array<Common::String> fileInfoStrings = loadStrings(stream, _flags);
+ _script = fileInfoStrings[0];
+
+ if (!_script.empty() && ConfMan.getBool("dump_scripts"))
+ dumpScript(_script.c_str(), kMovieScript, _movieScriptCount);
+
+ if (!_script.empty())
+ _lingo->addCode(_script.c_str(), kMovieScript, _movieScriptCount);
+
+ _movieScriptCount++;
+ _changedBy = fileInfoStrings[1];
+ _createdBy = fileInfoStrings[2];
+ _directory = fileInfoStrings[3];
+}
+
+Common::Array<Common::String> Score::loadStrings(Common::SeekableSubReadStreamEndian &stream, uint32 &entryType, bool hasHeader) {
+ Common::Array<Common::String> strings;
+ uint32 offset = 0;
+
+ if (hasHeader) {
+ offset = stream.readUint32();
+ /*uint32 unk1 = */ stream.readUint32();
+ /*uint32 unk2 = */ stream.readUint32();
+ entryType = stream.readUint32();
+ stream.seek(offset);
+ }
+
+ uint16 count = stream.readUint16();
+ offset += (count + 1) * 4 + 2; //positions info + uint16 count
+ uint32 startPos = stream.readUint32() + offset;
+
+ for (uint16 i = 0; i < count; i++) {
+ Common::String entryString;
+ uint32 nextPos = stream.readUint32() + offset;
+ uint32 streamPos = stream.pos();
+
+ stream.seek(startPos);
+
+ while (startPos != nextPos) {
+ entryString += stream.readByte();
+ ++startPos;
+ }
+
+ strings.push_back(entryString);
+
+ stream.seek(streamPos);
+ startPos = nextPos;
+ }
+
+ return strings;
+}
+
+void Score::loadFontMap(Common::SeekableSubReadStreamEndian &stream) {
+ uint16 count = stream.readUint16();
+ uint32 offset = (count * 2) + 2;
+ uint16 currentRawPosition = offset;
+
+ for (uint16 i = 0; i < count; i++) {
+ uint16 id = stream.readUint16();
+ uint32 positionInfo = stream.pos();
+
+ stream.seek(currentRawPosition);
+
+ uint16 size = stream.readByte();
+ Common::String font;
+
+ for (uint16 k = 0; k < size; k++) {
+ font += stream.readByte();
+ }
+
+ _fontMap[id] = font;
+ debug(3, "Fontmap. ID %d Font %s", id, font.c_str());
+ currentRawPosition = stream.pos();
+ stream.seek(positionInfo);
+ }
+}
+
+BitmapCast::BitmapCast(Common::SeekableSubReadStreamEndian &stream) {
+ /*byte flags = */ stream.readByte();
+ uint16 someFlaggyThing = stream.readUint16();
+ initialRect = Score::readRect(stream);
+ boundingRect = Score::readRect(stream);
+ regY = stream.readUint16();
+ regX = stream.readUint16();
+
+ if (someFlaggyThing & 0x8000) {
+ /*uint16 unk1 =*/ stream.readUint16();
+ /*uint16 unk2 =*/ stream.readUint16();
+ }
+ modified = 0;
+}
+
+TextCast::TextCast(Common::SeekableSubReadStreamEndian &stream) {
+ /*byte flags =*/ stream.readByte();
+ borderSize = static_cast<SizeType>(stream.readByte());
+ gutterSize = static_cast<SizeType>(stream.readByte());
+ boxShadow = static_cast<SizeType>(stream.readByte());
+ textType = static_cast<TextType>(stream.readByte());
+ textAlign = static_cast<TextAlignType>(stream.readUint16());
+ stream.skip(6); //palinfo
+ //for now, just supposition
+ fontId = stream.readUint32();
+
+ initialRect = Score::readRect(stream);
+ textShadow = static_cast<SizeType>(stream.readByte());
+ byte flags = stream.readByte();
+ if (flags & 0x1)
+ textFlags.push_back(kTextFlagEditable);
+ if (flags & 0x2)
+ textFlags.push_back(kTextFlagAutoTab);
+ if (flags & 0x4)
+ textFlags.push_back(kTextFlagDoNotWrap);
+ //again supposition
+ fontSize = stream.readUint16();
+ modified = 0;
+}
+
+ShapeCast::ShapeCast(Common::SeekableSubReadStreamEndian &stream) {
+ /*byte flags = */ stream.readByte();
+ /*unk1 = */ stream.readByte();
+ shapeType = static_cast<ShapeType>(stream.readByte());
+ initialRect = Score::readRect(stream);
+ pattern = stream.readUint16BE();
+ fgCol = stream.readByte();
+ bgCol = stream.readByte();
+ fillType = stream.readByte();
+ lineThickness = stream.readByte();
+ lineDirection = stream.readByte();
+ modified = 0;
+}
+
+Common::Rect Score::readRect(Common::SeekableSubReadStreamEndian &stream) {
+ Common::Rect *rect = new Common::Rect();
+ rect->top = stream.readUint16();
+ rect->left = stream.readUint16();
+ rect->bottom = stream.readUint16();
+ rect->right = stream.readUint16();
+
+ return *rect;
+}
+
+void Score::startLoop() {
+ initGraphics(_movieRect.width(), _movieRect.height(), true);
+
+ _surface->create(_movieRect.width(), _movieRect.height());
+ _trailSurface->create(_movieRect.width(), _movieRect.height());
+
+ if (_stageColor == 0)
+ _trailSurface->clear(_vm->getPaletteColorCount() - 1);
+ else
+ _trailSurface->clear(_stageColor);
+
+ _currentFrame = 0;
+ _stopPlay = false;
+ _nextFrameTime = 0;
+
+ _lingo->processEvent(kEventStartMovie, 0);
+ _frames[_currentFrame]->prepareFrame(this);
+
+ while (!_stopPlay && _currentFrame < _frames.size() - 2) {
+ update();
+ processEvents();
+
+ g_system->updateScreen();
+ g_system->delayMillis(10);
+ }
+}
+
+void Score::update() {
+ if (g_system->getMillis() < _nextFrameTime)
+ return;
+
+ _surface->clear();
+ _surface->copyFrom(*_trailSurface);
+
+ //Enter and exit from previous frame (Director 4)
+ _lingo->processEvent(kEventEnterFrame, _currentFrame);
+ _lingo->processEvent(kEventExitFrame, _currentFrame);
+ //TODO Director 6 - another order
+
+
+ //TODO Director 6 step: send beginSprite event to any sprites whose span begin in the upcoming frame
+ //for (uint16 i = 0; i < CHANNEL_COUNT; i++) {
+ // if (_frames[_currentFrame]->_sprites[i]->_enabled)
+ // _lingo->processEvent(kEventBeginSprite, i);
+ //}
+
+ //TODO Director 6 step: send prepareFrame event to all sprites and the script channel in upcoming frame
+ //_lingo->processEvent(kEventPrepareFrame, _currentFrame);
+ _currentFrame++;
+
+ Common::SortedArray<Label *>::iterator i;
+ for (i = _labels->begin(); i != _labels->end(); ++i) {
+ if ((*i)->number == _currentFrame) {
+ _currentLabel = (*i)->name;
+ }
+ }
+
+ _frames[_currentFrame]->prepareFrame(this);
+ //Stage is drawn between the prepareFrame and enterFrame events (Lingo in a Nutshell)
+
+ byte tempo = _frames[_currentFrame]->_tempo;
+
+ if (tempo) {
+ if (tempo > 161) {
+ //Delay
+ _nextFrameTime = g_system->getMillis() + (256 - tempo) * 1000;
+
+ return;
+ } else if (tempo <= 60) {
+ //FPS
+ _nextFrameTime = g_system->getMillis() + (float)tempo / 60 * 1000;
+ _currentFrameRate = tempo;
+ } else if (tempo >= 136) {
+ //TODO Wait for channel tempo - 135
+ } else if (tempo == 128) {
+ //TODO Wait for Click/Key
+ } else if (tempo == 135) {
+ //Wait for sound channel 1
+ while (_soundManager->isChannelActive(1)) {
+ processEvents();
+ g_system->delayMillis(10);
+ }
+ } else if (tempo == 134) {
+ //Wait for sound channel 2
+ while (_soundManager->isChannelActive(2)) {
+ processEvents();
+ g_system->delayMillis(10);
+ }
+ }
+ }
+ _nextFrameTime = g_system->getMillis() + (float)_currentFrameRate / 60 * 1000;
+}
+
+void Score::processEvents() {
+ if (_currentFrame > 0)
+ _lingo->processEvent(kEventIdle, _currentFrame - 1);
+
+ Common::Event event;
+
+ while (g_system->getEventManager()->pollEvent(event)) {
+ if (event.type == Common::EVENT_QUIT)
+ _stopPlay = true;
+
+ if (event.type == Common::EVENT_LBUTTONDOWN) {
+ Common::Point pos = g_system->getEventManager()->getMousePos();
+
+ //TODO there is dont send frame id
+ _lingo->processEvent(kEventMouseDown, _frames[_currentFrame]->getSpriteIDFromPos(pos));
+ }
+
+ if (event.type == Common::EVENT_LBUTTONUP) {
+ Common::Point pos = g_system->getEventManager()->getMousePos();
+
+ _lingo->processEvent(kEventMouseUp, _frames[_currentFrame]->getSpriteIDFromPos(pos));
+ }
+ }
+}
+
+Sprite *Score::getSpriteById(uint16 id) {
+ if (_frames[_currentFrame]->_sprites[id]) {
+ return _frames[_currentFrame]->_sprites[id];
+ } else {
+ warning("Sprite on frame %d width id %d not found", _currentFrame, id);
+ return nullptr;
+ }
+}
+
+Frame::Frame(DirectorEngine *vm) {
+ _vm = vm;
+ _transDuration = 0;
+ _transType = kTransNone;
+ _transArea = 0;
+ _transChunkSize = 0;
+ _tempo = 0;
+
+ _sound1 = 0;
+ _sound2 = 0;
+ _soundType1 = 0;
+ _soundType2 = 0;
+
+ _actionId = 0;
+ _skipFrameFlag = 0;
+ _blend = 0;
+
+ _sprites.resize(CHANNEL_COUNT);
+
+ for (uint16 i = 0; i < _sprites.size(); i++) {
+ Sprite *sp = new Sprite();
+ _sprites[i] = sp;
+ }
+}
+
+Frame::Frame(const Frame &frame) {
+ _vm = frame._vm;
+ _actionId = frame._actionId;
+ _transArea = frame._transArea;
+ _transDuration = frame._transDuration;
+ _transType = frame._transType;
+ _transChunkSize = frame._transChunkSize;
+ _tempo = frame._tempo;
+ _sound1 = frame._sound1;
+ _sound2 = frame._sound2;
+ _soundType1 = frame._soundType1;
+ _soundType2 = frame._soundType2;
+ _skipFrameFlag = frame._skipFrameFlag;
+ _blend = frame._blend;
+ _palette = new PaletteInfo();
+
+ _sprites.resize(CHANNEL_COUNT);
+
+ for (uint16 i = 0; i < CHANNEL_COUNT; i++) {
+ _sprites[i] = new Sprite(*frame._sprites[i]);
+ }
+}
+
+Frame::~Frame() {
+ delete[] &_sprites;
+ delete[] &_drawRects;
+ delete _palette;
+}
+
+void Frame::readChannel(Common::SeekableSubReadStreamEndian &stream, uint16 offset, uint16 size) {
+ if (offset >= 32) {
+ if (size <= 16)
+ readSprite(stream, offset, size);
+ else {
+ //read > 1 sprites channel
+ while (size > 16) {
+ byte spritePosition = (offset - 32) / 16;
+ uint16 nextStart = (spritePosition + 1) * 16 + 32;
+ uint16 needSize = nextStart - offset;
+ readSprite(stream, offset, needSize);
+ offset += needSize;
+ size -= needSize;
+ }
+ readSprite(stream, offset, size);
+ }
+ } else {
+ readMainChannels(stream, offset, size);
+ }
+}
+
+void Frame::readMainChannels(Common::SeekableSubReadStreamEndian &stream, uint16 offset, uint16 size) {
+ uint16 finishPosition = offset + size;
+
+ while (offset < finishPosition) {
+ switch(offset) {
+ case kScriptIdPosition:
+ _actionId = stream.readByte();
+ offset++;
+ break;
+ case kSoundType1Position:
+ _soundType1 = stream.readByte();
+ offset++;
+ break;
+ case kTransFlagsPosition: {
+ uint8 transFlags = stream.readByte();
+ if (transFlags & 0x80)
+ _transArea = 1;
+ else
+ _transArea = 0;
+ _transDuration = transFlags & 0x7f;
+ offset++;
+ }
+ break;
+ case kTransChunkSizePosition:
+ _transChunkSize = stream.readByte();
+ offset++;
+ break;
+ case kTempoPosition:
+ _tempo = stream.readByte();
+ offset++;
+ break;
+ case kTransTypePosition:
+ _transType = static_cast<TransitionType>(stream.readByte());
+ offset++;
+ break;
+ case kSound1Position:
+ _sound1 = stream.readUint16();
+ offset+=2;
+ break;
+ case kSkipFrameFlagsPosition:
+ _skipFrameFlag = stream.readByte();
+ offset++;
+ break;
+ case kBlendPosition:
+ _blend = stream.readByte();
+ offset++;
+ break;
+ case kSound2Position:
+ _sound2 = stream.readUint16();
+ offset += 2;
+ break;
+ case kSound2TypePosition:
+ _soundType2 = stream.readByte();
+ offset += 1;
+ break;
+ case kPaletePosition:
+ if (stream.readUint16())
+ readPaletteInfo(stream);
+ offset += 16;
+ default:
+ offset++;
+ stream.readByte();
+ debug("Field Position %d, Finish Position %d", offset, finishPosition);
+ break;
+ }
+ }
+}
+
+void Frame::readPaletteInfo(Common::SeekableSubReadStreamEndian &stream) {
+ _palette->firstColor = stream.readByte();
+ _palette->lastColor = stream.readByte();
+ _palette->flags = stream.readByte();
+ _palette->speed = stream.readByte();
+ _palette->frameCount = stream.readUint16();
+ stream.skip(8); //unknown
+}
+
+void Frame::readSprite(Common::SeekableSubReadStreamEndian &stream, uint16 offset, uint16 size) {
+ uint16 spritePosition = (offset - 32) / 16;
+ uint16 spriteStart = spritePosition * 16 + 32;
+
+ uint16 fieldPosition = offset - spriteStart;
+ uint16 finishPosition = fieldPosition + size;
+
+ Sprite &sprite = *_sprites[spritePosition];
+
+ while (fieldPosition < finishPosition) {
+ switch (fieldPosition) {
+ case kSpritePositionUnk1:
+ /*byte x1 = */ stream.readByte();
+ fieldPosition++;
+ break;
+ case kSpritePositionEnabled:
+ sprite._enabled = (stream.readByte() != 0);
+ fieldPosition++;
+ break;
+ case kSpritePositionUnk2:
+ /*byte x2 = */ stream.readUint16();
+ fieldPosition += 2;
+ break;
+ case kSpritePositionFlags:
+ sprite._flags = stream.readUint16();
+ sprite._ink = static_cast<InkType>(sprite._flags & 0x3f);
+
+ if (sprite._flags & 0x40)
+ sprite._trails = 1;
+ else
+ sprite._trails = 0;
+
+ fieldPosition += 2;
+ break;
+ case kSpritePositionCastId:
+ sprite._castId = stream.readUint16();
+ fieldPosition += 2;
+ break;
+ case kSpritePositionY:
+ sprite._startPoint.y = stream.readUint16();
+ fieldPosition += 2;
+ break;
+ case kSpritePositionX:
+ sprite._startPoint.x = stream.readUint16();
+ fieldPosition += 2;
+ break;
+ case kSpritePositionWidth:
+ sprite._width = stream.readUint16();
+ fieldPosition += 2;
+ break;
+ case kSpritePositionHeight:
+ sprite._height = stream.readUint16();
+ fieldPosition += 2;
+ break;
+ default:
+ //end cycle, go to next sprite channel
+ readSprite(stream, spriteStart + 16, finishPosition - fieldPosition);
+ fieldPosition = finishPosition;
+ break;
+ }
+ }
+}
+
+void Frame::prepareFrame(Score *score) {
+ renderSprites(*score->_surface, false);
+ renderSprites(*score->_trailSurface, true);
+
+ if (_transType != 0)
+ //TODO Handle changing area case
+ playTransition(score);
+
+ if (_sound1 != 0 || _sound2 != 0) {
+ playSoundChannel();
+ }
+
+ g_system->copyRectToScreen(score->_surface->getPixels(), score->_surface->pitch, 0, 0, score->_surface->getBounds().width(), score->_surface->getBounds().height());
+}
+
+void Frame::playSoundChannel() {
+ debug(0, "Sound2 %d", _sound2);
+ debug(0, "Sound1 %d", _sound1);
+}
+
+void Frame::playTransition(Score *score) {
+ uint16 duration = _transDuration * 250; // _transDuration in 1/4 of sec
+ duration = (duration == 0 ? 250 : duration); // director support transition duration = 0, but animation play like value = 1, idk.
+
+ if (_transChunkSize == 0)
+ _transChunkSize = 1; //equal 1 step
+
+ uint16 stepDuration = duration / _transChunkSize;
+ uint16 steps = duration / stepDuration;
+
+ switch (_transType) {
+ case kTransCoverDown:
+ {
+ uint16 stepSize = score->_movieRect.height() / steps;
+ Common::Rect r = score->_movieRect;
+
+ for (uint16 i = 1; i < steps; i++) {
+ r.setHeight(stepSize * i);
+
+ g_system->delayMillis(stepDuration);
+ score->processEvents();
+
+ g_system->copyRectToScreen(score->_surface->getPixels(), score->_surface->pitch, 0, 0, r.width(), r.height());
+ g_system->updateScreen();
+ }
+ }
+ break;
+ case kTransCoverUp:
+ {
+ uint16 stepSize = score->_movieRect.height() / steps;
+ Common::Rect r = score->_movieRect;
+
+ for (uint16 i = 1; i < steps; i++) {
+ r.setHeight(stepSize * i);
+
+ g_system->delayMillis(stepDuration);
+ score->processEvents();
+
+ g_system->copyRectToScreen(score->_surface->getPixels(), score->_surface->pitch, 0, score->_movieRect.height() - stepSize * i, r.width(), r.height());
+ g_system->updateScreen();
+ }
+ }
+ break;
+ case kTransCoverRight: {
+ uint16 stepSize = score->_movieRect.width() / steps;
+ Common::Rect r = score->_movieRect;
+
+ for (uint16 i = 1; i < steps; i++) {
+ r.setWidth(stepSize * i);
+
+ g_system->delayMillis(stepDuration);
+ score->processEvents();
+
+ g_system->copyRectToScreen(score->_surface->getPixels(), score->_surface->pitch, 0, 0, r.width(), r.height());
+ g_system->updateScreen();
+ }
+ }
+ break;
+ case kTransCoverLeft: {
+ uint16 stepSize = score->_movieRect.width() / steps;
+ Common::Rect r = score->_movieRect;
+
+ for (uint16 i = 1; i < steps; i++) {
+ r.setWidth(stepSize * i);
+
+ g_system->delayMillis(stepDuration);
+ score->processEvents();
+
+ g_system->copyRectToScreen(score->_surface->getPixels(), score->_surface->pitch, score->_movieRect.width() - stepSize * i, 0, r.width(), r.height());
+ g_system->updateScreen();
+ }
+ }
+ break;
+ case kTransCoverUpLeft: {
+ uint16 stepSize = score->_movieRect.width() / steps;
+ Common::Rect r = score->_movieRect;
+
+ for (uint16 i = 1; i < steps; i++) {
+ r.setWidth(stepSize * i);
+ r.setHeight(stepSize * i);
+
+ g_system->delayMillis(stepDuration);
+ score->processEvents();
+
+ g_system->copyRectToScreen(score->_surface->getPixels(), score->_surface->pitch, score->_movieRect.width() - stepSize * i, score->_movieRect.height() - stepSize * i, r.width(), r.height());
+ g_system->updateScreen();
+ }
+ }
+ break;
+ case kTransCoverUpRight: {
+ uint16 stepSize = score->_movieRect.width() / steps;
+ Common::Rect r = score->_movieRect;
+
+ for (uint16 i = 1; i < steps; i++) {
+ r.setWidth(stepSize * i);
+ r.setHeight(stepSize * i);
+
+ g_system->delayMillis(stepDuration);
+ score->processEvents();
+
+ g_system->copyRectToScreen(score->_surface->getPixels(), score->_surface->pitch, 0, score->_movieRect.height() - stepSize * i, r.width(), r.height());
+ g_system->updateScreen();
+ }
+ }
+ break;
+ case kTransCoverDownLeft: {
+ uint16 stepSize = score->_movieRect.width() / steps;
+ Common::Rect r = score->_movieRect;
+
+ for (uint16 i = 1; i < steps; i++) {
+ r.setWidth(stepSize * i);
+ r.setHeight(stepSize * i);
+
+ g_system->delayMillis(stepDuration);
+ score->processEvents();
+
+ g_system->copyRectToScreen(score->_surface->getPixels(), score->_surface->pitch, score->_movieRect.width() - stepSize * i, 0, r.width(), r.height());
+ g_system->updateScreen();
+ }
+ }
+ break;
+ case kTransCoverDownRight: {
+ uint16 stepSize = score->_movieRect.width() / steps;
+ Common::Rect r = score->_movieRect;
+
+ for (uint16 i = 1; i < steps; i++) {
+ r.setWidth(stepSize * i);
+ r.setHeight(stepSize * i);
+
+ g_system->delayMillis(stepDuration);
+ score->processEvents();
+
+ g_system->copyRectToScreen(score->_surface->getPixels(), score->_surface->pitch, 0, 0, r.width(), r.height());
+ g_system->updateScreen();
+ }
+ }
+ break;
+ default:
+ warning("Unhandled transition type %d %d %d", _transType, duration, _transChunkSize);
+ break;
+
+ }
+}
+
+void Frame::renderSprites(Graphics::ManagedSurface &surface, bool renderTrail) {
+ for (uint16 i = 0; i < CHANNEL_COUNT; i++) {
+ if (_sprites[i]->_enabled) {
+ if ((_sprites[i]->_trails == 0 && renderTrail) || (_sprites[i]->_trails == 1 && !renderTrail))
+ continue;
+
+ Cast *cast;
+ if (!_vm->_currentScore->_casts.contains(_sprites[i]->_castId)) {
+ if (!_vm->getSharedCasts()->contains(_sprites[i]->_castId)) {
+ warning("Cast id %d not found", _sprites[i]->_castId);
+ continue;
+ } else {
+ cast = _vm->getSharedCasts()->getVal(_sprites[i]->_castId);
+ }
+ } else {
+ cast = _vm->_currentScore->_casts[_sprites[i]->_castId];
+ }
+
+ if (cast->type == kCastText) {
+ renderText(surface, i);
+ continue;
+ }
+
+ Image::ImageDecoder *img = getImageFrom(_sprites[i]->_castId);
+
+ if (!img) {
+ warning("Image with id %d not found", _sprites[i]->_castId);
+ continue;
+ }
+
+ uint32 regX = static_cast<BitmapCast *>(_sprites[i]->_cast)->regX;
+ uint32 regY = static_cast<BitmapCast *>(_sprites[i]->_cast)->regY;
+ uint32 rectLeft = static_cast<BitmapCast *>(_sprites[i]->_cast)->initialRect.left;
+ uint32 rectTop = static_cast<BitmapCast *>(_sprites[i]->_cast)->initialRect.top;
+
+ int x = _sprites[i]->_startPoint.x - regX + rectLeft;
+ int y = _sprites[i]->_startPoint.y - regY + rectTop;
+ int height = _sprites[i]->_height;
+ int width = _sprites[i]->_width;
+
+ Common::Rect drawRect = Common::Rect(x, y, x + width, y + height);
+ _drawRects.push_back(drawRect);
+
+ switch (_sprites[i]->_ink) {
+ case kInkTypeCopy:
+ surface.blitFrom(*img->getSurface(), Common::Point(x, y));
+ break;
+ case kInkTypeBackgndTrans:
+ drawBackgndTransSprite(surface, *img->getSurface(), drawRect);
+ break;
+ case kInkTypeMatte:
+ drawMatteSprite(surface, *img->getSurface(), drawRect);
+ break;
+ case kInkTypeGhost:
+ drawGhostSprite(surface, *img->getSurface(), drawRect);
+ break;
+ case kInkTypeReverse:
+ drawReverseSprite(surface, *img->getSurface(), drawRect);
+ break;
+ default:
+ warning("Unhandled ink type %d", _sprites[i]->_ink);
+ surface.blitFrom(*img->getSurface(), Common::Point(x, y));
+ break;
+ }
+ }
+ }
+}
+
+void Frame::renderButton(Graphics::ManagedSurface &surface, uint16 spriteId) {
+ renderText(surface, spriteId);
+
+ uint16 castID = _sprites[spriteId]->_castId;
+ ButtonCast *button = static_cast<ButtonCast *>(_vm->_currentScore->_casts[castID]);
+
+ uint32 rectLeft = button->initialRect.left;
+ uint32 rectTop = button->initialRect.top;
+
+ int x = _sprites[spriteId]->_startPoint.x + rectLeft;
+ int y = _sprites[spriteId]->_startPoint.y + rectTop;
+ int height = _sprites[spriteId]->_height;
+ int width = _sprites[spriteId]->_width;
+
+ switch (button->buttonType) {
+ case kTypeCheckBox:
+ //Magic numbers: checkbox square need to move left about 5px from text and 12px side size (d4)
+ surface.frameRect(Common::Rect(x - 17, y, x + 12, y + 12), 0);
+ break;
+ case kTypeButton:
+ surface.frameRect(Common::Rect(x, y, x + width, y + height), 0);
+ break;
+ }
+}
+
+Image::ImageDecoder *Frame::getImageFrom(uint16 spriteId) {
+ uint16 imgId = spriteId + 1024;
+ Image::ImageDecoder *img = NULL;
+
+ if (_vm->_currentScore->getArchive()->hasResource(MKTAG('D', 'I', 'B', ' '), imgId)) {
+ img = new DIBDecoder();
+ img->loadStream(*_vm->_currentScore->getArchive()->getResource(MKTAG('D', 'I', 'B', ' '), imgId));
+ return img;
+ }
+
+ if (_vm->getSharedDIB()->contains(imgId)) {
+ img = new DIBDecoder();
+ img->loadStream(*_vm->getSharedDIB()->getVal(imgId));
+ return img;
+ }
+
+ if (_vm->_currentScore->getArchive()->hasResource(MKTAG('B', 'I', 'T', 'D'), imgId)) {
+ img = new Image::BitmapDecoder();
+ img->loadStream(*_vm->_currentScore->getArchive()->getResource(MKTAG('B', 'I', 'T', 'D'), imgId));
+ return img;
+ }
+
+ if (_vm->getSharedBMP()->contains(imgId)) {
+ img = new Image::BitmapDecoder();
+ img->loadStream(*_vm->getSharedBMP()->getVal(imgId));
+ return img;
+ }
+
+ warning("Image %d not found", spriteId);
+ return img;
+}
+
+
+void Frame::renderText(Graphics::ManagedSurface &surface, uint16 spriteID) {
+ uint16 castID = _sprites[spriteID]->_castId;
+
+ TextCast *textCast = static_cast<TextCast *>(_vm->_currentScore->_casts[castID]);
+ Common::SeekableSubReadStreamEndian *textStream;
+
+ if (_vm->_currentScore->_movieArchive->hasResource(MKTAG('S','T','X','T'), castID + 1024)) {
+ textStream = _vm->_currentScore->_movieArchive->getResource(MKTAG('S','T','X','T'), castID + 1024);
+ } else {
+ textStream = _vm->getSharedSTXT()->getVal(spriteID + 1024);
+ }
+ /*uint32 unk1 = */ textStream->readUint32();
+ uint32 strLen = textStream->readUint32();
+ /*uin32 dataLen = */ textStream->readUint32();
+ Common::String text;
+
+ for (uint32 i = 0; i < strLen; i++) {
+ byte ch = textStream->readByte();
+ if (ch == 0x0d) {
+ ch = '\n';
+ }
+ text += ch;
+ }
+
+ uint32 rectLeft = static_cast<TextCast *>(_sprites[spriteID]->_cast)->initialRect.left;
+ uint32 rectTop = static_cast<TextCast *>(_sprites[spriteID]->_cast)->initialRect.top;
+
+ int x = _sprites[spriteID]->_startPoint.x + rectLeft;
+ int y = _sprites[spriteID]->_startPoint.y + rectTop;
+ int height = _sprites[spriteID]->_height;
+ int width = _sprites[spriteID]->_width;
+
+ const char *fontName;
+
+ if (_vm->_currentScore->_fontMap.contains(textCast->fontId)) {
+ fontName = _vm->_currentScore->_fontMap[textCast->fontId].c_str();
+ } else if ((fontName = _vm->_wm->getFontName(textCast->fontId, textCast->fontSize)) == NULL) {
+ warning("Unknown font id %d, falling back to default", textCast->fontId);
+ fontName = _vm->_wm->getFontName(0, 12);
+ }
+
+ const Graphics::Font *font = _vm->_wm->getFont(fontName, Graphics::FontManager::kBigGUIFont);
+
+ font->drawString(&surface, text, x, y, width, 0);
+
+ if (textCast->borderSize != kSizeNone) {
+ uint16 size = textCast->borderSize;
+
+ //Indent from borders, measured in d4
+ x -= 1;
+ y -= 4;
+
+ height += 4;
+ width += 1;
+
+ while (size) {
+ surface.frameRect(Common::Rect(x, y, x + height, y + width), 0);
+ x--;
+ y--;
+ height += 2;
+ width += 2;
+ size--;
+ }
+ }
+
+ if (textCast->gutterSize != kSizeNone) {
+ x -= 1;
+ y -= 4;
+
+ height += 4;
+ width += 1;
+ uint16 size = textCast->gutterSize;
+
+ surface.frameRect(Common::Rect(x, y, x + height, y + width), 0);
+
+ while (size) {
+ surface.drawLine(x + width, y, x + width, y + height, 0);
+ surface.drawLine(x, y + height, x + width, y + height, 0);
+ x++;
+ y++;
+ size--;
+ }
+ }
+}
+
+void Frame::drawBackgndTransSprite(Graphics::ManagedSurface &target, const Graphics::Surface &sprite, Common::Rect &drawRect) {
+ uint8 skipColor = _vm->getPaletteColorCount() - 1; //FIXME is it always white (last entry in pallette) ?
+
+ for (int ii = 0; ii < sprite.h; ii++) {
+ const byte *src = (const byte *)sprite.getBasePtr(0, ii);
+ byte *dst = (byte *)target.getBasePtr(drawRect.left, drawRect.top + ii);
+
+ for (int j = 0; j < drawRect.width(); j++) {
+ if (*src != skipColor)
+ *dst = *src;
+
+ src++;
+ dst++;
+ }
+ }
+}
+
+void Frame::drawGhostSprite(Graphics::ManagedSurface &target, const Graphics::Surface &sprite, Common::Rect &drawRect) {
+ uint8 skipColor = _vm->getPaletteColorCount() - 1;
+ for (int ii = 0; ii < sprite.h; ii++) {
+ const byte *src = (const byte *)sprite.getBasePtr(0, ii);
+ byte *dst = (byte *)target.getBasePtr(drawRect.left, drawRect.top + ii);
+
+ for (int j = 0; j < drawRect.width(); j++) {
+ if ((getSpriteIDFromPos(Common::Point(drawRect.left + j, drawRect.top + ii)) != 0) && (*src != skipColor))
+ *dst = (_vm->getPaletteColorCount() - 1) - *src; //Oposite color
+
+ src++;
+ dst++;
+ }
+ }
+}
+
+void Frame::drawReverseSprite(Graphics::ManagedSurface &target, const Graphics::Surface &sprite, Common::Rect &drawRect) {
+ uint8 skipColor = _vm->getPaletteColorCount() - 1;
+ for (int ii = 0; ii < sprite.h; ii++) {
+ const byte *src = (const byte *)sprite.getBasePtr(0, ii);
+ byte *dst = (byte *)target.getBasePtr(drawRect.left, drawRect.top + ii);
+
+ for (int j = 0; j < drawRect.width(); j++) {
+ if ((getSpriteIDFromPos(Common::Point(drawRect.left + j, drawRect.top + ii)) != 0))
+ *dst = (_vm->getPaletteColorCount() - 1) - *src;
+ else if (*src != skipColor)
+ *dst = *src;
+ src++;
+ dst++;
+ }
+ }
+}
+
+void Frame::drawMatteSprite(Graphics::ManagedSurface &target, const Graphics::Surface &sprite, Common::Rect &drawRect) {
+ //Like background trans, but all white pixels NOT ENCLOSED by coloured pixels are transparent
+ Graphics::Surface tmp;
+ tmp.copyFrom(sprite);
+
+ // Searching white color in the corners
+ int whiteColor = -1;
+
+ for (int corner = 0; corner < 4; corner++) {
+ int x = (corner & 0x1) ? tmp.w - 1 : 0;
+ int y = (corner & 0x2) ? tmp.h - 1 : 0;
+
+ byte color = *(byte *)tmp.getBasePtr(x, y);
+
+ if (_vm->getPalette()[color * 3 + 0] == 0xff &&
+ _vm->getPalette()[color * 3 + 1] == 0xff &&
+ _vm->getPalette()[color * 3 + 2] == 0xff) {
+ whiteColor = color;
+ break;
+ }
+ }
+
+ if (whiteColor == -1) {
+ warning("No white color for Matte image");
+ whiteColor = *(byte *)tmp.getBasePtr(0, 0);
+ }
+
+ Graphics::FloodFill ff(&tmp, whiteColor, 0, true);
+
+ for (int yy = 0; yy < tmp.h; yy++) {
+ ff.addSeed(0, yy);
+ ff.addSeed(tmp.w - 1, yy);
+ }
+
+ for (int xx = 0; xx < tmp.w; xx++) {
+ ff.addSeed(xx, 0);
+ ff.addSeed(xx, tmp.h - 1);
+ }
+ ff.fillMask();
+
+ for (int yy = 0; yy < tmp.h; yy++) {
+ const byte *src = (const byte *)tmp.getBasePtr(0, yy);
+ const byte *mask = (const byte *)ff.getMask()->getBasePtr(0, yy);
+ byte *dst = (byte *)target.getBasePtr(drawRect.left, drawRect.top + yy);
+
+ for (int xx = 0; xx < drawRect.width(); xx++, src++, dst++, mask++)
+ if (*mask == 0)
+ *dst = *src;
+ }
+
+ tmp.free();
+}
+
+uint16 Frame::getSpriteIDFromPos(Common::Point pos) {
+ //Find first from top to bottom
+ for (uint16 i = _drawRects.size() - 1; i > 0; i--) {
+ if (_drawRects[i].contains(pos))
+ return i;
+ }
+
+ return 0;
+}
+
+Sprite::Sprite() {
+ _enabled = false;
+ _trails = 0;
+ _width = 0;
+ _ink = kInkTypeCopy;
+ _flags = 0;
+ _height = 0;
+ _castId = 0;
+ _constraint = 0;
+ _moveable = 0;
+ _castId = 0;
+ _backColor = 0;
+ _foreColor = 0;
+ _left = 0;
+ _right = 0;
+ _top = 0;
+ _bottom = 0;
+ _visible = false;
+ _movieRate = 0;
+ _movieTime = 0;
+ _startTime = 0;
+ _stopTime = 0;
+ _volume = 0;
+ _stretch = 0;
+ _type = 0;
+}
+
+Sprite::Sprite(const Sprite &sprite) {
+ _enabled = sprite._enabled;
+ _castId = sprite._castId;
+ _flags = sprite._flags;
+ _trails = sprite._trails;
+ _ink = sprite._ink;
+ _width = sprite._width;
+ _height = sprite._height;
+ _startPoint.x = sprite._startPoint.x;
+ _startPoint.y = sprite._startPoint.y;
+ _backColor = sprite._backColor;
+ _foreColor = sprite._foreColor;
+ _left = sprite._left;
+ _right = sprite._right;
+ _top = sprite._top;
+ _bottom = sprite._bottom;
+ _visible = sprite._visible;
+ _movieRate = sprite._movieRate;
+ _movieTime = sprite._movieTime;
+ _stopTime = sprite._stopTime;
+ _volume = sprite._volume;
+ _stretch = sprite._stretch;
+ _type = sprite._type;
+}
+
+Sprite::~Sprite() {
+ delete _cast;
+ delete &_startPoint;
+}
+
+} //End of namespace Director
diff --git a/engines/director/score.h b/engines/director/score.h
new file mode 100644
index 0000000000..d483afab1c
--- /dev/null
+++ b/engines/director/score.h
@@ -0,0 +1,455 @@
+/* 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 2
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef DIRECTOR_SCORE_H
+#define DIRECTOR_SCORE_H
+
+#include "common/rect.h"
+#include "common/stream.h"
+#include "common/array.h"
+#include "director/resource.h"
+#include "graphics/managed_surface.h"
+#include "common/str.h"
+#include "image/image_decoder.h"
+#include "graphics/font.h"
+
+namespace Director {
+
+class Lingo;
+class DirectorSound;
+class Score;
+class DirectorEngine;
+
+#define CHANNEL_COUNT 24
+
+enum CastType {
+ kCastBitmap = 1,
+ kCastFilmLoop,
+ kCastText,
+ kCastPalette,
+ kCastPicture,
+ kCastSound,
+ kCastButton,
+ kCastShape,
+ kCastMovie,
+ kCastDigitalVideo,
+ kCastScript
+};
+
+//Director v4
+enum SpriteType {
+ kInactiveSprite, //turns the sprite off
+ kBitmapSprite,
+ kRectangleSprite,
+ kRoundedRectangleSprite,
+ kOvalSprite,
+ kLineTopBottomSprite, //line from top left to bottom right
+ kLineBottomTopSprite, //line from bottom left to top right
+ kTextSprite,
+ kButtonSprite,
+ kCheckboxSprite,
+ kRadioButtonSprite,
+ kUndeterminedSprite = 16 //use castType property to examine the type of cast member associated with sprite
+};
+
+enum SpritePosition {
+ kSpritePositionUnk1 = 0,
+ kSpritePositionEnabled,
+ kSpritePositionUnk2,
+ kSpritePositionFlags = 4,
+ kSpritePositionCastId = 6,
+ kSpritePositionY = 8,
+ kSpritePositionX = 10,
+ kSpritePositionHeight = 12,
+ kSpritePositionWidth = 14
+};
+
+enum MainChannelsPosition {
+ kScriptIdPosition = 0,
+ kSoundType1Position,
+ kTransFlagsPosition,
+ kTransChunkSizePosition,
+ kTempoPosition,
+ kTransTypePosition,
+ kSound1Position,
+ kSkipFrameFlagsPosition = 8,
+ kBlendPosition,
+ kSound2Position,
+ kSound2TypePosition = 11,
+ kPaletePosition = 15
+};
+
+enum InkType {
+ kInkTypeCopy,
+ kInkTypeTransparent,
+ kInkTypeReverse,
+ kInkTypeGhost,
+ kInkTypeNotCopy,
+ kInkTypeNotTrans,
+ kInkTypeNotReverse,
+ kInkTypeNotGhost,
+ kInkTypeMatte,
+ kInkTypeMask,
+ //10-31 Not used (Lingo in a Nutshell)
+ kInkTypeBlend = 32,
+ kInkTypeAddPin,
+ kInkTypeAdd,
+ kInkTypeSubPin,
+ kInkTypeBackgndTrans,
+ kInkTypeLight,
+ kInkTypeSub,
+ kInkTypeDark
+};
+
+enum ScriptType {
+ kMovieScript = 0,
+ kSpriteScript = 1,
+ kFrameScript = 2,
+ kMaxScriptType = 2
+};
+
+enum TransitionType {
+ kTransNone,
+ kTransWipeRight,
+ kTransWipeLeft,
+ kTransWipeDown,
+ kTransWipeUp,
+ kTransCenterOutHorizontal,
+ kTransEdgesInHorizontal,
+ kTransCenterOutVertical,
+ kTransEdgesInVertical,
+ kTransCenterOutSquare,
+ kTransEdgesInSquare,
+ kTransPushLeft,
+ kTransPushRight,
+ kTransPushDown,
+ kTransPushUp,
+ kTransRevealUp,
+ kTransRevealUpRight,
+ kTransRevealRight,
+ kTransRevealDown,
+ kTransRevealDownRight,
+ kTransRevealDownLeft,
+ kTransRevealLeft,
+ kTransRevealUpLeft,
+ kTransDissolvePixelsFast,
+ kTransDissolveBoxyRects,
+ kTransDissolveBoxySquares,
+ kTransDissolvePatterns,
+ kTransRandomRows,
+ kTransRandomColumns,
+ kTransCoverDown,
+ kTransCoverDownLeft,
+ kTransCoverDownRight,
+ kTransCoverLeft,
+ kTransCoverRight,
+ kTransCoverUp,
+ kTransCoverUpLeft,
+ kTransCoverUpRight,
+ kTransTypeVenitianBlind,
+ kTransTypeCheckerboard,
+ kTransTypeStripsBottomBuildLeft,
+ kTransTypeStripsBottomBuildRight,
+ kTransTypeStripsLeftBuildDown,
+ kTransTypeStripsLeftBuildUp,
+ kTransTypeStripsRightBuildDown,
+ kTransTypeStripsRightBuildUp,
+ kTransTypeStripsTopBuildLeft,
+ kTransTypeStripsTopBuildRight,
+ kTransZoomOpen,
+ kTransZoomClose,
+ kTransVerticalBinds,
+ kTransDissolveBitsTrans,
+ kTransDissolvePixels,
+ kTransDissolveBits
+};
+
+struct Cast {
+ CastType type;
+ Common::Rect initialRect;
+ byte modified;
+};
+
+struct BitmapCast : Cast {
+ BitmapCast(Common::SeekableSubReadStreamEndian &stream);
+
+ Common::Rect boundingRect;
+ uint16 regX;
+ uint16 regY;
+ uint8 flags;
+};
+
+enum ShapeType {
+ kShapeRectangle,
+ kShapeRoundRect,
+ kShapeOval,
+ kShapeLine
+};
+
+struct ShapeCast : Cast {
+ ShapeCast(Common::SeekableSubReadStreamEndian &stream);
+
+ ShapeType shapeType;
+ uint16 pattern;
+ byte fgCol;
+ byte bgCol;
+ byte fillType;
+ byte lineThickness;
+ byte lineDirection;
+};
+
+enum TextType {
+ kTextTypeAdjustToFit,
+ kTextTypeScrolling,
+ kTextTypeFixed
+};
+
+enum TextAlignType {
+ kTextAlignRight = -1,
+ kTextAlignLeft,
+ kTextAlignCenter
+};
+
+enum TextFlag {
+ kTextFlagEditable,
+ kTextFlagAutoTab,
+ kTextFlagDoNotWrap
+};
+
+enum SizeType {
+ kSizeNone,
+ kSizeSmallest,
+ kSizeSmall,
+ kSizeMedium,
+ kSizeLarge,
+ kSizeLargest
+};
+
+struct TextCast : Cast {
+ TextCast(Common::SeekableSubReadStreamEndian &stream);
+
+ SizeType borderSize;
+ SizeType gutterSize;
+ SizeType boxShadow;
+
+ uint32 fontId;
+ uint16 fontSize;
+ TextType textType;
+ TextAlignType textAlign;
+ SizeType textShadow;
+ Common::Array<TextFlag> textFlags;
+};
+
+enum ButtonType {
+ kTypeButton,
+ kTypeCheckBox,
+ kTypeRadio
+};
+
+struct ButtonCast : TextCast {
+ ButtonCast(Common::SeekableSubReadStreamEndian &stream) : TextCast(stream) {
+ buttonType = static_cast<ButtonType>(stream.readUint16BE());
+ }
+
+ ButtonType buttonType;
+};
+
+struct CastInfo {
+ Common::String script;
+ Common::String name;
+ Common::String directory;
+ Common::String fileName;
+ Common::String type;
+};
+
+struct PaletteInfo {
+ uint8 firstColor;
+ uint8 lastColor;
+ uint8 flags;
+ uint8 speed;
+ uint16 frameCount;
+};
+
+class Sprite {
+public:
+ Sprite();
+ Sprite(const Sprite &sprite);
+ ~Sprite();
+ bool _enabled;
+ byte _castId;
+ InkType _ink;
+ uint16 _trails;
+ Cast *_cast;
+ uint16 _flags;
+ Common::Point _startPoint;
+ uint16 _width;
+ uint16 _height;
+ //TODO: default constraint = 0, if turned on, sprite is constrainted to the bounding rect
+ //As i know, constrainted != 0 only if sprite moveable
+ byte _constraint;
+ byte _moveable;
+ byte _backColor;
+ byte _foreColor;
+ uint16 _left;
+ uint16 _right;
+ uint16 _top;
+ uint16 _bottom;
+ byte _blend;
+ bool _visible;
+ byte _type;
+ //Using in digital movie sprites
+ byte _movieRate;
+ uint16 _movieTime;
+ uint16 _startTime;
+ uint16 _stopTime;
+ byte _volume;
+ byte _stretch;
+ //Using in shape sprites
+ byte _lineSize;
+ //Using in text sprites
+ Common::String _editableText;
+};
+
+class Frame {
+public:
+ Frame(DirectorEngine *vm);
+ Frame(const Frame &frame);
+ ~Frame();
+ void readChannel(Common::SeekableSubReadStreamEndian &stream, uint16 offset, uint16 size);
+ void prepareFrame(Score *score);
+ uint16 getSpriteIDFromPos(Common::Point pos);
+
+private:
+ void playTransition(Score *score);
+ void playSoundChannel();
+ void renderSprites(Graphics::ManagedSurface &surface, bool renderTrail);
+ void renderText(Graphics::ManagedSurface &surface, uint16 spriteId);
+ void renderButton(Graphics::ManagedSurface &surface, uint16 spriteId);
+ void readPaletteInfo(Common::SeekableSubReadStreamEndian &stream);
+ void readSprite(Common::SeekableSubReadStreamEndian &stream, uint16 offset, uint16 size);
+ void readMainChannels(Common::SeekableSubReadStreamEndian &stream, uint16 offset, uint16 size);
+ Image::ImageDecoder *getImageFrom(uint16 spriteID);
+ void drawBackgndTransSprite(Graphics::ManagedSurface &target, const Graphics::Surface &sprite, Common::Rect &drawRect);
+ void drawMatteSprite(Graphics::ManagedSurface &target, const Graphics::Surface &sprite, Common::Rect &drawRect);
+ void drawGhostSprite(Graphics::ManagedSurface &target, const Graphics::Surface &sprite, Common::Rect &drawRect);
+ void drawReverseSprite(Graphics::ManagedSurface &target, const Graphics::Surface &sprite, Common::Rect &drawRect);
+public:
+ uint8 _actionId;
+ uint8 _transDuration;
+ uint8 _transArea; //1 - Whole Stage, 0 - Changing Area
+ uint8 _transChunkSize;
+ TransitionType _transType;
+ PaletteInfo *_palette;
+ uint8 _tempo;
+
+ uint16 _sound1;
+ uint8 _soundType1;
+ uint16 _sound2;
+ uint8 _soundType2;
+
+ uint8 _skipFrameFlag;
+ uint8 _blend;
+ Common::Array<Sprite *> _sprites;
+ Common::Array<Common::Rect > _drawRects;
+ DirectorEngine *_vm;
+};
+
+struct Label {
+ Common::String name;
+ uint16 number;
+ Label(Common::String name1, uint16 number1) { name = name1; number = number1; }
+};
+
+class Score {
+public:
+ Score(DirectorEngine *vm);
+ ~Score();
+
+ static Common::Rect readRect(Common::SeekableSubReadStreamEndian &stream);
+ static int compareLabels(const void *a, const void *b);
+ void loadArchive();
+ void setStartToLabel(Common::String label);
+ void gotoloop();
+ void gotonext();
+ void gotoprevious();
+ void startLoop();
+ void processEvents();
+ Archive *getArchive() const { return _movieArchive; };
+ void loadCastData(Common::SeekableSubReadStreamEndian &stream);
+ void setCurrentFrame(uint16 frameId) { _currentFrame = frameId; }
+ Common::String getMacName() const { return _macName; }
+ Sprite *getSpriteById(uint16 id);
+private:
+ void update();
+ void readVersion(uint32 rid);
+ void loadConfig(Common::SeekableSubReadStreamEndian &stream);
+ void loadPalette(Common::SeekableSubReadStreamEndian &stream);
+ void loadFrames(Common::SeekableSubReadStreamEndian &stream);
+ void loadLabels(Common::SeekableSubReadStreamEndian &stream);
+ void loadActions(Common::SeekableSubReadStreamEndian &stream);
+ void loadCastInfo(Common::SeekableSubReadStreamEndian &stream, uint16 id);
+ void loadScriptText(Common::SeekableSubReadStreamEndian &stream);
+ void loadFileInfo(Common::SeekableSubReadStreamEndian &stream);
+ void loadFontMap(Common::SeekableSubReadStreamEndian &stream);
+ void dumpScript(const char *script, ScriptType type, uint16 id);
+ Common::String getString(Common::String str);
+ Common::Array<Common::String> loadStrings(Common::SeekableSubReadStreamEndian &stream, uint32 &entryType, bool hasHeader = true);
+
+public:
+ Common::Array<Frame *> _frames;
+ Common::HashMap<int, Cast *> _casts;
+ Common::HashMap<uint16, CastInfo *> _castsInfo;
+ Common::SortedArray<Label *> *_labels;
+ Common::HashMap<uint16, Common::String> _actions;
+ Common::HashMap<uint16, Common::String> _fontMap;
+ Graphics::ManagedSurface *_surface;
+ Graphics::ManagedSurface *_trailSurface;
+ Graphics::Font *_font;
+ Archive *_movieArchive;
+ Common::Rect _movieRect;
+
+private:
+ uint16 _versionMinor;
+ uint16 _versionMajor;
+ Common::String _macName;
+ Common::String _createdBy;
+ Common::String _changedBy;
+ Common::String _script;
+ Common::String _directory;
+ byte _currentFrameRate;
+ uint16 _castArrayStart;
+ uint16 _currentFrame;
+ Common::String _currentLabel;
+ uint32 _nextFrameTime;
+ uint32 _flags;
+ bool _stopPlay;
+ uint16 _castArrayEnd;
+ uint16 _movieScriptCount;
+ uint16 _stageColor;
+ Lingo *_lingo;
+ DirectorSound *_soundManager;
+ DirectorEngine *_vm;
+};
+
+} //End of namespace Director
+
+#endif
diff --git a/engines/director/sound.cpp b/engines/director/sound.cpp
new file mode 100644
index 0000000000..5f6d435392
--- /dev/null
+++ b/engines/director/sound.cpp
@@ -0,0 +1,99 @@
+/* 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 2
+* 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, write to the Free Software
+* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+*
+*/
+
+#include "director/sound.h"
+#include "audio/decoders/wave.h"
+#include "common/file.h"
+#include "audio/decoders/aiff.h"
+#include "common/system.h"
+#include "common/debug.h"
+
+namespace Director {
+
+DirectorSound::DirectorSound() {
+ _sound1 = new Audio::SoundHandle();
+ _sound2 = new Audio::SoundHandle();
+ _scriptSound = new Audio::SoundHandle();
+ _mixer = g_system->getMixer();
+}
+
+void DirectorSound::playWAV(Common::String filename, uint8 soundChannel) {
+ Common::File *file = new Common::File();
+
+ if (!file->open(filename)) {
+ warning("Failed to open %s", filename.c_str());
+
+ delete file;
+
+ return;
+ }
+
+ Audio::RewindableAudioStream *sound = Audio::makeWAVStream(file, DisposeAfterUse::YES);
+
+ if (soundChannel == 1)
+ _mixer->playStream(Audio::Mixer::kSFXSoundType, _sound1, sound);
+ else
+ _mixer->playStream(Audio::Mixer::kSFXSoundType, _sound2, sound);
+}
+
+void DirectorSound::playAIFF(Common::String filename, uint8 soundChannel) {
+ Common::File *file = new Common::File();
+
+ if (!file->open(filename)) {
+ warning("Failed to open %s", filename.c_str());
+ delete file;
+ return;
+ }
+
+ Audio::RewindableAudioStream *sound = Audio::makeAIFFStream(file, DisposeAfterUse::YES);
+
+ if (soundChannel == 1)
+ _mixer->playStream(Audio::Mixer::kSFXSoundType, _sound1, sound);
+ else
+ _mixer->playStream(Audio::Mixer::kSFXSoundType, _sound2, sound);
+}
+
+void DirectorSound::playMCI(Audio::AudioStream &stream, uint32 from, uint32 to) {
+ Audio::SeekableAudioStream *seekStream = dynamic_cast<Audio::SeekableAudioStream *>(&stream);
+ Audio::SubSeekableAudioStream *subSeekStream = new Audio::SubSeekableAudioStream(seekStream, Audio::Timestamp(from, seekStream->getRate()), Audio::Timestamp(to, seekStream->getRate()));
+
+ _mixer->playStream(Audio::Mixer::kSFXSoundType, _scriptSound, subSeekStream);
+}
+
+bool DirectorSound::isChannelActive(uint8 channelID) {
+ if (channelID == 1) {
+ return _mixer->isSoundHandleActive(*_sound1);
+ } else if (channelID == 2) {
+ return _mixer->isSoundHandleActive(*_sound2);
+ }
+
+ error("Incorrect sound channel");
+
+ return false;
+}
+
+void DirectorSound::stopSound() {
+ _mixer->stopHandle(*_sound1);
+ _mixer->stopHandle(*_sound2);
+}
+
+} //End of namespace Director
diff --git a/engines/director/sound.h b/engines/director/sound.h
new file mode 100644
index 0000000000..87a989c596
--- /dev/null
+++ b/engines/director/sound.h
@@ -0,0 +1,52 @@
+/* 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 2
+* 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, write to the Free Software
+* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+*
+*/
+
+#include "audio/audiostream.h"
+#include "audio/mixer.h"
+#include "common/str.h"
+
+#ifndef DIRECTOR_SOUND_H
+#define DIRECTOR_SOUND_H
+
+namespace Director {
+
+class DirectorSound {
+
+private:
+ Audio::SoundHandle *_sound1;
+ Audio::SoundHandle *_sound2;
+ Audio::SoundHandle *_scriptSound;
+ Audio::Mixer *_mixer;
+
+public:
+ DirectorSound();
+
+ void playWAV(Common::String filename, uint8 channelID);
+ void playAIFF(Common::String filename, uint8 channelID);
+ void playMCI(Audio::AudioStream &stream, uint32 from, uint32 to);
+ bool isChannelActive(uint8 channelID);
+ void stopSound();
+};
+
+} // End of namespace Director
+
+#endif