[Scummvm-git-logs] scummvm master -> c3dbc2c40f68aebf04fa76cbb225bf81f43a8c41
dreammaster
paulfgilbert at gmail.com
Thu Nov 28 05:10:59 UTC 2019
This automated email contains information about 9 new commits which have been
pushed to the 'scummvm' repo located at https://github.com/scummvm/scummvm .
Summary:
223f2613b2 GLK: AGT: Skeleton engine
f9921a7a17 GLK: AGT: Added detection entries
28c3584148 GLK: AGT: Added subengine files
88444ddc88 GLK: AGT: Initialization fixes
c0999ae5ba GLK: AGT: Startup fixes
5d7386e42b GLK: AGT: Properly exit when game window is closed
e3c2afe073 GLK: AGT: Remove old license information
43579347eb GLK: AGT: Move configuration options into AGT class
c3dbc2c40f GLK: AGT: Route savegames through the engine
Commit: 223f2613b2756ca8d63bb7dea75335f41d9ad75c
https://github.com/scummvm/scummvm/commit/223f2613b2756ca8d63bb7dea75335f41d9ad75c
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2019-11-27T21:10:29-08:00
Commit Message:
GLK: AGT: Skeleton engine
Changed paths:
A engines/glk/agt/agt.cpp
A engines/glk/agt/agt.h
A engines/glk/agt/detection.cpp
A engines/glk/agt/detection.h
A engines/glk/agt/detection_tables.h
engines/glk/configure.engine
engines/glk/detection.cpp
engines/glk/glk_types.h
engines/glk/module.mk
diff --git a/engines/glk/agt/agt.cpp b/engines/glk/agt/agt.cpp
new file mode 100644
index 0000000..29e7222
--- /dev/null
+++ b/engines/glk/agt/agt.cpp
@@ -0,0 +1,47 @@
+/* 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 "glk/agt/agt.h"
+#include "glk/quetzal.h"
+#include "common/config-manager.h"
+#include "common/translation.h"
+
+namespace Glk {
+namespace AGT {
+
+AGT::AGT(OSystem *syst, const GlkGameDescription &gameDesc) : GlkAPI(syst, gameDesc) {
+}
+
+void AGT::runGame() {
+ // TODO
+}
+
+Common::Error AGT::readSaveData(Common::SeekableReadStream *rs) {
+ return Common::kReadingFailed;
+}
+
+Common::Error AGT::writeGameData(Common::WriteStream *ws) {
+ return Common::kWritingFailed;
+}
+
+} // End of namespace AGT
+} // End of namespace Glk
diff --git a/engines/glk/agt/agt.h b/engines/glk/agt/agt.h
new file mode 100644
index 0000000..70bb98a
--- /dev/null
+++ b/engines/glk/agt/agt.h
@@ -0,0 +1,70 @@
+/* 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.
+ *
+ */
+
+/* Based on Agility interpreter version 1.1.1 */
+
+#ifndef GLK_AGT
+#define GLK_AGT
+
+#include "common/scummsys.h"
+#include "glk/glk_api.h"
+
+namespace Glk {
+namespace AGT {
+
+
+/**
+ * AGT Adams game interpreter
+ */
+class AGT : public GlkAPI {
+public:
+ /**
+ * Constructor
+ */
+ AGT(OSystem *syst, const GlkGameDescription &gameDesc);
+
+ /**
+ * Returns the running interpreter type
+ */
+ virtual InterpreterType getInterpreterType() const override { return INTERPRETER_AGT; }
+
+ /**
+ * Execute the game
+ */
+ virtual void runGame() override;
+
+ /**
+ * Load a savegame from the passed Quetzal file chunk stream
+ */
+ virtual Common::Error readSaveData(Common::SeekableReadStream *rs) override;
+
+ /**
+ * Save the game. The passed write stream represents access to the UMem chunk
+ * in the Quetzal save file that will be created
+ */
+ virtual Common::Error writeGameData(Common::WriteStream *ws) override;
+};
+
+} // End of namespace AGT
+} // End of namespace Glk
+
+#endif
diff --git a/engines/glk/agt/detection.cpp b/engines/glk/agt/detection.cpp
new file mode 100644
index 0000000..ec093b7
--- /dev/null
+++ b/engines/glk/agt/detection.cpp
@@ -0,0 +1,93 @@
+/* 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 "glk/agt/detection.h"
+#include "glk/agt/detection_tables.h"
+#include "glk/blorb.h"
+#include "common/file.h"
+#include "common/md5.h"
+#include "engines/game.h"
+
+namespace Glk {
+namespace AGT {
+
+void AGTMetaEngine::getSupportedGames(PlainGameList &games) {
+ for (const PlainGameDescriptor *pd = AGT_GAME_LIST; pd->gameId; ++pd)
+ games.push_back(*pd);
+}
+
+GameDescriptor AGTMetaEngine::findGame(const char *gameId) {
+ for (const PlainGameDescriptor *pd = AGT_GAME_LIST; pd->gameId; ++pd) {
+ if (!strcmp(gameId, pd->gameId))
+ return *pd;
+ }
+
+ return GameDescriptor::empty();
+}
+
+bool AGTMetaEngine::detectGames(const Common::FSList &fslist, DetectedGames &gameList) {
+ const char *const EXTENSIONS[] = { ".saga", ".dat", nullptr };
+
+ // Loop through the files of the folder
+ for (Common::FSList::const_iterator file = fslist.begin(); file != fslist.end(); ++file) {
+ // Check for a recognised filename
+ if (file->isDirectory())
+ continue;
+
+ Common::String filename = file->getName();
+ if (!filename.hasSuffixIgnoreCase(".d$$"))
+ continue;
+
+ Common::File gameFile;
+ if (!gameFile.open(*file))
+ continue;
+ Common::String md5 = Common::computeStreamMD5AsString(gameFile, 5000);
+ size_t filesize = (size_t)gameFile.size();
+
+ // Scan through the AGT game list for a match
+ const GlkDetectionEntry *p = AGT_GAMES;
+ while (p->_md5 && p->_filesize != filesize && md5 != p->_md5)
+ ++p;
+
+ if (!p->_gameId) {
+ const PlainGameDescriptor &desc = AGT_GAME_LIST[0];
+ gameList.push_back(GlkDetectedGame(desc.gameId, desc.description, filename, md5, filesize));
+ } else {
+ // Found a match
+ PlainGameDescriptor gameDesc = findGame(p->_gameId);
+ gameList.push_back(GlkDetectedGame(p->_gameId, gameDesc.description, filename));
+ }
+ }
+
+ return !gameList.empty();
+}
+
+void AGTMetaEngine::detectClashes(Common::StringMap &map) {
+ for (const PlainGameDescriptor *pd = AGT_GAME_LIST; pd->gameId; ++pd) {
+ if (map.contains(pd->gameId))
+ error("Duplicate game Id found - %s", pd->gameId);
+ map[pd->gameId] = "";
+ }
+}
+
+} // End of namespace AGT
+} // End of namespace Glk
diff --git a/engines/glk/agt/detection.h b/engines/glk/agt/detection.h
new file mode 100644
index 0000000..94bb533
--- /dev/null
+++ b/engines/glk/agt/detection.h
@@ -0,0 +1,60 @@
+/* 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 GLK_AGT_DETECTION
+#define GLK_AGT_DETECTION
+
+#include "common/fs.h"
+#include "common/hash-str.h"
+#include "engines/game.h"
+#include "glk/detection.h"
+
+namespace Glk {
+namespace AGT {
+
+class AGTMetaEngine {
+public:
+ /**
+ * Get a list of supported games
+ */
+ static void getSupportedGames(PlainGameList &games);
+
+ /**
+ * Returns a game description for the given game Id, if it's supported
+ */
+ static GameDescriptor findGame(const char *gameId);
+
+ /**
+ * Detect supported games
+ */
+ static bool detectGames(const Common::FSList &fslist, DetectedGames &gameList);
+
+ /**
+ * Check for game Id clashes with other sub-engines
+ */
+ static void detectClashes(Common::StringMap &map);
+};
+
+} // End of namespace AGT
+} // End of namespace Glk
+
+#endif
diff --git a/engines/glk/agt/detection_tables.h b/engines/glk/agt/detection_tables.h
new file mode 100644
index 0000000..86e92e0
--- /dev/null
+++ b/engines/glk/agt/detection_tables.h
@@ -0,0 +1,39 @@
+/* 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/game.h"
+#include "common/gui_options.h"
+#include "common/language.h"
+
+namespace Glk {
+namespace AGT {
+
+const PlainGameDescriptor AGT_GAME_LIST[] = {
+ { nullptr, nullptr }
+};
+
+const GlkDetectionEntry AGT_GAMES[] = {
+ DT_END_MARKER
+};
+
+} // End of namespace AGT
+} // End of namespace Glk
diff --git a/engines/glk/configure.engine b/engines/glk/configure.engine
index 507bcfe..a00f9fd 100644
--- a/engines/glk/configure.engine
+++ b/engines/glk/configure.engine
@@ -1,8 +1,9 @@
# This file is included from the main "configure" script
# add_engine [name] [desc] [build-by-default] [subengines] [base games] [deps]
-add_engine glk "Glk Interactive Fiction games" no "glk_adrift glk_advsys glk_alan2 glk_alan3 glk_archetype glk_frotz glk_glulxe glk_hugo glk_jacl glk_level9 glk_magnetic glk_quest glk_scott glk_tads" "Base" "16bit freetype2 jpeg png"
+add_engine glk "Glk Interactive Fiction games" no "glk_adrift glk_advsys glk_agt glk_alan2 glk_alan3 glk_archetype glk_frotz glk_glulxe glk_hugo glk_jacl glk_level9 glk_magnetic glk_quest glk_scott glk_tads" "Base" "16bit freetype2 jpeg png"
add_engine glk_adrift "ADRIFT" no
add_engine glk_advsys "AdvSys" no
+add_engine glk_agt "AGT" no
add_engine glk_alan2 "Alan2" no
add_engine glk_alan3 "Alan3" no
add_engine glk_archetype "Archetype" no
diff --git a/engines/glk/detection.cpp b/engines/glk/detection.cpp
index 9cbda0f..628158f 100644
--- a/engines/glk/detection.cpp
+++ b/engines/glk/detection.cpp
@@ -34,6 +34,11 @@
#include "glk/advsys/advsys.h"
#endif
+#ifdef ENABLE_GLK_AGT
+#include "glk/agt/detection.h"
+#include "glk/agt/agt.h"
+#endif
+
#ifdef ENABLE_GLK_ALAN2
#include "glk/alan2/detection.h"
#include "glk/alan2/alan2.h"
@@ -214,6 +219,10 @@ Common::Error GlkMetaEngine::createInstance(OSystem *syst, Engine **engine) cons
if ((*engine = create<Glk::AdvSys::AdvSysMetaEngine, Glk::AdvSys::AdvSys>(syst, gameDesc)) != nullptr) {}
else
#endif
+#ifdef ENABLE_GLK_AGT
+ if ((*engine = create<Glk::AGT::AGTMetaEngine, Glk::AGT::AGT>(syst, gameDesc)) != nullptr) {}
+ else
+#endif
#ifdef ENABLE_GLK_ALAN2
if ((*engine = create<Glk::Alan2::Alan2MetaEngine, Glk::Alan2::Alan2>(syst, gameDesc)) != nullptr) {}
else
@@ -303,6 +312,9 @@ PlainGameList GlkMetaEngine::getSupportedGames() const {
#ifdef ENABLE_GLK_ADVSYS
Glk::AdvSys::AdvSysMetaEngine::getSupportedGames(list);
#endif
+#ifdef ENABLE_GLK_AGT
+ Glk::AGT::AGTMetaEngine::getSupportedGames(list);
+#endif
#ifdef ENABLE_GLK_ALAN2
Glk::Alan2::Alan2MetaEngine::getSupportedGames(list);
#endif
@@ -357,6 +369,9 @@ PlainGameDescriptor GlkMetaEngine::findGame(const char *gameId) const {
#ifdef ENABLE_GLK_ALAN2
FIND_GAME(Alan2);
#endif
+#ifdef ENABLE_GLK_AGT
+ FIND_GAME(AGT);
+#endif
#ifdef ENABLE_GLK_ALAN3
FIND_GAME(Alan3);
#endif
@@ -407,6 +422,9 @@ DetectedGames GlkMetaEngine::detectGames(const Common::FSList &fslist) const {
#ifdef ENABLE_GLK_ADVSYS
Glk::AdvSys::AdvSysMetaEngine::detectGames(fslist, detectedGames);
#endif
+#ifdef ENABLE_GLK_AGT
+ Glk::AGT::AGTMetaEngine::detectGames(fslist, detectedGames);
+#endif
#ifdef ENABLE_GLK_ALAN2
Glk::Alan2::Alan2MetaEngine::detectGames(fslist, detectedGames);
#endif
@@ -455,6 +473,9 @@ void GlkMetaEngine::detectClashes() const {
#ifdef ENABLE_GLK_ADVSYS
Glk::AdvSys::AdvSysMetaEngine::detectClashes(map);
#endif
+#ifdef ENABLE_GLK_AGT
+ Glk::AGT::AGTMetaEngine::detectClashes(map);
+#endif
#ifdef ENABLE_GLK_ALAN2
Glk::Alan2::Alan2MetaEngine::detectClashes(map);
#endif
diff --git a/engines/glk/glk_types.h b/engines/glk/glk_types.h
index e730f15..7609a90 100644
--- a/engines/glk/glk_types.h
+++ b/engines/glk/glk_types.h
@@ -38,6 +38,7 @@ enum InterpreterType {
INTERPRETER_ADRIFT,
INTERPRETER_ADVSYS,
INTERPRETER_AGILITY,
+ INTERPRETER_AGT,
INTERPRETER_ALAN2,
INTERPRETER_ALAN3,
INTERPRETER_ARCHETYPE,
diff --git a/engines/glk/module.mk b/engines/glk/module.mk
index 74d3321..a37bd2b 100644
--- a/engines/glk/module.mk
+++ b/engines/glk/module.mk
@@ -69,6 +69,12 @@ MODULE_OBJS += \
advsys/vm.o
endif
+ifdef ENABLE_GLK_AGT
+MODULE_OBJS += \
+ agt/agt.o \
+ agt/detection.o
+endif
+
ifdef ENABLE_GLK_ALAN2
MODULE_OBJS += \
alan2/alan2.o \
Commit: f9921a7a177dd506640211dd05751e046100a57b
https://github.com/scummvm/scummvm/commit/f9921a7a177dd506640211dd05751e046100a57b
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2019-11-27T21:10:29-08:00
Commit Message:
GLK: AGT: Added detection entries
Changed paths:
engines/glk/agt/detection.cpp
engines/glk/agt/detection_tables.h
diff --git a/engines/glk/agt/detection.cpp b/engines/glk/agt/detection.cpp
index ec093b7..f1913c9 100644
--- a/engines/glk/agt/detection.cpp
+++ b/engines/glk/agt/detection.cpp
@@ -45,8 +45,6 @@ GameDescriptor AGTMetaEngine::findGame(const char *gameId) {
}
bool AGTMetaEngine::detectGames(const Common::FSList &fslist, DetectedGames &gameList) {
- const char *const EXTENSIONS[] = { ".saga", ".dat", nullptr };
-
// Loop through the files of the folder
for (Common::FSList::const_iterator file = fslist.begin(); file != fslist.end(); ++file) {
// Check for a recognised filename
@@ -54,7 +52,7 @@ bool AGTMetaEngine::detectGames(const Common::FSList &fslist, DetectedGames &gam
continue;
Common::String filename = file->getName();
- if (!filename.hasSuffixIgnoreCase(".d$$"))
+ if (!filename.hasSuffixIgnoreCase(".d$$") && !filename.hasSuffixIgnoreCase(".agx"))
continue;
Common::File gameFile;
@@ -74,7 +72,9 @@ bool AGTMetaEngine::detectGames(const Common::FSList &fslist, DetectedGames &gam
} else {
// Found a match
PlainGameDescriptor gameDesc = findGame(p->_gameId);
- gameList.push_back(GlkDetectedGame(p->_gameId, gameDesc.description, filename));
+ DetectedGame gd("glk", p->_gameId, gameDesc.description, p->_language, Common::kPlatformUnknown, p->_extra);
+ gd.addExtraEntry("filename", filename);
+ gameList.push_back(gd);
}
}
diff --git a/engines/glk/agt/detection_tables.h b/engines/glk/agt/detection_tables.h
index 86e92e0..eb8e2ba 100644
--- a/engines/glk/agt/detection_tables.h
+++ b/engines/glk/agt/detection_tables.h
@@ -27,11 +27,255 @@
namespace Glk {
namespace AGT {
+/* TODO: The following games aren't yet supported:
+ * Lost Gold : Doesn't come as separate gamefiles
+ * eather's Easter Egg Hunt - Comes as an .arc archive;
+ * still need to find something to uncompress it
+ */
+
const PlainGameDescriptor AGT_GAME_LIST[] = {
+ { "abloodylife", "A Bloody Life" },
+ { "alandria", "The Search for Princess Alandria" },
+ { "alchemistcastle", "Castle of the Alchemists" },
+ { "advalice", "The Adventures of Alice Who Went Through the Looking-Glass" },
+ { "apprenticetesting", "Apprentice - The Testing of a Magical Novice" },
+ { "sirarthur", "Sir Arthur" },
+ { "cercla", "Cercla" },
+ { "cardigan1", "Space Aliens Laughed at My Cardigan" },
+ { "cardigan2", "Still Laughing at my Cardigan" },
+ { "sanityclause", "Sanity Clause or, Why Santa Didn't Make It to YOUR House that Year" },
+ { "cliffdiver1", "Cliff Diver: Investigator for Hire - Case 1" },
+ { "cliffdiver2", "Cliff Diver: Investigator for Hire - Case 2" },
+ { "cosmoserve", "CosmoServe" },
+ { "crusade", "Crusade" },
+ { "agtdetective", "Detective" },
+ { "dragonschocolate", "Dragons in Chocolate Land" },
+ { "disenchanted", "Disenchanted" },
+ { "ducksoup", "Duck Soup" },
+ { "cavesofdyanty", "Caves of Dyanty" },
+ { "destinationearth", "Destination: Earth" },
+ { "dudleydilemma", "A Dudley Dilemma" },
+ { "80days", "Around the World in Eighty Days" },
+ { "electrabot", "Electrabot" },
+ { "elf20", "The Elf's Christmas Adventure" },
+ { "elfquest", "Elf Quest" },
+ { "eliescape", "Escape from the ELI" },
+ { "escapeprisonisland", "Escape from Prison Island" },
+ { "agtfable", "A Fable" },
+ { "firststupidgame", "My First Stupid Game" },
+ { "ccfirstadv", "Colossal Cave - The First Adventure" },
+ { "ggollek", "Ggollek I : The Dissolution" },
+ { "agtghosttown", "Ghost Town" },
+ { "giganticsecrets", "Secrets of the Gigantic" },
+ { "newenglandgothic", "New England Gothic" },
+ { "grailmisadventure", "The Misadventure of the Holy Grail" },
+ { "hardestadv", "The World's Hardest Adventure" },
+ { "helvera", "Helvera, Mistress of the Park" },
+ { "highe", "Highe, the Adventures of Elizabeth(\"El\") Highe" },
+ { "sirramichobbs", "Sir Ramic Hobbs and the High Level Gorilla" },
+ { "holmescasebook", "The Casebook of Sherlock Holmes" },
+ { "hotelnotell", "Hotel Notell" },
+ { "house2house", "House 2 House" },
+ { "agthugecave", "Adventure in Humongous Cave" },
+ { "hurryhurry", "Hurry!Hurry!Hurry!!" },
+ { "jackofhartz", "Jack of Hartz" },
+ { "jubileeroad", "Jubilee Road" },
+ { "killjustin", "Kill Justin" },
+ { "klaustrophobia1", "Klaustrophobia - Part I" },
+ { "klaustrophobia2", "Klaustrophobia - Part II" },
+ { "klaustrophobia3", "Klaustrophobia - Part III" },
+ { "klingonrpg", "In the Year 2366, Klingon Role Playing Game" },
+ { "deadlylabyrinth", "The Deadly Labyrinth" },
+ { "library", "Library - Library of Guilford College" },
+ { "lostinspace", "Lost in Space : Dr.Smith Goes Home" },
+ { "agtlottery", "Lottery" },
+ { "loststonemansion", "Lost Stone Mansion" },
+ { "agtpyramids", "The Pyramids of Mars" },
+ { "mdthief", "The Multi-Dimensional Thief" },
+ { "agtmhpquest", "Quest for the Magic Healing Plant" },
+ { "mopandmurder", "Mop and Murder" },
+ { "agtmst3k1", "Detective, An Interactive MiSTing (Mystery Science Theater 3000)" },
+ { "agtmst3k2", "Mystery Science Theater 3000, Adventure 102" },
+ { "spacemule", "Space Mule" },
+ { "myopia", "Myopia" },
+ { "nmr1", "Adventures in NMR" },
+ { "nmr2", "Adventures in NMR II : The Adventure Continues" },
+ { "oceana", "Oceana" },
+ { "agtodieus", "Odieus's Quest for the Magic Flingshot" },
+ { "oklib", "Oklib's Revenge" },
+ { "ovanpelt", "Orientation to Van Pelt Library of the University of Pennsylvania" },
+ { "peterpatzer", "The Adventures of Peter Patzer" },
+ { "blackpearl", "Quest for the Black Pearl" },
+ { "battleofphilip", "The Battle of Philip against the Forces of Creation" },
+ { "flightintofantasy", "The Pilot or A Flight into Fantasy" },
+ { "pork1", "PORK I : The Great Underground Sewer System" },
+ { "pork2", "PORK II, The Gizzard of Showbiz" },
+ { "starportal", "The Star Portal" },
+ { "pastoralpitfalls", "Pastoral Pitfalls" },
+ { "lostproperty", "Lost Property" },
+ { "gameofrecovery", "The Game of Recovery" },
+ { "rerunsagain", "Reruns Again version" },
+ { "derring", "Der Ring des Nibelungen" },
+ { "sherwoodadv", "Adventures in Sherwood" },
+ { "shapeshifteradv", "Shape Shifter Adventure!" },
+ { "sirguygallant", "Sir Guy Gallant and the Deadly Warning" },
+ { "shadesofgray", "Shades of Gray" },
+ { "sonofstagefright", "Son of Stagefright" },
+ { "spatent", "The Spatent Obstruction" },
+ { "squynchia", "The Squynchia Adventure" },
+ { "stiffy", "The Incredible Erotic Adventures of Stiffy Makane!" },
+ { "storms1", "Storms I" },
+ { "susan", "Susan (A Lustful Game)" },
+ { "tamoret", "Tamoret" },
+ { "tarabithia", "Escape from Tarabithia" },
+ { "tarksimmons", "The Adventure of Tark Simmons" },
+ { "tarotia", "The Books of Tarotia : Book 1" },
+ { "tempest", "The Tempest" },
+ { "thegame", "Whatever We Decide To Call This Game" },
+ { "therift", "The Rift" },
+ { "tja", "The Jeweled Arena" },
+ { "toho", "Toho Academy" },
+ { "tossedintospace", "Tossed into Space : Dr.Schmidt Goes Home" },
+ { "timesquared", "TimeSquared" },
+ { "folkestone", "Murder at the Folkestone Inn" },
+ { "wanderer1", "Black Wanderer 1 - The Darkest Road" },
+ { "wanderer2", "Black Wanderer 2 - The Unborn One" },
+ { "wanderer3", "Black Wanderer 3 - Twas a Time of Dread" },
+ { "weekendsurvival", "Weekend Survival" },
+ { "witchfinder", "Witchfinder" },
+ { "agtwizardscastle", "The Wizard's Castle" },
+ { "hobbswok", "Sir Ramic Hobbs and the Oriental Wok" },
+ { "wraithblaster", "Wraith Blaster" },
+ { "journeyintoxanth", "A Journey into Xanth" },
+ { "zanfar", "Zanfar" },
+
+ // Dutch games
+ { "querido", "Querido" },
+
{ nullptr, nullptr }
};
const GlkDetectionEntry AGT_GAMES[] = {
+ DT_ENTRY0("abloodylife", "c492e0ae0647d3a4835012ca864b99d5", 157221),
+ DT_ENTRY0("alchemistcastle", "7822dfaf1ae31b3e508e7b0a267d054b", 192051),
+ DT_ENTRY0("advalice", "0aaafb897b46baa28023bbbaf4091fd8", 23004),
+ DT_ENTRY0("apprenticetesting", "4e4244649dc1cd39546f3d10dc85acb5", 131868),
+ DT_ENTRY0("sirarthur", "46956e2d28f6b926fc6831d60f891ffc", 120204),
+ DT_ENTRY0("cardigan1", "301509b196fd27c87d5d176f895b94ea", 103356),
+ DT_ENTRY0("cardigan2", "f17a9d5401cb5cb1be4cb2719d0c9d34", 97767),
+ DT_ENTRY0("cercla", "a56219015b70f507d9a1f74e0a92db1f", 136080),
+ DT_ENTRY0("sanityclause", "a7ea1c9ae6200511af71dfcebb5d55ff", 246159),
+ DT_ENTRY0("cliffdiver1", "14ce6a122a061f2b361e725fe2c0c0e4", 120042),
+ DT_ENTRY0("cliffdiver2", "9cc68e22a0ba03fe13bd4bfb413e08df", 155682),
+ DT_ENTRY1("cosmoserve", "Final", "fce21feb3a6dfda1298d3eb3b46ef0b2", 377460),
+ DT_ENTRY0("cosmoserve", "e677a308c446af4e076a26ef0ca235ad", 365229),
+ DT_ENTRY0("crusade", "d7df6bc394d225ab023e4f099d982156", 50463),
+ DT_ENTRY0("agtdetective", "b17f780a90fa4e0e30e5bbf590f78cd5", 17901),
+ DT_ENTRY0("dragonschocolate", "6cb0714d337ed45ae03e6a54ed60fdc4", 143208),
+ DT_ENTRY0("disenchanted", "7003a85672bbfa067dc6a28a295a1ad1", 99630),
+ DT_ENTRY0("ducksoup", "e3c609c2a78e89b03c8cdefa19a50293", 83187),
+ DT_ENTRY1("dudleydilemma", "1.2", "2ff4de040b7cee9592bc8dc2e020d937", 111294),
+ DT_ENTRY1("dudleydilemma", "3.0", "4cdea9d3acc19f9a02072517e4bc463d", 190188),
+ DT_ENTRY0("cavesofdyanty", "267e8a2812d58e140be8582914d9cefb", 40662),
+ DT_ENTRY0("destinationearth", "d00cfa53e2b3315f0ee6813c064be74f", 12474),
+ DT_ENTRY0("80days", "0086c0151760c59aa4d9e8ca055de84d", 30294),
+ DT_ENTRY0("electrabot", "1c7096e4a9a0579526e9b5084aa27701", 8748),
+ DT_ENTRY0("elf20", "0fa1e888a452fec59bb4a5a6ffa43d78", 101088),
+ DT_ENTRY0("elfquest", "5419ab5d7a19037a5971c7e2de59cee4", 16929),
+ DT_ENTRY0("eliescape", "8d604abcccccbc0064b7488497f6242d", 72414),
+ DT_ENTRY0("escapeprisonisland", "8f6cf9b1f46e968b353bd00a48c2bd6b", 48762),
+ DT_ENTRY0("agtfable", "9acb005ddd793da7898eda2bbc79a9d3", 15147),
+ DT_ENTRY0("ccfirstadv", "8a8ff26cd6a396c193d865fa6e37594d", 83754),
+ DT_ENTRY0("firststupidgame", "859933f151a301f64f88a8101853f432", 21222),
+ DT_ENTRY0("ggollek", "e02fa5e1ddff57e89231481574218834", 75573),
+ DT_ENTRY0("agtghosttown", "33aa534de04a978c50f8a038a6bec3e7", 35235),
+ DT_ENTRY0("giganticsecrets", "66d6b6b5bf43149a8ad5578c45ad4731", 21627),
+ DT_ENTRY0("newenglandgothic", "10898900c3b872282a4298b32e851dfc", 104895),
+ DT_ENTRY0("grailmisadventure", "f7b0447cc01d1f4629e734952deccf98", 107487),
+ DT_ENTRY0("hardestadv", "326aaac9509503829e2b350b867c4ca5", 115263),
+ DT_ENTRY0("helvera", "aa1ba7a1f1726a90eec90b0eb998cce8", 104642),
+ DT_ENTRY0("highe", "8c08f8e0e215d1293398b0d652578baf", 15471),
+ DT_ENTRY0("sirramichobbs", "ba008ad6016d8302dd4311dd20ccb4e0", 132597),
+ DT_ENTRY0("holmescasebook", "391e0bd51cbf8bc4cfffe751a1a659b2", 256446),
+ DT_ENTRY0("hotelnotell", "0c54347ebbcfe32bbf143a1574cdb445", 111132),
+ DT_ENTRY0("house2house", "9e5ee1005108afc247063e5f770ab1cc", 78246),
+ DT_ENTRY0("agthugecave", "0364693bb31fb2e9a45927f9e542b1fa", 260415),
+ DT_ENTRY0("hurryhurry", "040ca0ed40cb4732b66c2ab9b68bca97", 165564),
+ DT_ENTRY0("jackofhartz", "74d754d8ce9bb7dca6f70b60c72ee27d", 97038),
+ DT_ENTRY0("klingonrpg", "93811c560f0c78f470f65dbe62834aa1", 15066),
+ DT_ENTRY0("deadlylabyrinth", "3a5d3ad2f80fb8c02baf5eb9894eb9b6", 113643),
+ DT_ENTRY0("library", "f23d106273f6e5fdb50f65d2acd4e4fc", 133407),
+ DT_ENTRY0("lostinspace", "322c226f26768b6962c2b3b147450410", 49410),
+ DT_ENTRY0("agtlottery", "7c0890c420d6585e4629f1cc228bf259", 24948),
+ DT_ENTRY0("loststonemansion", "f0ef6d965533e67b29acb130dd0f1213", 39933),
+ DT_ENTRY0("jubileeroad", "f24fef5bc936c22fbd84c0929d727cbf", 105543),
+ DT_ENTRY0("killjustin", "94d50b925733e70cf39079a8816b199c", 65043),
+ DT_ENTRY0("klaustrophobia1", "cbcc82df28e67d89399139e5f234d8fc", 242838),
+ DT_ENTRY0("klaustrophobia2", "b535015af4fece71c9f120730cb453dc", 292329),
+ DT_ENTRY0("klaustrophobia3", "47aad0cb89ebe10e54172db55124b8d1", 366039),
+ DT_ENTRY0("mdthief", "e62d36630c8a301a5da4192dfd28d650", 243729),
+ DT_ENTRY0("agtpyramids", "cb2aa53dea87209fee2e300cd5396e4a", 126522),
+ DT_ENTRY0("mopandmurder", "23c4a7ee63dbfb78871b7040a011cd89", 86913),
+ DT_ENTRY0("agtmhpquest", "5d657aac27f1dc150d74c50251584af0", 29646),
+ DT_ENTRY0("agtmst3k1", "53552013cadf6b62a5c8dcbb7f2af4a8", 127737),
+ DT_ENTRY0("agtmst3k2", "973cf89bf1cea65ebd8df72c6d01354d", 107001),
+ DT_ENTRY0("spacemule", "96cc0630552bc6a343e022777b40d9fd", 79056),
+ DT_ENTRY0("myopia", "b3f3d0ae4fe3bf1181fa437c69b90016", 69859),
+ DT_ENTRY0("nmr1", "c1758cd84fceade19866007f8d7c397f", 49734),
+ DT_ENTRY0("nmr2", "979ffa08dc3b102b59f6893e4a4dede9", 55485),
+ DT_ENTRY0("oceana", "63a163d87abf793a5e5c2f98f0d4c469", 178200),
+ DT_ENTRY0("agtodieus", "aef479600d4fb82f8eedbeda855a9706", 28512),
+ DT_ENTRY0("oklib", "d833679f11041ab1155b5207aabfc873", 166374),
+ DT_ENTRY0("ovanpelt", "60a49ce4b7f99968cf92ccef5ad403f7", 53298),
+ DT_ENTRY0("peterpatzer", "6a1be7e416f66c54b22e1305165fd7ee", 62842),
+ DT_ENTRY0("blackpearl", "12419db6d6088e66394ecf5f28baa68d", 80109),
+ DT_ENTRY0("battleofphilip", "8bbfd3d06b9eb4df0565e158e41312d8", 97443),
+ DT_ENTRY0("flightintofantasy", "063f4f434b64c25f2ca816a564edbe35", 100521),
+ DT_ENTRY0("pork1", "389deffc77cc58cce1ad8c0c57a5cfa8", 105948),
+ DT_ENTRY0("pork2", "13911c59cbe70ae877c87aa0ded89e47", 28269),
+ DT_ENTRY0("starportal", "0bf0f86fdeea607083c22a5cb41c6885", 172935),
+ DT_ENTRY0("pastoralpitfalls", "c35d440286c6bf67cd6ee1e5947c3483", 206469),
+ DT_ENTRY0("lostproperty", "8acf3d6994a3b39911827d5040e8873a", 30375),
+ DT_ENTRY0("gameofrecovery", "b497bb0e1e93023a892f0fa54d78a1c0", 108459),
+ DT_ENTRY0("rerunsagain", "d263341c871a2f52e0052c313bf3e525", 81648),
+ DT_ENTRY0("derring", "5553e1a6966525da7ab2d874090d3758", 52893),
+ DT_ENTRY0("sherwoodadv", "270be7ce551c615d4c34bc64acd4c190", 313551),
+ DT_ENTRY0("shapeshifteradv", "8a45a92074747edf8882ea2eaf6cfa54", 137862),
+ DT_ENTRY0("sirguygallant", "c4376d121b26bc691b6a43b9f77eb22a", 125698),
+ DT_ENTRY1("shadesofgray", "Final", "e93ed21cdafc1b998ee2ccab557f0649", 433350),
+ DT_ENTRY0("shadesofgray", "677753739047deb5ccf72f1b6555c677", 431568),
+ DT_ENTRY0("sonofstagefright", "9527fa27e910470deac8ffbcb29e2427", 116640),
+ DT_ENTRY0("spatent", "acc4c60cbb9d0239ab9b1900b239771a", 85455),
+ DT_ENTRY0("squynchia", "e9e5c99ee87f3b38a9ea8e7fdd1ed79f", 81000),
+ DT_ENTRY0("stiffy", "a7f1902ab7aa9972ca46d5b36d06d2b1", 32805),
+ DT_ENTRY0("storms1", "8567c2db37c80f015a950ef80d299a0a", 111942),
+ DT_ENTRY0("susan", "cb71705848aabcac90e7ea9e911ceee9", 15633),
+ DT_ENTRY0("tamoret", "3de37497ed763a58093e556a963ca14e", 156816),
+ DT_ENTRY0("tarabithia", "6734a6889d825dae528d2a7efaf6dee2", 83430),
+ DT_ENTRY0("tarksimmons", "cf6945fc43e8a3062a27fc39e01c3d6e", 116397),
+ DT_ENTRY0("tarotia", "fbeac90159dc88e82240b4201b6231d5", 61479),
+ DT_ENTRY0("tempest", "114b5224e7bb8def06a87c3910d7c4f3", 52650),
+ DT_ENTRY0("thegame", "af6e39aadf8dced6f29d03650125a6d6", 139968),
+ DT_ENTRY0("therift", "1c30da9b9a55d691226c45b8b53c11c3", 41877),
+ DT_ENTRY0("tja", "6699e867df8263dd7858d2f6fb84acde", 517185),
+ DT_ENTRY0("toho", "58a6fdf89b29966774beaca80f505fff", 228744),
+ DT_ENTRY0("tossedintospace", "515f06782c5b11108a719a20e182166c", 49491),
+ DT_ENTRY0("timesquared", "55e36771d5e1fe184cce8f5be666ff9f", 105300),
+ DT_ENTRY0("folkestone", "7e949a7376b0a64cee0d9412b0203611", 64557),
+ DT_ENTRY0("wanderer1", "0dcaff32c55dd2c1898da7893500de34", 53946),
+ DT_ENTRY0("wanderer2", "89dd16629022c75f3ffc171a6b126da6", 46980),
+ DT_ENTRY0("wanderer3", "839ab34bce5c82ec6194675f0186b15b", 45765),
+ DT_ENTRY0("weekendsurvival", "e770c0e75b7257eae9d4677340beca10", 91044),
+ DT_ENTRY0("witchfinder", "9acecd1803d2e99282970db1ef6ff344", 186300),
+ DT_ENTRY0("agtwizardscastle", "3adecad94b61babdadfbe20242e86b24", 18792),
+ DT_ENTRY0("hobbswok", "3178e271e8259a889df99545d6c65362", 198369),
+ DT_ENTRY0("wraithblaster", "392f507d42c006a30c55a20ec9e75f44", 194643),
+ DT_ENTRY0("journeyintoxanth", "2b073d48a8a01f91d7bca5db482e3ecd", 147177),
+ DT_ENTRY0("zanfar", "5fc6914fe02c0235f8a5343db8b6359e", 83106),
+
+ // Dutch games
+ DT_ENTRYL0("querido", Common::NL_NLD, "e52fe3a44d7b511bb362ce08a48435ef", 104166),
+
DT_END_MARKER
};
Commit: 28c3584148c49c1c6f118c5cbf13d0bf53d56726
https://github.com/scummvm/scummvm/commit/28c3584148c49c1c6f118c5cbf13d0bf53d56726
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2019-11-27T21:10:29-08:00
Commit Message:
GLK: AGT: Added subengine files
Changed paths:
A engines/glk/agt/agil.cpp
A engines/glk/agt/agility.h
A engines/glk/agt/agtread.cpp
A engines/glk/agt/agxfile.cpp
A engines/glk/agt/auxfile.cpp
A engines/glk/agt/config.h
A engines/glk/agt/debugcmd.cpp
A engines/glk/agt/disassemble.cpp
A engines/glk/agt/exec.cpp
A engines/glk/agt/exec.h
A engines/glk/agt/filename.cpp
A engines/glk/agt/gamedata.cpp
A engines/glk/agt/interface.cpp
A engines/glk/agt/interp.h
A engines/glk/agt/metacommand.cpp
A engines/glk/agt/object.cpp
A engines/glk/agt/os_glk.cpp
A engines/glk/agt/parser.cpp
A engines/glk/agt/runverb.cpp
A engines/glk/agt/savegame.cpp
A engines/glk/agt/token.cpp
A engines/glk/agt/util.cpp
A engines/glk/agt/vars.cpp
engines/glk/agt/agt.cpp
engines/glk/agt/agt.h
engines/glk/module.mk
diff --git a/engines/glk/agt/agil.cpp b/engines/glk/agt/agil.cpp
new file mode 100644
index 0000000..5bf19f7
--- /dev/null
+++ b/engines/glk/agt/agil.cpp
@@ -0,0 +1,980 @@
+/* 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 "glk/agt/agility.h"
+#include "glk/agt/interp.h"
+#include "common/str.h"
+
+namespace Glk {
+namespace AGT {
+
+/* ------------------------------------------------------------------- */
+/* Description Pointers */
+
+
+descr_ptr intro_ptr;
+descr_ptr title_ptr, ins_ptr; /* Only defined if agx_file is true */
+descr_ptr *err_ptr; /* [NUM_ERR];*/
+
+descr_ptr *msg_ptr; /* [MAX_MSG];*/
+descr_ptr *help_ptr, *room_ptr, *special_ptr; /*[ROOM] */
+descr_ptr *noun_ptr, *text_ptr, *turn_ptr, /* [NOUN] */
+ *push_ptr, *pull_ptr, *play_ptr;
+descr_ptr *talk_ptr, *ask_ptr, *creat_ptr; /* [CREAT] */
+
+descr_ptr *quest_ptr, *ans_ptr; /* [MAX_QUEST] */
+tline *question, *answer; /* [MAX_QUEST] */
+
+
+
+/* ------------------------------------------------------------------------ */
+/* Dynamically allocated data blocks (which are pointed to from elsewhere) */
+
+char **dict; /* dict[n] points to the nth dictionary word */
+long dp; /* Dictionary pointer: number of words in dict */
+
+#define DICT_INIT 12*1024 /* Starting size of dictstr */
+#define DICT_GRAN 1024 /* Granularity of dictstr size requests
+ must be at least 4. */
+char *dictstr; /* Pointer to memory block containing dict words */
+long dictstrptr, dictstrsize;
+/* dictstrptr points to the first unused byte in dictstr.
+ dictstrsize points to the end of the space currently allocated for
+ dictstr.
+*/
+
+char *static_str; /*Static string space */
+long ss_end; /* Pointer to end of used space in above */
+long ss_size; /* Current size of static string space */
+
+word *syntbl; /* Synonym list space */
+slist synptr; /* Points to end of used space */
+long syntbl_size; /* Long so we can catch overflows */
+
+long descr_maxmem;
+char *mem_descr; /* Copy of descriptor in memory */
+
+
+
+/* ------------------------------------------------------------------------ */
+/* Data structures used internally by agtread.c */
+
+/*The following are all set to NULL after agtread finishes. */
+long *cmd_ptr; /* ME only;Points to cmd start locs in gamefile.*/
+long *room_name, *noun_sdesc, *noun_pos, *creat_sdesc;
+long *t_pictlist, *t_pixlist, *t_songlist, *t_fontlist;
+
+/* These are only used by agtout (to allow the AGT reading routines to
+ pass back the count of nouns inside the given object) */
+integer *room_inside, *noun_inside, *creat_inside;
+
+/* This is used to translate ASCII codes */
+uchar fixchar[256];
+
+rbool text_file; /* Set if we are currently opening a binary file. */
+#ifdef OPEN_AS_TEXT
+rbool open_as_binary; /* Open text files as binary, anyhow. */
+#endif
+
+/* The following are AGT 'purity' flags; they turn off features of */
+/* my interpreter that are not fully consistent with the original AGT */
+/* and so could break some games. Some of these are trivial improvements; */
+/* some are more radical and should be used with caution. Several are */
+/* only useful if a game was designed with them in mind. */
+/* In all cases, setting the flag to 1 more closely follows the */
+/* behavior of the original interpreters */
+/* WARNING: Most of these haven't been tested in the non-default state. */
+/* Most of these will eventually become variables settable by the user */
+/* or from a (possibly game-specific) configuration file */
+
+rbool PURE_INPUT = 1; /* Is the input line bold? */
+
+rbool PURE_TONE = 0; /* Is low level sound enabled? */
+
+
+/*-------------------------------------------------------------------*/
+/* Misc. things to support the tokenizer and the dictionry. */
+/*-------------------------------------------------------------------*/
+
+/* The following should not be changed without also changing the
+ wtype enum statement in interp.h to match */
+static const char *ext_voc[] = {
+ "the", "my", "a", "an", /* These 4 are ignored in canonical AGT */
+ "then", ".", ";", "and", "," , "its", "all", "undo", "look", "g",
+ "pick", "go", "exits", "talk", "take", "door", "again", "but", "except",
+ "scene", "everything", "listexit", "listexits", "close",
+ "verb", "noun", "adjective", "prep", "object", "name", "step",
+ " any", "either", "both", "everyone", "everybody",
+ "he", "she", "it", "they", "him", "her", "them", "is", "are", "oops",
+ "was", "were",
+ /* Everything between 'in' and 'about' should be a preposition */
+ "in", "out", "into", "at", "to", "across", "inside", "with", "near", "for",
+ "of", "behind", "beside", "on", "off", "under", "from", "through",
+ "toward", "towards", "between", "around", "upon", "thru",
+ "by", "over", "up", "down",
+ "about"
+};
+/* 'about' must be the last element of this list */
+
+
+/*-------------------------------------------------------------------*/
+/* Routines to read in and use various auxilary files. */
+/* (.TTL, .INS, .VOC, .CFG) */
+/*-------------------------------------------------------------------*/
+
+static rbool emptyline(unsigned char *s)
+/* Check if s consists only of white space and control characters */
+{
+ unsigned char *p;
+
+ for (p = s; *p != 0; p++)
+ if (!rspace(*p) && *p > 26) return 0;
+ return 1;
+}
+
+static void print_title(fc_type fc) {
+ int height;
+ signed char center_mode; /* Center title? */
+ descr_line *buff;
+ char *s;
+ rbool skip_line; /* Skip first line: it has COLORS */
+
+ if (agx_file)
+ buff = read_descr(title_ptr.start, title_ptr.size);
+ else
+ buff = read_ttl(fc);
+
+ if (buff == NULL) {
+ writeln("");
+ writeln("");
+ s = formal_name(fc, fNONE);
+ if (s != NULL) {
+ s[0] = toupper(s[0]);
+ agt_center(1);
+ agt_textcolor(-1);
+ writeln(s);
+ agt_textcolor(-2);
+ agt_center(0);
+ rfree(s);
+ }
+ writeln("");
+ writeln("");
+
+ if (aver < AGX00)
+ writeln("This game was created with Malmberg and Welch's Adventure "
+ "Game Toolkit; it is being executed by");
+ else writeln("This game is being executed by ");
+ writeln("");
+ height = 0;
+ } else {
+ if (buff[0] != NULL && strncasecmp(buff[0], "COLORS", 6) == 0) {
+ /* Do screen colors */
+ skip_line = 1;
+ } else skip_line = 0;
+ /* Compute height and count the number of non-empty lines
+ starting with spaces. We use height as a loop variable
+ and center_mode to store the count temporarily. */
+ center_mode = 0;
+ for (height = skip_line; buff[height] != NULL; height++)
+ if (!emptyline((uchar *)buff[height])) {
+ if (rspace(buff[height][0])) center_mode++;
+ else center_mode--;
+ }
+
+ if (box_title || aver == AGTCOS) center_mode = TB_CENTER;
+ else /* includes aver==AGT135 */
+ if (center_mode <= 0) center_mode = TB_CENTER;
+ else center_mode = TB_NOCENT;
+
+ if (!bold_mode) agt_textcolor(-1);
+ agt_clrscr();
+ textbox(buff + skip_line, height - skip_line, center_mode |
+ (bold_mode ? 0 : TB_BOLD) | TB_TTL |
+ (box_title ? TB_BORDER : 0));
+ if (!bold_mode) agt_textcolor(-2); /* Bold off */
+ } /* End printing of title proper */
+
+ if (agx_file)
+ free_descr(buff);
+ else
+ free_ttl(buff);
+
+ agt_textcolor(7);
+ agt_center(1);
+ if (buff != NULL) {
+ if (aver < AGX00 && height <= screen_height - 6)
+ writeln("[Created with Malmberg and Welch's Adventure Game Toolkit]");
+ if (height <= screen_height - 9) writeln("");
+ if (height <= screen_height - 5) writeln("This game is being executed by");
+ }
+ agt_textcolor(-1);
+ s = (char *)rmalloc(80);
+ if (height <= screen_height - 5)
+ sprintf(s, "AGiliTy: "
+ "The (Mostly) Universal AGT Interpreter %s", version_str);
+ else
+ sprintf(s, "Being run by AGiliTy %s, "
+ "Copyright (C) 1996-99,2001 Robert Masenten",
+ version_str);
+ writeln(s);
+ rfree(s);
+ agt_textcolor(-2);
+ if (height <= screen_height - 5)
+ writeln("Copyright (C) 1996-99,2001 by Robert Masenten");
+ if (height <= screen_height - 3) writeln(portstr);
+ if (height <= screen_height - 10) writeln("");
+ agt_center(0);
+}
+
+
+
+/* .INS reading routines -------------------------------------- */
+
+void print_instructions(fc_type fc) {
+ char *buffer;
+ uchar *s;
+
+ writeln("INSTRUCTIONS:");
+ if (open_ins_file(fc, 1)) { /* Instruction file exists */
+ while (NULL != (buffer = read_ins_line())) {
+ for (s = (uchar *)buffer; *s != 0; s++) *s = trans_ascii[*s];
+ writeln(buffer);
+ }
+ }
+ writeln("");
+}
+
+/* Routines to build the verb menu from the .VOC information */
+
+static void build_verbmenu(void) {
+ int i, n;
+ char *p, *d;
+
+ verbmenu = (menuentry *)rmalloc(vm_size * sizeof(menuentry));
+ vm_width = 0;
+ for (i = 0; i < vm_size; i++) {
+ p = verbmenu[i];
+ d = dict[verbinfo[i].verb];
+ n = 0;
+ for (; n < MENU_WIDTH && *d != 0; p++, d++, n++) *p = *d;
+ if (verbinfo[i].prep != 0 && n + 1 < MENU_WIDTH) {
+ *p++ = ' ';
+ d = dict[verbinfo[i].prep];
+ *p++ = toupper(*d++);
+ for (; n < MENU_WIDTH && *d != 0; p++, d++, n++) *p = *d;
+ }
+ verbmenu[i][0] = toupper(verbmenu[i][0]);
+ *p = 0;
+ if (n > vm_width) vm_width = n;
+ }
+}
+
+/* .CFG reading routines -------------------------------------------- */
+
+#define opt(s) (strcasecmp(optstr[0],s)==0)
+
+/* These are the interpreter specific options; this is called
+ from cfg_option in agtdata.c. */
+void agil_option(int optnum, char *optstr[], rbool setflag, rbool lastpass) {
+ if (opt("ibm_char")) fix_ascii_flag = !setflag;
+ else if (!lastpass) return; /* On the first pass through the game specific
+ file, we ignore all but the above options */
+ else if (opt("tone")) PURE_TONE = setflag;
+ else if (opt("input_bold")) PURE_INPUT = setflag;
+ else if (opt("force_load")) FORCE_VERSION = setflag;
+ else if (!agt_option(optnum, optstr, setflag)) /* Platform specific options */
+ rprintf("Invalid option %s\n", optstr[0]);
+}
+
+/*-------------------------------------------------------------------*/
+/* Tokeniser: Split input into words and look them up in dictionary */
+/*-------------------------------------------------------------------*/
+
+static rbool noise_word(word w) {
+ if (w == ext_code[wthe] || w == ext_code[wa] || w == ext_code[wan]) return 1;
+ if (w == ext_code[wmy]) return 1;
+ if (aver >= AGT18 && aver <= AGT18MAX && w == ext_code[wis]) return 1;
+ return 0;
+}
+
+
+static rbool check_dot(char *prevtext, int prevcnt, char *lookahead)
+/* This routine is devoted to trying to figure out whether
+ we should treat '.' as punctuation or as a letter. */
+/* It returns true if '.' should be treated as punctuation. */
+/* prevtext=the current word, as far as it has been parsed.
+ prevcnt=the number of letters in prevtext
+ [which is *not* 0 terminated] */
+/* lookahead=the rest of the current input line *after* the period. */
+{
+ int i, endword, restcnt;
+
+ if (!PURE_DOT) return 1; /* No words with periods in them, so it must
+ be punctuation. */
+ /* We just start scanning the dictionary to see if any of them
+ are possible matches, looking ahead as neccessary. */
+
+ /* Find the next unambiguous word end. This ignores possible
+ word ends caused by periods. */
+ for (endword = 0; lookahead[endword] != 0; endword++)
+ if (isspace(lookahead[endword]) ||
+ lookahead[endword] == ',' || lookahead[endword] == ';')
+ break;
+
+ for (i = 0; i < dp; i++) {
+ if (i == ext_code[wp]) continue; /* Ignore matches with the word ".". */
+
+ /* If it doesn't contain a '.' at the right location, there is no
+ point in continuing. */
+ restcnt = strlen(dict[i]);
+ if (restcnt <= prevcnt || dict[i][prevcnt] != '.') continue;
+
+ /* Now make sure the previous characters are correct */
+ if (strncasecmp(prevtext, dict[i], prevcnt) != 0) continue;
+
+ /* Finally, compare the trailing text. This is complicated by
+ the fact that the trailing text could itself contain ambiguous '.'s */
+ restcnt -= prevcnt + 1; /* Number of characters in dict entry after '.' */
+ if (restcnt > endword) continue; /* Dictionary entry is longer than
+ following text */
+
+ /* Check to see if the dictionary entry can be found in the lookahead
+ buffer */
+ if (strncasecmp(lookahead, dict[i] + prevcnt + 1, restcnt) != 0) continue;
+
+ if (restcnt == endword) return 0; /* We have a match */
+ /* At this point, we know that restcnt<endword and the dictionary
+ entry matches as far as restcnt. */
+ /* endword ignores '.', though, so it could be we have a match
+ but are missing it because it is period-terminated. Check this. */
+ if (lookahead[restcnt] == '.') return 0;
+
+ /* Otherwise, no match... try again with the next word... */
+ }
+ return 1; /* No matches: treat it as punctuation. */
+}
+
+
+
+
+
+
+static void tokenise(char *buff)
+/* Convert input string into vocabulary codes */
+/* 0 here denotes an unrecognized word and -1 marks the end. */
+{
+ int ip_, j, k;
+ rbool punctuation;
+
+ j = 0;
+ ip_ = 0;
+ k = 0; /* k is the character pointer */
+ for (k = 0;; k++) {
+ /* If PURE_DOT is set, then there are periods in some of the dictionary
+ words, so '.' could be a letter or punctuation-- we have to examine
+ context to figure out which. */
+ if (buff[k] == '.' && PURE_DOT)
+ /* Note: check_dot is in agtdata.c, since it needs to access
+ internal implementation details of the dictionary */
+ punctuation = check_dot(in_text[ip_], j, buff + k + 1);
+ else
+ punctuation = (buff[k] == ',' || buff[k] == ';' || buff[k] == '.');
+ if (buff[k] != 0 && !isspace(buff[k]) && !punctuation) {
+ if (j < WORD_LENG - 1)
+ in_text[ip_][j++] = buff[k];
+ } else if (j > 0) { /* End of word: add it to input */
+ in_text[ip_][j] = 0;
+ input[ip_] = search_dict(in_text[ip_]);
+ if (input[ip_] == -1) input[ip_] = 0;
+ else if (input[ip_] == 0) input[ip_] = ext_code[w_any]; /* _Real_ 'ANY' */
+ if (!noise_word(input[ip_])) ip_ += 1;
+ /* i.e. if not one of the four ignored words, advance */
+ j = 0;
+ } /* If j=0 and not punct, then no new word; just skip the whitespace */
+ if (punctuation) {
+ in_text[ip_][0] = buff[k];
+ in_text[ip_][1] = 0;
+ input[ip_] = search_dict(in_text[ip_]);
+ if (input[ip_] == -1) input[ip_] = 0;
+ j = 0;
+ ip_++;
+ }
+ if (ip_ >= MAXINPUT - 1) {
+ writeln("Too many words in input; ignoring rest of line.");
+ break;
+ }
+ if (buff[k] == 0) break;
+ }
+ input[ip_] = -1;
+ in_text[ip_][0] = 0;
+}
+
+
+
+/*-------------------------------------------------------------------*/
+/* Main game loop: Get player input and call the parser. */
+/*-------------------------------------------------------------------*/
+
+static void game_end(void) {
+ rbool done_flag;
+ char *s;
+
+ if (winflag || deadflag) {
+ writeln("");
+ writeln("");
+ agt_center(1);
+ if (winflag)
+ gen_sysmsg(148, "***** $You$ have won! *****", MSG_MAIN, NULL);
+ if (deadflag)
+ gen_sysmsg(147, "***** $You$ have died! *****", MSG_MAIN, NULL);
+ writeln("");
+ writeln("");
+ agt_center(0);
+ }
+ if (deadflag && !endflag) {
+ if (curr_lives > 1) { /* Resurrection code */
+ if (curr_lives == max_lives)
+ gen_sysmsg(151, "Hmmm.... so $you$'ve gotten $your$self killed. "
+ "Would you like me to try a resurrection?", MSG_MAIN, NULL);
+ else gen_sysmsg(152, "<Sigh> $You$'ve died *again*. "
+ "Would you like me to try another resurrection?",
+ MSG_MAIN, NULL);
+ if (yesno("? ")) { /* Now do resurrection */
+ curr_lives--;
+ quitflag = deadflag = 0;
+ gen_sysmsg(154,
+ "$You$ emerge coughing from a cloud of dark green smoke.",
+ MSG_MAIN, NULL);
+ writeln("");
+ loc = resurrect_room - first_room;
+ newlife_flag = 1;
+ set_statline();
+ do_look = do_autoverb = 1;
+ newroom();
+ return;
+ } else writeln("As you wish...");
+ } else if (max_lives > 1)
+ gen_sysmsg(153, "$You$'ve used up all of $your$ lives.", MSG_MAIN, NULL);
+ }
+ writeln("");
+ print_score();
+ writeln("");
+ done_flag = quitflag; /* If player has QUIT, don't ask again */
+ while (!done_flag && !quitflag) {
+ writestr("Would you like to ");
+ if (restart_state != NULL) writestr("restart, ");
+ writestr("restore");
+ if (undo_state != NULL && can_undo)
+ writestr(", undo,");
+ else if (restart_state != NULL) writestr(",");
+ writestr(" or quit? ");
+ s = agt_readline(5);
+ if (strncasecmp(s, "RESTART", 7) == 0)
+ if (restart_state != NULL) {
+ restart_game();
+ done_flag = 1;
+ } else writeln("Sorry, I'm unable to do that because of limited memory.");
+ else if (strncasecmp(s, "RESTORE", 7) == 0)
+ if (loadgame()) {
+ done_flag = 1;
+ } else writeln("(RESTORE failed)");
+ else if (strncasecmp(s, "UNDO", 4) == 0)
+ if (can_undo && undo_state != NULL) {
+ putstate(undo_state);
+ done_flag = 1;
+ } else writeln("Insufficiant memory to support UNDO");
+ else if (toupper(s[0]) == 'Q') {
+ quitflag = 1;
+ done_flag = 1;
+ }
+ }
+ set_statline();
+}
+
+
+static void parse_loop(void)
+/* This exists to deal with THEN lists; parse() handles the indiviudual
+ commands */
+{
+ for (ip = 0; ip >= 0 && ip < MAXINPUT && input[ip] != -1;) {
+ if (!parse() || quitflag || winflag || deadflag || endflag) break;
+ if (doing_restore) break;
+ if (ip >= 0 && ip < MAXINPUT && input[ip] != -1)
+ writeln(""); /* Insert blank lines between commands when dealing
+ with THEN lists */
+ }
+}
+
+
+static long rm_start_size;
+static char memstr[100];
+
+static void mainloop(void) {
+ char *s;
+
+ doing_restore = 0;
+ while (!quitflag) {
+ if (DEBUG_MEM) {
+ sprintf(memstr,
+ "A:%ld F:%ld Delta:%ld Size:%ld+%ld=%ld (%ld left)\n",
+ ralloc_cnt, rfree_cnt, ralloc_cnt - rfree_cnt,
+ rm_start_size, rm_size - rm_start_size, rm_size,
+ rm_freesize);
+ writeln(memstr);
+ }
+ rm_size = 0; /* Reset it to zero */
+ rm_freesize = get_rm_freesize();
+ if (!menu_mode) {
+ prompt_out(1);
+ s = agt_readline(0);
+ agt_newline();
+ if (!doing_restore) tokenise(s); /* Tokenizes into input */
+ rfree(s);
+ if (!doing_restore) parse_loop();
+ } else
+ menu_cmd();
+ if (doing_restore) {
+ if (doing_restore == 1) loadgame();
+ else if (doing_restore == 2) restart_game();
+ else if (doing_restore == 3 || doing_restore == 4)
+ return; /* Quit or New game requested */
+ doing_restore = 0;
+ }
+ if (winflag || deadflag || endflag || quitflag)
+ game_end();
+ }
+}
+
+
+/*-------------------------------------------------------------------*/
+/* Start up and shut down: Routines to initialise the game state and */
+/* clean up after the game ends. */
+/*-------------------------------------------------------------------*/
+
+static int init(void) {
+ int i, can_save;
+ uchar *tmp1, *tmp2;
+
+ init_vals();
+ init_creat_fix();
+ if (!agx_file) dict[0][0] = 0; /* Turn "ANY" into "" */
+ l_stat[0] = r_stat[0] = 0; /* Clear the status line */
+ /* lactor=lobj=lnoun=NULL;*/
+ tscore = old_score = objscore = 0;
+ turncnt = 0;
+ curr_time = startup_time;
+ loc = start_room - first_room;
+ cmd_saveable = 0;
+ first_visit_flag = newlife_flag = room_firstdesc = 1;
+ curr_lives = max_lives;
+
+ /* Note: flag[0] is the debugging flag and is set elsewhere */
+ if (FLAG_NUM < 0) FLAG_NUM = 0;
+ dbgflagptr = flag = (rbool *)rrealloc(flag, sizeof(rbool) * (FLAG_NUM + 1));
+ for (i = 1; i <= FLAG_NUM; i++)
+ flag[i] = 0;
+ dbgcntptr = agt_counter = (short *)rmalloc(sizeof(short) * (CNT_NUM + 1));
+ for (i = 0; i <= CNT_NUM; i++) {
+ agt_counter[i] = -1;
+ }
+ dbgvarptr = agt_var = (long *)rmalloc(sizeof(*agt_var) * (VAR_NUM + 1));
+ for (i = 0; i <= VAR_NUM; i++)
+ agt_var[i] = 0;
+
+ for (i = 0; i <= maxnoun - first_noun; i++) {
+ if (noun[i].position == NULL || noun[i].position[0] == '\0')
+ noun[i].pos_prep = 0;
+ else noun[i].pos_prep = -1;
+ noun[i].pos_name = 0;
+ noun[i].initpos = noun[i].position;
+ }
+
+ nomatch_aware = 0; /* By default, not aware. */
+ smart_look = 1; /* By default, LOOK <x> --> EXAMINE */
+ for (i = 0; i < last_cmd; i++) {
+ if (command[i].nouncmd == -1 || command[i].objcmd == -1
+ || command[i].noun_adj == -1 || command[i].obj_adj == -1
+ || command[i].prep == -1)
+ nomatch_aware = 1;
+ if (command[i].verbcmd == ext_code[wlook] &&
+ (command[i].nouncmd > 0 || command[i].noun_adj > 0
+ || command[i].objcmd > 0 || command[i].obj_adj > 0
+ || command[i].prep > 0))
+ smart_look = 0;
+ }
+
+ pictable = (integer *)rmalloc(sizeof(int) * maxpict);
+ for (i = 0; i < maxpict; i++) pictable[i] = i;
+ init_state_sys(); /* Initialize the system for saving and restoring
+ game states */
+ tmp1 = (uchar *)rmalloc(MEM_MARGIN); /* Preserve some work space */
+
+ tmp2 = getstate(NULL); /* Make sure we have space to save */
+ if (tmp2 == NULL) can_save = 0;
+ else can_save = 1;
+
+ if (tmp2 != NULL)
+ undo_state = getstate(NULL);
+ else undo_state = NULL;
+
+ if (undo_state != NULL)
+ restart_state = getstate(NULL);
+ else restart_state = NULL;
+
+ rfree(tmp1);
+ rfree(tmp2);
+ rm_start_size = get_rm_size();
+ rm_freesize = get_rm_freesize();
+ return can_save;
+}
+
+
+static void ext_dict(void)
+/* Enter the vocabulary extensions into the dictionary */
+{
+ wtype i;
+ for (i = wthe; i <= wabout; i = (wtype)((int)i + 1))
+ ext_code[i] = add_dict(ext_voc[i]);
+}
+
+
+static void fix_dummy(void) {
+ int i;
+
+ /* At this point, all occurances in the game file of the dictionary
+ words have been converted to dictionary indices, and so as long as
+ we don't change the dictionary index values, we can change the contents
+ without interfering with the metacommand scanner (since it compares
+ dictionary indices, not actual strings) */
+
+ if (!PURE_DUMMY) {
+ for (i = 0; i < DUMB_VERB; i++)
+ dict[ syntbl[auxsyn[i + BASE_VERB]] ][5] = ' ';
+ /* Convert underscores into spaces:
+ i.e. 'dummy_verb5' -> 'dummy verb5' */
+ dict[ syntbl[auxsyn[21]] ][6] = ' '; /* change_locations */
+ dict[ syntbl[auxsyn[55]] ][5] = ' '; /* magic_word */
+ }
+
+ if (!PURE_SUBNAME) /* Replace the 'e' by a space */
+ for (i = 0; i < MAX_SUB; i++)
+ sprintf(dict[sub_name[i]], "subroutin %d", i + 1);
+ /* This must be no longer than 25 characters with the terminating null */
+
+ /* Now set PURE_DOT based on whether any dictionary word
+ contains a period. */
+ if (aver >= AGT18 && aver <= AGT18MAX) PURE_DOT = 0;
+ else {
+ PURE_DOT = FORCE_PURE_DOT;
+ for (i = 0; i < dp && !PURE_DOT; i++)
+ if (strchr(dict[i], '.') != NULL && /* i.e. dict[i] contains period */
+ i != ext_code[wp]) /* The period itself _is_ a dictionary word:
+ avoid this false match */
+ PURE_DOT = 1;
+ }
+}
+
+
+/* char *v */
+
+static void print_license(void) {
+ writeln("AGiliTy");
+ writestr("The (Mostly) Universal AGT Interpreter, ");
+ writeln(version_str);
+ writeln(" Copyright (C) 1996-1999,2001 by Robert Masenten");
+ writestr("[");
+ writestr(portstr);
+ writeln("]");
+ writeln("-----------------------------------------------------------");
+ writeln("");
+ writeln(" This is an interpreter for game files created with Malmberg and "
+ "Welch's _Adventure Game Toolkit_. AGiliTy is universal in the "
+ "sense that it understands and interprets most of the many versions "
+ "of the AGT game file format.");
+ writeln(" It is *not* a port of the original interpreters but rather a "
+ "completely new interpreter built around the game file format; "
+ "while it follows the original interpreters on most things, there "
+ "are some differences which are described in the file "
+ "'readme.agility' which should have come with this program.");
+ writeln("");
+ writeln(" This software is copyright 1996-1999,2001 by Robert Masenten ");
+ writeln(" This program is free software; you can redistribute it and/or "
+ "modify it under the terms of version 2 of the GNU General "
+ "Public License as published by the Free Software Foundation.");
+ writeln(" 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.");
+ writeln(" 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");
+ writeln("");
+ writeln(" Send comments and bug reports to Robert Masenten at:");
+ writeln(" rcm-math at pacbell.net");
+ writeln("");
+ writeln("ACKNOWLEDGMENTS");
+ writeln("Thanks to Jay Goemmer, who has sent me pages and pages of "
+ "comments and bug reports; "
+ "David Kinder, responsible for the Amiga and Windows ports and a "
+ "source of much valuble feedback; "
+ "David Youd, who has uncovered many subtle and complex bugs in "
+ "both AGiliTy and Magx; "
+ "Mitch Mlinar, who has contributed several patches and other "
+ "suggestions; "
+ "all of those who have sent me suggestions and bug reports, "
+ "including "
+ "Audrey DeLisle (responsible for the red smoke), David Doherty,"
+ "Al Golden, "
+ "John Hartnup, Walter Isaac, Sami Kivela, Alexander Lehmann, "
+ "Grant E. Metcalf, "
+ "Paul Mikell, Adam Myrow, Olav Nielsen, "
+ "D.J. Picton, Kevin Soucy, Ben Straub, \"Grand Moff Tarkin\", "
+ "Adam Thornton, "
+ "Mark Tilford, David Turpin, and Gil Williamson; "
+ "Volker Blasius,"
+ " original maintainer of the Interactive Fiction Archive; "
+ "Robert Pelak, who suggested the name \"AGiliTy\"; "
+ "and to everyone on Rec.arts.int-fiction who suggested names for "
+ "my interpreter.");
+ writeln("");
+ writeln("SPECIAL VERBS RECOGNIZED");
+ writeln("These are all of the special verbs recognized by the interpreter:");
+ writeln(" SCORE Print out your score.");
+ writeln(" NOTIFY Turn score notification on and off.");
+ writeln(" INSTRUCTIONS or INS Display the instructions for the game.");
+ writeln(" INTRODUCTION or INTRO Repeat the introduction of the game.");
+ writeln(" VIEW <picture> Views an illustration. (Not supported on all "
+ "platforms.)");
+ writeln(" BRIEF Don't print room descriptions for rooms you've seen.");
+ writeln(" VERBOSE Print room descriptions even for rooms you've already "
+ "seen.");
+ writeln(" LIST EXITS List the exits from a room.");
+ writeln(" LISTEXIT ON/OFF Turn on/off automatic listing of exits.");
+ writeln(" SCRIPT Start sending a transcript to a file.");
+ writeln(" UNSCRIPT Stop creating a transcript.");
+ writeln(" SOUND ON, OFF Turn sound on/off.");
+ writeln(" LOG Start sending all of your commands to a file.");
+ writeln(" REPLAY <number> Replay your commands from a file, "
+ "executing one every <number> seconds.");
+ writeln(" REPLAY FAST Replay your commands from a file without waiting "
+ "for you to read the scrolling text.");
+ writeln(" REPLAY STEP Replay your commands from a file, "
+ "one for every keypress.");
+ writeln(" AGILDEBUG Access debugging commands.");
+ writeln(" MENU Toggle menu mode on or off.");
+ writeln(" OOPS Correct a word you just mistyped; must be the first "
+ "command on a line.");
+ writeln(" UNDO Undo your last move; must be the first command on a line.");
+ writeln(" SAVE Save the game.");
+ writeln(" RESTORE Restore the game.");
+ writeln(" RESTART Restart the game.");
+ writeln(" QUIT Quit.");
+ writeln("");
+}
+
+
+/* This is a hack to get rid of the "What Now?" prompt. */
+static void fix_prompt(void) {
+ descr_line *d;
+
+ if (err_ptr == NULL) return;
+ d = read_descr(err_ptr[0].start, err_ptr[0].size);
+ if (d == NULL) return;
+ if (strncasecmp(d[0], "What Now?", 9) == 0)
+ err_ptr[0].size = err_ptr[0].start = 0;
+ free_descr(d);
+}
+
+
+
+void close_game(void); /* Called by setup_game, and so needs
+ to be defined here. */
+
+static fc_type setup_game(fc_type fc)
+/* game_name is the common filename of the AGT game files */
+{
+ int can_save;
+ char choice;
+ rbool have_ins;
+
+ bold_mode = 0;
+ rm_acct = 1;
+ rm_trap = 1;
+ rm_size = ralloc_cnt = rfree_cnt = 0;
+ mars_fix = 0;
+ no_auxsyn = 0;
+ debug_disambig = 0;
+ debug_any = 1;
+ dbg_nomsg = 1; /* Supress output of MSG arguments to metacommands */
+ textbold = 0;
+ debug_mode = 0;
+ aver = 0;
+ verboseflag = 1;
+ notify_flag = 0;
+ logflag = 0;
+ menu_mode = 0;
+ fast_replay = 0;
+ stable_random = BATCH_MODE || make_test;
+ if (make_test) BATCH_MODE = 0;
+ hold_fc = fc;
+ set_default_filenames(fc);
+
+ init_stack();
+ read_config(agt_globalfile(0), 1); /* Global configuration file */
+
+ /* Now that we *have* PATH information, go looking for the games */
+ /* At the very least, it creates an rmalloc'd copy of game_name */
+ read_config(openfile(fc, fCFG, NULL, 0), 0);
+ text_file = 0;
+ /* First pass through game specific config file */
+ build_trans_ascii();
+#ifdef PROFILE
+ resetwatch();
+#endif
+ writeln("Loading game...");
+ if (!read_agx(fc, 0) && !readagt(fc, 0))
+ fatal("Unable to load game.");
+#ifdef PROFILE
+ writeln(stopwatch());
+ agt_waitkey();
+#endif
+ if (have_opt)
+ menu_mode = opt_data[5]; /* See agtread.c for discussion of OPT file
+ format */
+ text_file = 1;
+ read_config(openfile(fc, fCFG, NULL, 0), 1); /*Game specific config file*/
+ text_file = 0;
+ if (min_ver > AGIL_VERID) {
+ if (FORCE_VERSION)
+ agtwarn("This game requires a later version of AGiliTy.", 0);
+ else
+ fatal("This game requires a later version of AGiliTy.");
+ }
+ sort_cmd();
+ ext_dict();
+ build_verbmenu();
+ fix_dummy(); /* Prevent player from calling dummy verbs or subroutines by
+ typing 'Subroutine n' on the command line */
+ can_save = init();
+ if (!agx_file) open_descr(fc);
+ fix_prompt(); /* Kill off 'What Now?' prompt. */
+ if (BATCH_MODE || make_test)
+ set_test_mode(fc);
+ start_interface(fc);
+ fontcmd(2, 0); /* Set initial font */
+ if (intro_first && intro_ptr.size > 0) {
+ agt_clrscr();
+ print_descr(intro_ptr, 1);
+ wait_return();
+ }
+ if (aver >= AGTME10)
+ pictcmd(3, 0); /* Show title image, if there is one */
+ print_title(fc);
+ have_ins = open_ins_file(fc, 0);
+ do {
+ if (have_ins)
+ writestr("Choose <I>nstructions, <A>GiliTy Information, "
+ "or <other> to start the game");
+ else
+ writestr("Choose <A>GiliTy Information or <other> to start the game");
+ choice = tolower(agt_getchar()); /* Wait for keypress */
+ agt_clrscr();
+ if (have_ins && choice == 'i') print_instructions(fc);
+ else if (choice == 'a') print_license();
+ } while ((choice == 'i' && have_ins) || choice == 'a');
+ close_ins_file();
+ if (!intro_first && intro_ptr.size > 0) {
+ print_descr(intro_ptr, 1);
+ wait_return();
+ agt_clrscr();
+ }
+ if (maxroom < first_room) {
+ close_game();
+ error("Invalid first room");
+ }
+ set_statline();
+ if (can_save == 0) {
+ writeln("[Insufficiant memory to support SAVE, RESTORE, or UNDO]");
+ } else if (undo_state == NULL)
+ writeln("[Insufficiant memory to support UNDO]");
+ do_look = do_autoverb = 1;
+ newroom();
+ rm_acct = 1; /* Turn on memory allocation accounting */
+ return fc;
+}
+
+
+/* We need to import save_lnoun from exec.c so that we can free it. */
+extern parse_rec *save_lnoun;
+
+void close_game(void) {
+ if (agx_file)
+ agx_close_descr();
+ else
+ close_descr();
+ fontcmd(1, -1); /* Restore original font */
+ musiccmd(7, -1); /* Clean up */
+ close_interface();
+
+ /* Now free everything in sight; this _shouldn't_ be necessary,
+ but why take chances? */
+ free_all_agtread();
+ rfree(restart_state);
+ rfree(undo_state);
+ rfree(pictable);
+ rfree(save_lnoun);
+ rfree(verbptr);
+ rfree(verbend);
+ rfree(agt_counter);
+ rfree(agt_var);
+ free_creat_fix();
+ flag = (rbool *)rrealloc(flag, sizeof(rbool)); /* Preserve the debugging flag */
+
+ if (DEBUG_MEM)
+ debug("\n\nAlloc:%ld Freed:%ld Difference:%ld\n", ralloc_cnt,
+ rfree_cnt, ralloc_cnt - rfree_cnt);
+}
+
+
+void run_game(fc_type fc) {
+ doing_restore = 0;
+ rm_acct = 1;
+ rm_trap = 1;
+ rm_size = ralloc_cnt = rfree_cnt = 0;
+ read_config(agt_globalfile(0), 1); /* Global configuration file:
+ get PATH information*/
+ fix_file_context(fc, fDA1);
+ do {
+ if (doing_restore == 3) {
+ release_file_context(&fc);
+ fc = setup_game(new_game());
+ } else setup_game(fc);
+ doing_restore = 0;
+ mainloop();
+ close_game();
+ } while (doing_restore == 3);
+ release_file_context(&fc);
+}
+
+} // End of namespace AGT
+} // End of namespace Glk
diff --git a/engines/glk/agt/agility.h b/engines/glk/agt/agility.h
new file mode 100644
index 0000000..34c125a
--- /dev/null
+++ b/engines/glk/agt/agility.h
@@ -0,0 +1,1223 @@
+/* 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 GLK_AGT_AGILITY
+#define GLK_AGT_AGILITY
+
+#include "glk/agt/config.h"
+#include "common/str.h"
+
+namespace Glk {
+namespace AGT {
+
+/* This indicates the AGiliTy version code for the current version. */
+/* 0=0.8.7 and the numbers are assigned sequentially from there.
+ 1=0.8.8
+ 2=1.0
+ (pre-0.8.7 versions of AGiliTy don't have version codes) */
+/* Don't touch this unless you know exactly what you're doing. */
+#define AGIL_VERID 3
+
+/*
+ This is the master header file for all of the AGT stuff.
+ It includes the global variables, the data types, etc.
+ (everything that is read in from the game file).
+ Variables not read in from the game file but used internally
+ by the AGiliTy interpreter are declared in interp.h.
+ Magx specific things are in comp.h.
+*/
+
+/* ------------------------------------------------------------------- */
+/* This is a preprocessor trick to ensure that space is only allocated
+ for global variables once. 'global' should only be defined in one
+ source file; for all of the other modules, it will be converted to
+ extern by the following lines */
+/* ------------------------------------------------------------------- */
+#ifndef global /* Don't touch this */
+#define global extern
+#define global_defined_agtread
+#endif
+
+
+/* ------------------------------------------------------------------- */
+/* DEFINITIONS OF SPECIAL DATA TYPES */
+/* These should by platform-independent. */
+/* ------------------------------------------------------------------- */
+
+#ifdef force16 /* This is for debugging purposes */
+#define int short int
+#endif
+
+/* General data types */
+
+typedef byte uchar;
+typedef int8 schar;
+typedef int16 integer;
+typedef uchar rbool;
+
+enum { INT8_MAX_VAL = 127, BYTE_MAX_VAL = 255 };
+
+#define WORD_LENG 25
+
+/* Game-specific data type */
+typedef char tline[81]; /* Both of these must include terminating null */
+typedef char words[WORD_LENG]; /* ...23 in classic, 16 in master's */
+typedef short word; /* A pointer into the dictionary */
+typedef short slist; /* Index into synlist marking beginning of this
+ synonym list. [SYNCNT]
+ list of word pointers -- eg synonyms */
+typedef char *descr_line; /* This is the type used to return descriptions.
+ They are actually returned as an array of
+ pointers to char, one for each line.
+ It is NULL terminated. */
+typedef char *filename; /* Datatype used for picture, sound, etc. names*/
+
+
+typedef enum {fNONE,
+ fDA1, fDA2, fDA3, fDA4, fDA5, fDA6, fDSS,
+ fHNT, fOPT, fTTL,
+ fSAV, fSCR, fLOG,
+ fAGX, fINS, fVOC, fCFG,
+ fAGT, fDAT, fMSG, fCMD, fSTD, fAGT_STD
+ } filetype;
+
+
+/* ------------------------------------------------------------------- */
+/* GLOBAL FLAGS */
+/* Many of the following should be made into command line options */
+/* ------------------------------------------------------------------- */
+
+/* #define AGT_16BIT */ /* Force interpreter to use 16-bit quantities */
+/* #define DUMP_VLIST */ /* Dump out the verb list info */
+/* #define USE_CMD_INDEX */ /* Read in metacommand index data for objects */
+
+
+#define SS_GRAIN 1024 /* Granularity of size request for static string
+ array (to avoid calling rrealloc too often) */
+#define SYN_GRAIN 32 /* Granularity of requests for synonym array; this
+ is in units of sizeof(word) */
+#define MAX_BADTOK 10 /* How many bad tokens to report before giving
+ up */
+
+
+
+/* The following are defaults that can be overridden from the command line */
+/* The real variable has the name without the "def_" */
+
+#define def_DIAG 0 /* Print out diagnostic data? */
+#define def_interp_arg 1 /* Decipher arguments to opcodes? */
+#define def_debug_da1 0 /* used to analyse .DA1 files */
+#define def_RAW_CMD_OUT 0 /*Print CMDs out raw, undecoded; sometimes useful
+ when trying to understand new gamefile version */
+#define def_ERR_LEVEL 2 /* Level of error reporting. */
+/* 0== almost none */
+/* 1== Report possibly serious conditions */
+/* 2== Report any fall from perfection */
+
+
+
+
+
+
+
+/* ------------------------------------------------------------------- */
+/* DEFINES OF GLOBAL PARAMETERS */
+/* ------------------------------------------------------------------- */
+
+#define FORMAT_CODE 0xFF /* Character used for special formatting codes:
+ --in 1.8x games, it designates bold
+ --in AGX+ games, it will prefix format codes
+ --otherwise it designates black (replacing
+ 0, which has obvious problems) */
+#define LAST_TEXTCODE 8 /* Last ascii code to be used for text attributes */
+
+
+
+#define MAX_PIX 31
+#define MAX_FLAG_NOUN 31
+
+
+
+#define OLD_VERB 59 /* Number of built in verbs in original interpreters--
+ this number includes ANY, which is verb 0. */
+#define DIR_ADDR_CODE (OLD_VERB+17) /* Verb ID used for direct address */
+#define AUX_VERB 18 /* Additional verbs supported by the interpreter */
+#define BASE_VERB (OLD_VERB+AUX_VERB) /* Total number of built-in verbs */
+#define DUMB_VERB (DVERB+MAX_SUB) /* Number of dummy verbs and subroutines */
+#define TOTAL_VERB (BASE_VERB+DUMB_VERB) /* Total count of verbs */
+
+
+/* The following numbers refer to the ideal code we are translating into,
+ not the parameters for the actual data file we're reading. */
+#define MAX_COND 143 /* Last condition token id */
+#define START_ACT 1000 /* First action code */
+#define PREWIN_ACT 1161 /* Last action code before WinGame */
+#define WIN_ACT 2000 /* Value of WinGame opcode */
+#define END_ACT (WIN_ACT+2) /* Lowest command-terminating action code */
+#define MAX_ACT (WIN_ACT+4) /* Highest action code */
+
+/* Note: the following values are used by Magx internally:
+ 3000-- Used to mark 'no entry' in the opcode hash table.
+ 8000-- Used for NEXT
+ 8001-- Used for PREV
+ */
+
+#define MAX_TOKEN_ID 250 /* Upper limit on legal (raw) token values
+ read from AGT files. Doesn't need to be exact. */
+
+/* Number of built in properties and attributes. */
+#define NUM_PROP 14
+#define NUM_ATTR 26
+
+
+/*
+ None of these are used any more, but I leave them here for reference
+ #define MAX_ROOM 300
+ #define MAX_NOUN 300
+ #define MAX_CREAT 200
+ #define MAX_CMD 1500
+ #define MAX_QUEST 100
+ #define MAX_MSG 1000
+ #define MAX_PICT 250
+ #define ABS_MAX_REC_CMD 34
+ #define MAX_OBJ (MAX_ROOM+MAX_NOUN+MAX_CREAT)
+ #define SYNCNT 15
+*/
+
+
+
+/* --------------------------------------------------------------------- */
+/* DATA STRUCTURE DEFINITIONS */
+/* */
+/* All of the internal data structures used to store the contents of the */
+/* game file */
+/* --------------------------------------------------------------------- */
+
+/* First, pointers to game descriptions */
+/* start and size may be measured in units of characters or in units */
+/* of tline */
+typedef struct {
+ long start;
+ long size;
+} descr_ptr;
+
+
+/* Entries in the opcode tables: the name of the opcode, the number of
+ arguments, and the types of those arguments */
+typedef struct {
+ const char *opcode;
+ integer argnum;
+ integer arg1, arg2;
+} opdef; /* Opcode table entry */
+
+
+
+/* This is the data type for opcode correction entries */
+/* These are used to translate the opcodes from the various versions */
+/* to a uniform internal format. */
+/* The actual translation tables built with this are in agtdata.c */
+typedef struct {
+ integer told, tnew; /* Old and new values. */
+} cmd_fix_rec;
+
+typedef const cmd_fix_rec *fix_array;
+
+
+/* ROOMS */
+typedef struct {
+ const char *name; /* [31] */
+ int32 flag_noun_bits, PIX_bits; /* Master's Ed. only */
+ slist replacing_word; /* List of words to be replaced */
+ word replace_word; /* Word that does replacing */
+ word autoverb; /* Verb automatically executed on entry to room */
+ integer path[13];
+ integer key;
+ integer contents; /* Used internally by the interpreter; not read in
+ from the game file */
+ integer points;
+ integer light; /* Object that lights room; 0=none needed, 1=any */
+ integer pict, initdesc;
+ integer oclass;
+ rbool seen, locked_door;
+ rbool end, win, killplayer;
+ rbool unused; /* True if this room entry is unused */
+} room_rec;
+
+
+/* NOUNS */
+typedef struct {
+ const char *shortdesc; /* tline */
+ const char *position;
+ /* 23 characters in position for classic ed, 31 for ME */
+ const char *initpos; /*Starting value of position;used for RESTART/RESTORE*/
+ int scratch; /* Scratch space for use by various parser routines. */
+ slist syns; /* List of synonyms */
+ word name, adj;
+ word related_name; /* Word that should appear on menu when this noun
+ is in scope */
+ word pos_prep, pos_name; /* Used internally by the interpreter */
+ /* pos_prep==-1 means use noun.position */
+ integer nearby_noun; /* Noun this noun is behind */
+ integer num_shots;
+ integer points;
+ integer weight, size;
+ integer key;
+ integer initdesc, pict;
+ integer location; /* 1=carried, 1000=worn */
+ integer contents, next; /* Used internally by the interpreter; not read in
+ from the game file */
+ integer oclass;
+ integer flagnum; /* Number of associated flag + 1. 0=no flag. */
+ rbool scope; /* Used internally by the interpreter */
+ rbool isglobal; /* True if global object */
+ uchar plural;
+ rbool something_pos_near_noun; /* Anybody behind us? */
+ rbool has_syns;
+ rbool pushable, pullable, turnable, playable, readable;
+ rbool on, closable, open, lockable, locked, edible, wearable;
+ rbool drinkable, poisonous, movable, light;
+ rbool shootable;
+ rbool win;
+ rbool unused; /* True if this noun entry is unused */
+ rbool seen; /* True if this noun has been seen by the player */
+ rbool proper; /* True if noun's name is to be treated as proper noun. */
+} noun_rec;
+
+
+/* CREATURES */
+typedef struct {
+ const char *shortdesc; /* tline */
+ int scratch; /* Scratch space for use by various parser routines. */
+ slist syns;
+ word name;
+ word adj;
+ integer location;
+ integer contents, next; /* Used internally by the interpreter; not read in
+ from the game file */
+ integer weapon; /* Killed by this */
+ integer points;
+ integer counter; /* How many times has player been nasty to it? */
+ integer threshold, timethresh, timecounter;
+ integer pict, initdesc;
+ integer oclass;
+ integer flagnum; /* Number of associated flag + 1. 0=no flag. */
+ rbool scope; /* Used internally by the interpreter */
+ rbool isglobal; /* True if global object */
+ rbool has_syns;
+ rbool groupmemb;
+ rbool hostile;
+ uchar gender;
+ rbool unused; /* True if this creature entry is unused */
+ rbool seen; /* True if this creature has been seen by the player */
+ rbool proper; /* True if this creature's name is to be treated as a proper
+ noun (i.e. not prepended with "the") */
+} creat_rec;
+
+/* These records are used to hold reference information for
+ user-defined flags and properties */
+/* These give the base index offset in objflag/objprop of each flag/prop
+ for rooms, nouns, and creatures. The flag record also contains the
+ bit offset in the given byte. */
+/* This approach allows for a single property to be located in different
+ places for each of the three types of objects. */
+/* A value of -1 for any of the three fields indicates that this
+ property doesn't exist for the given type of object. */
+
+typedef struct {
+ long r, n, c;
+ long str_cnt; /* values from 0..(str_cnt-1) have assoc strings */
+ long str_list; /* Index into propstr[] array */
+} propdef_rec;
+
+typedef struct {
+ long r, n, c;
+ char rbit, nbit, cbit;
+ const char *ystr, *nstr; /* Yes and no strings */
+} attrdef_rec;
+
+typedef struct {
+ long str_cnt;
+ long str_list;
+} vardef_rec;
+
+
+typedef struct {
+ const char *ystr, *nstr;
+} flagdef_rec;
+
+/* Metacommand headers and a pointer to the actual sequence of tokens
+ to execute if the metacommand is run. */
+typedef struct {
+ integer actor; /* Contains the actor object number;
+ 1==self(no explicit actor) 2=anybody
+ It is multiplied by negative one for redirected
+ commands */
+ /* 0 in any of the following word variables denotes ANY;
+ -1 denotes <*none*> and will only occur in Magx-generated files.
+ (verbcmd cannot be equal to -1). Support for -1 is still experimental */
+ word verbcmd, nouncmd, objcmd, prep; /* prep only in ME games */
+ word noun_adj, obj_adj; /* Adjectives for noun and obj; not
+ supported in original AGT games */
+ integer noun_obj, obj_obj; /* Actual object numbers,
+ or 0 if none specified */
+ integer *data; /* MaxSizeCommand */
+ integer cmdsize; /* Number of commands */
+ /* integer ptr; */ /* In ME games-- see below for replacement */
+} cmd_rec;
+
+
+/* FRS=file record size; these are the sizes to be allocated to the buffer
+ used to read in the various records from the files; they should be at
+ least as big as the worst case scenario. */
+#define FRS_ROOM 220
+#define FRS_NOUN 310
+#define FRS_CREAT 240
+#define FRS_CMD 150
+
+
+/* This is the record used to hold menu information for verbs */
+typedef struct { /*verb menu entry */
+ word verb; /* Verb word */
+ word prep; /* Associated preposition */
+ short objnum; /* Number of objects */
+} verbentry_rec;
+
+
+/* This is the datatype that will be used (tenatively) for hint info */
+/* This isn't implemented yet. */
+typedef struct {
+ integer dtype; /* The hint element type */
+ integer child; /* The hint element value */
+ const char *name; /* The hint element name */
+} hint_rec;
+
+
+/* This is the data type used to hold information about
+ built-in attributes and properties */
+struct prop_struct {
+ const char *name; /* Property name. */
+ int room, noun, creature; /* Offsets for the various object types. */
+};
+
+
+/* The following data structure is used to store info on fields of a struct
+ that may need to be read from/written to a file. */
+/* They are used by both the AGX and the Savefile routines */
+/* They should be organized in ftype==0 terminated arrays,
+ in the order they occur in the file (the file is assumed to have
+ no padding, so we don't need file offsets: they can be computed) */
+/* The following is used for both global variables and fields in
+ structures. For global variables, ptr is set to point at the variable
+ and offset is 0. For fields, offset is set to the offset of the field
+ in the structure and ptr is set internally */
+typedef struct {
+ int ftype; /* Type in file */
+ int dtype; /* Data type of field in memory; often ignored */
+ void *ptr; /* Pointer to variable */
+ size_t offset; /* Offset of field in structure */
+} file_info;
+
+
+
+/* This contains all of the information needed to find files. */
+#ifndef REPLACE_FC
+typedef struct {
+ char *gamename; /* Name as entered by user */
+ char *path; /* The path */
+ char *shortname; /* The filename w/o directory information */
+ char *ext; /* The preexisting extension/prefix */
+ filetype ft; /* The filetype corresponding to ext */
+ int special; /* Used to mark special files, such as UNIX pipes */
+} file_context_rec;
+
+typedef file_context_rec *fc_type;
+
+#endif
+
+
+/* ------------------------------------------------------------------- */
+/* GLOBAL VARIABLES */
+/* ------------------------------------------------------------------- */
+
+/* ------------------------------------------------------------------- */
+/* Flags used internally by the interpreter and reading routines */
+
+global uchar DIAG, interp_arg, debug_da1, RAW_CMD_OUT;
+global int ERR_LEVEL;
+
+global rbool agx_file; /* Are we reading an AGX file? */
+global rbool have_opt; /* Do we have an OPT file? */
+global rbool skip_descr; /* Causes read_filerec() to skip over description
+ pointers without actually reading them in.
+ Used to support RESTORE for multi-part games
+ such as Klaustrophobia */
+global rbool no_auxsyn; /* Prevents building of auxsyn and preplist
+ synonym lists; used by agt2agx. */
+
+
+global rbool BATCH_MODE, make_test;
+/* These indicates we are in testing mode:
+ -- The random number generator should be set to a standard state.
+ -- Automatically send output to <gamename>.scr
+ -- Automatically read/write input from <gamename>.log
+ (depending on whether we are in BATCH_MODE or make_test mode,
+ respectivly).
+*/
+
+
+/* ------------------------------------------------------------------- */
+/* Flags reflecting game version and configuration */
+
+global rbool have_meta; /* Do we have any metacommands? */
+global rbool debug_mode, freeze_mode, milltime_mode, bold_mode;
+global uchar score_mode, statusmode;
+global rbool intro_first;
+global rbool box_title;
+global rbool mars_fix;
+global rbool fix_ascii_flag; /* Translate IBM characters?
+ Defaults to fix_ascii #define'd above */
+global rbool dbg_nomsg; /* Causes printing of <msg> arguments by
+ debug disassembler to be supressed */
+
+global rbool irun_mode; /* If true, all messages will be in 1st person */
+global rbool verboseflag;
+
+global int font_status; /* 0=unknown; 1=force fixed font,
+ 2=allow proportional font. */
+
+
+/* The following are AGT 'purity' flags; they turn off features of */
+/* my interpreter that are not fully consistent with the original AGT. */
+/* They are defined in auxfile.c (see also interp.h and agil.c for */
+/* interpreter-specific flags) */
+/* Anything added here should also be correctly initialized in agt2agx */
+
+extern rbool PURE_ANSWER, PURE_TIME, PURE_ROOMTITLE;
+extern rbool PURE_AND, PURE_METAVERB, PURE_ERROR;
+extern rbool PURE_SYN, PURE_NOUN, PURE_ADJ, PURE_SIZE;
+extern rbool PURE_DUMMY, PURE_SUBNAME, PURE_PROSUB;
+extern rbool PURE_HOSTILE, PURE_GETHOSTILE;
+extern rbool PURE_DISAMBIG, PURE_ALL, PURE_OBJ_DESC;
+extern rbool PURE_GRAMMAR, PURE_SYSMSG, PURE_AFTER;
+extern rbool PURE_PROPER;
+
+extern rbool TWO_CYCLE, FORCE_VERSION;
+extern rbool MASTERS_OR;
+
+/* ------------------------------------------------------------------- */
+/* Variables containing limits and counts of objects */
+
+global integer FLAG_NUM, CNT_NUM, VAR_NUM; /* (255, 50, 50) */
+global integer MAX_USTR; /* Maximum number of user strings (25) */
+global integer MAX_SUB; /* Number of subroutines (15) */
+global integer DVERB; /* Number of real dummy_verbs (50) */
+global integer NUM_ERR; /* For ME is 185 */
+
+/* Number of objflags and objprops for each type of object */
+/* The flag counts are for groups of 8 flags. */
+global integer num_rflags, num_nflags, num_cflags;
+global integer num_rprops, num_nprops, num_cprops;
+global integer oprop_cnt, oflag_cnt; /* Size of flag and property tables */
+
+global integer maxroom, maxnoun, maxcreat;
+global long MaxQuestion;
+global integer first_room, first_noun, first_creat, last_obj;
+global long last_message, last_cmd;
+global long numglobal; /* Number of global nouns */
+global long maxpict, maxpix, maxfont, maxsong;
+global long num_prep; /* Number of user-defined prepositions */
+global int num_auxcomb;
+global int num_comb;
+
+global integer exitmsg_base; /* Number added to messages used as
+ "illegal direction" messages */
+
+
+/* ------------------------------------------------------------------- */
+/* Miscellaneous other variables read in from the game file */
+
+global integer start_room, treas_room, resurrect_room, max_lives;
+global long max_score;
+global integer startup_time, delta_time;
+
+/* ver contains the size of the game, aver indicates its version */
+/* See the #define's below for details */
+global int ver, aver; /* ver: 0=unknown, 1=small, 2=big, 4=masters1.5 */
+global long game_sig; /* 2-byte quantity used to identify game files */
+/* (It's declared long to avoid overflow problems when
+computing it) */
+global int vm_size; /* Size of verb menu */
+
+global int min_ver; /* Lowest version of AGiliTy this will run on. */
+
+/* ------------------------------------------------------------------- */
+/* Miscellaneous Game Data Structures */
+
+/* All of the following are allocated dynamically */
+global room_rec *room; /* [MAX_ROOM]; */
+global creat_rec *creature; /* [MAX_CREAT]; */
+global noun_rec *noun; /* [MAX_NOUN]; */
+global cmd_rec *command;
+
+global unsigned char *objflag;
+global long *objprop;
+
+/* Next we have tables linking abstract flag/property values to
+ the indices with the objflag/objprop arrays. */
+global attrdef_rec *attrtable;
+global propdef_rec *proptable;
+global vardef_rec *vartable; /* For itemized variables */
+global flagdef_rec *flagtable;
+
+/* Output strings associated with various property values. */
+/* See propdef_rec */
+global const char **propstr;
+global long propstr_size;
+
+global tline *userstr; /*[MAX_USTR];*/
+global word *sub_name; /* [MAX_SUB] Dictionary id's of all subroutines */
+
+/* Verb information */
+global verbentry_rec *verbinfo; /* Verb information */
+global short *verbptr, *verbend; /* [TOTAL_VERB] */
+global slist *synlist; /* [MAX_VERBS+1];*/
+global slist *comblist; /* Global combination list */
+global word *old_agt_verb; /* List of non-canonical verb synonyms in the
+ original AGT; these are not allowed to be
+ expanded as dummy verbs. */
+
+
+global slist *userprep; /* Array of user-defined prepostions */
+
+global word flag_noun[MAX_FLAG_NOUN], *globalnoun;
+global word pix_name[MAX_PIX];
+global filename *pictlist, *pixlist, *fontlist, *songlist;
+
+global uchar opt_data[14]; /* Contents of OPT file. For the format of this
+ block, see the comments to read_opt() in
+ agtread.c */
+
+/* These are built by reinit_dict */
+
+global slist *auxsyn; /* [TOTAL_VERB] Built-in synonym list */
+global slist *preplist; /* [TOTAL_VERB] */
+global uchar *verbflag; /* [TOTAL_VERB] Verb flags; see below */
+global slist *auxcomb; /* Built-in combination lists (for multi-word
+ verbs) */
+
+#ifdef PATH_SEP
+global char **gamepath;
+#endif
+
+/* ------------------------------------------------------------------- */
+/* Description Pointers */
+
+
+global descr_ptr intro_ptr;
+global descr_ptr title_ptr, ins_ptr; /* Only defined if agx_file is true */
+global descr_ptr *err_ptr; /* [NUM_ERR];*/
+
+global descr_ptr *msg_ptr; /* [MAX_MSG];*/
+global descr_ptr *help_ptr, *room_ptr, *special_ptr; /*[ROOM] */
+global descr_ptr *noun_ptr, *text_ptr, *turn_ptr, /* [NOUN] */
+ *push_ptr, *pull_ptr, *play_ptr;
+global descr_ptr *talk_ptr, *ask_ptr, *creat_ptr; /* [CREAT] */
+
+global descr_ptr *quest_ptr, *ans_ptr; /* [MAX_QUEST] */
+global tline *question, *answer; /* [MAX_QUEST] */
+
+
+
+/* ------------------------------------------------------------------------ */
+/* Dynamically allocated data blocks (which are pointed to from elsewhere) */
+
+global char **dict; /* dict[n] points to the nth dictionary word */
+global long dp; /* Dictionary pointer: number of words in dict */
+
+#define DICT_INIT 12*1024 /* Starting size of dictstr */
+#define DICT_GRAN 1024 /* Granularity of dictstr size requests
+ must be at least 4. */
+global char *dictstr; /* Pointer to memory block containing dict words */
+global long dictstrptr, dictstrsize;
+/* dictstrptr points to the first unused byte in dictstr.
+ dictstrsize points to the end of the space currently allocated for
+ dictstr.
+*/
+
+global char *static_str; /*Static string space */
+global long ss_end; /* Pointer to end of used space in above */
+global long ss_size; /* Current size of static string space */
+
+global word *syntbl; /* Synonym list space */
+global slist synptr; /* Points to end of used space */
+global long syntbl_size; /* Long so we can catch overflows */
+
+global long descr_maxmem;
+global char *mem_descr; /* Copy of descriptor in memory */
+
+
+
+/* ------------------------------------------------------------------------ */
+/* Data structures used internally by agtread.c */
+
+/*The following are all set to NULL after agtread finishes. */
+global long *cmd_ptr; /* ME only;Points to cmd start locs in gamefile.*/
+global long *room_name, *noun_sdesc, *noun_pos, *creat_sdesc;
+global long *t_pictlist, *t_pixlist, *t_songlist, *t_fontlist;
+
+/* These are only used by agtout (to allow the AGT reading routines to
+ pass back the count of nouns inside the given object) */
+global integer *room_inside, *noun_inside, *creat_inside;
+
+/* This is used to translate ASCII codes */
+global uchar fixchar[256];
+
+global rbool text_file; /* Set if we are currently opening a binary file. */
+#ifdef OPEN_AS_TEXT
+global rbool open_as_binary; /* Open text files as binary, anyhow. */
+#endif
+
+
+/* ------------------------------------------------------------------ */
+/* SYMBOLIC CONSTANTS: VERSION CODES */
+/* These are the values stored in the variable 'aver'. */
+/* ------------------------------------------------------------------ */
+
+#define AGT10 1 /* SPA */
+#define AGT118 2 /* TAMORET, PORK II */
+#define AGT12 3 /* SOS,... */
+#define AGTCOS 4 /* COSMOS and SOGGY: enhanced versions of 1.3x */
+#define AGT135 5 /* By far the most common version; includes practically
+ every version of Classic AGT from 1.19 to 1.7 */
+#define AGT182 6
+#define AGT183 7
+#define AGT15 8 /* HOTEL */
+#define AGT15F 9 /* MDTHIEF */
+#define AGT16 10 /* PORK */
+#define AGTME10 11 /* CLIFF2, ELF20 */
+#define AGTME10A 12 /* HURRY */
+#define AGTME15 13 /* WOK */
+#define AGTME155 14 /* TJA */
+#define AGTME16 15 /* also includes v1.56 and 1.7 */
+#define AGX00 16 /* Tenative */
+
+#define AGTMAST AGTME16
+#define AGTCLASS AGT16 /* Dividing line between master's ed and classic */
+#define AGT18 AGT182 /* Defines lowest 1.8x version */
+#define AGT18MAX AGT183 /* Defines the highest 1.8x version */
+#define AGTSTD AGT135 /* "Default" version of AGT */
+
+
+
+/* ------------------------------------------------------------------ */
+/* SYMBOLIC CONSTANTS: ARGUMENT TYPES */
+/* These are used to encode the argument types of metacommands for */
+/* opcode tables. */
+/* ------------------------------------------------------------------ */
+
+#define AGT_UNK 0 /* Unknown argument type */
+
+/* The following can all mix and match in various ways and so are
+ put together as powers of two. */
+#define AGT_NONE 1 /* 0 is allowed */
+#define AGT_SELF 2 /* 1 is allowed */
+#define AGT_WORN 4 /* 1000 is allowed */
+#define AGT_ROOM 8 /* A room # is allowed */
+#define AGT_ITEM 16 /* An item # is allowed */
+#define AGT_CREAT 32 /* A creature # is allowed */
+
+/* AGT_VAR is special, since it is always combined with another type--
+ the type that the variable is expected to be */
+#define AGT_VAR 64
+
+/* The rest of the values are always distinct; they never mix and so
+ they can be given simple consecutive indices */
+#define AGT_NUM 128
+#define AGT_FLAG 129
+#define AGT_QUEST 130 /* Question */
+#define AGT_MSG 131 /* Message */
+#define AGT_STR 132 /* String */
+#define AGT_CNT 133 /* Counter */
+#define AGT_DIR 134 /* Direction */
+#define AGT_SUB 135 /* Subroutine */
+#define AGT_PIC 136 /* Picture */
+#define AGT_PIX 137 /* Room picture */
+#define AGT_FONT 138
+#define AGT_SONG 139
+#define AGT_ROOMFLAG 140
+#define AGT_TIME 141
+#define AGT_ERR 142
+#define AGT_OBJFLAG 143 /* User defined object flags */
+#define AGT_OBJPROP 144 /* User defined object properties */
+#define AGT_ATTR 145 /* Built-in attribute */
+#define AGT_PROP 146 /* Built-in property */
+
+
+/* These next three may not occur as operand types */
+#define AGT_EXIT 147 /* Valid values for an exit: room, msg+msgbase, 0 */
+#define AGT_GENFLAG 148 /* PIX or Room Flag; used internally by compiler */
+#define AGT_GENPROP 149 /* ObjProp/Property */
+
+/* certain restrictions. Used internally
+ by the compiler in the parsing
+ of "[obj].[prop].[prop].[flag]"
+ constructions. */
+
+#define AGT_LVAL 150 /* Used by the compiler in certain psuedo-ops */
+
+
+#define AGT_DEFINE 256
+
+
+
+/* ------------------------------------------------------------------ */
+/* Verb flags for verbflag[]; these should be powers of 2 */
+
+#define VERB_TAKEOBJ 1
+#define VERB_META 2
+#define VERB_MULTI 4 /* Can the verb take mulitple objects? */
+#define VERB_GLOBAL 8 /* Does the verb have global scope? */
+
+/* ------------------------------------------------------------------ */
+/* SYMBOLIC CONSTANTS: FILE DATA TYPES */
+/* Data type specifiers for file i/o routines */
+/* The FT_* constants specify file data types */
+/* The DT_* constants specify internal data types */
+/* ------------------------------------------------------------------ */
+
+#define FT_COUNT 17 /* Number of file data types */
+
+#define FT_END 0 /* End of list of fields/variables in file */
+#define FT_INT16 1 /* DT_SHORT */
+#define FT_UINT16 2 /* DT_LONG */
+#define FT_INT32 3 /* DT_LONG */
+#define FT_UINT32 4
+#define FT_BYTE 5 /* aka uchar; DT_UCHAR */
+#define FT_VERSION 6 /* Game version */
+#define FT_BOOL 7 /* DT_BOOL. Adjacent rbooleans are packed */
+#define FT_DESCPTR 8 /* DT_DESCPTR */
+#define FT_STR 9 /* Integer pointer into static string array */
+#define FT_SLIST 10 /* Synonym list index */
+#define FT_WORD FT_INT16 /* Index into dictionary */
+#define FT_PATHARRAY 11 /* 13 integers in an array of directions */
+#define FT_CMDPTR 12 /* Pointer into command block */
+#define FT_DICTPTR 13 /* Pointer into dictionary text */
+#define FT_TLINE 14 /* TextLine */
+#define FT_CHAR 15 /* Characters. */
+#define FT_CFG 16 /* Configuration byte; 0=false, 1=true,
+ 2=none (don't change) */
+
+#define DT_DEFAULT 0 /* Default internal type for <ftype> */
+#define DT_LONG 1
+#define DT_DESCPTR 2 /* Description pointer, which are treated specially */
+#define DT_CMDPTR 3 /* Command block pointer, also treated specially */
+
+/* This is the end marker for the file definitions used by the file I/O
+ routines */
+#define endrec {FT_END,0,NULL,0}
+
+
+/* ------------------------------------------------------------------- */
+/* Date type macros */
+/* ------------------------------------------------------------------- */
+#define troom(x) ((x)>=first_room && (x)<=maxroom)
+#define tnoun(x) ((x)>=first_noun && (x)<=maxnoun)
+#define tcreat(x) ((x)>=first_creat && (x)<=maxcreat)
+
+
+/* ------------------------------------------------------------------- */
+/* FUNCTION PROTOTYPES AND INITIALIZED TABLES */
+/* ------------------------------------------------------------------- */
+
+/* This is intended for removing whitespace in AGT data files. */
+#define rspace(c) ((c)==' ' || (c)=='\t')
+
+/* ------------------------------------------------------------------- */
+/* In GAMEDATA.C */
+/* This module contains the major initialized data structures and */
+/* routines to manipulate game data structures, in particular the */
+/* game's dictionary */
+/* ------------------------------------------------------------------- */
+
+void init_dict(void); /* 1=set of verblist, 0=don't */
+void build_verblist(void); /* Creates verblist */
+void reinit_dict(void);
+void free_dict(void);
+word search_dict(const char *);
+word add_dict(const char *);
+const char *gdict(word w); /* Almost equivalent to dict[w], but with
+ some error checking and handling for
+ the w==-1 case. */
+
+int verb_code(word);
+int verb_builtin(word);
+int verb_authorsyn(word);
+void addsyn(word);
+slist add_multi_word(word);
+
+/* Commands to manipulate object flags */
+long objextsize(char op);
+long op_objprop(int op, int obj, int propid, long val);
+rbool op_objflag(int op, integer obj, int flagid);
+long lookup_objflag(int id, int t, char *ofs);
+long lookup_objprop(int id, int t);
+rbool have_objattr(rbool prop, integer obj, int id);
+int num_oattrs(int t, rbool isflag);
+rbool op_simpflag(uchar *flag, char ofs, int op);
+/* op: 0=clear, 1=set, 2=nop, 3=toggle two bits: <ab> */
+const char *get_objattr_str(int dtype, int id, long val);
+
+
+const opdef *get_opdef(integer op);
+char *objname(int);
+void sort_cmd(void);
+
+void agtwarn(const char *, int elev);
+void agtnwarn(const char *, int, int elev);
+
+void init_flags(void);
+
+#ifdef ZIP
+#define fatal agil_fatal
+#endif
+void fatal(const char *);
+
+long new_str(char *buff, int max_leng, rbool pasc);
+
+descr_line *read_descr(long start, long size);
+void free_descr(descr_line *p);
+
+extern const char trans_ibm[];
+extern const char nonestr[];
+
+/* Tables of opcodes */
+extern const opdef cond_def[], act_def[], end_def[], illegal_def;
+
+/* Table of built in properties and attributes */
+extern const prop_struct proplist[NUM_PROP];
+extern const prop_struct attrlist[NUM_ATTR];
+
+global words *verblist; /* List of prexisting words, intialized by init_dict */
+extern const fix_array FIX_LIST[];
+extern const char *exitname[13];
+extern const char *verstr[], *averstr[];
+extern const char *version_str, *portstr;
+
+
+/* ------------------------------------------------------------------- */
+/* In AGTREAD.C */
+/* Routines to read in AGT data files */
+/* ------------------------------------------------------------------- */
+
+void open_descr(fc_type fc);
+void close_descr(void);
+descr_line *agt_read_descr(long start, long len);
+rbool readagt(fc_type fc, rbool diag);
+void free_all_agtread(void); /* Cleans up everything allocated in agtread
+ should only be called at the very end of
+ the program */
+void free_ttl(descr_line *title);
+
+
+/* ------------------------------------------------------------------- */
+/* In AGXFILE.C */
+/* Routines to read and write AGX files */
+/* ------------------------------------------------------------------- */
+
+int read_agx(fc_type fc, rbool diag);
+descr_line *agx_read_descr(long start, long size);
+void agx_close_descr(void);
+
+/* The following are in the order they should be called */
+void agx_create(fc_type fc);
+void write_descr(descr_ptr *dp, descr_line *txt);
+void agx_write(void);
+void agx_wclose(void);
+void agx_wabort(void);
+
+/* ------------------------------------------------------------------- */
+/* In AUXFILE.C */
+/* Routines to read VOC, OPT, CFG, TTL, INS, etc. files */
+/* ------------------------------------------------------------------- */
+void read_opt(fc_type fc);
+void read_config(genfile cfgfile, rbool lastpass);
+rbool parse_config_line(char *s, rbool lastpass);
+
+descr_line *read_ttl(fc_type fc); /* This returns the title. The return string
+ must be freed with free_ttl() and not
+ with free_descr */
+void free_ttl(descr_line *title);
+
+void read_voc(fc_type fc);
+void init_verbrec(void);
+void add_verbrec(const char *verbline, rbool addnew); /* addnew should be 0 */
+void finish_verbrec(void);
+
+descr_line *read_ins(fc_type fc);
+void free_ins(descr_line *instr);
+rbool open_ins_file(fc_type fc, rbool report_error);
+char *read_ins_line(void); /* Reuses buffer, so return value should be copied
+ if it needs to be used past the next call to
+ read_ins_line or close_ins_file */
+void close_ins_file(void);
+
+void build_fixchar(void);
+
+/* ------------------------------------------------------------------- */
+/* In or used by DISASSEMBLE.C */
+/* Routines to disassemble metacommands (used by the interpreter for */
+/* tracing and by agtout to produce the metacommand output). */
+/* ------------------------------------------------------------------- */
+
+global rbool *dbgflagptr;
+global long *dbgvarptr;
+global short *dbgcntptr;
+
+void dbgprintf(const char *fmt, ...);
+void debugout(const char *s);
+int argout(int dtype, int dval, int optype);
+
+/* ------------------------------------------------------------------- */
+/* In INTERFACE.C, AGIL.C and/or AGILSTUB.C */
+/* agilstub.c provides minimal versions of these routines for use by */
+/* programs other than the interpreter */
+/* ------------------------------------------------------------------- */
+
+void writeln(const char *s);
+void writestr(const char *s);
+void agil_option(int optnum, char *optstr[], rbool setflag, rbool lastpass);
+void close_interface(void);
+
+void print_tos(void); /* Called by the disassembler; in either TOKEN.C
+ or AGTOUT.C */
+
+
+/* ------------------------------------------------------------------- */
+/* In UTIL.C */
+/* Low-level utilites, including memory allocation, string manip., */
+/* and buffered file I/O. */
+/* ------------------------------------------------------------------- */
+
+global uchar trans_ascii[256]; /* Table to translate ascii values read
+ in from file */
+
+void build_trans_ascii(void); /* Set up the above table. */
+
+void rprintf(const char *fmt, ...); /* General output routine, mainly used
+ for diagnostics. There can be a newline
+ at the end, but there shouldn't be
+ one in the middle of the string */
+
+
+
+/* Memory management variables and routines */
+
+extern rbool rm_trap; /* Trap memory allocation failures? */
+global rbool rm_acct; /* Turn on rmem accounting, to locate memory leaks */
+global long rfree_cnt, ralloc_cnt; /* # of allocs/frees since acct turned on */
+global long rm_size, rm_freesize; /* These hold worst case values */
+
+long get_rm_size(void); /* These get the current values */
+long get_rm_freesize(void);
+void *rmalloc(long size);
+void r_free(void *p);
+#define rfree(p) (r_free(p),p=NULL) /* Traps errors & catch memory leaks */
+void *rrealloc(void *p, long size);
+char *rstrdup(const char *s);
+
+
+/* String utilities */
+
+char *concdup(const char *s1, const char *s2); /* Concacate and duplicate */
+char *rstrncpy(char *dest, const char *src, int max);
+/* Copy at most max-1 characters */
+rbool match_str(const char **pstr, const char *match);
+
+#ifdef NEED_STR_CMP
+#undef strcasecmp
+//define strcasecmp Common::scumm_strcasecmp
+extern int strcasecmp(const char *s1, const char *s2);
+#endif
+#ifdef NEED_STRN_CMP
+#undef strncasecmp
+//define strncasecmp Common::scumm_strnicmp
+extern int strncasecmp(const char *s1, const char *s2, size_t len);
+#endif
+
+#undef isspace
+#define isspace Common::isSpace
+#undef isprint
+#define isprint Common::isPrint
+#undef isalpha
+#define isalpha Common::isAlpha
+#undef isalnum
+#define isalnum Common::isAlnum
+#undef islower
+#define islower Common::isLower
+#undef isupper
+#define isupper Common::isUpper
+#undef ispunct
+#define ispunct Common::isPunct
+
+/* The fixsign.. routines put together unsigned bytes to form signed ints */
+
+#ifndef FAST_FIXSIGN
+short fixsign16(uchar n1, uchar n2);
+long fixsign32(uchar n1, uchar n2, uchar n3, uchar n4);
+#else
+#define fixsign16(u1,u2) ( (u1) | ((u2)<<8) )
+#define fixsign32(u1,u2,u3,u4) ( ((long)u1) | (((long)u2)<<8) | \
+ (((long)u3)<<16) | (((long)u4)<<24) )
+#endif
+
+
+/* Miscellaneous */
+long rangefix(long n);
+
+/* File routines */
+genfile openfile(fc_type fc, filetype ext, const char *err, rbool ferr);
+genfile openbin(fc_type fc, filetype ext, const char *err, rbool ferr);
+
+#undef fopen
+genfile fopen(const char *name, const char *how);
+#undef fseek
+int fseek(genfile stream, long int offset, int whence);
+#undef fread
+size_t fread(void *ptr, size_t size, size_t nmemb, genfile stream);
+#undef fwrite
+size_t fwrite(const void *ptr, size_t size, size_t nmemb, genfile stream);
+#undef fclose
+#define fclose(f) delete f
+#undef ftell
+size_t ftell(genfile f);
+
+char *readln(genfile f, char *buff, int n);
+/* Read a line from a 'text' file */
+
+/* (None of the following routines are at all reentrant) */
+long buffopen(fc_type fc, filetype ext,
+ long minbuffsize, const char *rectype, long recnum);
+/* Open file for our buffered routines and make it our current file;
+returns the record size. Prints out error message on failure
+rectype="noun","room",etc.; recnum=expected # of objects in file */
+uchar *buffread(long index);
+/* seek to index*recsize, read buff_rsize bytes, return pointer to a
+ buffer with them. */
+void buffclose(void); /* Close the current file */
+
+void bw_open(fc_type fc, filetype ext); /* Open buffered file for writing */
+void bw_close(void); /* Close buffered file */
+void bw_abort(void); /* Close and delete buffered file */
+
+/* "Universal" file routines */
+extern const size_t ft_leng[FT_COUNT]; /* File lengths of the data types */
+long compute_recsize(file_info *recinfo);
+void *read_recblock(void *base, int ftype, long numrec, long offset,
+ long blocksize);
+/* Only works for FT_BYTE, FT_SLIST, FT_WORD, FT_DICTTEXT, FT_INT16 */
+void *read_recarray(void *base, long eltsize, long numelts,
+ file_info *field_info, const char *rectype,
+ long file_offset, long file_blocksize);
+void read_globalrec(file_info *global_info, const char *rectype,
+ long file_offset, long file_blocksize);
+
+long write_recarray(void *base, long eltsize, long numelts,
+ file_info *field_info, long file_offset);
+long write_globalrec(file_info *global_info, long file_offset);
+long write_recblock(void *base, int ftype, long numrec, long offset);
+
+char *textgets(genfile f, char *buf, size_t n);
+char textgetc(genfile f);
+void textungetc(genfile f, char c);
+bool texteof(genfile f);
+void textputs(genfile f, const char *s);
+
+void set_internal_buffer(void *p);
+/* Causes all of the above routines to write to the block of memory pointed
+ at by p instead of to a file */
+
+#ifdef PROFILE_SUPPORT
+/* These are functions to do quick-and-dirty timing of routines;
+ I added them to check the performance of the AGX code.
+ They aren't likely to work on anything other than a *nix box */
+void resetwatch(void);
+void startwatch(void);
+char *stopwatch(void);
+char *timestring(void);
+#define runwatch(cmd) do{resetwatch();cmd;printf("%s\n",stopwatch());}while(0)
+#else
+#define runwatch(cmd) cmd
+#endif
+
+
+
+/* ------------------------------------------------------------------- */
+/* In FILENAME.C */
+/* The low-level file routines */
+/* ------------------------------------------------------------------- */
+
+fc_type init_file_context(const char *game_name, filetype ft);
+/* This sets up the filename system, based around a game with name
+ "gamename". Must be called before any of the following are called
+ with relative filetypes (all filetypes that depend on the game's
+ name and location-- everything but the global config file.) */
+/* ft indicates the file type class. At the moment, it can be
+ fAGX, fSAV, fSCR, fLOG
+ */
+void fix_file_context(fc_type fc, filetype ft);
+fc_type convert_file_context(fc_type fc, filetype ft, const char *name);
+void release_file_context(fc_type *pfc);
+
+
+char *formal_name(fc_type fc, filetype ft); /* Used for messages */
+genfile badfile(filetype ft); /* Bad file of type ft */
+rbool fileexist(fc_type fc, filetype ft);
+
+genfile readopen(fc_type fc, filetype ft, const char **errstr);
+genfile writeopen(fc_type fc, filetype ft,
+ file_id_type *pfileid, const char **errstr);
+rbool filevalid(genfile f, filetype ft);
+void readclose(genfile f);
+void writeclose(genfile f, file_id_type fileid);
+
+void binremove(genfile f, file_id_type fileid);
+void binseek(genfile f, long offset);
+rbool binread(genfile f, void *buff, long recsize, long recnum, const char **errstr);
+long varread(genfile f, void *buff, long recsize, long recnum, const char **errstr);
+rbool binwrite(genfile f, void *buff, long recsize, long recnum, rbool ferr);
+long binsize(genfile f); /* Size of an open binary file */
+
+rbool textrewind(genfile f);
+
+char *assemble_filename(const char *path, const char *root,
+ const char *ext);
+
+
+#ifdef global_defined_agtread
+#undef global
+#undef global_defined_agtread
+#endif
+
+} // End of namespace AGT
+} // End of namespace Glk
+
+#endif
diff --git a/engines/glk/agt/agt.cpp b/engines/glk/agt/agt.cpp
index 29e7222..ea28cf8 100644
--- a/engines/glk/agt/agt.cpp
+++ b/engines/glk/agt/agt.cpp
@@ -28,11 +28,18 @@
namespace Glk {
namespace AGT {
+AGT *g_vm;
+
+extern void glk_main();
+extern int glk_startup_code(int argc, char *argv[]);
+
AGT::AGT(OSystem *syst, const GlkGameDescription &gameDesc) : GlkAPI(syst, gameDesc) {
+ g_vm = this;
}
void AGT::runGame() {
- // TODO
+ glk_startup_code(0, nullptr);
+ glk_main();
}
Common::Error AGT::readSaveData(Common::SeekableReadStream *rs) {
diff --git a/engines/glk/agt/agt.h b/engines/glk/agt/agt.h
index 70bb98a..1d4ed00 100644
--- a/engines/glk/agt/agt.h
+++ b/engines/glk/agt/agt.h
@@ -45,7 +45,9 @@ public:
/**
* Returns the running interpreter type
*/
- virtual InterpreterType getInterpreterType() const override { return INTERPRETER_AGT; }
+ virtual InterpreterType getInterpreterType() const override {
+ return INTERPRETER_AGT;
+ }
/**
* Execute the game
@@ -64,6 +66,8 @@ public:
virtual Common::Error writeGameData(Common::WriteStream *ws) override;
};
+extern AGT *g_vm;
+
} // End of namespace AGT
} // End of namespace Glk
diff --git a/engines/glk/agt/agtread.cpp b/engines/glk/agt/agtread.cpp
new file mode 100644
index 0000000..7fce329
--- /dev/null
+++ b/engines/glk/agt/agtread.cpp
@@ -0,0 +1,1721 @@
+/* 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 "glk/agt/agility.h"
+
+namespace Glk {
+namespace AGT {
+
+static void read_da6(fc_type fc);
+static void check_cmd_version(void);
+static void build_cmd_table(void);
+static void fixcmd(integer *, int);
+
+/* This parses the block of integers of a command to standardize
+ the command numbers */
+static short *cmd_table; /* Holds the command translation table used
+ by fixcmd */
+int topcmd; /* The highest legal opcode in the current AGT version. */
+
+
+
+static genfile fd_desc; /* File pointer for description file. */
+static long desc_size; /* Size of description file. */
+
+static int top_quest; /* Highest question actually referenced */
+/* This is computed by fixcmd */
+
+static int SL_NAME, SL_TEXT, SL_ROOM, SL_WORD;
+static integer MAX_CMD_SIZE;
+
+static rbool encrypt_desc = 1; /* Are descriptions encrypted? */
+
+
+/* This translates v1.8 status mode codes into ME statue mode codes */
+const uchar agt18_statmode[] = {0, 4, 3, 1};
+
+
+/*-------------------------------------------------------------------------*/
+/* Utilities to convert strings, do ASCII translation, etc. and to add */
+/* words to the dictionary (the actual dictionary routines are in */
+/* agtdata.c; these routines here are just wrappers that prepare words */
+/* to be added). */
+/*-------------------------------------------------------------------------*/
+
+
+
+static void fatals(const char *msg, const char *fname) {
+ Common::String str = Common::String::format(msg, fname);
+ error("%s", str.c_str());
+}
+
+
+static word add_dic1(uchar *buff, int n) {
+ char nbuff[100];
+
+ int i;
+ if (n > 100) n = 100;
+ for (i = 0; i < buff[0] && i < n; i++)
+ nbuff[i] = buff[i + 1];
+ nbuff[i] = 0;
+ return add_dict(nbuff);
+}
+
+
+static slist add_slist(uchar *buff) {
+ int j, k;
+ slist start_ptr;
+ char nbuff[100];
+
+ k = 0;
+ start_ptr = synptr;
+ if (buff[0] > 80) fatal("Invalid game file format");
+ for (j = 1; j <= buff[0]; j++)
+ if (rspace(buff[j]) && k > 0) {
+ nbuff[k] = 0;
+ addsyn(add_dict(nbuff));
+ k = 0;
+ } else nbuff[k++] = buff[j];
+ if (k > 0) {
+ nbuff[k] = 0;
+ addsyn(add_dict(nbuff));
+ }
+ addsyn(-1); /* End of list marker */
+ return start_ptr;
+}
+
+
+
+
+/*-------------------------------------------------------------------------*/
+/* Description file manipulation routines: routines to open and close the */
+/* description file and read in individual descriptions. */
+/*-------------------------------------------------------------------------*/
+
+/* The memory-based stuff is not done yet */
+
+
+void convert_agt_descr(uchar *s)
+/* Convert encrypted pascal string into plaintext C string */
+{
+ int j, n;
+
+ n = s[0];
+ if (n > 80) {
+ s[0] = 0;
+ return;
+ }
+
+ if (encrypt_desc)
+ for (j = 0; j < n; j++)
+ if (s[j + 1] != ' ')
+ s[j] = fixchar[(s[j + 1] - (j + 1) + 0x100) & 0xFF];
+ else s[j] = ' ';
+ else
+ for (j = 0; j < n; j++)
+ s[j] = fixchar[s[j + 1]];
+
+ s[n] = 0;
+}
+
+
+void open_descr(fc_type fc) {
+ const char *errstr;
+ long i;
+ int alpha, cnt;
+ tline buff;
+
+ fd_desc = readopen(fc, fDSS, &errstr);
+ if (errstr != NULL) fatal(errstr);
+ desc_size = binsize(fd_desc);
+ if (DIAG) {
+ char *s;
+ s = formal_name(fc, fDSS);
+ rprintf("Opened file %s (size:%ld)\n", s, desc_size);
+ rfree(s);
+ }
+
+ /* <Sigh> Now need to figure out if the input is encoded. Do this by
+ reading a few random lines and seeing if they "look" encoded */
+ alpha = cnt = 0;
+ if (aver > AGT135 || aver == AGTCOS)
+ encrypt_desc = 1;
+ else {
+ binread(fd_desc, buff, 81, 1, &errstr); /* Throw away first line */
+ while (cnt < 300) {
+ if (!binread(fd_desc, buff, 81, 1, &errstr)) { /* EOF */
+ writeln("");
+ agtwarn("EOF while analyzing descriptions", 0);
+ rprintf("......assuming type ");
+ break;
+ }
+ if (buff[0] > 0 && buff[1] != ' ' && buff[1] > 0) /* To avoid "banner"
+ lines */
+ {
+ for (i = 1; i <= buff[0]; i++) {
+ if (buff[i] >= 'A' && buff[i] <= 'z') alpha++;
+ if (buff[i] != ' ') cnt++;
+ }
+ }
+ }
+ if (3 * cnt < 4 * alpha) {
+ encrypt_desc = 0;
+ if (aver == AGT135) aver = AGT12;
+ } else encrypt_desc = 1;
+ }
+ if (DIAG) {
+ if (encrypt_desc) rprintf(" [encrypted]\n");
+ else rprintf(" [plaintext: %d/%d]\n", alpha, cnt);
+ }
+
+ mem_descr = NULL;
+ if (desc_size <= descr_maxmem) {
+ /* This is where we need to read the data in and convert it:
+ encrypted Pascal strings --> plaintext C strings */
+ binseek(fd_desc, 0);
+ mem_descr = (char *)rmalloc(desc_size);
+ /* Read in the whole file */
+ binread(fd_desc, mem_descr, desc_size, 1, &errstr);
+ if (errstr != NULL) fatal(errstr);
+ for (i = 0; i < desc_size; i += sizeof(tline))
+ convert_agt_descr((uchar *)(mem_descr + i));
+ /* Decode and convert to C string */
+ }
+}
+
+
+void close_descr(void) {
+ if (mem_descr != NULL)
+ rfree(mem_descr);
+ else {
+ readclose(fd_desc);
+ fd_desc = NULL;
+ }
+}
+
+
+descr_line *agt_read_descr(long start, long len) {
+ tline *d;
+ descr_line *lines;
+ long i;
+ const char *errstr;
+
+ if (len == -1 || start == -1) return NULL;
+ lines = (descr_line *)rmalloc(sizeof(descr_line) * (len + 1));
+
+ if (mem_descr != NULL) {
+ d = ((tline *)mem_descr) + start;
+ for (i = 0; i < len; i++)
+ lines[i] = (char *)(d + i);
+ } else {
+ d = (tline *)rmalloc(sizeof(tline) * len);
+ binseek(fd_desc, start * sizeof(tline));
+ binread(fd_desc, d, sizeof(tline), len, &errstr);
+ if (errstr != NULL) fatal(errstr);
+ for (i = 0; i < len; i++) {
+ lines[i] = (char *)(d + i);
+ convert_agt_descr((uchar *)(d + i));
+ }
+ }
+ lines[len] = NULL; /* Mark end of array */
+ return lines;
+}
+
+
+
+/*-------------------------------------------------------------------------*/
+/* Read DA2: The Room File. */
+/*-------------------------------------------------------------------------*/
+
+
+#define seti(a) (room[i].a=buff[bp] | (buff[bp+1]<<8),bp+=2)
+#define set32(a) (room[i].a=buff[bp] | (buff[bp+1]<<8) | (buff[bp+2]<<16)|\
+ (buff[bp+3]<<24), bp+=4)
+#define setb(a) (room[i].a=buff[bp],bp++)
+
+#define setstr(leng) (bp+=(leng),new_str((char*)buff+bp-(leng),(leng),1))
+#define setd(leng) (bp+=(leng),add_dic1(buff+bp-(leng),(leng)))
+#define setsl() (bp+=sizeof(tline),add_slist(buff+bp-sizeof(tline)))
+#define nonecheck(leng) (memcmp(buff+bp,nonestr,5)==0)
+
+
+static void read_da2(fc_type fc) {
+ int i, j, numroom;
+ uchar *buff; /* [FRS_ROOM];*/
+ long bp;
+
+ numroom = maxroom - first_room + 1;
+ if (numroom < 0) return;
+ room_name = (long *)rmalloc(numroom * sizeof(long));
+
+ buffopen(fc, fDA2, FRS_ROOM, "room", numroom);
+
+ bp = 0;
+ for (i = 0; i < numroom; i++) {
+ buff = buffread(i);
+ bp = 0;
+ if (nonecheck(SL_ROOM))
+ room[i].unused = 1;
+ else room[i].unused = 0;
+ room_name[i] = setstr(SL_ROOM);
+ room[i].replace_word = setd(SL_WORD);
+ room[i].replacing_word = setsl();
+ for (j = 0; j < 12; j++) seti(path[j]);
+
+ if (aver >= AGT15) set32(flag_noun_bits); /* Menu flags */
+ else room[i].flag_noun_bits = 0;
+
+ if (aver >= AGTME10) set32(PIX_bits); /* PIX bits */
+ else room[i].PIX_bits = 0;
+
+ seti(path[12]); /* Special */
+
+ /* There's a small possibility that the 1.5/Hotel flag_noun_bits
+ goes here, rather than above; 1.5/F is known to go above */
+
+ setb(seen);
+ seti(key);
+ setb(locked_door);
+ if (room_inside != NULL)
+ room_inside[i] = fixsign16(buff[bp], buff[bp + 1]);
+ bp += 2; /* Skip # of nouns in this room */
+
+ seti(points);
+ seti(light);
+ setb(end);
+ setb(win);
+ if (aver != AGT10) setb(killplayer); /* I'm guessing here */
+ else room[i].killplayer = room[i].end;
+
+ if (aver >= AGTME10) {
+ seti(initdesc);
+ seti(pict);
+ } else {
+ room[i].initdesc = 0;
+ room[i].pict = 0;
+ }
+ if (aver >= AGTME15) room[i].autoverb = setd(SL_WORD);
+ else room[i].autoverb = 0;
+ room[i].oclass = 0;
+ room[i].seen = 0;
+ }
+ if (DIAG)
+ rprintf(" Internal:%ld\n", bp);
+ buffclose();
+}
+
+
+
+/*-------------------------------------------------------------------------*/
+/* Read DA3: The Noun File. */
+/*-------------------------------------------------------------------------*/
+
+#undef seti
+#undef setb
+#define seti(a) (noun[i].a=buff[bp] | (buff[bp+1]<<8),bp+=2)
+#define setb(a) (noun[i].a=buff[bp],bp++)
+
+static void read_da3(fc_type fc) {
+ int i, numnoun;
+ long recsize;
+ uchar *buff; /* [FRS_NOUN];*/
+ long bp;
+
+ numnoun = maxnoun - first_noun + 1;
+ if (numnoun < 0) return;
+ noun_sdesc = (long *)rmalloc(numnoun * sizeof(long));
+ noun_pos = (long *)rmalloc(numnoun * sizeof(long));
+
+ recsize = buffopen(fc, fDA3, FRS_NOUN, "noun", numnoun);
+ if (aver == AGT15 && recsize > 263) aver = AGT15F;
+
+ bp = 0;
+ for (i = 0; i < numnoun; i++) {
+ buff = buffread(i);
+ bp = 0;
+ if (nonecheck(SL_NAME)) {
+ bp += SL_NAME;
+ noun[i].name = 0;
+ noun[i].unused = 1;
+ } else {
+ noun[i].name = setd(SL_NAME);
+ noun[i].unused = 0;
+ }
+ noun_sdesc[i] = setstr(SL_TEXT);
+ noun[i].adj = setd(SL_NAME);
+
+ if (aver >= AGT15F) seti(initdesc);
+ else noun[i].initdesc = 0;
+
+ setb(plural);
+ /* The following is a guess for ME games */
+ noun_pos[i] = setstr((ver == 3) ? SL_ROOM : SL_NAME);
+ setb(something_pos_near_noun); /* These may not be valid */
+ seti(nearby_noun); /* in masters ed. */
+
+ setb(has_syns);
+ noun[i].syns = setsl(); /*,SL_TEXT);*/
+ if (aver >= AGT15)
+ noun[i].related_name = setd(SL_NAME);
+ else
+ noun[i].related_name = 0;
+ seti(location);
+ seti(weight);
+ seti(size);
+ seti(key);
+ /* All of following flags known to be valid except
+ pullable, on, and win */
+ setb(pushable);
+ setb(pullable);
+ setb(turnable);
+ setb(playable);
+ setb(readable);
+ setb(on);
+ setb(closable);
+ setb(open);
+ setb(lockable);
+ setb(locked);
+ setb(edible);
+ setb(wearable);
+ setb(drinkable);
+ setb(poisonous);
+ setb(movable);
+ setb(light);
+ setb(shootable);
+ seti(num_shots);
+ seti(points);
+ if (noun_inside != NULL)
+ noun_inside[i] = fixsign16(buff[bp], buff[bp + 1]);
+ bp += 2; /* Skip # of nouns contained in this one */
+ setb(win);
+ if (ver == 3) seti(pict);
+ else noun[i].pict = 0;
+ noun[i].oclass = 0; /* AGT games don't support classes */
+ noun[i].isglobal = 0;
+ noun[i].flagnum = 0;
+ noun[i].seen = 0;
+ noun[i].proper = 0;
+ }
+ if (DIAG)
+ rprintf(" Internal:%ld\n", bp);
+ buffclose();
+}
+
+
+#undef seti
+#undef setb
+#define seti(a) (creature[i].a=buff[bp] | (buff[bp+1]<<8),bp+=2)
+#define setb(a) (creature[i].a=buff[bp],bp++)
+
+
+
+/*-------------------------------------------------------------------------*/
+/* Read DA4: The Creature File. */
+/*-------------------------------------------------------------------------*/
+
+static void read_da4(fc_type fc) {
+ int i, numcreat;
+ uchar *buff; /* [FRS_CREAT];*/
+ long bp;
+
+ numcreat = maxcreat - first_creat + 1;
+ if (numcreat <= 0) return;
+ creat_sdesc = (long *)rmalloc(numcreat * sizeof(long));
+
+ buffopen(fc, fDA4, FRS_CREAT, "creature", numcreat);
+
+ bp = 0;
+ for (i = 0; i < numcreat; i++) {
+ buff = buffread(i);
+ bp = 0;
+ if (nonecheck(SL_NAME)) {
+ bp += SL_NAME;
+ creature[i].name = 0;
+ creature[i].unused = 1;
+ } else {
+ creature[i].name = setd(SL_NAME);
+ creature[i].unused = 0;
+ }
+ creat_sdesc[i] = setstr(SL_TEXT);
+ creature[i].adj = setd(SL_NAME);
+ if (ver == 3) seti(initdesc);
+ else creature[i].initdesc = 0;
+ setb(has_syns);
+ creature[i].syns = setsl();
+ setb(groupmemb);
+ seti(location);
+ seti(weapon);
+ setb(hostile);
+ seti(points);
+ if (creat_inside != NULL)
+ creat_inside[i] = fixsign16(buff[bp], buff[bp + 1]);
+ bp += 2; /* Skip # of nouns the creature is carrying */
+ seti(counter);
+ seti(threshold);
+ seti(timethresh);
+ seti(timecounter);
+ setb(gender);
+ if (ver == 3) seti(pict);
+ else creature[i].pict = 0;
+ creature[i].oclass = 0; /* AGT games don't support classes */
+ creature[i].isglobal = 0;
+ creature[i].flagnum = 0;
+ creature[i].seen = 0;
+ creature[i].proper = 0;
+ }
+ if (DIAG)
+ rprintf(" Internal:%ld\n", bp);
+ buffclose();
+}
+
+#undef seti
+#undef setb
+
+
+
+
+/*-------------------------------------------------------------------------*/
+/* Read Commands (DA5 and DA6) and convert them to a uniform internal */
+/* format. */
+/*-------------------------------------------------------------------------*/
+
+
+static int translate_vnum(int vnum)
+/* actor is a numerical index occuring at the beginning of each command.
+ In general, it contains the verb number of the verb associated with
+ this command; because of AGiliTy's dictionary organization, we don't
+ really need this (the verb itself will be converted to a number anyhow),
+ but the field contains other useful information as well:
+ i)If this command header is really the object of a redirection command,
+ then the actor will have 1000 or 2000 added to it, depending on
+ AGT version.
+ ii)If this command is directed at an actor, then the creature number
+ will be in this field instead of the verb number.
+ Commands directed to ANYBODY have one plus the maximum verb number
+ in this field (a hassle since the maximum verb number depends on
+ AGT version: Classic:106, Master's:123); EVERYONE is the next
+ code after ANYBODY.
+ What this routine does is rationalize the differences between AGT versions.
+ --Verb values (ie. not referring to creatures) are converted to 1.
+ --Redirections are marked by multiplying by negative one and setting
+ cmdsize to 0.
+ --ANYBODY is set to 2
+ --EVERYBODY is set to 3
+*/
+{
+ rbool redir; /* Is this command redirected? */
+ integer anycode;
+ int redir_val;
+
+ /* Earlier games use 1000 as redirect value, later games use 2000: */
+ /* We strip it off, but remember whether it was there or not so we
+ can restore this information later. */
+ redir_val = (aver <= AGT18MAX ? 1000 : 2000);
+ if (vnum >= redir_val) {
+ vnum = vnum % redir_val;
+ redir = 1;
+ } else redir = 0;
+
+ anycode = (aver <= AGT18MAX) ? 106 : 123;
+
+ /* Now to correct ANYBODY to something consistent and set verb
+ references to 1 since we don't need them and they just confuse things */
+ if (vnum < anycode) vnum = 1; /* "player" */
+ else if (vnum == anycode) vnum = 2; /* ANYBODY */
+ else if (vnum == anycode + 1) vnum = 3; /* EVERYBODY */
+
+ /* Finally restore redirection info. We now use the sign of vnum
+ to indicate this.*/
+ if (redir) vnum = -vnum;
+
+ return vnum;
+}
+
+
+#define CREC_SIZE (FRS_CMD)
+
+static long badtokcnt;
+
+static void read_da5(fc_type fc) {
+ long i, j;
+ uchar *buff; /* [CREC_SIZE];*/
+ long bp;
+
+ if (!have_meta) return;
+ if (last_cmd <= 0)
+ fatal("Bogus last_cmd");
+
+ buffopen(fc, fDA5, CREC_SIZE, "command", last_cmd);
+
+ if (aver >= AGT15F) cmd_ptr = (long *)rmalloc(sizeof(long) * last_cmd);
+ else cmd_ptr = NULL;
+
+ bp = 0;
+ for (i = 0; i < last_cmd; i++) {
+ buff = buffread(i);
+ command[i].actor = translate_vnum(buff[0] + (buff[1] << 8));
+ bp = 2;
+ command[i].verbcmd = setd(SL_WORD);
+ command[i].nouncmd = setd(SL_WORD);
+ if (aver >= AGTME10)
+ command[i].prep = setd(SL_WORD);
+ else command[i].prep = 0;
+ command[i].objcmd = setd(SL_WORD);
+ command[i].noun_adj = command[i].obj_adj = 0;
+ command[i].noun_obj = command[i].obj_obj = 0;
+ if (aver < AGT15F) {
+ command[i].data = (integer *)rmalloc(MAX_CMD_SIZE * sizeof(integer));
+ for (j = 0; j < MAX_CMD_SIZE; j++)
+ command[i].data[j] = fixsign16(buff[bp + 2 * j], buff[bp + 2 * j + 1]);
+ bp += 2 * MAX_CMD_SIZE;
+ command[i].cmdsize = MAX_CMD_SIZE;
+ } else {
+ cmd_ptr[i] = (long)buff[bp] + (((long)buff[bp + 1]) << 8);
+ bp += 2;
+ }
+ }
+ if (DIAG)
+ rprintf(" Internal:%ld\n", bp);
+ buffclose();
+
+ /* Now to read in DA6 for versions that have it */
+ if (aver >= AGT15F) read_da6(fc);
+ check_cmd_version(); /* This uses the opcodes to check gamefile
+ version information and change it if neccesary. */
+ build_cmd_table(); /* Create the command translation table for
+ this version of AGT. */
+
+ badtokcnt = 0;
+ if (!RAW_CMD_OUT)
+ for (i = 0; i < last_cmd; i++)
+ fixcmd(command[i].data, command[i].cmdsize);
+ rfree(cmd_table);
+ if (badtokcnt > MAX_BADTOK)
+ agtnwarn("Total number of bad opcodes:", badtokcnt, 1);
+}
+
+
+
+static void read_da6(fc_type fc)
+/* This will only be called for versions with a DA6 file--
+ i.e. Master's Edition and proto-ME games. */
+{
+ genfile fda6;
+ char *fullname;
+ const char *errstr;
+ long fsize; /* Size of the file */
+ long frame; /* The first element of the file that is in the buffer. */
+ uchar *cbuf; /* Buffer */
+ long cfile_size, cbuf_size; /* Number of tokens in file and in buffer */
+ long i, j;
+ long cmdstart, cmdend; /* Marks the start and end of the current command */
+ long ip; /* Points to instruction in cmd.data[] that we are writing to */
+ long bp; /* Pointer into buffer */
+ long endp; /* Used to indicate end of current read loop
+ (with an infinite buffer, this would always be an adjusted
+ cmdend) */
+ long adj_cbuf_size; /* Stores number of bytes actually read in to cbuf */
+
+ fda6 = openbin(fc, fDA6, "Could not open code file '%s'.", 1);
+ fsize = binsize(fda6);
+ fullname = formal_name(fc, fDA6);
+ if (DIAG) rprintf("Reading code file %s (size:%ld)\n", fullname, fsize);
+
+ if (aver == AGT15F && fsize == 20000) aver = AGT16;
+ if (aver >= AGTME10) cfile_size = 20000;
+ else if (aver == AGT16) cfile_size = 10000;
+ else cfile_size = 5000;
+
+ if (fsize != 2 * cfile_size)
+ fatals("Code file %s is the wrong size.", fullname);
+
+ cbuf_size = (cfile_size < CBUF_SIZE) ? cfile_size : CBUF_SIZE;
+ cbuf = (uchar *)rmalloc(2 * cbuf_size);
+ frame = cfile_size + 1; /* Guarentee frame will be wrong */
+
+ for (i = 0; i < last_cmd; i++)
+ if (cmd_ptr[i] >= 2) {
+ for (j = i + 1; j < last_cmd && cmd_ptr[j] <= cmd_ptr[i]; j++);
+ if (j < last_cmd) cmdend = cmd_ptr[j];
+ else cmdend = cfile_size;
+ if (cmdend > cfile_size) fatals("Code file overrun(%s)", fullname);
+ --cmdend;
+ cmdstart = cmd_ptr[i] - 1;
+ command[i].cmdsize = cmdend - cmdstart;
+ command[i].data = (integer *)rmalloc(command[i].cmdsize * sizeof(integer));
+
+ ip = 0;
+ bp = cmdstart - frame;
+ adj_cbuf_size = cbuf_size;
+
+ while (ip < command[i].cmdsize) {
+ if (bp < 0 || bp >= adj_cbuf_size) { /* Read in new block */
+ frame = frame + bp;
+ binseek(fda6, frame * 2);
+ if (frame + cbuf_size <= cfile_size)
+ adj_cbuf_size = cbuf_size;
+ else
+ adj_cbuf_size = cfile_size - frame;
+ if (adj_cbuf_size <= 0) fatal("Unexpected end of file.");
+ if (!binread(fda6, cbuf, 2, adj_cbuf_size, &errstr))
+ fatal(errstr);
+ bp = 0;
+ }
+ endp = cmdend - frame;
+ if (endp > cbuf_size) endp = cbuf_size;
+ for (; bp < endp; ip++, bp++)
+ command[i].data[ip] = fixsign16(cbuf[bp * 2L], cbuf[bp * 2L + 1]);
+ }
+ } else {
+ command[i].data = NULL;
+ command[i].cmdsize = 0;
+ }
+ rfree(cbuf);
+ readclose(fda6);
+ rfree(fullname);
+}
+
+
+
+
+
+static int check_endcmd(void)
+/* What is the most common last byte for metacommands? Except
+ under very abnormal situations, this should be the EndCmd opcode */
+{
+ int count[MAX_TOKEN_ID + 1];
+ int i, tok, maxcnt, maxtok;
+ /* int nextcnt; */
+
+ for (i = 0; i <= MAX_TOKEN_ID; i++) count[i] = 0;
+ for (i = 0; i < last_cmd; i++)
+ if (command[i].cmdsize > 0) {
+ tok = command[i].data[command[i].cmdsize - 1];
+ if (tok >= 0 && tok <= MAX_TOKEN_ID) count[tok]++;
+ }
+ maxcnt = maxtok = 0; /* nextcnt=0;*/
+ for (i = 0; i <= MAX_TOKEN_ID; i++)
+ if (count[i] >= maxcnt) {
+ /* nextcnt=maxcnt; */
+ maxcnt = count[i];
+ maxtok = i;
+ }
+ return maxtok;
+}
+
+static int compute_endcode(int ver_)
+/* Computes the correct endcode for a given gamefile version */
+{
+ int i;
+
+ for (i = 0; FIX_LIST[ver_][i].tnew != -1; i++);
+ return (FIX_LIST[ver_][i].told - 3); /* -3 to get to EndCmd */
+}
+
+
+static void check_cmd_version(void)
+/* Run through the commands looking at the last legal byte. This is
+ normally the EndCmd token code, which can give us info on which
+ token encoding scheme is being used. */
+{
+ int endcode;
+
+ endcode = check_endcmd();
+ if (DIAG) rprintf(" (EndCmd=%d)\n", endcode);
+ if (endcode < 150) return; /* No metacommands, or something else is wrong. */
+ if (endcode == compute_endcode(aver)) return; /* We're okay */
+
+ /* Check for the special cases we know about */
+ if (aver == AGT135) {
+ if (endcode == compute_endcode(AGT182)) {
+ aver = AGT182;
+ return;
+ } else if (endcode == compute_endcode(AGT118)) {
+ aver = AGT118;
+ return;
+ }
+ }
+ if (aver == AGTME10)
+ if (endcode == compute_endcode(AGTME10A)) {
+ aver = AGTME10A;
+ return;
+ }
+ if (aver == AGTMAST)
+ if (endcode == compute_endcode(AGTME155)) {
+ aver = AGTME155;
+ return;
+ }
+
+ /* If we still haven't fixed the problem, print out a warning and
+ pray. */
+ agtnwarn("Game has invalid EndCmd: ", endcode, 1);
+}
+
+
+
+static void build_cmd_table(void) {
+ int told, tnew, fp;
+ const cmd_fix_rec *fixtbl;
+
+ topcmd = compute_endcode(aver) + 3;
+ cmd_table = (short *)rmalloc(sizeof(short) * topcmd);
+
+ fixtbl = FIX_LIST[aver];
+ fp = 0; /* Pointer into fix table */
+ tnew = 0; /* This shouldn't be neccessary */
+ for (told = 0; told < topcmd;) {
+ if (told == fixtbl[fp].told) tnew = fixtbl[fp++].tnew;
+ cmd_table[told++] = tnew++;
+ }
+}
+
+
+
+static void badtokerr(const char *s, int tok) {
+ if (++badtokcnt <= MAX_BADTOK) agtnwarn(s, tok, 1);
+}
+
+static void fixcmd(integer *clist, int cnt)
+/* Okay, we need to go through the elements of clist (which is an array,
+ actually), figure out which ones are commands (as opposed to arguments)
+ and tweak them to hide version differences. */
+{
+ long ip;
+
+ /* Now need to go through and adjust opcodes. */
+ for (ip = 0; ip < cnt; ip++)
+ if (clist[ip] >= topcmd || clist[ip] < 0)
+ badtokerr("Invalid token found: ", clist[ip]);
+ else {
+
+ clist[ip] = cmd_table[clist[ip]]; /* Translate */
+
+ /* Now increment ip by the length of the instruction */
+ /* Remember that we are already incrementing by one automatically */
+
+ if (clist[ip] >= END_ACT) break; /* CMD end marker */
+ if (clist[ip] <= MAX_COND)
+ ip += cond_def[clist[ip]].argnum;
+ else if (clist[ip] < WIN_ACT) {
+ if (clist[ip] == 1087 && ip + 1 < cnt) /* AskQuestion: Adjust top_quest */
+ if (top_quest < clist[ip + 1]) top_quest = clist[ip + 1];
+ ip += act_def[clist[ip] - START_ACT].argnum;
+ }
+ /* else do nothing */
+ }
+}
+
+
+
+
+
+/*-------------------------------------------------------------------------*/
+/* DA1 Reading Utilites: routines to read the various lines of the DA1 file */
+/*-------------------------------------------------------------------------*/
+
+static void chop_newline(char *s)
+/* Remove trailing \r,\n, etc. characters */
+{
+ char *t;
+
+ for (t = s; *t != 0; t++); /* Find the end of the string */
+ for (; t >= s && (*t == 0 || *t == '\r' || *t == '\n'); t--);
+ *(t + 1) = 0;
+}
+
+static void fix_answer(char *s)
+/* Put answer s into standard format: all lower case and with trailing/
+ following whitespace removed */
+{
+ char *t, *p;
+
+ for (t = s; *t != 0; t++)
+ *t = tolower(*t);
+ /* Eliminate trailing space and newlines */
+ for (; t >= s && (*t == 0 || rspace(*t)); t--);
+ *(t + 1) = 0;
+ /* Eliminate leading space and newlines */
+ for (t = s; rspace(*t); t++);
+ if (t != s) {
+ for (p = s; *t != 0;)
+ *(p++) = *(t++);
+ *p = 0;
+ }
+}
+
+
+static char linebuffer[81];
+static int bhold;
+static int linenum;
+static rbool unexpected_eof;
+
+static void read_line(genfile fd, const char *typestr)
+/* Read a line into buffer, unless bhold=1 in which case we want
+ to use the last line read */
+{
+ if (bhold == 0) {
+ readln(fd, linebuffer, 80);
+ if (linebuffer[0] == 0 && texteof(fd)) {
+ unexpected_eof = 1;
+ strcpy(linebuffer, ">End Of File<");
+ } else chop_newline(linebuffer);
+ linenum++;
+ }
+ if (debug_da1 && typestr != NULL) {
+ rprintf("%s %4d:%s", typestr, linenum, linebuffer);
+ if (bhold) rprintf(" *");
+ writeln("");
+ }
+ bhold = 0;
+}
+
+
+static void report(const char *s, genfile fd) {
+ if (DIAG) rprintf("REPORT:%s at %d\n", s, linenum);
+}
+
+static int isbool(genfile fd) {
+ read_line(fd, NULL);
+ bhold = 1;
+ return (strncasecmp(linebuffer, "TRUE", 4) == 0 ||
+ strncasecmp(linebuffer, "FALSE", 5) == 0);
+}
+
+static int isnum(genfile fd) {
+ char *errstr;
+
+ read_line(fd, NULL);
+ bhold = 1;
+ (void)strtol(linebuffer, &errstr, 10);
+ while (*errstr == '\n' || *errstr == '\r') errstr++;
+ if (debug_da1)
+ rprintf("NUMCHK: %s==>%c\n", linebuffer, *errstr);
+ return (*errstr == '\0');
+}
+
+static rbool readrbool(genfile fd) {
+ read_line(fd, "BOOL");
+ return (strncasecmp(linebuffer, "TRUE", 4) == 0);
+}
+
+
+static long readnum(genfile fd) {
+ read_line(fd, "NUM ");
+ return strtol(linebuffer, NULL, 10);
+}
+
+
+static void readptr(genfile fd, descr_ptr *desc) {
+ read_line(fd, "PTR ");
+ desc->start = strtol(linebuffer, NULL, 10);
+ read_line(fd, "LEN");
+ desc->size = strtol(linebuffer, NULL, 10);
+}
+
+
+static void readjunk(genfile fd) {
+ read_line(fd, "JUNK");
+}
+
+static void readtext(genfile fd, tline s) {
+ read_line(fd, "TEXT");
+ strncpy((char *)s, linebuffer, 80);
+ s[80] = 0;
+}
+
+static long readfname(genfile fd) {
+ read_line(fd, "FILE");
+ return new_str(linebuffer, 0, 0);
+ /* Copy filename string to static string space and return index */
+}
+
+static word readdict(genfile fd) {
+ read_line(fd, "DICT");
+ return add_dict(linebuffer);
+}
+
+
+static slist readslist(genfile fd) { /* Read in synonym list line */
+ slist start_ptr;
+ char nbuff[50];
+ int j, k;
+
+ start_ptr = synptr;
+ read_line(fd, "SYN ");
+ /* Need to see if it is none or * terminated. */
+ for (j = 0; linebuffer[j] != 0 && linebuffer[j] != '*'; j++);
+ linebuffer[j] = 0;
+ k = 0;
+ for (j = 0; linebuffer[j] != 0; j++)
+ if (rspace(linebuffer[j]) && k > 0) {
+ nbuff[k] = 0;
+ addsyn(add_dict(nbuff));
+ k = 0;
+ } else if (!rspace(linebuffer[j]))
+ nbuff[k++] = linebuffer[j];
+ if (k > 0) {
+ nbuff[k] = 0;
+ addsyn(add_dict(nbuff));
+ }
+ addsyn(-1);
+ return start_ptr;
+}
+
+
+
+/*-------------------------------------------------------------------------*/
+/* Version analysis: Utilities to analyse the file format version and */
+/* deduce sizes. */
+/*-------------------------------------------------------------------------*/
+
+static int soggy_test(fc_type fc) {
+ genfile fda3;
+ long fsize;
+
+ if (DIAG) {
+ char *s;
+ s = formal_name(fc, fDA3);
+ rprintf("Testing %s for abnormal noun organization....", s);
+ rfree(s);
+ }
+ fda3 = openbin(fc, fDA3, "Could not find room file '%s'.", 1);
+ fsize = binsize(fda3);
+ readclose(fda3);
+
+ if (fsize % (maxnoun - 300 + 1) != 0) {
+ if (DIAG) rprintf("FOUND!\n");
+ return 1;
+ }
+ if (fsize / (maxnoun - 300 + 1) > 300) {
+ if (DIAG) rprintf("FOUND!\n");
+ return 1;
+ }
+ if (DIAG) rprintf("nope.\n");
+ return 0;
+}
+
+
+static void deduce_sizes(fc_type fc, rbool diag)
+/* If diag is true, we will also allocate space for
+noun inside information; this is used by agtout */
+{
+ if (ver == 0) {
+ ver = 1;
+ if (maxroom >= 200) ver = 2;
+ else if (maxnoun != 0)
+ if (maxnoun < 300)
+ if (maxcreat != 0)
+ if (maxcreat >= 500) ver = 4; /* SOGGY */
+ else ver = 1; /* Small */
+ else if (aver == AGTCOS) ver = 4; /* SOGGY */
+ else ver = 1; /* Small */
+ else if (aver != AGTCOS) ver = 2; /* Large */
+ else if (soggy_test(fc)) ver = 4;
+ else ver = 2;
+ else if (maxcreat != 0)
+ if (maxcreat >= 500)
+ if (aver != AGTCOS) ver = 2; /* Large */
+ else if (soggy_test(fc)) ver = 4; /* Either large or SOGGY */
+ else ver = 2;
+ else ver = 1; /* Small */
+ else
+ agtwarn("No nouns or creatures: unable to determine version."
+ "\nAssuming AGT Small", 0);
+ }
+
+ if (aver < AGTME15)
+ MaxQuestion = 25;
+ else
+ MaxQuestion = 100; /* This is a guess. */
+ if (aver == AGTCOS)
+ MaxQuestion = 10;
+ if (aver == AGT15 || aver == AGT15F)
+ MaxQuestion = 57;
+ first_room = 2;
+ if (ver == 1) {
+ first_noun = 200;
+ first_creat = 300;
+ last_obj = 399;
+ last_message = 250;
+ } else { /* ver 2 or 3 or 4 */
+ if (ver != 4)
+ first_noun = 300;
+ else first_noun = 200;
+ first_creat = 500;
+ last_obj = 699;
+ if (aver <= AGT12) last_message = 500;
+ else if (aver < AGTME155) last_message = 600;
+ else last_message = 800;
+ }
+ if (aver == AGTCOS) {
+ if (ver == 4) last_obj = 610;
+ else last_obj = 599;
+ if (ver == 4) last_message = 810; /* Soggy case */
+ else last_message = 700;
+ }
+
+ if (aver >= AGT18 && aver <= AGT18MAX) {
+ bold_mode = 1;
+ build_fixchar();
+ fixchar['\\'] = FORMAT_CODE;
+ }
+
+ if (aver < AGTME10) {
+ SL_TEXT = 81;
+ SL_NAME = SL_WORD = 23;
+ SL_ROOM = 31;
+ } else {
+ SL_TEXT = 81;
+ SL_NAME = SL_WORD = 16;
+ SL_ROOM = 31;
+ }
+ if (aver == AGT15 || aver == AGT15F) SL_NAME = SL_WORD = 16;
+
+ if (aver >= AGTME10) {
+ MAX_USTR = 25;
+ MAX_SUB = 15;
+ } else MAX_SUB = MAX_USTR = 0;
+
+ if (aver >= AGT15)
+ NUM_ERR = 185; /* Number of standard error messages */
+ else
+ NUM_ERR = 0;
+
+ DVERB = 50;
+ FLAG_NUM = 255;
+ CNT_NUM = VAR_NUM = 50;
+ exitmsg_base = 1000;
+
+ num_rflags = num_nflags = num_cflags = 0;
+ num_rprops = num_nprops = num_cprops = 0;
+ objflag = NULL;
+ objprop = NULL;
+ attrtable = NULL;
+ proptable = NULL;
+ oflag_cnt = 0;
+ oprop_cnt = 0;
+ propstr = NULL;
+ propstr_size = 0;
+ vartable = NULL;
+ flagtable = NULL;
+
+
+
+ /* Now to allocate space for all of the 'immortal' data structures */
+ /* We do this all at once to avoid fragmentation; all of the following
+ will be around for the life of the program (unless we restart) and so
+ should be allocated first */
+
+ synlist = (slist *)rmalloc(sizeof(slist) * TOTAL_VERB);
+ comblist = NULL; /* The original AGT didn't support multi-word verbs */
+ num_comb = 0;
+ userprep = NULL; /* ... nor did it allow user-defined prepostions */
+ num_prep = 0;
+
+ if (numglobal > 0)
+ globalnoun = (word *)rmalloc(numglobal * sizeof(word));
+
+ if (aver < AGTME15 && aver != AGT10) {
+ question = (tline *)rmalloc(MaxQuestion * sizeof(tline));
+ answer = (tline *)rmalloc(MaxQuestion * sizeof(tline));
+ } else if (aver >= AGTME15) {
+ quest_ptr = (descr_ptr *)rmalloc(MaxQuestion * sizeof(descr_ptr));
+ ans_ptr = (descr_ptr *)rmalloc(MaxQuestion * sizeof(descr_ptr));
+ }
+ msg_ptr = (descr_ptr *)rmalloc((last_message) * sizeof(descr_ptr));
+
+ if (maxroom >= first_room) {
+ room = (room_rec *)rmalloc((maxroom - first_room + 1) * sizeof(room_rec));
+ room_ptr = (descr_ptr *)rmalloc((maxroom - first_room + 1) * sizeof(descr_ptr));
+ help_ptr = (descr_ptr *)rmalloc((maxroom - first_room + 1) * sizeof(descr_ptr));
+ special_ptr = (descr_ptr *)rmalloc((maxroom - first_room + 1) * sizeof(descr_ptr));
+ if (diag) room_inside = (integer *)rmalloc((maxroom - first_room + 1) * sizeof(integer));
+ }
+
+ if (maxnoun >= first_noun) {
+ noun = (noun_rec *)rmalloc((maxnoun - first_noun + 1) * sizeof(noun_rec));
+ noun_ptr = (descr_ptr *)rmalloc((maxnoun - first_noun + 1) * sizeof(descr_ptr));
+ push_ptr = (descr_ptr *)rmalloc((maxnoun - first_noun + 1) * sizeof(descr_ptr));
+ pull_ptr = (descr_ptr *)rmalloc((maxnoun - first_noun + 1) * sizeof(descr_ptr));
+ text_ptr = (descr_ptr *)rmalloc((maxnoun - first_noun + 1) * sizeof(descr_ptr));
+ turn_ptr = (descr_ptr *)rmalloc((maxnoun - first_noun + 1) * sizeof(descr_ptr));
+ play_ptr = (descr_ptr *)rmalloc((maxnoun - first_noun + 1) * sizeof(descr_ptr));
+ if (diag) noun_inside = (integer *)rmalloc((maxnoun - first_noun + 1) * sizeof(integer));
+ }
+
+ if (maxcreat >= first_creat) {
+ creature = (creat_rec *)rmalloc((maxcreat - first_creat + 1) * sizeof(creat_rec));
+ creat_ptr = (descr_ptr *)rmalloc((maxcreat - first_creat + 1) * sizeof(descr_ptr));
+ ask_ptr = (descr_ptr *)rmalloc((maxcreat - first_creat + 1) * sizeof(descr_ptr));
+ talk_ptr = (descr_ptr *)rmalloc((maxcreat - first_creat + 1) * sizeof(descr_ptr));
+ if (diag) creat_inside = (integer *)rmalloc((maxcreat - first_creat + 1) * sizeof(integer));
+ }
+
+ if (aver >= AGTME10) {
+ userstr = (tline *)rmalloc(MAX_USTR * sizeof(tline));
+ sub_name = (word *)rmalloc(MAX_SUB * sizeof(word));
+ }
+ command = (cmd_rec *)rmalloc(sizeof(cmd_rec) * last_cmd);
+
+ if (aver >= AGT15)
+ err_ptr = (descr_ptr *)rmalloc(NUM_ERR * sizeof(descr_ptr));
+
+
+ reinit_dict(); /* The dictionary grows dynamically so we want to
+ allocate it AFTER we have allocated all the permenent
+ things */
+}
+
+
+
+
+/*-------------------------------------------------------------------------*/
+/* Read DA1: The Info file; this is a text file containing a miscellany of */
+/* game information that wouldn't fit elsewhere */
+/*-------------------------------------------------------------------------*/
+
+static int try_read_da1(fc_type fc, genfile fda1, rbool diag)
+/* Returns new aver value to try, or 0 on success. */
+/* diag determines if noun inside info will be read */
+/* VER values: 1=Small
+ 2=Large
+ 3=Master's Edition
+ 4="Soggy Large", with a larger last_room
+*/
+/* AVER values: see agility.h for the current values */
+/* NOTE: This routine is allowed to set *ver*, but is not allowed to
+ change *aver*; should it be neccessary to change *aver*, then the routine
+ should return the new *aver* value.
+ (The only exception to this is in the very beginning-- and that may get
+ changed)
+ [This is done to allow the user to force a version number]
+*/
+{
+ int i;
+
+ MAX_CMD_SIZE = 30;
+ maxpict = maxpix = maxfont = maxsong = 0;
+ linenum = 0;
+ bhold = 0;
+ game_sig = 0;
+ unexpected_eof = 0;
+
+ if (aver == 0 && isbool(fda1)) aver = AGTMAST;
+ /* From this point on can assume ME detected */
+
+ freeze_mode = 0; /* The default values */
+ if (aver >= AGTME10) { /* 2 rbool */
+ debug_mode = readrbool(fda1); /* DEBUG */
+ if (aver >= AGTME15) {
+ if (!isbool(fda1)) aver = AGTME10;
+ else freeze_mode = readrbool(fda1);
+ } /* FREEZE */
+ ver = 3;
+ }
+
+ start_room = readnum(fda1);
+ treas_room = readnum(fda1);
+ if (aver != AGT10) resurrect_room = readnum(fda1);
+ else resurrect_room = start_room;
+ if (aver >= AGTME10) { /* 4 int */
+ score_mode = readnum(fda1); /* Score option */
+ statusmode = readnum(fda1); /* Status option */
+ startup_time = readnum(fda1); /* Starting time */
+ delta_time = readnum(fda1); /* Delta_time */
+ } else {
+ score_mode = statusmode = 0;
+ startup_time = delta_time = 0;
+ }
+ max_lives = readnum(fda1);
+ if (aver != AGT10) max_score = readnum(fda1);
+ else max_score = 0;
+ maxroom = readnum(fda1);
+ maxnoun = readnum(fda1);
+ maxcreat = readnum(fda1);
+ if (aver >= AGTME10) numglobal = readnum(fda1); /* # of global nouns? */
+ else numglobal = 0;
+ last_cmd = readnum(fda1);
+ readjunk(fda1); /* Number of items being carried */
+ readjunk(fda1); /* Number of items being worn */
+ if (isbool(fda1)) return AGT10; /* AGT v1.0 */
+ /* From this point on, can assume AGT v1.0 is detected. */
+ readptr(fda1, &intro_ptr);
+
+ deduce_sizes(fc, diag);
+
+ if (aver >= AGTME10) {
+
+ (void)readdict(fda1); /* ?!?! Not sure what this is */
+
+ report("Reading global and flag nouns", fda1);
+
+ for (i = 0; i < MAX_FLAG_NOUN; i++)
+ flag_noun[i] = readdict(fda1); /* Read in flag nouns; may be NONE */
+ for (; i < 32; i++)
+ readjunk(fda1);
+
+ for (i = 0; i < numglobal; i++)
+ globalnoun[i] = readdict(fda1); /* Global nouns */
+ } else
+ for (i = 0; i < MAX_FLAG_NOUN; i++)
+ flag_noun[i] = 0;
+
+ report("Reading questions and junk", fda1);
+
+ if (aver < AGTME15 && aver != AGT10) {
+ for (i = 0; i < MaxQuestion; i++) {
+ readtext(fda1, question[i]); /* Question[i]== question #(i+1) */
+ chop_newline(question[i]);
+ readtext(fda1, answer[i]);
+ fix_answer(answer[i]);
+ }
+ } else if (aver >= AGTME15) {
+ /* There are 400 lines of description pointers, meaning
+ 200 descriptions. I'm guessing they're all questions and
+ answers, which means that there are 100 questions here. */
+ for (i = 0; i < MaxQuestion; i++) {
+ readptr(fda1, &quest_ptr[i]);
+ readptr(fda1, &ans_ptr[i]);
+ }
+ }
+
+ if (!isbool(fda1)) { /* Something is wrong... */
+ if (aver == AGTMAST)
+ return AGTME10;
+ else if (aver != AGTCOS && aver != AGT15 && aver != AGT15F) return AGTCOS;
+ else return AGT15;
+ }
+ report("Reading have_meta", fda1);
+ have_meta = readrbool(fda1);
+
+ if (have_meta) {
+ for (i = 0; i <= last_obj; i++) { /* i.e. iterate over all objects */
+ readjunk(fda1);
+ readjunk(fda1);
+ }
+ }
+
+ /* The Master's Edition apparently _always_ sets have_meta,
+ even if there are no metacommands. The only way to determine
+ if there are really metacommands is to check last_cmd */
+ if (aver >= AGTME10 && last_cmd == 0) have_meta = 0;
+
+ report("Reading synonyms", fda1);
+
+ for (i = 0; i < TOTAL_VERB; i++)
+ synlist[i] = synptr; /* Is this correct? */
+ addsyn(-1); /* Put an end-of-list marker in place */
+
+ for (i = 0; i < 56; i++)
+ synlist[i] = readslist(fda1); /* May read <none> */
+
+ if (aver >= AGTME10) { /* Unknown verbs */
+ synlist[56] = readslist(fda1); /* VIEW */
+ synlist[57] = synlist[14]; /* AFTER */
+ synlist[14] = readslist(fda1); /* THROW */
+ }
+
+ if (aver == AGT183) {
+ /* Eliminate LIST_EXITS and add INSTRUCTIONS */
+ synlist[58] = synlist[52];
+ /* Move 'REMOVE'-- the last thing before INS in 1.83 verblist --
+ up to INSTRUCTIONS where it belongs */
+ for (i = 52; i > 42; i--) /* i:=Remove to Brief (above List Exits) */
+ synlist[i] = synlist[i - 1];
+ synlist[41] = synptr; /* LIST_EXITS, which doesn't exist in 1.83 and so
+ can't have synonyms */
+ addsyn(-1);
+ }
+
+ report("Starting dummy verbs", fda1);
+ for (i = 0; i < 25; i++) {
+ if (i != 0 || aver < AGTME10)
+ synlist[i + BASE_VERB] = readslist(fda1);
+ synlist[i + BASE_VERB + 25] = readslist(fda1);
+ }
+ if (aver >= AGTME10) {
+ synlist[BASE_VERB] = readslist(fda1);
+ for (i = 0; i < 15; i++) /* Subroutines */
+ synlist[i + BASE_VERB + 50] = readslist(fda1);
+ }
+ report("Reading DESC ptrs", fda1);
+ if (aver >= AGT15)
+ for (i = 0; i < NUM_ERR; i++)
+ readptr(fda1, &err_ptr[i]); /* Read in "standard" error messages. */
+ else /* Otherwise need to initialize them to nothing */
+ for (i = 0; i < NUM_ERR; i++) {
+ err_ptr[i].start = 0;
+ err_ptr[i].size = -1;
+ }
+
+ report("Reading messages", fda1);
+ if (DIAG) rprintf(" MSGS:1..%ld [%ld]\n", last_message, last_message);
+ for (i = 0; i < last_message; i++)
+ readptr(fda1, &msg_ptr[i]);
+
+ report("Reading room descs", fda1);
+ for (i = 0; i <= maxroom - first_room; i++) {
+ readptr(fda1, &room_ptr[i]);
+ readptr(fda1, &help_ptr[i]);
+ readptr(fda1, &special_ptr[i]);
+ }
+
+ report("Reading noun descs", fda1);
+ for (i = 0; i <= maxnoun - first_noun; i++) {
+ readptr(fda1, &noun_ptr[i]);
+ readptr(fda1, &push_ptr[i]);
+ readptr(fda1, &pull_ptr[i]);
+ readptr(fda1, &text_ptr[i]);
+ readptr(fda1, &turn_ptr[i]);
+ readptr(fda1, &play_ptr[i]);
+ }
+
+ report("Reading creatures", fda1);
+ if (maxcreat >= first_creat) {
+ for (i = 0; i <= maxcreat - first_creat; i++) {
+ readptr(fda1, &creat_ptr[i]);
+ if (aver != 0 && aver <= AGTCLASS) {
+ ask_ptr[i].start = talk_ptr[i].start = 0;
+ ask_ptr[i].size = talk_ptr[i].size = -1;
+ } else {
+ readptr(fda1, &talk_ptr[i]);
+ readptr(fda1, &ask_ptr[i]);
+ if (aver == 0 && (talk_ptr[i].size == 0 || ask_ptr[i].size == 0 ||
+ unexpected_eof)) return AGT135;
+ }
+ }
+ }
+ if (aver == AGT135 && unexpected_eof) return AGT12;
+
+ if (aver >= AGTME10) {
+ if (aver >= AGTME155 && !isnum(fda1)) return AGTME15;
+
+ maxpict = rangefix(readnum(fda1)); /* Number of pictures */
+ maxpix = rangefix(readnum(fda1)); /* Numper of PIXs */
+ maxsong = rangefix(readnum(fda1)); /* Number of sounds */
+ maxfont = rangefix(readnum(fda1)); /* Number of fonts. */
+
+ if (maxpix > MAX_PIX) {
+ rprintf("Invalid MAXPIX value?!?\n");
+ maxpix = MAX_PIX;
+ }
+
+ t_pictlist = (long *)rmalloc(sizeof(long) * maxpict);
+ t_pixlist = (long *)rmalloc(sizeof(long) * maxpix);
+ t_songlist = (long *)rmalloc(sizeof(long) * maxsong);
+ t_fontlist = (long *)rmalloc(sizeof(long) * maxfont);
+
+ for (i = 0; i < maxpict; i++)
+ t_pictlist[i] = readfname(fda1); /* picture file names */
+ for (i = 0; i < maxpix; i++)
+ pix_name[i] = readdict(fda1); /* PIX names */
+ for (i = 0; i < maxpix; i++)
+ t_pixlist[i] = readfname(fda1); /* PIX filenames */
+ for (i = 0; i < maxsong; i++)
+ t_songlist[i] = readfname(fda1); /* Sound filenames */
+ for (i = 0; i < maxfont; i++)
+ t_fontlist[i] = readfname(fda1); /* Font filenames */
+
+ for (i = 0; i < MAX_USTR; i++)
+ readtext(fda1, userstr[i]); /* This is just a guess-- should be
+ tested. */
+ } else {
+ for (i = 0; i < maxpix; i++) pix_name[i] = 0;
+ maxpict = maxpix = maxsong = maxfont = 0;
+ }
+ if ((aver == AGT135 || aver == 0) && isnum(fda1)) return AGT183;
+ if (aver == AGT183) {
+ long tval;
+ tval = readnum(fda1); /* Needs to be translated */
+ if (tval >= 1 && tval <= 4)
+ statusmode = agt18_statmode[tval - 1];
+ else statusmode = 0;
+ tval = readnum(fda1); /* Hours */
+ startup_time = readnum(fda1); /* Minutes */
+ tval += startup_time / 60;
+ startup_time = (startup_time % 60) + 100 * tval;
+ if (readrbool(fda1) && startup_time < 1200)
+ startup_time += 1200;
+ milltime_mode = readrbool(fda1); /* Military time */
+ delta_time = readnum(fda1);
+ }
+ if (DIAG) rprintf("Read in %d lines\n", linenum);
+ return 0;
+}
+
+
+
+static void set_da1_null(void)
+/* Set pointers that are malloc'd by try_read_da1 to NULL, to clear
+ the way for free_da1_stuff to recover them */
+{
+ static_str = NULL;
+ ss_end = ss_size = 0;
+ command = NULL;
+ cmd_ptr = NULL;
+ synlist = NULL;
+ userstr = NULL;
+ sub_name = NULL;
+ globalnoun = NULL;
+ err_ptr = NULL;
+ quest_ptr = ans_ptr = NULL;
+ question = answer = NULL;
+ msg_ptr = room_ptr = help_ptr = special_ptr = NULL;
+ noun_ptr = push_ptr = pull_ptr = text_ptr = turn_ptr = play_ptr = NULL;
+ room_inside = noun_inside = creat_inside = NULL;
+ creat_ptr = ask_ptr = talk_ptr = NULL;
+ pictlist = pixlist = fontlist = songlist = NULL;
+ room = NULL;
+ noun = NULL;
+ creature = NULL;
+ command = NULL;
+ t_pictlist = t_pixlist = t_fontlist = t_songlist = NULL;
+}
+
+
+
+static void free_da1_stuff(void)
+/* Free all data structures malloc'd by try_read_da1 */
+/* (This is neccessary since try_read_da1 may have to restart) */
+/* Note that if a pointer is NULL, rfree does nothing */
+/* Recall that rfree() is a macro that sets its argument to NULL */
+/* after freeing it */
+{
+ rfree(static_str);
+ ss_end = ss_size = 0;
+ rfree(userstr);
+ rfree(sub_name);
+ rfree(globalnoun);
+ rfree(err_ptr);
+ rfree(synlist);
+ rfree(quest_ptr);
+ rfree(ans_ptr);
+ rfree(question);
+ rfree(answer);
+ rfree(msg_ptr);
+ rfree(room_ptr);
+ rfree(help_ptr);
+ rfree(special_ptr);
+ rfree(noun_ptr);
+ rfree(push_ptr);
+ rfree(pull_ptr);
+ rfree(text_ptr);
+ rfree(room_inside);
+ rfree(noun_inside);
+ rfree(creat_inside);
+ rfree(turn_ptr);
+ rfree(play_ptr);
+ rfree(creat_ptr);
+ rfree(ask_ptr);
+ rfree(talk_ptr);
+ rfree(t_pictlist);
+ rfree(t_pixlist);
+ rfree(t_songlist);
+ rfree(t_fontlist);
+ rfree(room);
+ rfree(noun);
+ rfree(creature);
+ rfree(command);
+ free_dict();
+}
+
+static rbool read_da1(fc_type fc, rbool diag)
+/* diag is set by agtout to save extra diagnostic information */
+/* It has nothing to do with DIAG */
+{
+ genfile fda1;
+ int i;
+
+ ver = 0;
+ aver = 0;
+ top_quest = 0; /* Highest question actually referenced; set by fixcmd */
+ fda1 = openfile(fc, fDA1, NULL, 0);
+ if (!filevalid(fda1, fDA1)) return 0;
+
+ if (DIAG) {
+ char *s;
+ s = formal_name(fc, fDA1);
+ rprintf("Reading info file %s\n", s);
+ rfree(s);
+ }
+ set_da1_null();
+ while ((i = try_read_da1(fc, fda1, diag)) != 0) {
+ if (aver == i) {
+ rprintf("[Recoginiton loop: AVER=%d]\n", aver);
+ fatal("AGT version not recognized\n");
+ }
+ aver = i;
+ /* fseek(fda1,0,SEEK_SET); Go back to beginning... */
+ textrewind(fda1);
+ if (DIAG)
+ rprintf("...Found incompatibility; restarting, w/ AVER=%d\n", aver);
+ free_da1_stuff();
+ /* set_da1_null();*/
+ ver = 0;
+ }
+ if (aver == 0) aver = AGTSTD; /* i.e. if we didn't notice any differences from
+ standard format, it must be a standard file. */
+ readclose(fda1);
+ return 1; /* Success */
+}
+
+
+
+/*-------------------------------------------------------------------------*/
+/* Miscellaneous routines to tie up loose ends and clean up afterwards. */
+/*-------------------------------------------------------------------------*/
+
+static void finish_read(rbool cleanup)
+/* cleanup=0 means it will leave cmd_ptr, 1=it cleans up cmd_ptr */
+/* The only reason to set cleanup==0 is if we are writing a diagnostic
+ program of some sort */
+{
+ int i;
+
+ if (aver >= AGT18 && aver <= AGT18MAX) {
+ intro_first = 1;
+ max_lives = 1;
+ TWO_CYCLE = 1;
+ PURE_AFTER = 0;
+ } else {
+ intro_first = 0;
+ TWO_CYCLE = 0;
+ PURE_AFTER = 1;
+ }
+
+ min_ver = 0; /* All original AGT games will run with any version of
+ AGiliTy. */
+
+ if (aver >= AGTME10)
+ PURE_ROOMTITLE = 0;
+
+ if (aver >= AGT15)
+ box_title = 1;
+ else box_title = 0;
+
+ /* Compute max_score if it isn't already computed */
+ if (max_score == 0) {
+ for (i = 0; i < maxroom - first_room + 1; i++)
+ if (!room[i].unused) max_score += room[i].points;
+ for (i = 0; i < maxnoun - first_noun + 1; i++)
+ if (!noun[i].unused) max_score += noun[i].points;
+ for (i = 0; i < maxcreat - first_creat + 1; i++)
+ if (!creature[i].unused) max_score += creature[i].points;
+ }
+
+ if (cleanup) rfree(cmd_ptr);
+ if (ss_end > 0)
+ static_str = (char *)rrealloc(static_str, sizeof(char) * ss_end);
+
+ /* Now convert string handles into honest pointers */
+ for (i = 0; i <= maxroom - first_room; i++)
+ room[i].name = static_str + room_name[i];
+ for (i = 0; i <= maxnoun - first_noun; i++) {
+ noun[i].shortdesc = static_str + noun_sdesc[i];
+ noun[i].position = static_str + noun_pos[i];
+ }
+ for (i = 0; i <= maxcreat - first_creat; i++)
+ creature[i].shortdesc = static_str + creat_sdesc[i];
+
+ if (aver >= AGTME10) {
+ pictlist = (filename *)rmalloc(sizeof(filename) * maxpict);
+ pixlist = (filename *)rmalloc(sizeof(filename) * maxpix);
+ songlist = (filename *)rmalloc(sizeof(filename) * maxsong);
+ fontlist = (filename *)rmalloc(sizeof(filename) * maxfont);
+
+ for (i = 0; i < maxpict; i++)
+ pictlist[i] = static_str + t_pictlist[i];
+ for (i = 0; i < maxpix; i++)
+ pixlist[i] = static_str + t_pixlist[i];
+ for (i = 0; i < maxsong; i++)
+ songlist[i] = static_str + t_songlist[i];
+ for (i = 0; i < maxfont; i++)
+ fontlist[i] = static_str + t_fontlist[i];
+ }
+
+ /* Free the various temporary arrays */
+ rfree(room_name);
+ rfree(noun_sdesc);
+ rfree(noun_pos);
+ rfree(creat_sdesc);
+ rfree(t_pictlist);
+ rfree(t_pixlist);
+ rfree(t_songlist);
+ rfree(t_fontlist);
+
+ /* Reallocate questions and asnwers to only use the space that they need */
+ if (!RAW_CMD_OUT && top_quest < MaxQuestion) {
+ MaxQuestion = top_quest; /* top_quest is computed by fixcmd */
+ if (top_quest == 0) {
+ rfree(question);
+ rfree(answer);
+ rfree(quest_ptr);
+ rfree(ans_ptr);
+ } else {
+ if (question != NULL)
+ question = (tline *)rrealloc(question, top_quest * sizeof(tline));
+ if (answer != NULL)
+ answer = (tline *)rrealloc(answer, top_quest * sizeof(tline));
+ if (quest_ptr != NULL)
+ quest_ptr = (descr_ptr *)rrealloc(quest_ptr, top_quest * sizeof(descr_ptr));
+ if (ans_ptr != NULL)
+ ans_ptr = (descr_ptr *)rrealloc(ans_ptr, top_quest * sizeof(descr_ptr));
+ }
+ }
+}
+
+void free_all_agtread() {
+ int i;
+
+ if (!agx_file)
+ for (i = 0; i < last_cmd; i++)
+ rfree(command[i].data);
+ free_da1_stuff();
+ /* userstr, globalnoun, quest_ptr, ans_ptr, question, answer, msg_ptr,
+ room_ptr, help_ptr, special_ptr, noun_ptr, push_ptr, pull_ptr,
+ text_ptr, turn_ptr, play_ptr, creat_ptr, ask_ptr, talk_ptr,
+ room_inside, noun_inside, creat_inside
+ pictlist, pixlist, songlist, fontlist,
+ room, noun, creature, command,
+ dictionary data structures */
+}
+
+rbool readagt(fc_type fc, rbool diag)
+/* If diag==1, then extra diagnostic information is preserved */
+{
+ agx_file = 0;
+ mem_descr = NULL;
+ build_fixchar();
+ init_dict();
+ if (!read_da1(fc, diag)) return 0; /* Couldn't open DA1 file */
+ read_da2(fc);
+ read_da3(fc);
+ read_da4(fc);
+ read_da5(fc);
+ read_voc(fc);
+ read_opt(fc);
+ finish_read(!diag);
+ return 1;
+}
+
+} // End of namespace AGT
+} // End of namespace Glk
diff --git a/engines/glk/agt/agxfile.cpp b/engines/glk/agt/agxfile.cpp
new file mode 100644
index 0000000..04729f5
--- /dev/null
+++ b/engines/glk/agt/agxfile.cpp
@@ -0,0 +1,1483 @@
+/* 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 "glk/agt/agility.h"
+
+namespace Glk {
+namespace AGT {
+
+/* NOTES ON CHANGING THE AGX FILE FORMAT
+
+ First of all, don't.
+
+ One of the benefits of adventure creation systems like this is
+ that the same game files can be played on a variety of different
+ platforms without any extra effort on the part of the game
+ author. If you change the file format, this is no longer true: games
+ created under the new format won't run on the old interpreters.
+
+ Even if you distribute a new interpreter with your game, there are two
+ problems:
+
+ i) People on other platforms won't be able to play your game unless
+ and until your modified interpreter is ported to their machine. Since
+ I-F players as a group tend to use a wider range of different computers
+ and operating systems than the population at large, this is bad.
+
+ ii) Even for machines that you port your modified interpreter to,
+ people will now need to maintain two interpreters: the original one
+ (for most of the games) and your modified one (for your new game).
+ This is not only a nuisance but it wastes disk space.
+
+
+ If you *do* decide to change the file format anyhow, please adhere to
+ the following guidelines, to minimize confusion.
+
+GUIDLINES FOR NEW FILE FORMAT VERSIONS
+
+ File format version are labled by a series of four bytes near the
+beginning of the file. (They are actually the fifth, sixth, seventh,
+and eight bytes-- the first four bytes are the file format signature
+that indicate the file is an AGX file and not, say, a PCX file)
+
+ In order, they are the version owner id and number, and the
+extension owner id and number. In "vanilla" AGX, both owner id's are
+'R', the version number is 2, and the extension number is 1 (the
+extension associated with AGiliTy 1.0). (There are AGX files with
+version numbers 0 and 1 created by earlier releases of agt2agx. In
+fact, for downward compatibility, agt2agx will sometimes create a file
+of version 1 and extnum 7 if later features aren't being used.)
+
+ I will discuss the difference between "versions" and "extensions"
+further below, but briefly: extensions are minor changes whereas versions
+represent vast reoganizations of the file format. The routines below
+will still try to read in a file with an unrecognized extension, but
+they will give up on an unrecognized version.
+
+ If you create a new extension, then you should first change the
+extension owner id to something else; the owner id is intended to
+indicate who is responsible for defining this extension; 'R' indicates
+Robert Masenten (the author of this file) and so shouldn't be used by anyone
+else. The id isn't required to be a printable character.
+
+ You can then define the extension number however you want. The
+extension number is intended to differentiate between different
+extensions defined by the same source (e.g. two extensions both
+defined by me would have the same owner id but different extension
+numbers). The extensions that I define are numbered sequentially
+starting at 0, but you don't have to follow this convention if you
+don't want to.
+
+ Finally, send me an e-mail note telling me what you've done so I can
+keep track of the different extensions and prevent conflicts between
+owner-ids.
+
+ Creating a new version works the same way: change the version owner-id to
+something no one is using and set the version number however you want.
+If you're defining a new version, you can do whatever you want with
+the two extension bytes.
+
+
+EXTENSIONS AND VERSIONS
+
+ For purposes of the file format, an 'extension' is a change to the
+format that follows certain restrictions given below; a 'version' is one that
+violates one or more of these restrictions.
+
+ If at all possible you should try to fit your changes to the format
+within the limitations of an 'extension': it is more likely that other
+programs will work with your new file format and it is also more
+likely that your modified interpreter will still be able to understand
+the original file format.
+
+ An extension shouldn't change any of the existing data fields; nor
+should it insert new data fields into the middle of records. An
+extension *may* add new fields onto the end of one or more of the
+records and it can define new blocks.
+
+ Examples of things that would be extensions (create a new extension
+id and number, but keep the version the same):
+
+--Adding a new field onto the end of the creature record, containing
+ the code for a sound file that should be played whenever
+ the creature is in the room.
+
+--Adding a new block to the file containing debugging information for
+ the new AGT compiler you've just written, numbered 35.
+
+
+ Things that would *not* be extensions (create a new version id and
+number; do what you want with the extension id and number)
+
+ --Going to 32-bit object numbers for everything. (There *are*
+sneaky ways you could make this an extension; but not if you just do this
+by converting all the int16 fields in the file to int32)
+
+ --Changing metacommands to accept an arbitrary string of tokens
+instead of just ACTOR,VERB NOUN PREP OBJECT.
+
+
+
+A FEW NOTES ON BACKWARD COMPATIBILITY
+
+ (These notes only apply if you are creating an extension; if you are
+creating a new version, then anything goes)
+
+ If you add a new field onto an existing record (like the creature
+soundtrack example above) and read in an old-format file, then the new
+data fields will be automatically initialized to zero so long as none
+of them are individually longer than 81 bytes (if any *are* longer than
+81 bytes then the file routines may break). (There is nothing magic
+about 81 bytes; it just happens to be the length of the largest data
+structure that shows up in 'vanilla' AGX files).
+
+ If you add a new block, then you should check the extension number
+of the file and if the block doesn't exists be prepared to either
+initialize the data to something sensible or to exit cleanly with an
+error message.
+
+ */
+
+/* AGX File format versions and corresponding versions of AGiliTy
+ and Magx: (versions given as 'Version-Extension')
+
+ AGX AGiliTy Magx
+ 0-0 0.5
+ 0-1 0.7 0.1
+ 1-0 0.7.2 0.2
+ 1-1 0.8
+ 1-2 0.8.1 0.3
+ 1-3 0.8.3 0.4
+ 1-4 0.8.5
+ 1-5 0.8.6 0.5
+ 1-6 0.8.7 0.5.2
+ 1-7 0.8.8 0.6
+ 2-0 0.8.9 0.6.1
+ 2-1 1.0 0.6.3
+ 2-2 1.1 0.6.4
+ */
+
+/* Changes is AGX File format versions:
+ 0-0: Original format.
+ 0-1: Add
+ PURE_TIME, PURE_OBJ_DESC, exitmsg_base
+ noun.related_name
+ command.noun_adj, command.obj_adj
+ 1-0: Change index file format, fixing a bug
+ 1-1: Add
+ Multi-word verb block(28)
+ Preposition block(29)
+ (room|noun|creature).oclass
+ 1-2: Add (room|noun|creature).unused
+ 1-3: Add PURE_GRAMMAR
+ 1-4: Add (noun|creature).isglobal and (noun|creature).flagnum
+ Add TWO_CYCLE
+ 1-5: Add min_ver
+ Add PURE_AFTER
+ 1-6: Add (noun|creature).seen
+ 1-7: Add objflag and objprop blocks (with corrosponding
+ support data in the gameinfo block).
+ 2-0: No change in file format; version upped to protect against
+ a bug in early versions of the AGX file code.
+ 2-1: Added (noun|creature).proper
+ 2-2: Added noun_obj and obj_obj to cmd header
+ Added objflag.ystr, objflag.nstr, objprop.str_cnt, objprop.str_list
+ Added propstr block.
+ Added fallback_ext to file header.
+*/
+
+#define AGX_NUMBLOCK 37
+#define AGT_FILE_SIG 0x51C1C758L
+
+/* AGX File format:
+ (This tends to lag a little behind the code below;
+ you should double check against the actual file_info definitions
+ below)
+All integer values stored little-endian.
+desc_ptrs: int32 ofs, int32 leng (both in lines)
+dictionary word: int16
+slist ptr: int16
+tline: char[81]
+filename: char[10]
+rbool values are packed into bytes, 1 bit per value, from lsb to msb.
+cfgopt: 0=false, 1=true, 2=leave alone
+
+Mandatory blocks are marked with astericks.
+
+*File header: 16 bytes
+ uint 32 File ID [AGT_FILE_SIG, 4 bytes]
+ byte Version owner: 'R'
+ byte Version 0
+ byte Extension owner 'R'
+ byte Extension 0
+ char[2]: '\n\r' -- to catch download errors
+ byte Extension fallback. For non-'R' extensions, this gives the
+ 'R' extension to fall back to.
+ char[5] Reserved for future use (should be 0 right now)
+*0-File index:
+ For each block (including itself): [16 bytes]
+ uint32 starting offset
+ uint32 block size
+ uint32 number of records
+ uint32 size of a record (recsize*numrec == blocksize)
+11-Description strings (block of tline)
+12-Command text (block of int16)
+*1-Game header
+ uint16 AGT_version_code; +1 for "big/soggy" games
+ uint32 game_sig (game signature, used to check save files and debug info)
+ rbool debug_mode, freeze_mode, milltime_mode, bold_mode,
+ have_meta, mars_fix, intro_first, TWO_CYCLE;
+ uchar score_mode, statusmode;
+ uint16 max_lives
+ uint32 max_score;
+ uint16 startup_time, delta_time;
+ descr_ptr intro_ptr, title_ptr, ins_ptr;
+ int16 start_room, treas_room, resurrect_room
+ int16 first_room, first_noun, first_creat
+ int16 FLAG_NUM, CNT_NUM, VAR_NUM
+ int16 BASE_VERB
+ cfgopt PURE_ANSWER, PURE_TIME, PURE_ROOMTITLE;
+ cfgopt PURE_AND, PURE_METAVERB;
+ cfgopt PURE_SYN, PURE_NOUN, PURE_ADJ;
+ cfgopt PURE_DUMMY, PURE_SUBNAME, PURE_PROSUB;
+ cfgopt PURE_HOSTILE, PURE_GETHOSTILE;
+ cfgopt PURE_DISAMBIG, PURE_ALL;
+ cfgopt irun_mode, verboseflag;
+ cfgopt PURE_GRAMMAR (Extension R1-R3)
+ rbool TWO_CYCLE (R1-R4)
+ PURE_AFTER (R1-R5)
+ int16 min_ver
+ uchar font_status;
+ int16 num_rflags, num_nflags, num_cflags;
+ int16 num_rprops, num_nprops, num_cprops;
+2-Room data (room_rec format, pointers->int ref into static string)
+ include help, desc, special ptrs
+3-Noun data (noun_rec format)
+ include noun, text, turn, push, pull, play ptrs
+4-Creature data (creat_rec format)
+ include creature, talk, ask ptrs
+5-Command headers (cmd_rec format), pointers into command text
+ must be in increasing order.
+6-Standard error message ptrs (array of descptr
+7-Message ptrs (array of descptr)
+8-Question pointers (array of descptr)
+9-Answer pointers (array of descptr)
+10-User strings (array of tline)
+*13-Static string block (block of chars)
+14-Subroutine dictionary ids (array of word:int16)
+*15-Synlist (for verbs) (array of slist:int16)
+16-Pix names (array of word:int16 -- pointers into dictionary)
+17-Global nouns (array of word:int16 -- ptrs into dictionary)
+18-Flag nouns (array of word:int16)
+*19-Syntbl (block of word:int16)
+*20-Dictionary text (block of char)
+*21-Dictionary 'index' (array of uint32)
+22-OPT block (14 bytes)
+23-Picture filename ptrs
+24-Pix filename ptrs
+25-Font filename ptrs
+26-Sound filename ptrs
+27-VOC block, an array of verbinfo_rec
+28-Multi-word verbs (Extension R1-R1)
+29-Prep table (Extension R1-R1)
+30- ObjFlag Data (Extension R1-R7)
+31- ObjProp Data (Extension R1-R7)
+32- ObjFlag Lookup (Extension R1-R7)
+33- ObjProp Lookup (Extension R1-R7)
+34- ObjProp string pointers (array of FT_STR) (Extension R2-R2)
+35- Variable itemization array (Extension R2-R2)
+36- Flag itemization array (Extension R2-R2)
+
+*/
+
+/* AGT Version IDs; +1 for LARGE/SOGGY
+ 00000=v1.0
+ 01800=v1.18
+ 01900=v1.19
+ 02000=v1.20 ("Early Classic")
+ 03200=v1.32/COS
+ 03500=v1.35 ("Classic")
+ 05000=v1.5/H
+ 05050=v1.5/F (MDT)
+ 05070=v1.6 (PORK)
+ 08200=v1.82
+ 08300=v1.83
+ 10000=ME/1.0
+ 15000=ME/1.5
+ 15500=ME/1.55
+ 16000=ME/1.6
+ 20000=Magx/0.0
+ etc.
+*/
+
+
+
+
+/* ------------------------------------------------------------- */
+/* AGX Block Descriptions */
+/* ------------------------------------------------------------- */
+
+
+static integer old_base_verb;
+
+/* AGX file info blocks */
+
+#define g(ft,dt,var) {ft,dt,&var,0}
+#define r(ft,dt,str,f) {ft,dt,NULL,offsetof(str,f)}
+#define dptype {FT_DESCPTR,DT_DESCPTR,NULL,0}
+#define xx DT_DEFAULT
+#define u16 FT_UINT16
+#define u32 FT_UINT32
+#define bb FT_BOOL
+#define i16 FT_INT16
+
+static file_info fi_gameinfo[] = {
+ /* 0 */
+ g(FT_VERSION, xx, aver), /* FT_VERSION converter also sets ver */
+ g(u32, DT_LONG, game_sig),
+ /* 6 */
+ g(bb, xx, debug_mode), g(bb, xx, freeze_mode), g(bb, xx, milltime_mode),
+ g(bb, xx, bold_mode), g(bb, xx, have_meta), g(bb, xx, mars_fix),
+ g(bb, xx, intro_first), g(bb, xx, box_title),
+ /* 7 */
+ g(FT_BYTE, xx, score_mode), g(FT_BYTE, xx, statusmode),
+ g(i16, xx, max_lives), g(u32, DT_LONG, max_score),
+ /* 15 */
+ g(i16, xx, startup_time), g(i16, xx, delta_time),
+ /* 19 */
+ g(FT_DESCPTR, xx, intro_ptr), g(FT_DESCPTR, xx, title_ptr),
+ g(FT_DESCPTR, xx, ins_ptr),
+ /* 43 */
+ g(i16, xx, treas_room),
+ g(i16, xx, start_room), g(i16, xx, resurrect_room),
+ g(i16, xx, first_room), g(i16, xx, first_noun),
+ g(i16, xx, first_creat), g(i16, xx, FLAG_NUM),
+ g(i16, xx, CNT_NUM), g(i16, xx, VAR_NUM),
+ g(i16, xx, old_base_verb),
+ /* 63 */
+ g(FT_CFG, xx, PURE_ANSWER), g(FT_CFG, xx, PURE_ROOMTITLE),
+ g(FT_CFG, xx, PURE_AND), g(FT_CFG, xx, PURE_METAVERB),
+ g(FT_CFG, xx, PURE_SYN), g(FT_CFG, xx, PURE_NOUN), g(FT_CFG, xx, PURE_ADJ),
+ g(FT_CFG, xx, PURE_DUMMY), g(FT_CFG, xx, PURE_SUBNAME),
+ g(FT_CFG, xx, PURE_PROSUB), g(FT_CFG, xx, PURE_HOSTILE),
+ g(FT_CFG, xx, PURE_GETHOSTILE), g(FT_CFG, xx, PURE_DISAMBIG),
+ g(FT_CFG, xx, PURE_ALL),
+ g(FT_CFG, xx, irun_mode), g(FT_CFG, xx, verboseflag),
+ g(FT_CFG, xx, PURE_TIME), /* Ext R0-1 */
+ g(FT_CFG, xx, PURE_OBJ_DESC), /* Ext R0-1 */
+ /* 81 */
+ g(i16, xx, exitmsg_base), /* Ext R0-1 */
+ /* 83 */
+ g(FT_CFG, xx, PURE_GRAMMAR), /* Ext R1-3 */
+ g(bb, xx, TWO_CYCLE), /* Ext R1-4 */
+ g(bb, xx, PURE_AFTER), /* Ext R1-5 */
+ g(i16, xx, min_ver), /* Ext R1-5 */
+ g(FT_BYTE, xx, font_status), /* Ext R1-5 */
+ g(i16, xx, num_rflags), g(i16, xx, num_nflags), g(i16, xx, num_cflags), /* Ext R1-7 */
+ g(i16, xx, num_rprops), g(i16, xx, num_nprops), g(i16, xx, num_cprops), /* Ext R1-7 */
+ endrec
+};
+
+static file_info fi_room[] = {
+ dptype, /* help */
+ dptype, /* desc */
+ dptype, /* special */
+ r(FT_STR, xx, room_rec, name),
+ r(FT_INT32, xx, room_rec, flag_noun_bits),
+ r(FT_INT32, xx, room_rec, PIX_bits),
+ r(FT_SLIST, xx, room_rec, replacing_word),
+ r(FT_WORD, xx, room_rec, replace_word),
+ r(FT_WORD, xx, room_rec, autoverb),
+ r(FT_PATHARRAY, xx, room_rec, path),
+ r(FT_INT16, xx, room_rec, key),
+ r(FT_INT16, xx, room_rec, points),
+ r(FT_INT16, xx, room_rec, light),
+ r(FT_INT16, xx, room_rec, pict),
+ r(FT_INT16, xx, room_rec, initdesc),
+ r(bb, xx, room_rec, seen), r(bb, xx, room_rec, locked_door),
+ r(bb, xx, room_rec, end), r(bb, xx, room_rec, win), r(bb, xx, room_rec, killplayer),
+ r(bb, xx, room_rec, unused), /* Ext R1-2: Can add here since rbool */
+ r(FT_INT16, xx, room_rec, oclass), /* Ext R1-1 */
+ endrec
+};
+
+static file_info fi_noun[] = {
+ dptype, /* Noun */
+ dptype, /* Text */
+ dptype, dptype, dptype, dptype, /* Turn, push, pull, play */
+ r(FT_STR, xx, noun_rec, shortdesc),
+ r(FT_STR, xx, noun_rec, position),
+ r(FT_SLIST, xx, noun_rec, syns),
+ r(FT_WORD, xx, noun_rec, name), r(FT_WORD, xx, noun_rec, adj),
+ /* r(FT_WORD,xx,noun_rec,pos_prep), r(FT_WORD,xx,noun_rec,pos_name),*/
+ r(FT_INT16, xx, noun_rec, nearby_noun),
+ r(FT_INT16, xx, noun_rec, num_shots), r(FT_INT16, xx, noun_rec, points),
+ r(FT_INT16, xx, noun_rec, weight), r(FT_INT16, xx, noun_rec, size),
+ r(FT_INT16, xx, noun_rec, key),
+ r(FT_INT16, xx, noun_rec, initdesc), r(FT_INT16, xx, noun_rec, pict),
+ r(FT_INT16, xx, noun_rec, location),
+ r(bb, xx, noun_rec, plural),
+ r(bb, xx, noun_rec, something_pos_near_noun),
+ r(bb, xx, noun_rec, has_syns),
+ r(bb, xx, noun_rec, pushable), r(bb, xx, noun_rec, pullable),
+ r(bb, xx, noun_rec, turnable), r(bb, xx, noun_rec, playable),
+ r(bb, xx, noun_rec, readable), r(bb, xx, noun_rec, on),
+ r(bb, xx, noun_rec, closable), r(bb, xx, noun_rec, open),
+ r(bb, xx, noun_rec, lockable), r(bb, xx, noun_rec, locked),
+ r(bb, xx, noun_rec, edible), r(bb, xx, noun_rec, wearable),
+ r(bb, xx, noun_rec, drinkable), r(bb, xx, noun_rec, poisonous),
+ r(bb, xx, noun_rec, movable), r(bb, xx, noun_rec, light),
+ r(bb, xx, noun_rec, shootable), r(bb, xx, noun_rec, win),
+ r(bb, xx, noun_rec, unused), /* Ext R1-2: Can add here since packed rbool*/
+ r(bb, xx, noun_rec, isglobal), /* Ext R1-4: ditto (&room for 1 more). */
+ r(FT_WORD, xx, noun_rec, related_name), /* Ext R0-1 */
+ r(FT_INT16, xx, noun_rec, oclass), /* Ext R1-1 */
+ r(FT_INT16, xx, noun_rec, flagnum), /* Ext R1-4 */
+ r(bb, xx, noun_rec, seen), /* Ext R1-6 */
+ r(bb, xx, noun_rec, proper), /* Ext R2-1 */
+ endrec
+};
+
+static file_info fi_creat[] = {
+ dptype, /* Creature */
+ dptype, dptype, /* Talk, ask */
+ r(FT_STR, xx, creat_rec, shortdesc),
+ r(FT_SLIST, xx, creat_rec, syns),
+ r(FT_WORD, xx, creat_rec, name), r(FT_WORD, xx, creat_rec, adj),
+ r(FT_INT16, xx, creat_rec, location),
+ r(FT_INT16, xx, creat_rec, weapon), r(FT_INT16, xx, creat_rec, points),
+ r(FT_INT16, xx, creat_rec, counter), r(FT_INT16, xx, creat_rec, threshold),
+ r(FT_INT16, xx, creat_rec, timethresh), r(FT_INT16, xx, creat_rec, timecounter),
+ r(FT_INT16, xx, creat_rec, pict), r(FT_INT16, xx, creat_rec, initdesc),
+ r(bb, xx, creat_rec, has_syns), r(bb, xx, creat_rec, groupmemb),
+ r(bb, xx, creat_rec, hostile),
+ r(bb, xx, creat_rec, unused), /* Ext R1-2: Can add since packed rbool */
+ r(bb, xx, creat_rec, isglobal), /* Ext R1-4: ditto (& space for 3 more) */
+ r(FT_BYTE, xx, creat_rec, gender),
+ r(FT_INT16, xx, creat_rec, oclass), /* Ext R1-1 */
+ r(FT_INT16, xx, creat_rec, flagnum), /* Ext R1-4 */
+ r(bb, xx, creat_rec, seen), /* Ext R1-6 */
+ r(bb, xx, creat_rec, proper), /* Ext R2-1 */
+ endrec
+};
+
+static file_info fi_cmdhead[] = {
+ {FT_CMDPTR, DT_CMDPTR, NULL, 0},
+ r(FT_INT16, xx, cmd_rec, actor),
+ r(FT_WORD, xx, cmd_rec, verbcmd), r(FT_WORD, xx, cmd_rec, nouncmd),
+ r(FT_WORD, xx, cmd_rec, objcmd), r(FT_WORD, xx, cmd_rec, prep),
+ r(FT_INT16, xx, cmd_rec, cmdsize),
+ r(FT_WORD, xx, cmd_rec, noun_adj), r(FT_WORD, xx, cmd_rec, obj_adj), /* Ext R0-1*/
+ r(FT_INT16, xx, cmd_rec, noun_obj), /* Ext R2-2 */
+ r(FT_INT16, xx, cmd_rec, obj_obj), /* Ext R2-2 */
+ endrec
+};
+
+static file_info fi_verbentry[] = {
+ r(FT_WORD, xx, verbentry_rec, verb),
+ r(FT_WORD, xx, verbentry_rec, prep),
+ r(FT_INT16, xx, verbentry_rec, objnum),
+ endrec
+};
+
+
+static file_info fi_descptr[] = {
+ r(FT_INT32, xx, descr_ptr, start),
+ r(FT_INT32, xx, descr_ptr, size),
+ endrec
+};
+
+static file_info fi_tline[] = {
+ {FT_TLINE, xx, NULL, 0},
+ endrec
+};
+
+static file_info fi_attrrec[] = { /* Ext R1-R7 */
+ r(FT_INT32, xx, attrdef_rec, r),
+ r(FT_INT32, xx, attrdef_rec, n),
+ r(FT_INT32, xx, attrdef_rec, c),
+ r(FT_BYTE, xx, attrdef_rec, rbit),
+ r(FT_BYTE, xx, attrdef_rec, nbit),
+ r(FT_BYTE, xx, attrdef_rec, cbit),
+ r(FT_STR, xx, attrdef_rec, ystr), /* Ext R2-R2 */
+ r(FT_STR, xx, attrdef_rec, nstr), /* Ext R2-R2 */
+ endrec
+};
+
+static file_info fi_proprec[] = { /* Ext R1-R7 */
+ r(FT_INT32, xx, propdef_rec, r),
+ r(FT_INT32, xx, propdef_rec, n),
+ r(FT_INT32, xx, propdef_rec, c),
+ r(FT_INT32, xx, propdef_rec, str_cnt), /* Ext R2-R2 */
+ r(FT_INT32, xx, propdef_rec, str_list), /* Ext R2-R2 */
+ endrec
+};
+
+static file_info fi_varrec[] = { /* Ext R2-R2 */
+ r(FT_INT32, xx, vardef_rec, str_cnt),
+ r(FT_INT32, xx, vardef_rec, str_list),
+ endrec
+};
+
+static file_info fi_flagrec[] = { /* Ext R2-R2 */
+ r(FT_STR, xx, flagdef_rec, ystr),
+ r(FT_STR, xx, flagdef_rec, nstr),
+ endrec
+};
+
+#undef g
+#undef r
+#undef xx
+#undef u16
+#undef u32
+#undef bb
+#undef i16
+#undef dptype
+
+static void set_endrec(file_info *fi, int index) {
+ fi[index].ftype = FT_END;
+ fi[index].dtype = 0;
+ fi[index].ptr = NULL;
+ fi[index].offset = 0;
+}
+
+/* ------------------------------------------------------------- */
+
+/* If <to_intern> is true, convert "0" string ptrs to "yes/no" ptrs.
+ If it is false, convert the other way. */
+/* This is done for the sake of downward compatibility.
+ It *does* mean that the first string in static_str cannot
+ be an attribute's yes/no string. */
+
+/* "0" pointers in this case will actually be equal to static_str
+ (since that is the base point for all pointers to static strings.) */
+
+const char base_yesstr[] = "yes";
+const char base_nostr[] = "no";
+
+static void conv_fstr(const char **s, rbool yes, rbool to_intern) {
+ if (to_intern) { /* Convert to internal form */
+ assert(*s != NULL);
+ if (*s == static_str) *s = yes ? base_yesstr : base_nostr;
+ } else { /* convert to external form */
+ if (*s == NULL || *s == base_yesstr || *s == base_nostr)
+ *s = static_str;
+ }
+}
+
+static void fix_objflag_str(rbool to_intern) {
+ int i;
+ for (i = 0; i < oflag_cnt; i++) {
+ conv_fstr(&attrtable[i].ystr, 1, to_intern);
+ conv_fstr(&attrtable[i].nstr, 0, to_intern);
+ }
+ if (flagtable)
+ for (i = 0; i <= FLAG_NUM; i++) {
+ conv_fstr(&flagtable[i].ystr, 1, to_intern);
+ conv_fstr(&flagtable[i].nstr, 0, to_intern);
+ }
+}
+
+/* ------------------------------------------------------------- */
+/* AGX Reading Code */
+/* ------------------------------------------------------------- */
+
+
+static long descr_ofs;
+
+void agx_close_descr(void) {
+ if (mem_descr != NULL)
+ rfree(mem_descr);
+ else if (descr_ofs != -1)
+ buffclose(); /* This closes the whole AGX file */
+}
+
+descr_line *agx_read_descr(long start, long size) {
+ long i, line, len;
+ descr_line *txt;
+ char *buff;
+
+ if (size <= 0) return NULL;
+
+ if (mem_descr == NULL && descr_ofs != -1)
+ buff = (char *)read_recblock(NULL, FT_CHAR, size,
+ descr_ofs + start, size * ft_leng[FT_CHAR]);
+ else
+ buff = mem_descr + start;
+
+ len = 0;
+ for (i = 0; i < size; i++) /* Count the number of lines */
+ if (buff[i] == 0) len++;
+ txt = (descr_line *)rmalloc(sizeof(descr_line) * (len + 1));
+ txt[0] = buff;
+ i = 0;
+ for (line = 1; line < len;) /* Determine where each of the lines is */
+ if (buff[i++] == 0)
+ txt[line++] = buff + i;
+ txt[len] = NULL; /* Mark the end of the array */
+ return txt;
+}
+
+
+/* We need to read in command text and use cmd_rec[] values to
+ rebuild command[].data. We are guaranteed that cmd_rec[] is in
+ increasing order */
+
+static void read_command(long cmdcnt, long cmdofs, rbool diag) {
+ long i;
+
+ for (i = 0; i < last_cmd; i++) {
+ command[i].data = (integer *)rmalloc(sizeof(integer) * command[i].cmdsize);
+ read_recblock(command[i].data, FT_INT16, command[i].cmdsize,
+ cmdofs + 2 * cmd_ptr[i], 2 * command[i].cmdsize);
+ }
+ if (!diag) rfree(cmd_ptr);
+}
+
+
+/* Correct for differences between old_base_verb and BASE_VERB.
+ This means that the interpreter's set of built-inv verbs has changed
+ since the file was created. */
+static void correct_synlist(void) {
+ int i;
+ if (BASE_VERB == old_base_verb) return; /* Nothing needs to be done */
+
+ /* Need to move everything >= old_base_verb to BASE_VERB */
+ memmove(synlist + BASE_VERB, synlist + old_base_verb,
+ sizeof(slist) * (DVERB + MAX_SUB));
+
+ if (BASE_VERB < old_base_verb) /* We've _lost_ verbs */
+ agtwarn("Missing built-in verbs.", 0);
+
+ /* Now we need to give the "new" verbs empty synonym lists */
+ for (i = old_base_verb; i < BASE_VERB; i++)
+ synlist[i] = synptr;
+ addsyn(-1);
+}
+
+
+
+static void set_roomdesc(file_info fi[]) {
+ fi[0].ptr = help_ptr = (descr_ptr *)rmalloc(sizeof(descr_ptr) * (maxroom - first_room + 1));
+ fi[1].ptr = room_ptr = (descr_ptr *)rmalloc(sizeof(descr_ptr) * (maxroom - first_room + 1));
+ fi[2].ptr = special_ptr = (descr_ptr *)rmalloc(sizeof(descr_ptr) * (maxroom - first_room + 1));
+}
+
+static void wset_roomdesc(file_info fi[]) {
+ fi[0].ptr = help_ptr;
+ fi[1].ptr = room_ptr;
+ fi[2].ptr = special_ptr;
+}
+
+static void set_noundesc(file_info *fi) {
+ fi[0].ptr = noun_ptr = (descr_ptr *)rmalloc(sizeof(descr_ptr) * (maxnoun - first_noun + 1));
+ fi[1].ptr = text_ptr = (descr_ptr *)rmalloc(sizeof(descr_ptr) * (maxnoun - first_noun + 1));
+ fi[2].ptr = turn_ptr = (descr_ptr *)rmalloc(sizeof(descr_ptr) * (maxnoun - first_noun + 1));
+ fi[3].ptr = push_ptr = (descr_ptr *)rmalloc(sizeof(descr_ptr) * (maxnoun - first_noun + 1));
+ fi[4].ptr = pull_ptr = (descr_ptr *)rmalloc(sizeof(descr_ptr) * (maxnoun - first_noun + 1));
+ fi[5].ptr = play_ptr = (descr_ptr *)rmalloc(sizeof(descr_ptr) * (maxnoun - first_noun + 1));
+}
+
+static void wset_noundesc(file_info *fi) {
+ fi[0].ptr = noun_ptr;
+ fi[1].ptr = text_ptr;
+ fi[2].ptr = turn_ptr;
+ fi[3].ptr = push_ptr;
+ fi[4].ptr = pull_ptr;
+ fi[5].ptr = play_ptr;
+}
+
+static void set_creatdesc(file_info *fi) {
+ fi[0].ptr = creat_ptr = (descr_ptr *)rmalloc(sizeof(descr_ptr) * (maxcreat - first_creat + 1));
+ fi[1].ptr = talk_ptr = (descr_ptr *)rmalloc(sizeof(descr_ptr) * (maxcreat - first_creat + 1));
+ fi[2].ptr = ask_ptr = (descr_ptr *)rmalloc(sizeof(descr_ptr) * (maxcreat - first_creat + 1));
+}
+
+static void wset_creatdesc(file_info *fi) {
+ fi[0].ptr = creat_ptr;
+ fi[1].ptr = talk_ptr;
+ fi[2].ptr = ask_ptr;
+}
+
+static void set_cmdptr(file_info *fi) {
+ fi[0].ptr = cmd_ptr = (long *)rmalloc(sizeof(long) * last_cmd);
+}
+
+static void wset_cmdptr(file_info *fi) {
+ fi[0].ptr = cmd_ptr;
+}
+
+
+typedef struct { /* Entries in the index header of the AGX file */
+ uint32 file_offset;
+ uint32 blocksize;
+ uint32 numrec;
+ uint32 recsize;
+} index_rec;
+
+static file_info fi_index[] = {
+ {FT_UINT32, DT_DEFAULT, NULL, offsetof(index_rec, file_offset)},
+ {FT_UINT32, DT_DEFAULT, NULL, offsetof(index_rec, blocksize)},
+ {FT_UINT32, DT_DEFAULT, NULL, offsetof(index_rec, numrec)},
+ {FT_UINT32, DT_DEFAULT, NULL, offsetof(index_rec, recsize)},
+ endrec
+};
+
+
+/*
+ uint32 File ID ['....' 4 bytes]
+ byte Version owner: 'R'
+ byte Version 0
+ byte Extension owner 'R'
+ byte Extension 0
+ */
+
+typedef struct {
+ unsigned long fileid;
+ unsigned long res1; /* Reserved for future use */
+ uchar res2;
+ uchar eol_chk1; /* Catch non-binary upload errors */
+ uchar eol_chk2;
+ uchar ver_own;
+ uchar version;
+ uchar ext_own;
+ uchar extnum;
+ uchar fallback_ext; /* For non-'R' extensions, this is the 'R' extension
+ to fall back to. */
+} file_head_rec;
+
+static file_info fi_header[] = {
+ {FT_UINT32, DT_LONG, NULL, offsetof(file_head_rec, fileid)}, /* File ID */
+ {FT_BYTE, DT_DEFAULT, NULL, offsetof(file_head_rec, ver_own)}, /* Owner */
+ {FT_BYTE, DT_DEFAULT, NULL, offsetof(file_head_rec, version)}, /* Version */
+ {FT_BYTE, DT_DEFAULT, NULL, offsetof(file_head_rec, ext_own)}, /*Ext owner*/
+ {FT_BYTE, DT_DEFAULT, NULL, offsetof(file_head_rec, extnum)}, /* Ext vers */
+ {FT_BYTE, DT_DEFAULT, NULL, offsetof(file_head_rec, eol_chk1)},
+ {FT_BYTE, DT_DEFAULT, NULL, offsetof(file_head_rec, eol_chk2)},
+ {FT_BYTE, DT_DEFAULT, NULL, offsetof(file_head_rec, fallback_ext)},
+ {FT_BYTE, DT_DEFAULT, NULL, offsetof(file_head_rec, res2)},
+ {FT_UINT32, DT_DEFAULT, NULL, offsetof(file_head_rec, res1)},
+ endrec
+};
+
+static const char *block_name[AGX_NUMBLOCK] = {
+ "Index", "Game Info", "Room(DA2)", "Noun(DA3)", "Creature(DA4)",
+ "Command Header(DA5)", "Error Message(STD)", "Message",
+ "Question", "Answer", "User String", "Description Text(D$$)",
+ "Command Tokens(DA6)", "Static String", "Subroutine ID",
+ "Verb Synonym", "RoomPIX", "Global Noun", "Flag Noun", "Word Lists(Syntbl)",
+ "Dictionary Text", "Dictionary Index", "OPT",
+ "Picture Filename", "RoomPIX Filename", "Font Filename", "Sound Filename",
+ "Menu(VOC)", "Multi-word Verb", "Preposition", "ObjFlag", "ObjProp",
+ "Attrtable", "PropTable", "PropStr", "Itemized Variables",
+ "Itemized Flags"
+};
+
+
+/* Return 0 on failure, 1 on success */
+int read_agx(fc_type fc, rbool diag) {
+ file_head_rec filehead;
+ unsigned long fsize;
+ index_rec *index;
+ long i;
+ int index_recsize;
+ int index_start;
+
+ agx_file = 1;
+ fsize = buffopen(fc, fAGX, 16, NULL, 1);
+ if (fsize == 0) {
+ agx_file = 0;
+ return 0;
+ }
+
+ /* Read header */
+ read_recarray(&filehead, sizeof(file_head_rec), 1, fi_header,
+ "File Header", 0, compute_recsize(fi_header));
+ if (filehead.fileid != AGT_FILE_SIG) {
+ buffclose();
+ return 0;
+ }
+ if (DIAG) {
+ rprintf("AGX file format");
+ if (isprint(filehead.ver_own) && isprint(filehead.ext_own))
+ rprintf(" Version:%c%d\tExtension:%c%d\n",
+ filehead.ver_own, filehead.version,
+ filehead.ext_own, filehead.extnum);
+ else
+ rprintf(" Version:%d:%d\tExtension:%d:%d\n",
+ filehead.ver_own, filehead.version,
+ filehead.ext_own, filehead.extnum);
+ }
+ if (filehead.ver_own != 'R' || filehead.version > 2) {
+ rprintf("Unsupported AGX file version.\n");
+ rprintf(" Either the file is corrupted or or you need a more recent "
+ "version of AGiliTy.\n");
+ rprintf("\n");
+ fatal("Can't read AGX file.");
+ }
+
+ index_recsize = compute_recsize(fi_index);
+ if (filehead.version == 0) {
+ if (debug_da1)
+ rprintf("[AGX version 0: obsolete.]\n");
+ index_recsize += 8; /* Extra junk block in version 0. */
+ index_start = 8;
+ } else {
+ index_start = 16;
+ if (filehead.eol_chk1 != '\n' || filehead.eol_chk2 != '\r')
+ fatal("File apparently downloaded as non-binary file.");
+ }
+ if (filehead.ext_own != 'R'
+ || (filehead.version == 0 && filehead.extnum > 1)
+ || (filehead.version == 1 && filehead.extnum > 7)
+ || (filehead.version == 2 && filehead.extnum > 2))
+ agtwarn("Unrecognized extension to AGX file format.", 0);
+ if (filehead.ext_own != 'R') { /* Assume lowest common denomenator */
+ if (filehead.version < 2)
+ fatal("Extensions of AGX beta versions not supported.");
+ if (filehead.fallback_ext < 1) filehead.fallback_ext = 1;
+ }
+
+ /* Now read master index */
+ /* This assumes that the file is long enough to absorb any
+ 'extra' blocks we read in in early versions with fewer blocks. */
+ /* (Right now, this must be true: the next block alone is big enough) */
+ index = (index_rec *)read_recarray(NULL, sizeof(index_rec), AGX_NUMBLOCK,
+ fi_index, "File Index", index_start,
+ index_recsize * AGX_NUMBLOCK);
+
+ /* Zero index entries for any blocks that are beyond the bounds of the
+ file's index */
+ if (AGX_NUMBLOCK > index[0].numrec)
+ memset(index + index[0].numrec, 0,
+ (AGX_NUMBLOCK - index[0].numrec)*sizeof(index_rec));
+
+ if (DIAG) {
+ rprintf("\n");
+ rprintf("File Index:\n");
+ rprintf(" Offset Size NumRec RecSz\n");
+ rprintf(" ------ ------ ------ ------\n");
+ for (i = 0; i < AGX_NUMBLOCK; i++)
+ rprintf("%2d: %6d %6d %6d %6d %s\n", i,
+ index[i].file_offset, index[i].blocksize,
+ index[i].numrec, index[i].recsize, block_name[i]);
+ }
+ if ((int)index[0].file_offset != index_start)
+ fatal("File header corrupted.");
+
+ for (i = 0; i < AGX_NUMBLOCK; i++) { /* Error checking */
+#ifdef DEBUG_AGX
+ rprintf(" Verifying block %d...\n", i);
+#endif
+ if (index[i].recsize * index[i].numrec != index[i].blocksize)
+ fatal("File header corrupted.");
+ if (index[i].file_offset + index[i].blocksize > fsize)
+ fatal("File index points past end of file.");
+ }
+
+ /* Check for mandatory fields */
+ if (!index[0].numrec /* File index */
+ || !index[1].numrec /* Game header */
+ || !index[13].numrec /* Static string block */
+ || !index[15].numrec /* Synonym list */
+ || !index[19].numrec /* Syntbl */
+ || !index[20].numrec /* Dictionary text */
+ || !index[21].numrec /* Dictionary index */
+ )
+ fatal("AGX file missing mandatory block.");
+
+
+ read_globalrec(fi_gameinfo, "Game Info", index[1].file_offset,
+ index[1].blocksize);
+ if (filehead.version == 0 && filehead.extnum == 0) {
+ exitmsg_base = 1000;
+ if (aver >= AGT15)
+ box_title = 1;
+ }
+ if (index[1].blocksize == 83 && filehead.version == 1 && filehead.extnum >= 5) {
+ /* Detect 0.8-compatibility hack */
+ filehead.extnum = 2;
+ }
+ if (filehead.version == 0 || (filehead.version == 1 && filehead.extnum < 5)) {
+ if (aver >= AGT182 && aver <= AGT18MAX) {
+ if (filehead.extnum < 4) TWO_CYCLE = 1;
+ } else
+ PURE_AFTER = 1;
+ }
+
+ /* Need to read in ss_array before rooms/nouns/creatures */
+ ss_size = ss_end = index[13].numrec;
+ static_str = (char *)read_recblock(NULL, FT_CHAR,
+ index[13].numrec, index[13].file_offset,
+ index[13].blocksize);
+
+ synptr = syntbl_size = index[19].numrec;
+ syntbl = (word *)read_recblock(NULL, FT_WORD, index[19].numrec, index[19].file_offset,
+ index[19].blocksize);
+
+ maxroom = first_room + index[2].numrec - 1;
+ set_roomdesc(fi_room);
+ room = (room_rec *)read_recarray(NULL, sizeof(room_rec), index[2].numrec,
+ fi_room, "Room", index[2].file_offset, index[2].blocksize);
+
+ maxnoun = first_noun + index[3].numrec - 1;
+ set_noundesc(fi_noun);
+ noun = (noun_rec *)read_recarray(NULL, sizeof(noun_rec), index[3].numrec,
+ fi_noun, "Noun", index[3].file_offset, index[3].blocksize);
+
+ last_obj = maxcreat = first_creat + index[4].numrec - 1;
+ set_creatdesc(fi_creat);
+ creature = (creat_rec *)read_recarray(NULL, sizeof(creat_rec), index[4].numrec,
+ fi_creat, "Creature", index[4].file_offset,
+ index[4].blocksize);
+
+ last_cmd = index[5].numrec;
+ set_cmdptr(fi_cmdhead);
+ command = (cmd_rec *)read_recarray(NULL, sizeof(cmd_rec), index[5].numrec,
+ fi_cmdhead, "Metacommand", index[5].file_offset,
+ index[5].blocksize);
+ if (filehead.ext_own != 'R' && filehead.fallback_ext <= 1) {
+ for (i = 0; i < last_cmd; i++)
+ command[i].noun_obj = command[i].obj_obj = 0;
+ }
+
+ NUM_ERR = index[6].numrec;
+ err_ptr = (descr_ptr *)read_recarray(NULL, sizeof(descr_ptr), index[6].numrec,
+ fi_descptr, "Error Message", index[6].file_offset,
+ index[6].blocksize);
+
+ last_message = index[7].numrec;
+ msg_ptr = (descr_ptr *)read_recarray(NULL, sizeof(descr_ptr), index[7].numrec,
+ fi_descptr, "Message", index[7].file_offset,
+ index[7].blocksize);
+
+ MaxQuestion = index[8].numrec;
+ question = answer = NULL;
+ quest_ptr = (descr_ptr *)read_recarray(NULL, sizeof(descr_ptr), index[8].numrec,
+ fi_descptr, "Question", index[8].file_offset,
+ index[8].blocksize);
+ if (index[9].numrec != index[8].numrec)
+ fatal("File corrputed: questions and answers don't match.");
+ ans_ptr = (descr_ptr *)read_recarray(NULL, sizeof(descr_ptr), index[9].numrec,
+ fi_descptr, "Answer", index[9].file_offset,
+ index[9].blocksize);
+
+ MAX_USTR = index[10].numrec;
+ userstr = (tline *)read_recarray(NULL, sizeof(tline), index[10].numrec,
+ fi_tline, "User String", index[10].file_offset,
+ index[10].blocksize);
+
+ MAX_SUB = index[14].numrec;
+ sub_name = (word *)read_recblock(NULL, FT_WORD, index[14].numrec, index[14].file_offset,
+ index[14].blocksize);
+
+ if (index[16].numrec > MAX_PIX) {
+ index[16].numrec = MAX_PIX;
+ index[16].blocksize = index[16].recsize * index[16].numrec;
+ }
+ maxpix = index[16].numrec;
+ for (i = 0; i < MAX_PIX; i++) pix_name[i] = 0; /* In case there are less than
+ MAX_PIX names */
+ read_recblock(pix_name, FT_WORD, index[16].numrec, index[16].file_offset,
+ index[16].blocksize);
+
+ numglobal = index[17].numrec;
+ globalnoun = (word *)read_recblock(NULL, FT_WORD,
+ index[17].numrec, index[17].file_offset,
+ index[17].blocksize);
+
+ if (index[18].numrec > MAX_FLAG_NOUN) {
+ index[18].numrec = MAX_FLAG_NOUN;
+ index[18].blocksize = index[18].recsize * index[18].numrec;
+ }
+
+ for (i = 0; i < MAX_FLAG_NOUN; i++) flag_noun[i] = 0;
+ read_recblock(flag_noun, FT_WORD, index[18].numrec, index[18].file_offset,
+ index[18].blocksize);
+
+
+
+ DVERB = index[15].numrec - old_base_verb - MAX_SUB;
+ synlist = (slist *)read_recblock(NULL, FT_SLIST, index[15].numrec, index[15].file_offset,
+ index[15].blocksize);
+ correct_synlist();
+
+ num_comb = index[28].numrec;
+ comblist = (slist *)read_recblock(NULL, FT_SLIST, index[28].numrec, index[28].file_offset,
+ index[28].blocksize);
+
+ num_prep = index[29].numrec;
+ userprep = (slist *)read_recblock(NULL, FT_SLIST, index[29].numrec, index[29].file_offset,
+ index[29].blocksize);
+
+ /* dicstr must be read in before dict */
+ dictstrsize = dictstrptr = index[20].numrec;
+ dictstr = (char *)read_recblock(NULL, FT_CHAR, index[20].numrec, index[20].file_offset,
+ index[20].blocksize);
+
+ dp = index[21].numrec;
+ dict = (char **)read_recblock(NULL, FT_DICTPTR,
+ index[21].numrec, index[21].file_offset,
+ index[21].blocksize);
+
+ have_opt = (index[22].numrec != 0);
+ for (i = 0; i < 14; i++) opt_data[i] = 0;
+ if (have_opt) {
+ if (index[22].numrec > 14) index[22].numrec = 14;
+ read_recblock(opt_data, FT_BYTE, index[22].numrec, index[22].file_offset,
+ index[22].blocksize);
+ }
+
+ maxpict = index[23].numrec;
+ pictlist = (filename *)read_recblock(NULL, FT_STR, index[23].numrec, index[23].file_offset,
+ index[23].blocksize);
+ maxpix = index[24].numrec;
+ pixlist = (filename *)read_recblock(NULL, FT_STR, index[24].numrec, index[24].file_offset,
+ index[24].blocksize);
+ maxfont = index[25].numrec;
+ fontlist = (filename *)read_recblock(NULL, FT_STR, index[25].numrec, index[25].file_offset,
+ index[25].blocksize);
+ maxsong = index[26].numrec;
+ songlist = (filename *)read_recblock(NULL, FT_STR, index[26].numrec, index[26].file_offset,
+ index[26].blocksize);
+
+ vm_size = index[27].numrec;
+ verbinfo = (verbentry_rec *)read_recarray(NULL, sizeof(verbentry_rec), index[27].numrec,
+ fi_verbentry, "Menu Vocabulary", index[27].file_offset,
+ index[27].blocksize);
+
+ /* Check that objflag and objprop fields are of correct size */
+ if (index[30].numrec != (uint32)objextsize(0))
+ fatal("Object flag block not of the correct size.");
+
+ if (index[31].numrec != (uint32)objextsize(1))
+ fatal("Object property block not of the correct size.");
+
+ objflag = (uchar *)read_recblock(NULL, FT_BYTE, index[30].numrec, index[30].file_offset,
+ index[30].blocksize);
+ objprop = (long *)read_recblock(NULL, FT_INT32, index[31].numrec, index[31].file_offset,
+ index[31].blocksize);
+
+ oflag_cnt = index[32].numrec;
+ attrtable = (attrdef_rec *)read_recarray(NULL, sizeof(attrdef_rec), index[32].numrec,
+ fi_attrrec, "Object Flag Table",
+ index[32].file_offset,
+ index[32].blocksize);
+ /* Objflags are converted to internal form later, after
+ block 36 has been read in. */
+
+ oprop_cnt = index[33].numrec;
+ proptable = (propdef_rec *)read_recarray(NULL, sizeof(propdef_rec), index[33].numrec,
+ fi_proprec, "Object Property Table",
+ index[33].file_offset,
+ index[33].blocksize);
+
+ if (filehead.ext_own != 'R' && filehead.fallback_ext <= 1) {
+ /* Non-standard extension */
+// int i;
+ for (i = 0; i < oflag_cnt; i++) /* These are converted later */
+ attrtable[i].ystr = NULL;
+ attrtable[i].nstr = NULL;
+ for (i = 0; i < oprop_cnt; i++)
+ proptable[i].str_cnt = 0;
+ propstr_size = 0;
+ propstr = NULL;
+ vartable = NULL;
+ flagtable = NULL;
+ } else { /* Normal case */
+ propstr_size = index[34].numrec;
+ propstr = (const char **)read_recblock(NULL, FT_STR, index[34].numrec,
+ index[34].file_offset, index[34].blocksize);
+
+ if (index[35].numrec && index[35].numrec != (uint32)VAR_NUM + 1)
+ fatal("AGX file corrupted: variable itemization table size mismatch.");
+ vartable = (vardef_rec *)read_recarray(NULL, sizeof(vardef_rec), index[35].numrec,
+ fi_varrec, "Variable Itemization Table",
+ index[35].file_offset,
+ index[35].blocksize);
+
+ if (index[36].numrec && index[36].numrec != (uint32)FLAG_NUM + 1)
+ fatal("AGX file corrupted: flag itemization table size mismatch.");
+ flagtable = (flagdef_rec *)read_recarray(NULL, sizeof(flagdef_rec), index[36].numrec,
+ fi_flagrec, "Flag Itemization Table",
+ index[36].file_offset,
+ index[36].blocksize);
+ }
+
+ fix_objflag_str(1); /* Convert flags and objflags to internal form */
+
+
+ /* Block 12: Command text */
+ read_command(index[12].numrec, index[12].file_offset, diag);
+
+ /* Block 11 is description block; it doesn't get read in by
+ agxread() but during play */
+ if ((long)index[11].blocksize <= descr_maxmem) {
+ /* ... if we decided to load descriptions into memory */
+ mem_descr = (char *)read_recblock(NULL, FT_CHAR, index[11].numrec,
+ index[11].file_offset,
+ index[11].blocksize);
+ buffclose(); /* Don't need to keep it open */
+ descr_ofs = -1;
+ } else {
+ descr_ofs = index[11].file_offset;
+ mem_descr = NULL;
+ }
+ reinit_dict();
+ return 1;
+}
+
+
+
+
+/* ------------------------------------------------------------- */
+/* AGX Writing Code */
+/* ------------------------------------------------------------- */
+
+static index_rec *gindex;
+
+
+/* This patches the block descriptions to create AGiliTy-0.8
+ compatible files. This is just a quick hack to solve a short-term
+ problem. */
+void patch_08(void) {
+ set_endrec(fi_gameinfo, 48); /* Should give size of 83 */
+ set_endrec(fi_noun, 45);
+ set_endrec(fi_creat, 23);
+}
+
+
+/* This writes the file header; it needs to be called near the
+ end */
+void write_header(void) {
+ int i;
+ rbool simple;
+ file_head_rec filehead;
+
+ filehead.fileid = AGT_FILE_SIG;
+ filehead.ver_own = filehead.ext_own = 'R';
+ /* The following will be converted to 1-7 if advanced features aren't
+ being used. */
+ filehead.version = 2;
+ filehead.extnum = 2;
+ filehead.fallback_ext = 2; /* 'R' extension to fall back to;
+ only meaningful if ext_own is *not* 'R' */
+ filehead.eol_chk1 = '\n';
+ filehead.eol_chk2 = '\r';
+ filehead.res1 = 0;
+ filehead.res2 = 0;
+
+ /* This automatically patches the block descriptions to create
+ pre-AGiliTy-0.8.8 compatible files. If it can't (because the
+ file uses 0.8.8+ features) then it leaves the version at 2;
+ otherwise the version is reduced to 1. */
+ /* The files thus created are actually hybrid files-- they
+ have some 0.8.8+ features, just not the ones that might
+ break pre-0.8.8 interpreters. */
+ simple = 1;
+ for (i = 30; i < AGX_NUMBLOCK; i++)
+ if (gindex[i].numrec != 0) simple = 0;
+ if (simple) {
+ gindex[0].numrec = 30; /* 0.8.7 compatibility */
+ gindex[0].blocksize = gindex[0].recsize * gindex[0].numrec;
+ filehead.version = 1;
+ filehead.extnum = 7;
+ }
+ write_recarray(&filehead, sizeof(file_head_rec), 1, fi_header, 0);
+}
+
+
+static void agx_compute_index(void)
+/* This computes the blocksize and offset values for all blocks */
+{
+ int i;
+
+ for (i = 0; i < AGX_NUMBLOCK; i++)
+ gindex[i].blocksize = gindex[i].recsize * gindex[i].numrec;
+ gindex[0].file_offset = 16;
+ gindex[11].file_offset = gindex[0].file_offset + gindex[0].blocksize;
+ gindex[12].file_offset = gindex[11].file_offset + gindex[11].blocksize;
+ gindex[1].file_offset = gindex[12].file_offset + gindex[12].blocksize;
+ for (i = 2; i <= AGX_NUMBLOCK - 1; i++)
+ if (i == 13)
+ gindex[13].file_offset = gindex[10].file_offset + gindex[10].blocksize;
+ else if (i != 11 && i != 12)
+ gindex[i].file_offset = gindex[i - 1].file_offset + gindex[i - 1].blocksize;
+}
+
+
+/* Create the preliminary gindex for the new file and set it up so we can
+ write descriptions to the new file */
+void agx_create(fc_type fc) {
+ int i;
+
+ bw_open(fc, fAGX);
+ gindex = (index_rec *)rmalloc(sizeof(index_rec) * AGX_NUMBLOCK);
+
+ gindex[0].numrec = AGX_NUMBLOCK;
+ for (i = 1; i < AGX_NUMBLOCK; i++) /* Initialize the rest to 0 */
+ gindex[i].numrec = 0;
+
+ /* This writes random data to the file; their only purpose
+ is to prevent problems with seeking beyond the end of file */
+ write_recarray(NULL, sizeof(file_head_rec), 1, fi_header, 0);
+ write_recarray(NULL, sizeof(index_rec), AGX_NUMBLOCK, fi_index, 16);
+
+ old_base_verb = BASE_VERB; /* This will be constant for any given version
+ of the interpreter, but may change across
+ versions of the interpreter */
+ /* Set record sizes */
+ gindex[0].recsize = compute_recsize(fi_index);
+ gindex[1].recsize = compute_recsize(fi_gameinfo);
+ gindex[2].recsize = compute_recsize(fi_room);
+ gindex[3].recsize = compute_recsize(fi_noun);
+ gindex[4].recsize = compute_recsize(fi_creat);
+ gindex[5].recsize = compute_recsize(fi_cmdhead);
+ gindex[6].recsize = gindex[7].recsize = gindex[8].recsize =
+ gindex[9].recsize = compute_recsize(fi_descptr);
+ gindex[10].recsize = ft_leng[FT_TLINE];
+ gindex[11].recsize = ft_leng[FT_CHAR];
+ gindex[12].recsize = ft_leng[FT_INT16];
+ gindex[13].recsize = gindex[20].recsize = ft_leng[FT_CHAR];
+ gindex[14].recsize = gindex[16].recsize = gindex[17].recsize =
+ gindex[18].recsize = ft_leng[FT_WORD];
+ gindex[15].recsize = ft_leng[FT_SLIST];
+ gindex[19].recsize = ft_leng[FT_WORD];
+ gindex[21].recsize = ft_leng[FT_DICTPTR];
+ gindex[22].recsize = ft_leng[FT_BYTE];
+ gindex[23].recsize = gindex[24].recsize = gindex[25].recsize =
+ gindex[26].recsize = ft_leng[FT_STR];
+ gindex[27].recsize = compute_recsize(fi_verbentry);
+ gindex[28].recsize = ft_leng[FT_SLIST];
+ gindex[29].recsize = ft_leng[FT_SLIST];
+ gindex[30].recsize = ft_leng[FT_BYTE];
+ gindex[31].recsize = ft_leng[FT_INT32];
+ gindex[32].recsize = compute_recsize(fi_attrrec);
+ gindex[33].recsize = compute_recsize(fi_proprec);
+ gindex[34].recsize = ft_leng[FT_STR];
+ gindex[35].recsize = compute_recsize(fi_varrec);
+ gindex[36].recsize = compute_recsize(fi_flagrec);
+
+ agx_compute_index(); /* Only the first 10 blocks will be correct */
+ /* The important thing is to get the offset of block 11, the desciption
+ text block, so we can write to it. */
+ /* Block 11 is the description block; it doesn't get written by agxwrite()
+ but by its own routines. */
+}
+
+
+static void agx_finish_index(void) {
+ /* Still have 11, 27-29 */
+ /* Block 12 is taken care of elsewhere (in write_command) */
+
+ gindex[1].numrec = 1;
+ gindex[2].numrec = rangefix(maxroom - first_room + 1);
+ gindex[3].numrec = rangefix(maxnoun - first_noun + 1);
+ gindex[4].numrec = rangefix(maxcreat - first_creat + 1);
+ gindex[5].numrec = last_cmd;
+ gindex[6].numrec = NUM_ERR;
+ gindex[7].numrec = last_message;
+ gindex[8].numrec = gindex[9].numrec = MaxQuestion;
+ if (userstr != NULL)
+ gindex[10].numrec = MAX_USTR;
+ else gindex[10].numrec = 0;
+ gindex[13].numrec = ss_end;
+ gindex[14].numrec = MAX_SUB;
+ gindex[15].numrec = TOTAL_VERB;
+ gindex[16].numrec = maxpix;
+ gindex[17].numrec = numglobal;
+ gindex[19].numrec = synptr;
+ gindex[20].numrec = dictstrptr;
+ gindex[21].numrec = dp;
+ gindex[23].numrec = maxpict;
+ gindex[24].numrec = maxpix;
+ gindex[25].numrec = maxfont;
+ gindex[26].numrec = maxsong;
+ gindex[27].numrec = vm_size;
+ gindex[28].numrec = num_comb;
+ gindex[29].numrec = num_prep;
+ gindex[30].numrec = objextsize(0);
+ gindex[31].numrec = objextsize(1);
+ gindex[32].numrec = oflag_cnt;
+ gindex[33].numrec = oprop_cnt;
+ gindex[34].numrec = propstr_size;
+ gindex[35].numrec = (vartable ? VAR_NUM + 1 : 0);
+ gindex[36].numrec = (flagtable ? FLAG_NUM + 1 : 0);
+
+ /* These may also be zero (?) */
+ gindex[22].numrec = have_opt ? 14 : 0;
+ gindex[18].numrec = MAX_FLAG_NOUN;
+
+ agx_compute_index(); /* This time it will be complete except for
+ the VOC-TTL-INS blocks at the end */
+}
+
+
+
+/* The following routine writes a description to disk,
+ and stores the size and length in dp */
+void write_descr(descr_ptr *dp_, descr_line *txt) {
+ long i;
+ long size;
+ char *buff, *buffptr, *src;
+
+ size = 0;
+ if (txt == NULL) {
+ dp_->start = 0;
+ dp_->size = 0;
+ return;
+ }
+
+ for (i = 0; txt[i] != NULL; i++) /* Compute size */
+ size += strlen(txt[i]) + 1; /* Remember trailing \0 */
+ buff = (char *)rmalloc(sizeof(char) * size);
+
+ buffptr = buff;
+ for (i = 0; txt[i] != NULL; i++) {
+ for (src = txt[i]; *src != 0; src++, buffptr++)
+ *buffptr = *src;
+ *buffptr++ = 0;
+ }
+ dp_->start = gindex[11].numrec;
+ dp_->size = size;
+ gindex[11].numrec +=
+ write_recblock(buff, FT_CHAR, size,
+ gindex[11].file_offset + gindex[11].numrec);
+ rfree(buff);
+}
+
+/* Write command text to file and return number of bytes written. */
+static long write_command(long cmdofs) {
+ long i, cnt;
+
+ cmd_ptr = (long *)rmalloc(sizeof(long) * last_cmd);
+ cnt = 0;
+ for (i = 0; i < last_cmd; i++) {
+ cmd_ptr[i] = cnt;
+ write_recblock(command[i].data, FT_INT16, command[i].cmdsize,
+ cmdofs + 2 * cnt);
+ cnt += command[i].cmdsize;
+ }
+ return cnt;
+}
+
+
+
+
+/* Write the bulk of the AGX file. This requires that the descriptions,
+ etc. have already been written */
+void agx_write(void) {
+ gindex[11].blocksize = gindex[11].numrec * gindex[11].recsize;
+ gindex[12].file_offset = gindex[11].file_offset + gindex[11].blocksize;
+
+ gindex[12].numrec = write_command(gindex[12].file_offset);
+
+ agx_finish_index();
+
+ /* Need to write these blocks in order */
+
+ write_globalrec(fi_gameinfo, gindex[1].file_offset);
+
+ wset_roomdesc(fi_room);
+ write_recarray(room, sizeof(room_rec), gindex[2].numrec,
+ fi_room, gindex[2].file_offset);
+
+ wset_noundesc(fi_noun);
+ write_recarray(noun, sizeof(noun_rec), gindex[3].numrec,
+ fi_noun, gindex[3].file_offset);
+
+ wset_creatdesc(fi_creat);
+ write_recarray(creature, sizeof(creat_rec), gindex[4].numrec,
+ fi_creat, gindex[4].file_offset);
+
+ wset_cmdptr(fi_cmdhead);
+ write_recarray(command, sizeof(cmd_rec), gindex[5].numrec,
+ fi_cmdhead, gindex[5].file_offset);
+
+ write_recarray(err_ptr, sizeof(descr_ptr), gindex[6].numrec,
+ fi_descptr, gindex[6].file_offset);
+ write_recarray(msg_ptr, sizeof(descr_ptr), gindex[7].numrec,
+ fi_descptr, gindex[7].file_offset);
+ write_recarray(quest_ptr, sizeof(descr_ptr), gindex[8].numrec,
+ fi_descptr, gindex[8].file_offset);
+ write_recarray(ans_ptr, sizeof(descr_ptr), gindex[9].numrec,
+ fi_descptr, gindex[9].file_offset);
+
+ if (userstr != NULL)
+ write_recarray(userstr, sizeof(tline), gindex[10].numrec,
+ fi_tline, gindex[10].file_offset);
+
+ write_recblock(static_str, FT_CHAR,
+ gindex[13].numrec, gindex[13].file_offset);
+
+ write_recblock(sub_name, FT_WORD, gindex[14].numrec, gindex[14].file_offset);
+ write_recblock(synlist, FT_SLIST, gindex[15].numrec, gindex[15].file_offset);
+ write_recblock(pix_name, FT_WORD, gindex[16].numrec, gindex[16].file_offset);
+ write_recblock(globalnoun, FT_WORD, gindex[17].numrec, gindex[17].file_offset);
+ write_recblock(flag_noun, FT_WORD, gindex[18].numrec, gindex[18].file_offset);
+ write_recblock(syntbl, FT_WORD, gindex[19].numrec, gindex[19].file_offset);
+ write_recblock(dictstr, FT_CHAR, gindex[20].numrec, gindex[20].file_offset);
+ write_recblock(dict, FT_DICTPTR, gindex[21].numrec, gindex[21].file_offset);
+ if (have_opt)
+ write_recblock(opt_data, FT_BYTE, gindex[22].numrec, gindex[22].file_offset);
+
+ write_recblock(pictlist, FT_STR, gindex[23].numrec, gindex[23].file_offset);
+ write_recblock(pixlist, FT_STR, gindex[24].numrec, gindex[24].file_offset);
+ write_recblock(fontlist, FT_STR, gindex[25].numrec, gindex[25].file_offset);
+ write_recblock(songlist, FT_STR, gindex[26].numrec, gindex[26].file_offset);
+
+ write_recarray(verbinfo, sizeof(verbentry_rec), gindex[27].numrec,
+ fi_verbentry, gindex[27].file_offset);
+ write_recblock(comblist, FT_SLIST, gindex[28].numrec, gindex[28].file_offset);
+ write_recblock(userprep, FT_SLIST, gindex[29].numrec, gindex[29].file_offset);
+ write_recblock(objflag, FT_BYTE, gindex[30].numrec, gindex[30].file_offset);
+ write_recblock(objprop, FT_INT32, gindex[31].numrec, gindex[31].file_offset);
+ fix_objflag_str(0); /* Convert to external form */
+ write_recarray(attrtable, sizeof(attrdef_rec),
+ gindex[32].numrec, fi_attrrec, gindex[32].file_offset);
+ write_recarray(proptable, sizeof(propdef_rec),
+ gindex[33].numrec, fi_proprec, gindex[33].file_offset);
+ write_recblock(propstr, FT_STR, gindex[34].numrec, gindex[34].file_offset);
+ write_recarray(vartable, sizeof(vardef_rec),
+ gindex[35].numrec, fi_varrec, gindex[35].file_offset);
+ write_recarray(flagtable, sizeof(flagdef_rec),
+ gindex[36].numrec, fi_flagrec, gindex[36].file_offset);
+ fix_objflag_str(1); /* Restore to internal form */
+}
+
+
+/* Write header and master gindex and then close AGX file */
+void agx_wclose(void) {
+ write_header();
+ write_recarray(gindex, sizeof(index_rec), AGX_NUMBLOCK, fi_index, 16);
+ bw_close();
+ rfree(gindex);
+}
+
+
+void agx_wabort(void) {
+ bw_abort();
+ rfree(gindex);
+}
+
+} // End of namespace AGT
+} // End of namespace Glk
diff --git a/engines/glk/agt/auxfile.cpp b/engines/glk/agt/auxfile.cpp
new file mode 100644
index 0000000..bfc9728
--- /dev/null
+++ b/engines/glk/agt/auxfile.cpp
@@ -0,0 +1,596 @@
+/* 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 "glk/agt/agility.h"
+#include "common/str.h"
+
+namespace Glk {
+namespace AGT {
+
+/* ------------------------------------------------------------------- */
+/* Purity flag initialization */
+/* Logically, these belong in agtdata.c, but I wanted to keep them */
+/* near the CFG reading routines. */
+/* ------------------------------------------------------------------- */
+/* The following are AGT 'purity' flags; they turn off features of */
+/* my interpreter that are not fully consistent with the original AGT */
+/* and so could break some games. Some of these are trivial improvements; */
+/* some are more radical and should be used with caution. Several are */
+/* only useful if a game was designed with them in mind. */
+/* In all cases, setting the flag to 1 more closely follows the */
+/* behavior of the original interpreters */
+/* WARNING: Many of these haven't been tested extenstivly in the non-default
+ state. */
+
+
+rbool PURE_ANSWER = 0; /* For ME questions, requires that AND-separated
+ answers be in the same order in the player's
+ answer as they are in the game file. According
+ to the AGT documentation, AND should ignore
+ the order, but the original AGT interpreters
+ (at least the one I've tested) don't conform
+ to this. */
+
+rbool PURE_TIME = 1; /* Set to 0 causes time to always be increased
+ by delta_time rather than by a random amount
+ between 0 and delta_time. Only really of any use
+ to a game author who wanted to write a game
+ explicitly for AGiliTy. */
+
+/* rbool PURE_BOLD=1; Set to 0 causes the backslash to toggle bold on and
+ off for all versions of AGT, not just 1.8x.
+ I can think of no reason to do this unless
+ you are an AGT author who wants to use the 1.8x
+ bold feature with the Master's Edition compiler. */
+
+rbool PURE_AND = 1; /* increment the turn counter for each noun in a
+ chain of <noun> AND <noun> AND ... If 0, the turn
+ counter will only be incremented by one in such a case.
+ (need to do something about metacommands, as well...) */
+
+rbool PURE_METAVERB = 1; /* If set, ANY and AFTER commands are run even
+ if you type in a metaverb (SAVE, RESTORE,...
+ that is, any verb that doesn't cause time to
+ pass). Verb specific metacommands are _always_
+ run. */
+
+rbool PURE_ROOMTITLE = 1; /* If 0, the interpreter will print out room
+ names before room descriptions even for
+ pre-ME games */
+
+rbool PURE_SYN = 0; /* Treats synonyms as nouns when parsing: that is, they
+ must show up only as the last word and they have the
+ same priority as noun matches during disambiguation.
+ If this is 0, then synonyms can appear anywhere in
+ the name the player types in but are still
+ disambiguated as nouns. */
+
+rbool PURE_NOUN = 0; /* _Requires_ a noun to end a word. This is only
+ imperfectly supported: if there are no other
+ possible matches the parser will take the adjective-
+ only one anyhow. Frankly, I can't think of any reason
+ to set this to 1, but it's included for completeness
+ sake (and for any AGT Purists out there :-) ) */
+
+rbool PURE_ADJ = 1; /* Picks noun/syn-matches over pure adj matches
+ when disambiguating. This is redundant if PURE_NOUN=1
+ since in that case pure adjective matches will
+ be rejected anyhow. */
+
+rbool PURE_DUMMY = 0; /* If set, the player can running dummy verbs
+ in the game by typing 'dummy_verb3'; otherwise,
+ this will produce an error message */
+
+rbool PURE_SUBNAME = 0; /* If set, the player can run subroutines from
+ the parse line by typing (e.g.) 'subroutine4'
+ (yes, the original AGT interpreters actually
+ allow this). If cleared, this cheat isn't
+ available */
+rbool PURE_PROSUB = 0; /* If clear, then $you$ substitutions are done
+ everywhere $$ substitutions are, even in
+ messages written by the game author.
+ If set, these substitutions are only made
+ in internal game messages */
+
+rbool PURE_HOSTILE = 1; /* =0 Will allow you to leave a room with a hostile
+ creature if you go back the way you came */
+rbool PURE_ALL = 1; /* =0 will cause the parser to expand ALL */
+rbool PURE_DISAMBIG = 1; /* =0 will cause intelligent disambiguation */
+rbool PURE_GETHOSTILE = 1; /* =0 will prevent the player from picking things
+ up in a room with a hostile creature */
+
+rbool PURE_OBJ_DESC = 1; /* =0 prevents [providing light] messages
+ from being shown */
+
+rbool PURE_ERROR = 0; /* =1 means no GAME ERROR messages will be printed
+ out */
+
+rbool PURE_SIZE = 1; /* =0 eliminates size/weight limits on how many
+ things the player can wear or carry. (But it's
+ still impossible to pick things up that are
+ in themselves larger than the player's capacity) */
+
+rbool PURE_GRAMMAR = 1; /* =0 prints error messages if the player uses a
+ built in verb with an extra object.
+ (e.g. YELL CHAIR). Otherwise, the extra object
+ will just be ignored. */
+
+rbool PURE_SYSMSG = 1; /* =0 causes AGiliTy to always use the default
+ messages even if the game file has its own
+ standard error messages. */
+
+rbool PURE_AFTER = 1; /* =0 causes LOOK and other end-of-turn events
+ to happen *before* AFTER commands run. */
+
+rbool PURE_PROPER = 1; /* Don't automatically treat creatures as proper nouns */
+
+rbool TWO_CYCLE = 0; /* AGT 1.83-style two-cycle metacommand execution. */
+rbool FORCE_VERSION = 0; /* Load even if the version is wrong. */
+
+
+/*-------------------------------------------------------------------------*/
+/* .CFG reading routines */
+/*-------------------------------------------------------------------------*/
+
+/* The main interpreter handles configuration in this order:
+ 1) Global configuration file
+ 2) First pass through game specific CFG to get the settings for
+ SLASH_BOLD and IBM_CHAR which we need to know _before_ reading
+ in the game.
+ 3) Read in the game.
+ 4) Main pass through game specific CFG. Doing it here ensures that
+ its settings will override those in the gamefile.
+ Secondary programs (such as agt2agx) usually only call this once, for
+ the game specific configuration file.
+ */
+
+#define opt(s) (strcasecmp(optstr[0],s)==0)
+
+static void cfg_option(int optnum, char *optstr[], rbool lastpass)
+/* This is passed each of the options; it is responsible for parsing
+ them or passing them on to the platform-specific option handler
+ agt_option() */
+/* lastpass is set if it is the last pass through this configuration
+ file; it is false only on the first pass through the game specific
+ configuration file during the run of the main interpreter */
+{
+ rbool setflag;
+
+ if (optnum == 0 || optstr[0] == NULL) return;
+
+ if (strncasecmp(optstr[0], "no_", 3) == 0) {
+ optstr[0] += 3;
+ setflag = 0;
+ } else setflag = 1;
+
+ if (opt("slash_bold")) bold_mode = setflag;
+ else if (!lastpass) {
+ /* On the first pass, we ignore all but a few options */
+ agil_option(optnum, optstr, setflag, lastpass);
+ return;
+ } else if (opt("irun")) irun_mode = setflag;
+ else if (opt("block_hostile")) PURE_HOSTILE = setflag;
+ else if (opt("get_hostile")) PURE_GETHOSTILE = setflag;
+ else if (opt("debug")) {
+ if (!agx_file && aver <= AGTME10) debug_mode = setflag;
+ if (setflag == 0) debug_mode = 0; /* Can always turn debugging support off */
+ } else if (opt("pure_answer")) PURE_ANSWER = setflag;
+ else if (opt("const_time")) PURE_TIME = !setflag;
+ else if (opt("fix_multinoun")) PURE_AND = !setflag;
+ else if (opt("fix_metaverb")) PURE_METAVERB = !setflag;
+ else if (opt("roomtitle")) PURE_ROOMTITLE = !setflag;
+ else if (opt("pure_synonym")) PURE_SYN = setflag;
+ else if (opt("adj_noun")) PURE_ADJ = !setflag;
+ else if (opt("pure_dummy")) PURE_DUMMY = setflag;
+ else if (opt("pure_subroutine")) PURE_SUBNAME = setflag;
+ else if (opt("pronoun_subs")) PURE_PROSUB = !setflag;
+ else if (opt("verbose")) verboseflag = setflag;
+ else if (opt("fixed_font")) font_status = 1 + !setflag;
+ else if (opt("alt_any")) mars_fix = setflag;
+ else if (opt("smart_disambig")) PURE_DISAMBIG = !setflag;
+ else if (opt("expand_all")) PURE_ALL = !setflag;
+ else if (opt("object_notes")) PURE_OBJ_DESC = setflag;
+ else if (opt("error")) PURE_ERROR = !setflag;
+ else if (opt("ignore_size")) PURE_SIZE = !setflag;
+ else if (opt("check_grammar")) PURE_GRAMMAR = !setflag;
+ else if (opt("default_errors")) PURE_SYSMSG = !setflag;
+ else if (opt("pure_after")) PURE_AFTER = !setflag;
+ else if (opt("proper_creature")) PURE_PROPER = !setflag;
+ else agil_option(optnum, optstr, setflag, lastpass);
+}
+
+#undef opt
+
+/* Returns false if it there are too many tokens on the line */
+rbool parse_config_line(char *buff, rbool lastpass) {
+ char *opt[50], *p;
+ int optc;
+
+ optc = 0;
+ opt[0] = NULL;
+ for (p = buff; *p; p++) {
+ if (isspace(*p)) { /* Whitespace */
+ if (opt[optc] != NULL) { /*... which means this is the first whitespace */
+ if (optc == 50) return 0; /* Too many */
+ opt[++optc] = NULL;
+ }
+ *p = 0;
+ } else /* No whitespace */
+ if (opt[optc] == NULL) /* ...this is the first non-whitespace */
+ opt[optc] = p;
+ }
+ if (opt[optc] != NULL) opt[++optc] = NULL;
+ cfg_option(optc, opt, lastpass);
+ return 1;
+}
+
+
+/* For the meaning of lastpass, see comments to cfg_option() above */
+void read_config(genfile cfgfile, rbool lastpass) {
+ char buff[100];
+
+ if (!filevalid(cfgfile, fCFG)) return;
+
+ while (readln(cfgfile, buff, 99)) {
+ if (buff[0] == '#') continue; /* Comments */
+ /* Now we parse the line into words, with opt[] pointing at the words
+ and optc counting how many there are. */
+ if (!parse_config_line(buff, lastpass))
+ rprintf("Too many tokens on configuration line.\n");
+ }
+ readclose(cfgfile);
+}
+
+
+
+/*-------------------------------------------------------------------------*/
+/* Read OPT file */
+/* (most of these routines used to be in agil.c) */
+/*-------------------------------------------------------------------------*/
+
+/* .OPT reading routines */
+/* I've put the comments on the format here because they don't really
+ belong anywhere else. (Maybe in agility.h, but I don't want to further
+ clutter that already quite cluttered file with something as peripheral
+ as this) */
+/* OPT file format: the .OPT file consists of 14 bytes. They are:
+ 0 Screen size(0=43/50 rows, 1=25 rows)
+ 1 Status line(1=top, 0=none, -1=bottom)
+ 2 Unknown, always seems to be 0
+ 3 Put box around status line?
+ 4 Sound on?
+ 5 Menus on?
+ 6 Fixed input line?
+ 7 Print transcript?
+ 8 Height of menus (3, 4, 5, 6, 7, or 8)
+ 9 Unknown, always seems to be 0
+ 10-13 Color scheme: output/status/input/menu, specified in DOS attribute
+ format (Bbbbffff, B=blink, b=backround, f=foreground,
+ MSB of foreground specifies intensity ("bold") ). */
+/* The interpreter ignores almost all of this. */
+
+void read_opt(fc_type fc) {
+ const char *errstr;
+ genfile optfile;
+
+ have_opt = 0;
+ optfile = openbin(fc, fOPT, NULL, 0);
+ if (filevalid(optfile, fOPT)) {
+ if (!binread(optfile, opt_data, 14, 1, &errstr))
+ fatal("Invalid OPT file.");
+ have_opt = 1;
+ readclose(optfile);
+ }
+}
+
+
+/*-------------------------------------------------------------------------*/
+/* Read and process TTL */
+/* (most of these routines used to be in agil.c) */
+/*-------------------------------------------------------------------------*/
+
+/* Shades of Gray uses a custom interpreter that prints out the names
+ of the authors as the program loads. */
+/* Normally I wouldn't bother with this, but Shades of Gray is probably
+ the best known of all AGT games */
+
+#define SOGCREDIT 7
+static const char *sogauthor[SOGCREDIT] = {
+ "Mark \"Sam\" Baker",
+ "Steve \"Aaargh\" Bauman",
+ "Belisana \"The\" Magnificent",
+ "Mike \"of Locksley\" Laskey",
+ "Judith \"Teela Brown\" Pintar",
+ "Hercules \"The Loyal\" SysOp",
+ "Cindy \"Nearly Amelia\" Yans"
+};
+
+static rbool check_dollar(char *s)
+/* Determines if s consists of an empty string with a single dollar sign
+ and possibly whitespace */
+{
+ rbool dfound;
+ dfound = 0;
+ for (; *s != 0; s++)
+ if (*s == '$' && !dfound) dfound = 1;
+ else if (!rspace(*s)) return 0;
+ return dfound;
+}
+
+descr_line *read_ttl(fc_type fc) {
+ genfile ttlfile;
+ int i, j, height;
+ descr_line *buff;
+
+ ttlfile = openfile(fc, fTTL, NULL, 0);
+ /* "Warning: Could not open title file '%s'." */
+ if (!filevalid(ttlfile, fTTL)) return NULL;
+ build_fixchar();
+
+ buff = (descr_line *)rmalloc(sizeof(descr_line));
+ i = 0;
+ while (NULL != (buff[i] = readln(ttlfile, NULL, 0))) {
+ if (strncmp(buff[i], "END OF FILE", 11) == 0) break;
+ else if (aver >= AGT18 && aver <= AGT18MAX && check_dollar(buff[i]))
+ statusmode = 4;
+ else {
+ for (j = 0; buff[i][j] != 0; j++)
+ buff[i][j] = fixchar[(uchar)buff[i][j]];
+ /* Advance i and set the next pointer to NULL */
+ buff = (descr_line *)rrealloc(buff, sizeof(descr_line) * (++i + 1));
+ buff[i] = NULL;
+ }
+ rfree(buff[i]);
+ }
+ readclose(ttlfile);
+
+ rfree(buff[i]);
+ while (buff[i] == NULL || strlen(buff[i]) <= 1) { /* Discard 'empty' lines */
+ if (i == 0) break;
+ rfree(buff[i]);
+ i--;
+ }
+ height = i;
+
+ if (aver == AGTCOS && ver == 4 && height >= 17) /* SOGGY */
+ for (i = 0; i < SOGCREDIT; i++)
+ if (strlen(sogauthor[i]) + 9 + i < strlen(buff[i + 7]))
+ memcpy(buff[i + 7] + 9 + i, sogauthor[i], strlen(sogauthor[i]));
+
+ return buff;
+}
+
+void free_ttl(descr_line *title) {
+ int i;
+ if (title == NULL) return;
+ for (i = 0; title[i] != NULL; i++)
+ rfree(title[i]);
+ rfree(title);
+}
+
+
+/*-------------------------------------------------------------------------*/
+/* Read and convert VOC */
+/* (most of these routines used to be in agil.c) */
+/*-------------------------------------------------------------------------*/
+
+
+static const char *newvoc[] = { "1 Menu", "1 Restart", "1 Undo" };
+static int newindex = 0; /* Points into newvoc */
+
+void add_verbrec(const char *verb_line, rbool addnew) {
+ char s[3];
+ Common::String verbStr(verb_line);
+
+ while (!verbStr.empty() && rspace(verbStr.firstChar()))
+ verbStr.deleteChar(0);
+
+ if (verbStr.empty() || verbStr.hasPrefix("!"))
+ return; /* Comment or empty line */
+
+ /* The following guarentees automatic initialization of the verbrec structures */
+ if (!addnew)
+ while (newindex < 3 && strcasecmp(verbStr.c_str() + 2, newvoc[newindex] + 2) > 0)
+ add_verbrec(newvoc[newindex++], 1);
+
+ verbinfo = (verbentry_rec *)rrealloc(verbinfo, (vm_size + 1) * sizeof(verbentry_rec));
+
+ s[0] = verbStr.firstChar();
+ s[1] = 0;
+ verbinfo[vm_size].objnum = strtol(s, NULL, 10) - 1;
+
+ verbStr.deleteChar(0);
+ verbStr.deleteChar(0);
+
+ verbinfo[vm_size].verb = verbinfo[vm_size].prep = 0;
+
+ uint idx = 0;
+ while (idx < verbStr.size()) {
+ while (idx < verbStr.size() && !rspace(verbStr[idx]))
+ ++idx;
+ if (idx < verbStr.size()) {
+ verbStr.setChar('\0', idx);
+ ++idx;
+ }
+
+ verbinfo[vm_size].verb = search_dict(verbStr.c_str());
+ if (verbinfo[vm_size].verb == -1) {
+ verbinfo[vm_size].verb = 0;
+ return;
+ }
+ if (idx < verbStr.size()) {
+ verbinfo[vm_size].prep = search_dict(verbStr.c_str() + idx);
+ if (verbinfo[vm_size].prep == -1)
+ verbinfo[vm_size].prep = 0;
+ }
+ }
+
+ vm_size++;
+}
+
+void init_verbrec(void)
+/* Need to insert special verbs into verbinfo */
+/* Fill in vnum field */
+/* UNDO, RESTART, MENU */
+{
+ verbinfo = NULL;
+ vm_size = 0;
+ newindex = 0;
+ if (freeze_mode) newindex = 1; /* Don't include MENU option if we can't
+ use it. */
+}
+
+void finish_verbrec(void) {
+ for (; newindex < 3; newindex++) add_verbrec(newvoc[newindex], 1);
+}
+
+
+void read_voc(fc_type fc) {
+ char linbuf[80];
+ genfile vocfile;
+
+ init_verbrec();
+ vocfile = openfile(fc, fVOC, NULL, 0);
+ if (filevalid(vocfile, fVOC)) { /* Vocabulary file exists */
+ while (readln(vocfile, linbuf, 79))
+ add_verbrec(linbuf, 0);
+ readclose(vocfile);
+ finish_verbrec();
+ }
+}
+
+
+
+
+/*-------------------------------------------------------------------------*/
+/* Read INS file */
+/* (most of these routines used to be in agil.c) */
+/*-------------------------------------------------------------------------*/
+
+
+static genfile insfile = BAD_TEXTFILE;
+static char *ins_buff;
+
+static descr_line *ins_descr = NULL;
+static int ins_line; /* Current instruction line */
+
+
+/* Return 1 on success, 0 on failure */
+rbool open_ins_file(fc_type fc, rbool report_error) {
+ ins_buff = NULL;
+ ins_line = 0;
+
+ if (ins_descr != NULL) return 1;
+
+ if (filevalid(insfile, fINS)) {
+ textrewind(insfile);
+ return 1;
+ }
+
+ if (agx_file) {
+ ins_descr = read_descr(ins_ptr.start, ins_ptr.size);
+ if (ins_descr != NULL) return 1;
+
+ /* Note that if the AGX file doesn't contain an INS block, we
+ don't immediatly give up but try opening <fname>.INS */
+ }
+
+ insfile = openfile(fc, fINS,
+ report_error
+ ? "Sorry, Instructions aren't available for this game"
+ : NULL,
+ 0);
+ return (filevalid(insfile, fINS));
+}
+
+char *read_ins_line(void) {
+ if (ins_descr) {
+ if (ins_descr[ins_line] != NULL)
+ return ins_descr[ins_line++];
+ else return NULL;
+ } else {
+ rfree(ins_buff);
+ ins_buff = readln(insfile, NULL, 0);
+ return ins_buff;
+ }
+}
+
+void close_ins_file(void) {
+ if (ins_descr) {
+ free_descr(ins_descr);
+ ins_descr = NULL;
+ } else if (filevalid(insfile, fINS)) {
+ rfree(ins_buff);
+ readclose(insfile);
+ insfile = BAD_TEXTFILE;
+ }
+}
+
+
+
+descr_line *read_ins(fc_type fc) {
+ descr_line *txt;
+ char *buff;
+ int i;
+
+ i = 0;
+ txt = NULL;
+ if (open_ins_file(fc, 0)) { /* Instruction file exists */
+ while (NULL != (buff = read_ins_line())) {
+ /* Enlarge txt; we use (i+2) here to leave space for the trailing \0 */
+ txt = (descr_line *)rrealloc(txt, sizeof(descr_ptr) * (i + 2));
+ txt[i++] = rstrdup(buff);
+ }
+ if (txt != NULL)
+ txt[i] = 0; /* There is space for this since we used (i+2) above */
+ close_ins_file();
+ }
+ return txt;
+}
+
+
+void free_ins(descr_line *instr) {
+ int i;
+ if (instr == NULL) return;
+ for (i = 0; instr[i] != NULL; i++)
+ rfree(instr[i]);
+ rfree(instr);
+}
+
+
+
+/* Character translation routines, used by agtread.c and read_ttl() */
+void build_fixchar(void) {
+ int i;
+ for (i = 0; i < 256; i++) {
+ if (i == '\r' || i == '\n') fixchar[i] = ' ';
+ else if (i == '\\' && bold_mode) fixchar[i] = FORMAT_CODE;
+ else if (i >= 0x80 && fix_ascii_flag)
+ fixchar[i] = trans_ibm[i & 0x7f];
+ else if (i == 0) /* Fix color and blink codes */
+ fixchar[i] = FORMAT_CODE;
+ else fixchar[i] = i;
+ }
+}
+
+} // End of namespace AGT
+} // End of namespace Glk
diff --git a/engines/glk/agt/config.h b/engines/glk/agt/config.h
new file mode 100644
index 0000000..0780e1a
--- /dev/null
+++ b/engines/glk/agt/config.h
@@ -0,0 +1,289 @@
+/* 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 GLK_AGT_CONFIG
+#define GLK_AGT_CONFIG
+
+#include "glk/glk_api.h"
+#include "common/stream.h"
+
+namespace Glk {
+namespace AGT {
+
+/*
+ This file contains most of the configuration information
+ including the platform-dependent #define statements
+ It's in three major sections:
+ --Platform specific defines for various platforms, each
+ surrounded by "#ifdef <platform>" and "#endif"
+ --Various defaults
+ --Filename extensions
+
+ Ideally, a port to a new platform should only need to modify this
+ file, the makefile, os_<whatever>.c, and possibly filename.c. (In
+ practice, you may also need to tweak the the high-level I/O code
+ in interface.c or the memory-allocation code in util.c. If you
+ find yourself needing to do more than that, get in touch with me.) */
+
+#undef _WIN32 /* GARGLK */
+
+
+/* Default to PLAIN platform */
+/* At the moment, you can replace this with LINUX, HPUX, AMIGA, */
+/* MSDOS, SUN, or NEXT; some of these may require the correct os_... */
+/* file to work */
+/* (In particular, AMIGA requires David Kinder's os_amiga.c file) */
+/* The actual platform specific defines don't start until a few */
+/* lines down, past the #includes and the definition of global */
+#ifndef PLAIN
+#define PLAIN
+#endif
+
+/* ------------------------------------------------------------------- */
+/* PLATFORM SPECIFIC DEFINITIONS, ETC. */
+/* See agility.doc or porting.txt for more information. */
+/* Things you can currently define: */
+/* fix_ascii: 1=translate IBM character set, 0=don't */
+/* NEED_STR_CMP: define if strcasecmp() not defined on your system */
+/* NEED_STRN_CMP: define if strncasecmp() not defined on your system */
+/* HAVE_STRDUP: define if strdup() exists on your system */
+/* REPLACE_GETFILE: define if you replace the default get_user_file(). */
+/* REPLACE_MENU if you replace agt_menu(). */
+/* REPLACE_MAIN: define if you replace the default main(). */
+/* (replacements should be defined in the relevant os_<platform>.c file) */
+/* DA1,DA2,...DA6,DSS,pTTL: file name extensions for the various AGT
+ files */
+/* HAVE_SLEEP if your platform has the sleep() function */
+/* BUFF_SIZE is the maximum size of the buffer to use when reading
+ in files. Regardless, it will be made no bigger than the file
+ being read in and no smaller than the record size; thus setting
+ it to 0 will cause the smallest buffer to always be used and
+ setting this to 1MB will in practice always use a buffer the
+ sizs of the file. It defaults to 32K */
+/* CBUF_SIZE is the maximum size of the buffer used for reading in
+ the Master's Edition DA6 files; the size of the buffer in bytes
+ is twice this value (since an individual token is two bytes long). */
+/* DESCR_BUFFSIZE is the maximum size of the description text block before
+ the interpreter will read it from disk rather than storing it in
+ memory during play. At the moment this only affects AGX games;
+ original AGT games always use the disk. */
+/* DOHASH to use a hash table for dictionary searches; the only
+ reason not to have this would be memory */
+/* HASHBITS determines the size of the hash table: (2^HASHBITS)*sizeof(word);
+ the hash table must be at least as large as the dictionary.
+ In practice this means HASHBITS should be at least 12;
+ this is the current default. */
+/* MAXSTRUC The maximum size (in chars) which a single data structure can
+ be on this platform. This defaults to 1MB (i.e. no limit for
+ practical purposes). In practice I know of no game files that
+ require any structures bigger than about 30K. */
+/* LOWMEM Define this if you are low on memory. At the moment this
+ only saves a few K.*/
+/* PORTSTR Is the string describing this particular port.
+ e.g. #define PORTSTR "OrfDOS Port by R.J. Wright" */
+/* UNIX_IO if you have Unix-like low level file I/O functions.
+ (MS-DOS, for example, does). This speeds up the reading
+ of the large game data files on some platforms. If this is
+ defined, READFLAG, WRITEFLAG, and FILE_PERM also need to
+ be defined. (Giving the flags needed for opening a file for
+ reading or writing, and the file permissions to be given to newly
+ created files. */
+/* OPEN_AS_TEXT Define to cause text files to be opened as text files. */
+/* PREFIX_EXT Add filename extensions at the beginning of the name,
+ rather than at the end. */
+/* PATH_SEP, if defined, is a string containing all characters which
+ can be used to separate the path from the filename. */
+/* pathtest(s) is a macro that should check whether the given string
+ is an absolute path. If this is left undefined, then _all_
+ paths will be treated as absolute. You don't need to define
+ this if you are replacing filename.c. */
+/* ------------------------------------------------------------------- */
+
+/* force16 is used purely for debugging purposes, to make sure that
+ everything works okay even with 16-bit ints */
+/* #define force16 */
+
+#define DOHASH
+
+/*
+ * The Glk port is very similar to plain ASCII, to give it the best
+ * chance at success on multiple Glk platforms. The only basic change
+ * is to turn off IBM character translations; Glk works in ISO 8859
+ * Latin-1, which can offer slightly closer translation of the IBM
+ * code page 437 characters that the simpler mappings in the core
+ * AGiliTy code. The os_glk.c module handles the translations.
+ */
+#ifdef GARGLK
+#define NEED_STR_CMP /* Inherited from PLAIN. */
+#define NEED_STRN_CMP /* Inherited from PLAIN. */
+#define BUFF_SIZE 0 /* Inherited from PLAIN. */
+#define CBUF_SIZE (5000L) /* Inherited from PLAIN. */
+#define INBUFF_SIZE (1024) /* Inherited from PLAIN. */
+#define fix_ascii 0 /* os_glk.c does translations. */
+#define MAXSTRUC (1024L*1024L) /* 32Kb from PLAIN is too small for
+ several games (including Soggy). */
+#define PORTSTR "Glk version" /* Identify ourselves discreetly. */
+#define REPLACE_GETFILE /* Override get_user_file. */
+#define REPLACE_MAIN /* Override main. */
+#define fnamecmp strcasecmp /* Case insensitive filename compare. */
+#undef PLAIN
+
+#endif
+
+/* PLAIN should always come last, giving everyone else a chance
+ to #undef it. */
+#ifdef PLAIN /* This should work if nothing else does */
+#define NEED_STR_CMP
+#define NEED_STRN_CMP
+#define BUFF_SIZE 0
+#define CBUF_SIZE (5000L)
+#define INBUFF_SIZE (1024) /* Used by Magx */
+#define MAXSTRUC (32L*1024L) /* IIRC, 32K is the minimum required by
+ the ANSI standard */
+#define PORTSTR "Pure ANSI C version"
+#endif
+
+
+/* __GNUC__ */
+
+
+/* ------------------------------------------------------------------- */
+/* DEFAULTS FOR "PLATFORM SPECIFIC" DEFINES */
+/* ------------------------------------------------------------------- */
+
+#ifdef __STRICT_ANSI__
+#define NEED_STR_CMP
+#define NEED_STRN_CMP
+#undef HAVE_STRDUP
+#endif
+
+#ifndef fix_ascii
+#define fix_ascii 1 /* Translate IBM character set by default */
+#endif
+
+#ifndef BUFF_SIZE
+#ifdef LOWMEM
+#define BUFF_SIZE 0 /* i.e. unbuffered */
+#else
+#define BUFF_SIZE (32L*1024L) /* 32K */
+#endif
+#endif /* BUFF_SIZE */
+
+#ifndef MAXSTRUC
+#define MAXSTRUC (1024L*1024L)
+#endif
+
+#ifndef DESCR_BUFFSIZE
+#define DESCR_BUFFSIZE 0 /* Always load descriptions from disk */
+#endif
+
+#ifndef HASHBITS
+#ifdef LOWMEM
+#define HASHBITS 12 /* 4K entries */
+#else
+#define HASHBITS 13 /* 8K entries in hash table */
+#endif
+#endif /* HASHBITS */
+
+#ifndef fnamecmp /* Used to compare filenames */
+#define fnamecmp strcmp
+#endif
+
+#ifndef fnamencmp /* Also used to compare filenames */
+#define fnamencmp strncmp
+#endif
+
+/* If DOSFARDATA hasn't been defined, define it as the empty string. */
+#ifndef DOSFARDATA
+#define DOSFARDATA
+#endif
+
+/* ---------------------------------------------------------------------- */
+/* FILENAME EXTENSIONS */
+/* These are the various filename extensions for the different data files.*/
+/* ---------------------------------------------------------------------- */
+
+/* The following are only used by the interpreter, agtout, and agt2agx */
+#ifndef DA1
+#define DA1 ".da1" /* General info (text file) */
+#define DA2 ".da2" /* Rooms */
+#define DA3 ".da3" /* Items */
+#define DA4 ".da4" /* Creatures */
+#define DA5 ".da5" /* Commands, headers */
+#define DA6 ".da6" /* Commands, code (Master's Ed only) */
+#define DSS ".d$$" /* Description strings */
+#define pHNT ".hnt" /* Popup hint file; not used yet. */
+#define pOPT ".opt" /* Interface specification file */
+#endif
+
+/* The following are only used by the Magx compiler */
+#ifndef pAGT
+#define pAGT ".agt"
+#define pDAT ".dat"
+#define pMSG ".msg"
+#define pCMD ".cmd"
+#define pSTD ".std"
+#define AGTpSTD "agt.std" /* Default error message file */
+#endif
+
+/* The following are used by both the interpreter and the compiler */
+#ifndef pAGX
+#define pAGX ".agx" /* Extension for new Adventure Game eXecutable format */
+#define pTTL ".ttl" /* Title file */
+#define pINS ".ins" /* Instruction file */
+#define pVOC ".voc" /* Menu vocabulary file */
+#define pCFG ".cfg" /* Game configuration file */
+#define pEXT "." /* Separator between extension and base of filename */
+#endif
+
+
+#ifndef pSAV
+#define pSAV ".sav" /* Extension for save files */
+#endif
+
+#ifndef pSCR
+#define pSCR ".scr" /* Script file */
+#endif
+
+#ifndef pLOG
+#define pLOG ".log" /* LOG/REPLAY file */
+#endif
+
+
+
+
+
+/* Finally, two potentially platform dependent type defintions,
+ for binary and text files respectively. Don't change these
+ unless you are also changing filename.c */
+
+typedef Common::Stream *genfile;
+typedef char *file_id_type; /* i.e. the filename */
+
+#define NO_FILE_ID NULL
+#define BAD_TEXTFILE NULL
+#define BAD_BINFILE NULL
+
+} // End of namespace AGT
+} // End of namespace Glk
+
+#endif
diff --git a/engines/glk/agt/debugcmd.cpp b/engines/glk/agt/debugcmd.cpp
new file mode 100644
index 0000000..83be57a
--- /dev/null
+++ b/engines/glk/agt/debugcmd.cpp
@@ -0,0 +1,866 @@
+/* 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 "glk/agt/agility.h"
+#include "glk/agt/interp.h"
+#include "glk/agt/exec.h"
+
+namespace Glk {
+namespace AGT {
+
+static void d_moveobj(int obj, int dest)
+/* 1=the player, -1=unknown: ask */
+{
+ if (obj == -1) {
+ writestr("Which object? ");
+ obj = read_number();
+ if (obj != 1 && !tnoun(obj) && !tcreat(obj)) {
+ writeln("Invalid object");
+ return;
+ }
+ }
+ if (dest == -1) {
+ writestr("To where? ");
+ dest = read_number();
+ if (dest != 1 && dest != 0 && !tnoun(dest) && !tcreat(dest) && !troom(dest)) {
+ writeln("Invalid object");
+ return;
+ }
+ }
+ if (obj != 1)
+ it_move(obj, dest);
+ else {
+ if (!troom(dest)) {
+ writeln("Player can only be moved to a room");
+ return;
+ }
+ goto_room(dest - first_room);
+ }
+}
+
+static int print_objid(int obj) {
+ char buff[10];
+ char *s;
+ int n;
+
+ sprintf(buff, "%4d: ", obj);
+ writestr(buff);
+ s = objname(obj);
+ for (n = 0; s[n] != 0; n++)
+ if (s[n] <= 8 || (uchar)s[n] == 0xFF) s[n] = ' '; /* Strip out format codes */
+ writestr(s);
+ n = strlen(s);
+ rfree(s);
+ return n;
+}
+
+static void d_listroom() {
+ int i;
+
+ writeln(" ROOM");
+ writeln(" ------");
+ writeln("");
+ for (i = 0; i <= maxroom - first_room; i++) {
+ print_objid(i + first_room);
+ writeln("");
+ }
+}
+
+#define SEPLENG 27 /* Width between beginning of object column and
+ location column */
+
+static void d_listnoun() {
+ int i;
+ int len;
+
+ writestr(" NOUN ");
+ padout(SEPLENG - 6);
+ writeln(" LOCATION ");
+ writestr(" ------");
+ padout(SEPLENG - 6);
+ writeln(" ----------");
+ writeln("");
+ len = SEPLENG - print_objid(1);
+ padout(len);
+ writestr("[");
+ print_objid(loc);
+ writeln("]");
+
+ nounloop(i) {
+ len = print_objid(i + first_noun);
+ len = SEPLENG - len;
+ if (len > 0) padout(len);
+ writestr("[");
+ print_objid(noun[i].location);
+ writeln("]");
+ }
+}
+
+static void d_listcreat() {
+ int i;
+ int len;
+
+ writestr(" CREATURE ");
+ padout(SEPLENG - 11);
+ writeln(" LOCATION ");
+ writestr(" ----------");
+ padout(SEPLENG - 11);
+ writeln(" ----------");
+ writeln("");
+
+ creatloop(i) {
+ len = print_objid(i + first_creat);
+ len = SEPLENG - len;
+ if (len > 0) padout(len);
+ writestr(" [");
+ print_objid(creature[i].location);
+ writeln("]");
+ }
+}
+
+static void writetbl(const char *s, int width)
+/* This writes out s and then prints out any additional spaces needed
+ to make the output string *width* wide. */
+{
+ writestr(s);
+ width = width - strlen(s);
+ if (width > 0) padout(width);
+}
+
+static void var_edit(int vtype)
+/* vtype=0 for variable, 1 for counter, 2 for flag */
+{
+ long n;
+ int i;
+ int imax;
+ char sbuff[30];
+
+ switch (vtype) {
+ case 0:
+ imax = VAR_NUM;
+ break;
+ case 1:
+ imax = CNT_NUM;
+ break;
+ case 2:
+ imax = FLAG_NUM;
+ break;
+ default:
+ writeln("INTERNAL ERROR: Invalid vtype.");
+ return;
+ }
+
+ for (;;) {
+ agt_clrscr();
+ writeln("");
+ switch (vtype) {
+ case 0:
+ writeln("Variables");
+ break;
+ case 1:
+ writeln("Counters (-1 means the counter is off)");
+ break;
+ case 2:
+ writeln("Flags ( f=false [OFF] and t=true [ON] )");
+ break;
+ }
+ writeln("");
+ for (i = 0; i <= imax; i++) {
+ switch (vtype) {
+ case 0:
+ sprintf(sbuff, "[Var%3d]=%4ld", i, (long)agt_var[i]);
+ break;
+ case 1:
+ sprintf(sbuff, "[Cnt%3d]=%4ld", i, (long)agt_counter[i]);
+ break;
+ case 2:
+ sprintf(sbuff, "%3d%c", i, flag[i] ? 't' : 'f');
+ break;
+ }
+ writetbl(sbuff, (vtype == 2) ? 5 : 20);
+ }
+ writeln("");
+ writeln("");
+ for (;;) {
+ switch (vtype) {
+ case 0:
+ writestr("Variable to change");
+ break;
+ case 1:
+ writestr("Counter to change");
+ break;
+ case 2:
+ writestr("Flag to toggle");
+ break;
+ }
+ writestr(" (-1 to quit): ");
+ i = read_number();
+ if (i < 0) return;
+ if (i <= imax) {
+ if (vtype != 2) {
+ if (vtype == 0)
+ sprintf(sbuff, "[Var%d]=%ld", i, (long)agt_var[i]);
+ else sprintf(sbuff, "[Cnt%d]=%ld (-1 means it's off)",
+ i, (long)agt_counter[i]);
+ writestr(sbuff);
+ writestr("; new value = ");
+ n = read_number();
+ if (vtype == 0)
+ agt_var[i] = n;
+ else if (n < -1 || n > (((long)1) << 15) - 1)
+ writeln("Invalid value for a counter.");
+ else agt_counter[i] = n;
+ } else flag[i] = !flag[i];
+ break;
+ } else
+ writeln("Invalid index.");
+ }
+ }
+}
+
+/* Routines to edit user strings */
+static void edit_str() {
+ int i, j;
+ char buff[10];
+ char *tmpstr;
+
+ if (MAX_USTR == 0 || userstr == NULL) {
+ writeln("This game doesn't contain any user strings");
+ return;
+ }
+ for (;;) {
+ agt_clrscr();
+ writeln("User Definable Strings");
+ writeln("");
+ for (i = 0; i < MAX_USTR; i++) {
+ sprintf(buff, "%2d:", i + 1);
+ writestr(buff);
+ writeln(userstr[i]);
+ }
+ writestr(" (0 to quit): ");
+ i = read_number();
+ if (i == 0) return;
+ if (i > 0 && i <= MAX_USTR) {
+ writeln("Enter new string:");
+ tmpstr = agt_readline(3);
+ j = strlen(tmpstr) - 1;
+ if (j > 0 && tmpstr[j] == '\n') tmpstr[j] = 0;
+ strncpy(userstr[i - 1], tmpstr, 80);
+ } else writeln("Invalid string number");
+ }
+}
+
+static uchar attrcol; /* Determines which column the attribute is put in */
+static uchar attrwidth; /* Number of attribute columns */
+
+static void next_col() {
+ if (++attrcol == attrwidth) {
+ writeln("");
+ attrcol = 0;
+ } else
+ padout(10);
+}
+
+static void writeattr(const char *attrname, rbool attrval) {
+ writestr(attrname);
+ padout(15 - strlen(attrname));
+ if (attrval) writestr("yes");
+ else writestr("no ");
+ next_col();
+}
+
+static void writegender(const char *gendername, uchar genderval) {
+ writestr(gendername);
+ padout(15 - strlen(gendername) - 3);
+ switch (genderval) {
+ case 2:
+ writestr("Male ");
+ break;
+ case 1:
+ writestr("Female");
+ break;
+ case 0:
+ writestr("Thing");
+ break;
+ }
+ next_col();
+}
+
+static void writeprop(const char *propname, int obj) {
+ writestr(propname);
+ writestr(" [");
+ print_objid(obj);
+ writeln("]");
+}
+
+static int writedir(int index, int dir, int obj) {
+ char sbuff[40];
+
+ sprintf(sbuff, "%2d.%-2s %d", index, exitname[dir], obj);
+ writestr(sbuff);
+ return strlen(sbuff);
+}
+
+void writenum(const char *propname, int n) {
+ char sbuff[20];
+
+ writestr(propname);
+ sprintf(sbuff, "%4d", n);
+ writeln(sbuff);
+}
+
+static void writeflags(const char *flagname, int32 flags) {
+ int i;
+ char sbuff[5];
+
+ writestr(flagname);
+ for (i = 0; i < 32; i++) {
+ if (flags & 1) {
+ sprintf(sbuff, "%2d ", i);
+ writestr(sbuff);
+ } else
+ writestr(" ");
+ flags >>= 1;
+ if (i % 12 == 11) {
+ writeln("");
+ padout(strlen(flagname));
+ }
+ }
+ writeln("");
+}
+
+static void readflags(int32 *flags) {
+ long n;
+
+ writestr("Room flag to toggle (0-31)? ");
+ n = read_number();
+ if (n <= 31 && n >= 0)
+ *flags ^= (((long)1) << n);
+}
+
+static long readval(const char *prompt, int type) {
+ long val;
+
+ for (;;) {
+ writestr(prompt);
+ writestr(" ");
+ val = read_number();
+ if (argvalid(type, val)) return val;
+ writeln("Invalid value.");
+ }
+}
+
+static uchar readgender() {
+ char c;
+
+ writestr("Gender (M/F/N): ");
+ for (;;) {
+ c = tolower(agt_getchar());
+ switch (c) {
+ case 'm':
+ return 2;
+ case 'w':
+ case 'f':
+ return 1;
+ case 'n':
+ case 't':
+ return 0;
+ default: ;/* Do nothing */
+ }
+ }
+}
+
+static void edit_objattr(int obj) {
+ int i, k, kprop, n;
+ long v;
+
+ for (;;) {
+ k = 1;
+ agt_clrscr();
+ print_objid(obj);
+ writeln("");
+ if (oflag_cnt > 0) {
+ writeln("ObjFlags:");
+ for (i = 0; i < oflag_cnt; i++)
+ if (have_objattr(0, obj, i)) {
+ v = op_objflag(2, obj, i);
+ rprintf("%2d. ObjProp%2d:%c %-40s\n", k++, i, (v ? '+' : '-'),
+ get_objattr_str(AGT_OBJFLAG, i, v));
+ }
+ writeln("");
+ }
+ kprop = k;
+ if (oprop_cnt > 0) {
+ writeln("ObjProps:");
+ for (i = 0; i < oprop_cnt; i++)
+ if (have_objattr(1, obj, i)) {
+ v = op_objprop(2, obj, i, 0);
+ rprintf("%2d. ObjFlag%2d: [%3ld] %-40s\n", k++, i, v,
+ get_objattr_str(AGT_OBJPROP, i, v));
+ }
+ writeln("");
+ }
+ writestr("Field to change (0 to return to main view)? ");
+ n = read_number();
+ if (n == 0) return;
+ if (n < 1 || n >= k) continue;
+ k = 0;
+ if (n < kprop) { /* Attribute */
+ for (i = 0; i < oflag_cnt; i++)
+ if (have_objattr(0, obj, i))
+ if (n == ++k) break;
+ if (n == k && have_objattr(0, obj, i))
+ op_objflag(3, obj, i); /* Toggle it */
+ } else { /* Property */
+ for (i = 0; i < oprop_cnt; i++)
+ if (have_objattr(1, obj, i))
+ if (n == ++k) break;
+ if (n == k && have_objattr(1, obj, i))
+ op_objprop(1, obj, i, readval("New value:", AGT_NUM));
+ }
+ }
+}
+
+static void room_edit(int i) {
+ int n, j;
+
+ for (;;) {
+ agt_clrscr();
+ writestr("ROOM ");
+ print_objid(i + first_room);
+ writeln("");
+ writeln("");
+ attrcol = 0;
+ attrwidth = 2;
+ writeattr("1.*WinGame:", room[i].win);
+ writeattr("4. Seen:", room[i].seen);
+ writeattr("2.*EndGame:", room[i].end);
+ writeattr("5. LockedDoor:", room[i].locked_door);
+ writeattr("3.*Die:", room[i].killplayer);
+ writeln("");
+ writeln("");
+ writeprop("6.*Key =", room[i].key);
+ writeprop("7. Light =", room[i].light);
+ writenum("8. Points =", room[i].points);
+ writeprop("9. Class = ", room[i].oclass);
+ writeln("");
+ writeln("EXITS:");
+ for (j = 0; j < 12; j++) {
+ n = writedir(j + 10, j, room[i].path[j]);
+ if (j % 4 == 3) writeln("");
+ else padout(15 - n);
+ }
+ writeprop("22. SPECIAL:", room[i].path[12]);
+ writeflags("23. Room Flags:", room[i].flag_noun_bits);
+ writeln("24. Object properties and attributes.");
+ writeln("");
+ writeln("(Fields marked with an * are not saved or restored.)");
+ /* writeln(""); */
+ writestr("Field to change (0 to exit)? ");
+ n = read_number();
+ if (n == 0) return;
+ switch (n) {
+ case 1:
+ room[i].win = !room[i].win;
+ break;
+ case 2:
+ room[i].end = !room[i].end;
+ break;
+ case 3:
+ room[i].killplayer = !room[i].killplayer;
+ break;
+ case 4:
+ room[i].seen = !room[i].seen;
+ break;
+ case 5:
+ room[i].locked_door = !room[i].locked_door;
+ break;
+ case 6:
+ room[i].key = readval("Key = ", AGT_ITEM | AGT_NONE);
+ break;
+ case 7:
+ room[i].light = readval("Light = ", AGT_ITEM | AGT_NONE | AGT_SELF);
+ break;
+ case 8:
+ room[i].points = readval("Points = ", AGT_NUM);
+ break;
+ case 9:
+ room[i].oclass = readval("Class = ", AGT_ROOM | AGT_NONE);
+ break;
+ case 22:
+ room[i].path[12] = readval("SPECIAL: ", AGT_NUM);
+ break;
+ case 23:
+ readflags(&room[i].flag_noun_bits);
+ break;
+ case 24:
+ edit_objattr(i + first_room);
+ break;
+ default:
+ if (n >= 10 && n < 22) { /* Direction */
+ room[i].path[n - 10] = readval(exitname[n - 10], AGT_NUM);
+ } else writeln("Invalid field");
+ }
+ }
+}
+
+#define tog(x) {x=!x;break;}
+
+static void noun_edit(int i) {
+ int n;
+
+ for (;;) {
+ agt_clrscr();
+ /* writeln("");*/
+ writestr("NOUN ");
+ print_objid(i + first_noun);
+ /* writeln("");*/
+ /* writeln("");*/
+ writeprop(" Location=", noun[i].location);
+ writeln("");
+ attrcol = 0;
+ attrwidth = 3;
+ writeattr(" 1.*Pushable:", noun[i].pushable);
+ writeattr(" 8.*Lockable:", noun[i].lockable);
+ writeattr("15.*Drinkable:", noun[i].drinkable);
+ writeattr(" 2.*Pullable:", noun[i].pullable);
+ writeattr(" 9.*Light:", noun[i].light);
+ writeattr("16.*Poisonous:", noun[i].poisonous);
+ writeattr(" 3.*Turnable:", noun[i].turnable);
+ writeattr("10.*Plural:", noun[i].plural);
+ writeattr("17. Open:", noun[i].open);
+ writeattr(" 4.*Playable:", noun[i].playable);
+ writeattr("11. Movable:", noun[i].movable);
+ writeattr("18. Locked:", noun[i].locked);
+ writeattr(" 5.*Readable:", noun[i].readable);
+ writeattr("12.*Shootable:", noun[i].shootable);
+ writeattr("19.*Win Game:", noun[i].win);
+ writeattr(" 6.*Wearable:", noun[i].wearable);
+ writeattr("13. On:", noun[i].on);
+ writeattr("20.*Global:", noun[i].isglobal);
+ writeattr(" 7.*Closable:", noun[i].closable);
+ writeattr("14.*Edible:", noun[i].edible);
+ writeattr("21.*Proper:", noun[i].proper);
+
+ writeln("");
+ writenum("22. Shots =", noun[i].num_shots);
+ writenum("23. Points =", noun[i].points);
+ writenum("24. Weight =", noun[i].weight);
+ writenum("25. Size =", noun[i].size);
+ writeprop("26.*Key =", noun[i].key);
+ writeprop("27. Class =", noun[i].oclass);
+ writenum("28. Flag =", noun[i].flagnum);
+ writeln("");
+ /* writeln(""); */
+ writeln("29. Object properties and attributes.");
+ writeln("");
+ writeln("(Fields marked with an * are not saved or restored.)");
+ writestr("Field to change (0 to exit)? ");
+ n = read_number();
+ if (n == 0) return;
+ switch (n) {
+ case 1:
+ tog(noun[i].pushable); /* tog() macro includes break */
+ case 2:
+ tog(noun[i].pullable);
+ case 3:
+ tog(noun[i].turnable);
+ case 4:
+ tog(noun[i].playable);
+ case 5:
+ tog(noun[i].readable);
+ case 6:
+ tog(noun[i].wearable);
+ case 7:
+ tog(noun[i].closable);
+ case 8:
+ tog(noun[i].lockable);
+ case 9:
+ tog(noun[i].light);
+ case 10:
+ tog(noun[i].plural);
+ case 11:
+ tog(noun[i].movable);
+ case 12:
+ tog(noun[i].shootable);
+ case 13:
+ tog(noun[i].on);
+ case 14:
+ tog(noun[i].edible);
+ case 15:
+ tog(noun[i].drinkable);
+ case 16:
+ tog(noun[i].poisonous);
+ case 17:
+ tog(noun[i].open);
+ case 18:
+ tog(noun[i].locked);
+ case 19:
+ tog(noun[i].win);
+ case 20:
+ tog(noun[i].isglobal);
+ case 21:
+ tog(noun[i].proper);
+
+ case 22:
+ noun[i].num_shots = readval("Shots =", AGT_NUM);
+ break;
+ case 23:
+ noun[i].points = readval("Points =", AGT_NUM);
+ break;
+ case 24:
+ noun[i].weight = readval("Weight =", AGT_NUM);
+ break;
+ case 25:
+ noun[i].size = readval("Size =", AGT_NUM);
+ break;
+ case 26:
+ noun[i].key = readval("Key =", AGT_ITEM | AGT_NONE);
+ break;
+ case 27:
+ noun[i].oclass = readval("Class =", AGT_ITEM | AGT_NONE);
+ break;
+ case 28:
+ noun[i].flagnum = readval("Flag Number=", AGT_ROOMFLAG);
+ break;
+ case 29:
+ edit_objattr(i + first_noun);
+ break;
+ default:
+ writeln("Invalid field");
+ }
+ }
+}
+
+static void creat_edit(int i) {
+ int n;
+
+ for (;;) {
+ agt_clrscr();
+ writestr("CREATURE ");
+ print_objid(i + first_creat);
+ writeln("");
+ writeln("");
+ writeprop("Location =", creature[i].location);
+ writeln("");
+ attrcol = 0;
+ attrwidth = 2;
+ writeattr(" 1. Hostile:", creature[i].hostile);
+ writeattr(" 4. Global:", creature[i].isglobal);
+ writeattr(" 2. Grp member:", creature[i].groupmemb);
+ writeattr(" 5.*Proper:", creature[i].proper);
+ writegender(" 3.*Gender:", creature[i].gender);
+ writeln("");
+ writeln("");
+ writeprop(" 6.*Weapon = ", creature[i].weapon);
+ writenum(" 7. Points = ", creature[i].points);
+ writenum(" 8.*Attack Threshold = ", creature[i].threshold);
+ writenum(" 9. Attack counter = ", creature[i].counter);
+ writenum("10.*Attack Time Limit = ", creature[i].timethresh);
+ writenum("11. Attack timer = ", creature[i].timecounter);
+ writeprop("12. Class = ", creature[i].oclass);
+ writenum("13. Flag = ", creature[i].flagnum);
+ writeln("");
+ writeln("14. Object properties and attributes.");
+ writeln("");
+ writeln("(Fields marked with an * are not saved or restored.)");
+ writeln("");
+ writestr("Field to change (0 to exit)? ");
+ n = read_number();
+ if (n == 0) return;
+ switch (n) {
+ case 1:
+ tog(creature[i].hostile);
+ case 2:
+ tog(creature[i].groupmemb);
+ case 3:
+ tog(creature[i].isglobal);
+ case 4:
+ tog(creature[i].proper);
+
+ case 5:
+ creature[i].gender = readgender();
+ break;
+ case 6:
+ creature[i].weapon = readval("Weapon =", AGT_ITEM | AGT_NONE);
+ break;
+ case 7:
+ creature[i].points = readval("Points =", AGT_NUM);
+ break;
+ case 8:
+ creature[i].threshold = readval("Threshold =", AGT_NUM);
+ break;
+ case 9:
+ creature[i].counter = readval("Attack counter =", AGT_NUM);
+ break;
+ case 10:
+ creature[i].timethresh = readval("Time limit =", AGT_NUM);
+ break;
+ case 11:
+ creature[i].timecounter = readval("Timer =", AGT_NUM);
+ break;
+ case 12:
+ creature[i].oclass = readval("Class =", AGT_ITEM | AGT_NONE);
+ break;
+ case 13:
+ noun[i].flagnum = readval("Flag Number=", AGT_ROOMFLAG);
+ break;
+ case 14:
+ edit_objattr(i + first_creat);
+ break;
+ default:
+ writeln("Invalid field");
+ }
+ }
+}
+
+#undef tog
+
+
+static void obj_edit() {
+ int n;
+
+ for (;;) {
+ writeln("");
+ do {
+ writestr("Enter object number (0 to exit)? ");
+ n = read_number();
+ if (n <= 0) return;
+ } while (!troom(n) && !tnoun(n) && !tcreat(n));
+
+ if (troom(n)) room_edit(n - first_room);
+ else if (tnoun(n)) noun_edit(n - first_noun);
+ else if (tcreat(n)) creat_edit(n - first_creat);
+ else writeln("[Not yet implemented]");
+
+ }
+}
+
+static const char *yesnostr[] = { "No", "Yes" };
+
+static void set_debug_options() {
+ char buff[80];
+ int n;
+
+ for (;;) {
+ agt_clrscr();
+ writeln("DEBUGGING OPTIONS:");
+ writeln("");
+ sprintf(buff, " 1. Trace metacommands: %s", yesnostr[DEBUG_AGT_CMD]);
+ writeln(buff);
+ sprintf(buff, " 2. Trace ANY metacommands: %s", yesnostr[debug_any]);
+ writeln(buff);
+ sprintf(buff, " 3. Trace during disambiguation: %s",
+ yesnostr[debug_disambig]);
+ writeln(buff);
+ writeln("");
+ writeln("(<2> and <3> are ignored if <1> is not set; option <1> can"
+ " also be changed from the main debugging menu)");
+ writeln("");
+ writestr("Option to toggle (0 to exit): ");
+ n = read_number();
+ switch (n) {
+ case 0:
+ return;
+ case 1:
+ DEBUG_AGT_CMD = !DEBUG_AGT_CMD;
+ break;
+ case 2:
+ debug_any = !debug_any;
+ break;
+ case 3:
+ debug_disambig = !debug_disambig;
+ break;
+ default:
+ writeln("Not a valid option");
+ }
+ }
+}
+
+void get_debugcmd() {
+ int n;
+
+ for (;;) {
+ writeln("DEBUGGING COMMANDS");
+ writeln("");
+ writeln("1. Move player 8. List Rooms");
+ writeln("2. Get Noun 9. List Nouns");
+ writeln("3. Move object 10. List Creatures");
+ writeln("4. View/Edit object 11. List/Set Flags");
+ writeln("5. Toggle Trace 12. List/Set Variables");
+ writeln("6. Set Debug Options 13. List/Set Counters");
+ writeln("7. Edit User Strings");
+ writeln("");
+ writestr("Enter choice (0 to exit): ");
+ n = read_number();
+ switch (n) {
+ case -1:
+ case 0:
+ return;
+ case 1:
+ d_moveobj(1, -1);
+ break;
+ case 2:
+ d_moveobj(-1, 1);
+ break;
+ case 3:
+ d_moveobj(-1, -1);
+ break;
+ case 4:
+ obj_edit();
+ break;
+ case 5:
+ DEBUG_AGT_CMD = !DEBUG_AGT_CMD;
+ break;
+ case 6:
+ set_debug_options();
+ break;
+ case 7:
+ edit_str();
+ break;
+ case 8:
+ d_listroom();
+ break;
+ case 9:
+ d_listnoun();
+ break;
+ case 10:
+ d_listcreat();
+ break;
+ case 11:
+ var_edit(2);
+ break;
+ case 12:
+ var_edit(0);
+ break;
+ case 13:
+ var_edit(1);
+ break;
+ default:
+ writeln("Not a valid option");
+ }
+ writeln("");
+ };
+}
+
+} // End of namespace AGT
+} // End of namespace Glk
diff --git a/engines/glk/agt/disassemble.cpp b/engines/glk/agt/disassemble.cpp
new file mode 100644
index 0000000..cd24c48
--- /dev/null
+++ b/engines/glk/agt/disassemble.cpp
@@ -0,0 +1,317 @@
+/* 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 "glk/agt/agility.h"
+
+namespace Glk {
+namespace AGT {
+
+void dbgprintf(const char *fmt, ...) {
+ va_list vp;
+ char buff[300];
+
+ va_start(vp, fmt);
+ vsprintf(buff, fmt, vp);
+ va_end(vp);
+
+ debugout(buff);
+}
+
+
+static void print_msg(descr_ptr dptr) {
+ int j;
+ descr_line *txt;
+
+ txt = read_descr(dptr.start, dptr.size);
+ if (txt != NULL) {
+ for (j = 0; txt[j] != NULL; j++) {
+ dbgprintf("\n");
+ debugout(txt[j]);
+ }
+ }
+ free_descr(txt);
+}
+
+
+static char *getname(int inum)
+/* Name should be 20 chars or less */
+{
+ if (inum == 0) return rstrdup("* 0 *");
+ return objname(inum);
+}
+
+
+extern integer dobj, iobj, actor;
+
+void print_special_obj(int i)
+/* This is called by the disassembler in agtdbg.c */
+/* i=0 NOUN, 1 OBJECT, 2 NAME */
+{
+ int dval;
+ char *s;
+ switch (i) {
+ case 0:
+ dval = dobj;
+ dbgprintf("NOUN");
+ break;
+ case 1:
+ dval = iobj;
+ dbgprintf("OBJECT");
+ break;
+ case 2:
+ dval = actor;
+ dbgprintf("NAME");
+ break;
+ default:
+ dval = 0; /* Silence compiler warnings. */
+ fatal("INTERNAL ERROR: Invalid *dval* in print_special_obj.");
+ }
+ if (dbgflagptr == NULL)
+ /* This determines whether we are linked with agtout or agil */
+ return;
+ s = getname(dval);
+ dbgprintf("(%d:%s)", dval, s);
+ rfree(s);
+}
+
+#define printval(str,index,ptr) {dbgprintf("[%s%d",str,index);\
+ if (ptr==NULL) dbgprintf("]");\
+ else dbgprintf("=%ld]",(long)ptr[index]);}
+
+int argout(int dtype, int dval, int optype) {
+ char *s;
+
+ if (dtype & AGT_VAR) dtype = AGT_VAR;
+
+ if ((optype & 3) == 1) /* variable */
+ dtype = AGT_VAR;
+ if (optype & 2) { /* NOUN or OBJECT */
+ if (dtype >= 64 && dtype != AGT_NUM)
+ dbgprintf("ILL:");
+ if (optype == 2)
+ print_special_obj(0); /* NOUN */
+ else
+ print_special_obj(1); /* OBJECT */
+ return 0;
+ }
+
+ if (!interp_arg)
+ dbgprintf("%d", dval);
+ else {
+ if (dtype < 64) {
+ if (dval == -1)
+ print_special_obj(2); /* NAME */
+ else {
+ s = getname(dval);
+ if (dtype & (AGT_ITEM | AGT_CREAT | AGT_SELF | AGT_WORN))
+ dbgprintf("<%d:%s>", dval, s);
+ else
+ dbgprintf("{%d:%s}", dval, s);
+ rfree(s);
+ }
+ } else if ((dtype & AGT_VAR) != 0) {
+ if (dval == -1)
+ print_tos();
+ else
+ printval("Var", dval, dbgvarptr);
+ } else switch (dtype) {
+ case AGT_TIME:
+ dbgprintf("%2d:%2d", dval / 100, dval % 100);
+ break;
+ case AGT_NUM: /* Numeric */
+ dbgprintf("%d", dval);
+ break;
+ case AGT_FLAG: /* Flag */
+ printval("Flg", dval, dbgflagptr);
+ break;
+ case AGT_ROOMFLAG: /* Roomflag */
+ dbgprintf("RoomFlag%d", dval);
+ break;
+ case AGT_QUEST: /* Question */
+ if (dval <= MaxQuestion && dval >= 1 && question != NULL) {
+ dbgprintf("\nQ%d:%s\n", dval, question[dval - 1]);
+ dbgprintf("[A:%s]", answer[dval - 1]);
+ } else if (quest_ptr != NULL) {
+ dbgprintf("\nQ%d: ", dval);
+ print_msg(quest_ptr[dval - 1]);
+ dbgprintf("[A:");
+ print_msg(ans_ptr[dval - 1]);
+ }
+ break;
+ case AGT_MSG: /* Message */
+ if (dval > last_message || dval < 1 || msg_ptr == NULL)
+ dbgprintf("ILLEGAL MESSAGE");
+ else {
+ dbgprintf("(Msg%d)", dval);
+ if (!dbg_nomsg)
+ print_msg(msg_ptr[dval - 1]);
+ }
+ break;
+ case AGT_ERR: /* Message */
+ if (dval > NUM_ERR || dval < 1 || err_ptr == NULL)
+ dbgprintf("ILLEGAL MESSAGE");
+ else {
+ dbgprintf("(Std%d)", dval);
+ if (!dbg_nomsg)
+ print_msg(err_ptr[dval - 1]);
+ }
+ break;
+ case AGT_STR: /* String */
+ if (dval - 1 >= MAX_USTR || userstr == NULL)
+ dbgprintf("ILLEGAL STRING");
+ else
+ dbgprintf("\nStr%d:%s", dval, userstr[dval]);
+ break;
+ case AGT_CNT: /* Counter */
+ printval("Cnt", dval, dbgcntptr);
+ break;
+ case AGT_DIR: /* Direction */
+ if (dval >= 1 && dval <= 13)
+ dbgprintf("%s", exitname[dval - 1]);
+ else dbgprintf("ILL_DIR(%d)", dval);
+ break;
+ case AGT_SUB: /* Subroutine */
+ dbgprintf("Subroutine %d", dval);
+ break;
+ case AGT_PIC: /* Picture */
+ case AGT_PIX:
+ dbgprintf("Picture #%d", dval);
+ break;
+ case AGT_FONT: /* Font */
+ dbgprintf("Font #%d", dval);
+ break;
+ case AGT_SONG: /* Song */
+ dbgprintf("Song #%d", dval);
+ break;
+ case AGT_OBJFLAG:
+ dbgprintf("ObjFlag%d", dval);
+ break;
+ case AGT_OBJPROP:
+ dbgprintf("ObjProp%d", dval);
+ break;
+ case AGT_ATTR:
+ if (dval < 0 || dval >= NUM_ATTR)
+ dbgprintf("UnkownAttr%d", dval);
+ else
+ dbgprintf("%s", attrlist[dval].name);
+ break;
+ case AGT_PROP:
+ if (dval < 0 || dval >= NUM_PROP)
+ dbgprintf("UnknownProp%d", dval);
+ else
+ dbgprintf("%s", proplist[dval].name);
+ break;
+ case AGT_EXIT:
+ if (dval >= exitmsg_base)
+ argout(AGT_MSG, dval - exitmsg_base, 0);
+ else
+ argout(AGT_ROOM, dval, 0);
+ break;
+ default:
+ dbgprintf("?+%d", dval);
+ }
+ }
+ return 1;
+}
+
+
+void debug_newline(integer op, rbool first_nl) {
+ rbool early_nl;
+
+ if (!dbg_nomsg) return;
+ early_nl = (op == 1008 || op == 1027 || op == 1083 || op == 1105
+ || (op >= 1126 && op <= 1131));
+ if (early_nl == first_nl)
+ debugout("\n");
+}
+
+
+void debug_cmd_out(int ip, integer op, int arg1, int arg2, int optype) {
+ int j;
+ const opdef *opdata;
+ rbool save_dbg_nomsg;
+
+ dbgprintf(" %2d:", ip);
+ save_dbg_nomsg = 0; /* Just to silence compiler warnings. */
+
+ opdata = get_opdef(op);
+ if (opdata == &illegal_def)
+ dbgprintf("ILLEGAL %d\n", op);
+ else {
+ if (op >= END_ACT) dbgprintf("!"); /* "Terminal" Actions */
+ else if (op <= MAX_COND) dbgprintf("?"); /* Condition */
+ if (op == 1063) { /* RandomMessage needs special handling */
+ save_dbg_nomsg = dbg_nomsg;
+ dbg_nomsg = 1;
+ }
+ dbgprintf("%s", opdata->opcode);
+ for (j = 0; j < opdata->argnum; j++) {
+ dbgprintf("\t");
+ argout(j == 0 ? opdata->arg1 : opdata->arg2 , j == 0 ? arg1 : arg2,
+ optype >> 2);
+ optype <<= 2;
+ }
+ if (op == 1063)
+ dbg_nomsg = save_dbg_nomsg;
+ }
+ debug_newline(op, 1);
+}
+
+
+void debug_head(int i) {
+ int v, w, a;
+
+ v = verb_code(command[i].verbcmd);
+ if (v >= BASE_VERB && v < BASE_VERB + DUMB_VERB && syntbl[synlist[v]] != 0)
+ w = syntbl[synlist[v]];
+ else w = command[i].verbcmd;
+ if (command[i].actor > 0) {
+ dbgprintf("CMD %d: ", i);
+ a = command[i].actor;
+ } else {
+ dbgprintf("REDIR: ");
+ a = -command[i].actor;
+ }
+
+ if (a == 2)
+ dbgprintf("anybody, ");
+ else if (a > 2) {
+ char *name;
+ name = objname(a);
+ name[0] = toupper(name[0]);
+ dbgprintf("%s, ", name);
+ rfree(name);
+ }
+
+ dbgprintf("%s ", w == 0 ? "any" : dict[w]);
+ if (command[i].noun_adj != 0)
+ dbgprintf("%s ", gdict(command[i].noun_adj));
+ dbgprintf("%s %s ", gdict(command[i].nouncmd),
+ (ver == 3) ? gdict(command[i].prep) : "->");
+ if (command[i].obj_adj != 0)
+ dbgprintf("%s ", gdict(command[i].obj_adj));
+ dbgprintf("%s\n", gdict(command[i].objcmd));
+
+}
+
+} // End of namespace AGT
+} // End of namespace Glk
diff --git a/engines/glk/agt/exec.cpp b/engines/glk/agt/exec.cpp
new file mode 100644
index 0000000..983002a
--- /dev/null
+++ b/engines/glk/agt/exec.cpp
@@ -0,0 +1,1375 @@
+/* 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 "glk/agt/agility.h"
+#include "glk/agt/interp.h"
+#include "glk/agt/exec.h"
+
+namespace Glk {
+namespace AGT {
+
+/* This file contains the wrapper for running player commands,
+ routines run at the end of the turn, and various other functions
+ needed by runverb.c and token.c. */
+
+#define global
+
+static rbool pronoun_mode;
+
+word realverb = 0; /* Name of current verb. (normally ~= input[vp])*/
+
+
+/* ------------------------------------------------------------------- */
+/* High level output functions, used for printing messages, error */
+/* messages, and everything else. They call the direct output functions */
+/* in interface.c The reason they're in runverb.c is that they need to */
+/* access item info in order to fill in the blanks */
+
+
+/* This updates the contents of compass_rose, which can be used by the
+ OS layer to print out some sort of representation of which way the
+ player can go. */
+
+static void set_compass_rose(void) {
+ int i, bit;
+
+ compass_rose = 0;
+ if (!islit()) return; /* No compass in darkness */
+ for (i = 0, bit = 1; i < 12; i++, bit <<= 1)
+ if (troom(room[loc].path[i])) compass_rose |= bit;
+}
+
+static void time_out(char *s) {
+ int hr, min;
+
+ hr = curr_time / 100;
+ min = curr_time % 100;
+
+ if (milltime_mode)
+ sprintf(s, "%02d:%02d", hr, min);
+ else {
+ if (hr > 12) hr = hr - 12;
+ if (hr == 0) hr = 12;
+ sprintf(s, "%2d:%02d %s", hr, min, (curr_time >= 1200) ? "pm" : "am");
+ }
+}
+
+
+
+
+
+void set_statline() {
+ char timestr[20];
+
+ recompute_score();
+ set_compass_rose();
+
+ rstrncpy(l_stat, room[loc].name, 81);
+
+ time_out(timestr);
+
+ switch (statusmode) {
+ case 0:
+ sprintf(r_stat, "Score: %ld Moves: %d", tscore, turncnt);
+ break;
+ case 1:
+ sprintf(r_stat, "Score: %ld %s", tscore, timestr);
+ break;
+ case 2:
+ sprintf(r_stat, "Moves: %d", turncnt);
+ break;
+ case 3:
+ sprintf(r_stat, "%s", timestr);
+ break;
+ case 4:
+ r_stat[0] = '\0';
+ break; /* 'Trinity style' status line */
+ case 5:
+ sprintf(r_stat, "Score: %ld", tscore);
+ break;
+ }
+}
+
+
+/* -------------------------------------------------------------------- */
+/* Message printing / $ substitution Routines */
+/* -------------------------------------------------------------------- */
+
+#define FILL_SIZE 100
+
+
+/* Tries to convert *pstr to a number, which it returns.
+ If it fails, or if the number is not in the range 0..maxval,
+ it returns -1.
+ It advances *pstr to point after the number and after the
+ terminating character, if relevant.
+ <term_char> is the terminating character; if this is 0, then
+ the calling routine will worry about the terminating character.
+ <maxval> of 0 indicates no upper bound
+*/
+
+static int extract_number(const char **pstr, int maxval,
+ char term_char) {
+ const char *s;
+ long n; /* n holds the value to be returned; i holds
+ the number of characters parsed. */
+ n = 0;
+ s = *pstr;
+ while (*s == ' ' || *s == '\t') s++;
+ for (; *s != 0; s++) {
+ if (*s < '0' || *s > '9') break;
+ n = 10 * n + (*s - '0');
+ if (maxval && n > maxval) return -1;
+ }
+ if (term_char) {
+ if (*s == term_char) s++;
+ else return -1;
+ }
+ *pstr = s;
+ return n;
+}
+
+#define BAD_PROP (-1000)
+
+/* This is used by #PROP[obj].[prop]# and $ATTR[obj].[attr]$, etc. */
+/* isprop: Are we looking for a property (as opposed to an attribute)? */
+static void extract_prop_val(const char **pstr,
+ int *id, int *val,
+ rbool isprop, const char term_char) {
+ const char *s;
+ int v; /* object number / final value */
+ int i; /* Attribute id */
+ rbool builtin; /* Expect builtin property or attribute? */
+
+ *id = i = BAD_PROP;
+ *val = 0; /* Failure case by default */
+ builtin = 0;
+ s = *pstr;
+ if (match_str(&s, "NOUN")) v = dobj;
+ else if (match_str(&s, "OBJECT")) v = iobj;
+ else v = extract_number(&s, maxcreat, 0); /* Must be object number */
+ while (*s == '.') {
+ s++;
+ if (*s == '-') {
+ builtin = 1;
+ s++;
+ } else
+ builtin = 0;
+ i = extract_number(&s, 0, 0);
+ if (!troom(v) && !tnoun(v) && !tcreat(v)) {
+ i = -1;
+ continue;
+ }
+ if (isprop || *s == '.') /* Treat as property */
+ v = builtin ? getprop(v, i) : op_objprop(2, v, i, 0);
+ else /* Treat as attribute */
+ v = builtin ? getattr(v, i) : op_objflag(2, v, i);
+ }
+ if (*s != term_char) return;
+ *pstr = s + 1;
+ if (i < 0) return;
+ *id = builtin ? -1 : i;
+ *val = v;
+}
+
+
+
+
+static word it_pronoun(int item, rbool ind_form)
+/* Return the correct pronoun to go with item;
+ ind_form is 1 if we want the object form, 0 if we want the
+ subject form */
+{
+ if (it_plur(item))
+ return (ind_form ? ext_code[wthem] : ext_code[wthey]);
+ if (tcreat(item))
+ switch (creature[item - first_creat].gender) {
+ case 0:
+ return ext_code[wit];
+ case 1:
+ return (ind_form ? ext_code[wher] : ext_code[wshe]);
+ case 2:
+ return (ind_form ? ext_code[whim] : ext_code[whe]);
+ }
+ return ext_code[wit];
+}
+
+/* This sets the value of "The" for the given noun. */
+/* (In particular, proper nouns shouldn't have a "the") */
+static void theset(char *buff, int item) {
+ if (it_proper(item))
+ strcpy(buff, "");
+ else
+ strcpy(buff, "the ");
+}
+
+
+static void num_name_func(parse_rec *obj_rec, char *fill_buff, word prev_adj)
+/* This is a subroutine to wordcode_match. */
+/* It gives either a noun name or a number, depending. */
+/* prev_adj is a word if this was preceded by its associated $adjective$;
+ the goal is to avoid having $adjective$ $noun$ expand to (e.g.)
+ 'silver silver' when the player types in "get silver" to pick up
+ a magic charm with synonym 'silver'. */
+{
+ word w;
+
+ if (obj_rec == NULL) {
+ strcpy(fill_buff, "");
+ return;
+ }
+
+ w = 0;
+ if (obj_rec->noun != 0) w = obj_rec->noun;
+ if ((w == 0 || w == prev_adj) && obj_rec->obj != 0)
+ w = it_name(obj_rec->obj);
+
+ if (w == 0) {
+ if (obj_rec->info == D_NUM) sprintf(fill_buff, "%ld", (long)obj_rec->num);
+ else strcpy(fill_buff, "");
+#if 0
+ strcpy(fill_buff, "that"); /* We can try and hope */
+#endif
+ return;
+ }
+
+ if (w == prev_adj) /* ... and prev_adj!=0 but we don't need to explicity
+ test that since w!=0 */
+ fill_buff[0] = 0; /* i.e. an empty string */
+ else {
+ rstrncpy(fill_buff, dict[w], FILL_SIZE);
+ if (it_proper(obj_rec->obj)) fill_buff[0] = toupper(fill_buff[0]);
+ }
+}
+
+static word get_adj(parse_rec *obj_rec, char *buff) {
+ word w;
+
+ if (obj_rec->adj != 0) w = obj_rec->adj;
+ else w = it_adj(obj_rec->obj);
+
+ if (w == 0) strcpy(buff, "");
+ else {
+ rstrncpy(buff, dict[w], FILL_SIZE);
+ if (it_proper(obj_rec->obj)) buff[0] = toupper(buff[0]);
+ }
+
+ return w;
+}
+
+
+
+#define d2buff(i) {rstrncpy(fill_buff,dict[i],FILL_SIZE);return 1;}
+#define num_name(obj_rec,jsa) {num_name_func(obj_rec,fill_buff,jsa);return 1;}
+/* jsa= Just seen adj */
+#define youme(mestr,youstr) {strcpy(fill_buff,irun_mode?mestr:youstr);\
+ return 1;}
+
+word just_seen_adj; /* This determines if we just saw $adjective$; if so,
+ this is set to it, otherwise it is zero. See
+ num_name_func above. */
+
+static int wordcode_match(const char **pvarname, char *fill_buff,
+ int context, const char *pword)
+/* Check <*p*pvarname> for a match; put subs text in fill_buf
+ <context> indicates who called us; this determines
+ what substitutions are valid. See interp.h for possible
+ values. Move *p*pvarname after whatever is matched.
+ <pword> contains the parse word when context is MSG_PARSE. */
+/* $ forms:
+ $verb$, $noun$, $adjective$, $prep$, $object$, $name$,
+ $n_pro$, $o_pro$, $n_indir$, $o_indir$,
+ $name_pro$, $name_indir$
+ $n_is$, $o_is$, $name_is$
+ $c_name$
+ $n_was$, $o_was$, $name_was$
+ $the_n$, $the_o$, $the_name$
+ */
+/* Also $STRn$, $FLAGn$, $ONn$, $OPENn$, $LOCKEDn$ */
+/* Support for FLAG, ON, OPEN, and LOCKED added by Mitch Mlinar */
+/* Return 0 if no match, 1 if there is */
+{
+ int hold_val, hold_id;
+
+ fill_buff[0] = 0; /* By default, return "\0" string */
+ if (match_str(pvarname, "STR")) { /* String variable */
+ hold_id = extract_number(pvarname, MAX_USTR, '$');
+ if (hold_id < 1) return 0;
+ rstrncpy(fill_buff, userstr[hold_id - 1], FILL_SIZE);
+ return 1;
+ } else if (match_str(pvarname, "VAR")) {
+ hold_id = extract_number(pvarname, VAR_NUM, '$');
+ if (hold_id < 0) return 0;
+ hold_val = agt_var[hold_id];
+ rstrncpy(fill_buff,
+ get_objattr_str(AGT_VAR, hold_id, hold_val), FILL_SIZE);
+ return 1;
+ } else if (match_str(pvarname, "FLAG")) {
+ hold_id = extract_number(pvarname, FLAG_NUM, '$');
+ if (hold_id < 0) return 0;
+ rstrncpy(fill_buff,
+ get_objattr_str(AGT_FLAG, hold_id, flag[hold_id]), FILL_SIZE);
+ return 1;
+ } else if (match_str(pvarname, "ATTR")) {
+ extract_prop_val(pvarname, &hold_id, &hold_val, 0, '$');
+ if (hold_id == BAD_PROP) return 1;
+ rstrncpy(fill_buff,
+ get_objattr_str(AGT_OBJFLAG, hold_id, hold_val), FILL_SIZE);
+ return 1;
+ } else if (match_str(pvarname, "PROP")) {
+ extract_prop_val(pvarname, &hold_id, &hold_val, 1, '$');
+ if (hold_id == BAD_PROP) return 1;
+ rstrncpy(fill_buff,
+ get_objattr_str(AGT_OBJPROP, hold_id, hold_val), FILL_SIZE);
+ return 1;
+ } else if (match_str(pvarname, "OPEN")) {
+ hold_val = extract_number(pvarname, maxnoun, '$');
+ strcpy(fill_buff, it_open(hold_val) ? "open" : "closed");
+ return 1;
+ } else if (match_str(pvarname, "ON")) {
+ hold_val = extract_number(pvarname, maxnoun, '$');
+ strcpy(fill_buff, it_on(hold_val) ? "on" : "off");
+ return 1;
+ } else if (match_str(pvarname, "LOCKED")) {
+ hold_val = extract_number(pvarname, maxnoun, '$');
+ strcpy(fill_buff, it_locked(hold_val, 0) ? "locked" : "unlocked");
+ return 1;
+ }
+
+ if (context == MSG_MAIN) return 0;
+
+ if (context == MSG_PARSE) {
+ /* The only special subsitution allowed is $word$. */
+ if (match_str(pvarname, "WORD$")) {
+ if (pword == NULL) fill_buff[0] = 0;
+ else rstrncpy(fill_buff, pword, FILL_SIZE);
+ return 1;
+ } else return 0;
+ }
+
+ /* d2buff is a macro that returns 1 */
+ if (match_str(pvarname, "NOUN$"))
+ num_name(dobj_rec, just_seen_adj);
+ just_seen_adj = 0; /* It doesn't matter. */
+ if (match_str(pvarname, "VERB$"))
+ d2buff(realverb); /* auxsyn[vb][0] */
+ if (match_str(pvarname, "OBJECT$"))
+ num_name(iobj_rec, 0);
+ if (match_str(pvarname, "NAME$"))
+ num_name(actor_rec, 0);
+ if (match_str(pvarname, "ADJECTIVE$")) {
+ just_seen_adj = get_adj(dobj_rec, fill_buff);
+ return 1;
+ }
+ if (match_str(pvarname, "PREP$"))
+ d2buff(prep);
+ if (match_str(pvarname, "N_PRO$"))
+ d2buff(it_pronoun(dobj, 0));
+ if (match_str(pvarname, "O_PRO$"))
+ d2buff(it_pronoun(iobj, 0));
+ if (match_str(pvarname, "NAME_PRO$"))
+ d2buff(it_pronoun(actor, 0));
+ if (match_str(pvarname, "N_INDIR$"))
+ d2buff(it_pronoun(dobj, 1));
+ if (match_str(pvarname, "O_INDIR$"))
+ d2buff(it_pronoun(iobj, 1));
+ if (match_str(pvarname, "NAME_INDIR$"))
+ d2buff(it_pronoun(actor, 1));
+ if (match_str(pvarname, "N_IS$")) {
+ if (!it_plur(dobj)) d2buff(ext_code[wis])
+ else d2buff(ext_code[ware]);
+ }
+ if (match_str(pvarname, "O_IS$")) {
+ if (!it_plur(iobj)) d2buff(ext_code[wis])
+ else d2buff(ext_code[ware]);
+ }
+ if (match_str(pvarname, "NAME_IS$")) {
+ if (!it_plur(actor)) d2buff(ext_code[wis])
+ else d2buff(ext_code[ware]);
+ }
+
+ if (match_str(pvarname, "N_WAS$")) {
+ if (!it_plur(dobj)) d2buff(ext_code[wwas])
+ else d2buff(ext_code[wwere]);
+ }
+ if (match_str(pvarname, "O_WAS$")) {
+ if (!it_plur(iobj)) d2buff(ext_code[wwas])
+ else d2buff(ext_code[wwere]);
+ }
+ if (match_str(pvarname, "NAME_WAS$")) {
+ if (!it_plur(actor)) d2buff(ext_code[wwas])
+ else d2buff(ext_code[wwere]);
+ }
+ if (match_str(pvarname, "THE_N$")) {
+ theset(fill_buff, dobj);
+ return 1;
+ }
+ if (match_str(pvarname, "THE_O$")) {
+ theset(fill_buff, iobj);
+ return 1;
+ }
+ if (match_str(pvarname, "THE_NAME$")) {
+ theset(fill_buff, actor);
+ return 1;
+ }
+ if (match_str(pvarname, "THE_C$")) {
+ theset(fill_buff, curr_creat_rec->obj);
+ return 1;
+ }
+ if (match_str(pvarname, "C_NAME$"))
+ num_name(curr_creat_rec, 0);
+ if (match_str(pvarname, "TIME$")) {
+ time_out(fill_buff);
+ return 1;
+ }
+
+ if (pronoun_mode && match_str(pvarname, "YOU$"))
+ youme("I", "you");
+ if (pronoun_mode && match_str(pvarname, "ARE$"))
+ youme("am", "are");
+ if (pronoun_mode && match_str(pvarname, "YOU_OBJ$"))
+ youme("me", "you");
+ if (pronoun_mode && match_str(pvarname, "YOUR$"))
+ youme("my", "your");
+ if (pronoun_mode && match_str(pvarname, "YOU'RE$"))
+ youme("i'm", "you're");
+ return 0; /* Don't recognize $word$ */
+}
+
+
+
+static int capstate(const char *varname) {
+ if (islower(varname[0])) return 0; /* $word$ */
+ if (islower(varname[1])) return 2; /* $Word$ */
+ if (!isalpha(varname[1]) && varname[1] != 0
+ && islower(varname[2])) return 2;
+ else return 1; /* $WORD$ */
+}
+
+static char fill_buff[FILL_SIZE]; /* Buffer to hold returned string */
+
+static char *wordvar_match(const char **pvarname, char match_type,
+ int context, const char *pword)
+/* Match_type=='#' for variables, '$' for parsed words */
+/* Possible # forms: #VARn#, #CNTn# */
+/* See above for $ forms */
+/* Moves *pvarname to point after matched object */
+{
+ int i, hold_val, hold_prop;
+ const char *start;
+
+ start = *pvarname;
+ if (match_type == '$') {
+ i = wordcode_match(pvarname, fill_buff, context, pword);
+ if (i == 0) return NULL;
+ /* Now need to fix capitalization */
+ switch (capstate(start)) {
+ case 0:
+ break; /* $word$ */
+ case 1: /* $WORD$ */
+ for (i = 0; fill_buff[i] != '\0'; i++)
+ fill_buff[i] = toupper(fill_buff[i]);
+ break;
+ case 2: /* $Word$ */
+ fill_buff[0] = toupper(fill_buff[0]);
+ break;
+ }
+ } else { /* So match type is '#' */
+ if (match_str(pvarname, "VAR")) {
+ hold_val = extract_number(pvarname, VAR_NUM, '#');
+ if (hold_val < 0) return NULL;
+ hold_val = agt_var[hold_val];
+ } else if (match_str(pvarname, "CNT") ||
+ match_str(pvarname, "CTR")) {
+ hold_val = extract_number(pvarname, CNT_NUM, '#');
+ if (hold_val < 0) return NULL;
+ hold_val = cnt_val(agt_counter[hold_val]);
+ } else if (match_str(pvarname, "PROP")) {
+ extract_prop_val(pvarname, &hold_prop, &hold_val, 1, '#');
+ if (hold_prop == BAD_PROP) hold_val = 0;
+ } else
+ return NULL;
+
+ /* Now to convert hold_val into a string */
+ sprintf(fill_buff, "%d", hold_val);
+
+ }
+ return fill_buff;
+}
+
+static char *format_line(const char *s, int context, const char *pword)
+/* Do $word$ substituations; return the result */
+{
+ char *t; /* The new string after all the substitutions. */
+ int t_size; /* How much space has been allocated for it. */
+ const char *p, *oldp; /* Pointer to the original string */
+ int i;
+ char *fill_word, *q; /* Word used to fill in the blanks, and a pointer
+ used to iterate through it*/
+ char fill_type; /* '#'=#variable#, '$'=$word$ */
+
+ /* Need to do subsitutions and also correct for tabs */
+ t_size = 200;
+ t = (char *)rmalloc(t_size + FILL_SIZE + 10);
+ just_seen_adj = 0;
+
+ /* Note that I leave some margin here: t is 310 characters, but i will never
+ be allowed above around 200. This is to avoid having to put special
+ checking code throughout the following to make sure t isn't overrun */
+ for (p = s, i = 0; *p != '\0'; p++) {
+ if (i >= t_size) {
+ t_size = i + 100;
+ t = (char *)rrealloc(t, t_size + FILL_SIZE + 10);
+ }
+ if (!rspace(*p) && *p != '$')
+ just_seen_adj = 0;
+ if (*p == '$' || *p == '#') {
+ /* Read in $word$ or #var# and do substitution */
+ fill_type = *p;
+ oldp = p++; /* Save old value in case we are wrong and then
+ increment p */
+ fill_word = wordvar_match(&p, fill_type, context, pword);
+ if (fill_word == NULL) {
+ /*i.e. no match-- so just copy it verbatim */
+ t[i++] = fill_type;
+ just_seen_adj = 0;
+ p = oldp; /* Go back and try again... */
+ } else { /* Fill in word */
+ p--;
+ if (fill_word[0] == '\0') { /* Empty string */
+ /* We need to eliminate a 'double space' in this case */
+ if ((oldp == s || rspace(*(oldp - 1))) && rspace(*(p + 1)))
+ p++;
+ } else /* Normal case */
+ for (q = fill_word; *q != '\0';)
+ t[i++] = *q++;
+ }
+ } /* End $/# matcher */
+ else
+ t[i++] = *p;
+ } /* End scanning loop */
+
+ if (aver < AGX00 && i > 0 && t[i - 1] == ' ') {
+ /* For pre-Magx, delete trailing spaces */
+ do
+ i--;
+ while (i > 0 && t[i] == ' ');
+ i++;
+ }
+ t[i] = 0;
+ t = (char *)rrealloc(t, i + 1);
+ return t;
+}
+
+void raw_lineout(const char *s, rbool do_repl, int context, const char *pword) {
+ char *outstr;
+
+ if (do_repl) {
+ outstr = format_line(s, context, pword);
+ writestr(outstr);
+ rfree(outstr);
+ } else
+ writestr(s);
+}
+
+
+static void lineout(const char *s, rbool nl, int context, const char *pword) {
+ raw_lineout(s, 1, context, pword);
+ if (nl) writeln("");
+ else writestr(" ");
+}
+
+static void gen_print_descr(descr_ptr dp_, rbool nl,
+ int context, const char *pword) {
+ int j;
+ descr_line *txt;
+
+ agt_textcolor(7);
+ textbold = 0;
+ agt_par(1);
+ txt = read_descr(dp_.start, dp_.size);
+ if (txt != NULL)
+ for (j = 0; txt[j] != NULL; j++)
+ lineout(txt[j], nl || (txt[j + 1] != NULL), context, pword);
+ free_descr(txt);
+ agt_par(0);
+ agt_textcolor(7);
+ textbold = 0;
+}
+
+void print_descr(descr_ptr dp_, rbool nl) {
+ gen_print_descr(dp_, nl, MSG_DESC, NULL);
+}
+
+void quote(int msgnum) {
+ char **qptr;
+ descr_line *txt;
+ int i;
+ int len;
+
+ txt = read_descr(msg_ptr[msgnum - 1].start, msg_ptr[msgnum - 1].size);
+ if (txt != NULL) {
+ for (len = 0; txt[len] != NULL; len++);
+ qptr = (char **)rmalloc(len * sizeof(char *));
+ for (i = 0; i < len; i++)
+ qptr[i] = format_line(txt[i], MSG_DESC, NULL);
+ free_descr(txt);
+ textbox(qptr, len, TB_BORDER | TB_CENTER);
+ rfree(qptr);
+ }
+}
+
+
+void msgout(int msgnum, rbool add_nl) {
+ print_descr(msg_ptr[msgnum - 1], add_nl);
+}
+
+
+#define MAX_NUM_ERR 240 /* Highest numbered STANDARD message */
+#define OLD_MAX_STD_MSG 185
+
+/* Fallback messages should always have msgid less than the original */
+int stdmsg_fallback[MAX_NUM_ERR - OLD_MAX_STD_MSG] = {
+ 0, 0, 0, 12, 0, /* 186 - 190 */
+ 0, 0, 0, 0, 0, /* 191 - 195 */
+ 0, 13, 13, 5, 10, /* 196 - 200 */
+ 10, 61, 10, 16, 59, /* 201 - 205 */
+ 90, 107, 116, 135, 140, /* 206 - 210 */
+ 184, 3, 47, 185, 61, /* 211 - 215 */
+ 0, 0, 0, 0, 0, /* 216 - 220 */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 221 - 230 */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 /* 231 - 240 */
+};
+
+void gen_sysmsg(int msgid, const char *s, int context, const char *pword)
+/* Prints either STANDARD message number <msgid> or default msg <s>;
+ A <msgid> of 0 means there is no standard message counterpart.
+ <context> determines what $$ substitutions are meaningful
+ <parseword> gives the $pword$ substitution for MSG_PARSE messages
+ msgid 3 should probably *not* be redirected to avoid giving hints to
+ the player as to what nouns exist in the game.
+*/
+{
+ /* Use gamefile's redefined version of message? */
+ rbool use_game_msg;
+ rbool nl; /* Should it be followed by a newline? */
+
+ nl = 1; /* By default, follow message with newline */
+
+ /* The following msgids shouldn't be followed by newlines: */
+ if (msgid == 1 || msgid == 145 || (msgid >= 218 && msgid <= 223)
+ || msgid == 225)
+ nl = 0;
+
+ if (DEBUG_SMSG) rprintf("\nSTD %d", msgid);
+
+ use_game_msg = ((PURE_SYSMSG || s == NULL)
+ && msgid != 0 && msgid <= NUM_ERR
+ && err_ptr != NULL);
+
+ if (use_game_msg) {
+ /* Check for fall-back messages */
+ if (err_ptr[msgid - 1].size <= 0
+ && msgid > OLD_MAX_STD_MSG && msgid <= MAX_NUM_ERR) {
+ msgid = stdmsg_fallback[msgid - OLD_MAX_STD_MSG - 1];
+ if (DEBUG_SMSG) rprintf("==> %3d");
+ }
+ if (msgid != 0 && err_ptr[msgid - 1].size > 0) {
+ if (DEBUG_SMSG) rprintf(" : From gamefile\n");
+ gen_print_descr(err_ptr[msgid - 1], nl, context, pword);
+ } else use_game_msg = 0;
+ }
+
+ if (DEBUG_SMSG && !use_game_msg) rprintf(" : Default\n");
+
+ if (!use_game_msg) {
+ /* Either the game doesn't redefine the message, or we're ignoring
+ redefinitions */
+ if (s == NULL) return;
+ pronoun_mode = 1;
+ lineout(s, nl, context, pword);
+ pronoun_mode = !PURE_PROSUB;
+ }
+}
+
+
+void sysmsg(int msgid, const char *s) {
+ gen_sysmsg(msgid, s, MSG_RUN, NULL);
+}
+
+
+void alt_sysmsg(int msgid, const char *s, parse_rec *new_dobjrec, parse_rec *new_iobjrec) {
+ parse_rec *save_dobjrec, *save_iobjrec;
+ integer save_dobj, save_iobj;
+
+ save_dobj = dobj;
+ save_dobjrec = dobj_rec;
+ dobj = p_obj(new_dobjrec);
+ dobj_rec = new_dobjrec;
+
+ save_iobj = iobj;
+ save_iobjrec = iobj_rec;
+ iobj = p_obj(new_iobjrec);
+ iobj_rec = new_iobjrec;
+
+ gen_sysmsg(msgid, s, MSG_RUN, NULL);
+
+ dobj = save_dobj;
+ dobj_rec = save_dobjrec;
+ iobj = save_iobj;
+ iobj_rec = save_iobjrec;
+}
+
+
+void sysmsgd(int msgid, const char *s, parse_rec *new_dobjrec)
+/* Front end for sysmsg w/alternative direct object */
+{
+ alt_sysmsg(msgid, s, new_dobjrec, NULL);
+}
+
+
+/* -------------------------------------------------------------------- */
+/* QUESTION and ANSWER processing */
+/* -------------------------------------------------------------------- */
+
+
+static char *match_string(char *ans, char *corr_ans, int n)
+/* Searches for s (w/ surrounding whitespace removed) inside ans */
+/* looking at only n characters of s */
+{
+ char *s;
+ char *corr;
+ int i;
+
+ s = rstrdup(corr_ans);
+ for (i = n - 1; i > 0 && isspace(s[i]); i--); /* Kill trailing whitespace */
+ s[i + 1] = 0;
+ for (i = 0; s[i] != 0; i++) s[i] = tolower(s[i]);
+ for (i = 0; isspace(s[i]); i++); /* Kill leading whitespace */
+ corr = strstr(ans, s + i);
+ rfree(s);
+ return corr;
+}
+
+
+static rbool check_answer(char *ans, long start, long size)
+/* qnum has already been fixed to start from 0 */
+/* Master's edition answer checker. Master's edition answers can */
+/* be seperate by AND and OR characters. If there is one OR in the */
+/* answer, all ANDs will also be treated as ORs */
+/* Furthermore, AND-delimited strings must appear in the correct order */
+/* unless PURE_ANSWER is false */
+{
+ char *corr, *corr2; /* Pointer into answer to match correct answers */
+ int match_mode; /* 0=AND mode, 1=OR mode */
+ descr_line *astr; /* Holds the answer string */
+ int i; /* Index to line of astr we're on. */
+ char *p, *q, *r; /* Used to break astr up into pieces and
+ loop over them */
+
+ astr = read_descr(start, size);
+ if (astr == NULL) {
+ if (!PURE_ERROR)
+ writeln("GAME ERROR: Empty answer field.");
+ return 1;
+ }
+
+ match_mode = 0;
+ for (i = 0; astr[i] != NULL; i++)
+ if (strstr(astr[i], "OR") != NULL) {
+ match_mode = 1;
+ break;
+ }
+
+ corr = ans;
+ for (i = 0; astr[i] != NULL; i++) { /* loop over all lines of the answer */
+ p = astr[i];
+ do {
+ q = strstr(p, "OR");
+ r = strstr(p, "AND");
+ if (q == NULL || (r != NULL && r < q)) q = r;
+ if (q == NULL) q = p + strlen(p); /* i.e. points at the concluding null */
+ corr2 = match_string(corr, p, q - p);
+ if (corr2 == NULL && match_mode == 0) {
+ free_descr(astr);
+ return 0;
+ }
+ if (corr2 != NULL && match_mode == 1) {
+ free_descr(astr);
+ return 1;
+ }
+ if (PURE_ANSWER && match_mode == 0) corr = corr2;
+ if (*q == 'O') p = q + 2;
+ else if (*q == 'A') p = q + 3;
+ else assert(*q == 0);
+ } while (*q != 0);
+ }
+ free_descr(astr);
+ if (match_mode == 0) return 1; /* AND: Matched them all */
+ else return 0; /* OR: Didn't find a single match */
+}
+
+
+/* Does the answer in string ans match answer anum? */
+/* Warning: this changes and then rfrees ans */
+rbool match_answer(char *ans, int anum) {
+ char *corr;
+ rbool ans_corr;
+
+ for (corr = ans; *corr != 0; corr++)
+ *corr = tolower(*corr);
+ if (answer != NULL) {
+ /* corr=strstr(ans,answer[anum]); */
+ corr = match_string(ans, answer[anum], strlen(answer[anum]));
+ rfree(ans);
+ if (corr == NULL) return 0;
+ } else if (ans_ptr != NULL) {
+ ans_corr = check_answer(ans, ans_ptr[anum].start, ans_ptr[anum].size);
+ rfree(ans);
+ return ans_corr;
+ } else writeln("INT ERR: Invalid answer pointer.");
+ return 1;
+
+}
+
+
+rbool ask_question(int qnum)
+/* 1=got it right, 0=got it wrong */
+{
+ char *ans;
+
+ qnum--;
+
+ /* Now actually ask the question and compare the answers */
+ if (question != NULL)
+ writeln(question[qnum]);
+ else if (quest_ptr != NULL)
+ print_descr(quest_ptr[qnum], 1);
+ else {
+ writeln("INT ERR: Invalid question pointer");
+ return 1;
+ }
+ ans = agt_readline(2);
+ return match_answer(ans, qnum);
+}
+
+
+/* -------------------------------------------------------------------- */
+/* Miscellaneous support routines */
+/* -------------------------------------------------------------------- */
+
+long read_number(void) {
+ char *s, *err;
+ long n;
+
+ n = 1;
+ do {
+ if (n != 1) gen_sysmsg(218, "Please enter a *number*. ", MSG_MAIN, NULL);
+ s = agt_readline(1);
+ n = strtol(s, &err, 10);
+ if (err == s) err = NULL;
+ rfree(s);
+ } while (err == NULL);
+ return n;
+}
+
+
+
+
+void runptr(int i, descr_ptr dp_[], const char *msg, int msgid,
+ parse_rec *nounrec, parse_rec *objrec)
+/* Prints out description unless it doesn't exist, in which
+ case it prints out either system message #msgid or the message
+ contained in msg. */
+{
+ if (dp_[i].size > 0) print_descr(dp_[i], 1);
+ else alt_sysmsg(msgid, msg, nounrec, objrec);
+}
+
+
+
+/* Score modes:
+ S:Score, R:Room +=list '(out of..'), -=don't list at all.
+ 0-- S+ R+
+ 1-- S+ R
+ 2-- S R+
+ 3-- S R
+ 4-- S+ R-
+ 5-- S R-
+ 6-- S- R+
+ 7-- S- R
+ 8-- S- R- and disable SCORE.
+ */
+
+
+void print_score(void) {
+ char s[80];
+ int i, rmcnt, totroom;
+
+ if (score_mode < 5) {
+ if (score_mode == 0 || score_mode == 1 || score_mode == 4)
+ sprintf(s, "Your score is %ld (out of %ld possible).", tscore, max_score);
+ else sprintf(s, "Your score is %ld.", tscore);
+ writeln(s);
+ }
+
+ if (score_mode < 4 || score_mode == 6 || score_mode == 7) {
+ rmcnt = 0;
+ totroom = 0;
+ for (i = 0; i <= maxroom - first_room; i++)
+ if (!room[i].unused) {
+ if (room[i].seen) rmcnt++;
+ /* Should really compute this once at the beginning, but */
+ /* I don't want to add yet another global variable, particulary */
+ /* since this is only used here. */
+ totroom++;
+ }
+ if (score_mode % 2 == 0)
+ sprintf(s, "You have visited %d locations (out of %d in the game)", rmcnt,
+ totroom);
+ else sprintf(s, "You have visited %d locations.", rmcnt);
+ writeln(s);
+ }
+}
+
+
+int normalize_time(int tnum) { /* Convert hhmm so mm<60 */
+ int min, hr;
+
+ min = tnum % 100; /* The minutes */
+ hr = tnum / 100; /* The hours */
+ hr += min / 60;
+ min = min % 60;
+ while (hr < 0) hr += 24;
+ hr = hr % 24;
+ return hr * 100 + min;
+}
+
+
+void add_time(int dt) {
+ int min, hr;
+
+ min = curr_time % 100; /* The minutes */
+ hr = curr_time / 100; /* The hours */
+ if (aver == AGT183) min += dt; /* AGT 1.83 version */
+ else { /* Normal version */
+ min += dt % 100;
+ hr += dt / 100;
+ }
+ while (min < 0) {
+ min = min + 60;
+ hr++;
+ }
+ hr += min / 60;
+ min = min % 60;
+ while (hr < 0) hr += 24;
+ hr = hr % 24;
+ curr_time = hr * 100 + min;
+}
+
+
+void look_room(void) {
+ compute_seen();
+ writeln("");
+ if (islit()) {
+ if (room[loc].name != NULL && room[loc].name[0] != 0 &&
+ (!PURE_ROOMTITLE)) {
+ agt_textcolor(-1); /* Emphasized text on */
+ writestr(room[loc].name);
+ agt_textcolor(-2);
+ writeln("");
+ } /* Emphasized text off */
+ if (room_firstdesc && room[loc].initdesc != 0)
+ msgout(room[loc].initdesc, 1);
+ else if (room_ptr[loc].size > 0)
+ print_descr(room_ptr[loc], 1);
+ print_contents(loc + first_room, 1);
+ if (listexit_flag)
+ v_listexit();
+ } else
+ sysmsg(room[loc].light == 1 ? 6 : 7,
+ "It is dark. $You$ can't see anything.");
+ room_firstdesc = 0;
+ do_look = 0;
+}
+
+
+static void run_autoverb(void) {
+ int v0; /* Will hold the verb number of the autoverb */
+ int savevb;
+ integer saveactor, savedobj, saveiobj;
+ parse_rec *save_actor_rec, *save_dobj_rec, *save_iobj_rec;
+ word saveprep;
+
+
+ beforecmd = 1;
+
+ /* This is the penalty for vb, actor, etc being global variables. */
+ savevb = vb;
+ saveactor = actor;
+ savedobj = dobj;
+ saveprep = prep;
+ saveiobj = iobj;
+ save_actor_rec = copy_parserec(actor_rec);
+ save_dobj_rec = copy_parserec(dobj_rec);
+ save_iobj_rec = copy_parserec(iobj_rec);
+
+ if (room[loc].autoverb != 0) {
+ v0 = verb_code(room[loc].autoverb);
+ (void)scan_metacommand(0, v0, 0, 0, 0, NULL);
+ }
+ free_all_parserec();
+ vb = savevb;
+ actor = saveactor;
+ dobj = savedobj;
+ iobj = saveiobj;
+ actor_rec = save_actor_rec;
+ dobj_rec = save_dobj_rec;
+ iobj_rec = save_iobj_rec;
+ prep = saveprep;
+}
+
+
+
+/* ------------------------------------------------------------------- */
+/* MAIN COMMAND EXECUTION ROUTINES-- */
+/* These routines handle the execution of player commands */
+/* Then they change the status line, update counters, etc. */
+/* ------------------------------------------------------------------- */
+
+static void creat_initdesc(void) {
+ int i;
+
+ creatloop(i)
+ if (creature[i].location == loc + first_room &&
+ creature[i].initdesc != 0) {
+ msgout(creature[i].initdesc, 1);
+ creature[i].initdesc = 0;
+ }
+}
+
+/* Print out picture names, remember to put intro before first one. */
+/* This should be called with s==NULL before and after:
+ before to reset it, after to put the trailing newline on. */
+void listpictname(const char *s) {
+ static rbool first_pict = 1; /* True until we output first picture */
+
+ if (s == NULL) {
+ if (!first_pict) writeln(""); /* Trailing newline */
+ first_pict = 1;
+ return;
+ }
+ if (first_pict) {
+ writeln(""); /* Skip a line */
+ sysmsg(219, " Illustrations:");
+ first_pict = 0;
+ }
+ writestr(" ");
+ writestr(s);
+}
+
+
+void listpict(int obj) {
+ char *s;
+
+ if (it_pict(obj) != 0) {
+ s = objname(obj);
+ listpictname(s);
+ rfree(s);
+ }
+}
+
+
+void list_viewable(void)
+/* List pictures that can be viewed, if any */
+{
+ int i;
+
+ listpictname(NULL);
+
+ if (room[loc].pict != 0)
+ listpictname("scene");
+ contloop(i, 1)
+ listpict(i);
+ contloop(i, 1000)
+ listpict(i);
+ contloop(i, loc + first_room)
+ listpict(i);
+
+ for (i = 0; i < maxpix; i++)
+ if (room[loc].PIX_bits & (1L << i))
+ listpictname(dict[pix_name[i]]);
+ listpictname(NULL);
+}
+
+
+
+void newroom(void) {
+ rbool save_do_look;
+ integer prevloc;
+
+ do {
+ save_do_look = do_look;
+ if (do_look == 1) look_room();
+ creat_initdesc();
+ if (save_do_look == 1 && aver >= AGTME10)
+ list_viewable(); /* Print out picts that can be viewed here. */
+ do_look = 0;
+
+ prevloc = loc;
+ if (do_autoverb) {
+ do_autoverb = 0;
+ run_autoverb();
+ }
+
+ if (!room[loc].seen) { /* This only runs on the first turn */
+ room[loc].seen = 1;
+ tscore += room[loc].points;
+ }
+ } while (prevloc != loc); /* Autoverb could move player */
+}
+
+
+static int min_delta(void) {
+ return (aver == AGT183) ? 1 : 0 ;
+}
+
+
+void increment_turn(void) {
+ int i;
+
+ compute_seen();
+
+ newlife_flag = 0;
+
+ if (quitflag) return;
+
+ newroom();
+
+ if (winflag || deadflag || endflag) return;
+
+ if (was_metaverb) return; /* No time should pass during a metaverb. */
+
+ turncnt++;
+ /* Now increment the time counter */
+ if (delta_time > 0) {
+ if (PURE_TIME)
+ add_time(agt_rand(min_delta(), delta_time));
+ else /* if !PURE_TIME */
+ add_time(delta_time);
+ }
+
+ for (i = 0; i <= CNT_NUM; i++)
+ if (agt_counter[i] >= 0) ++agt_counter[i];
+ creatloop(i)
+ if (creature[i].location == loc + first_room && creature[i].hostile &&
+ creature[i].timethresh > 0) {
+ parse_rec tmpcreat; /* Used for creature messages */
+ make_parserec(i + first_creat, &tmpcreat);
+ curr_creat_rec = &tmpcreat;
+
+ if (++creature[i].timecounter >= creature[i].timethresh) {
+ /* Creature attacks */
+ sysmsg(16, "$The_c$$c_name$ suddenly attacks $you_obj$!");
+ sysmsg(creature[i].gender == 0 ? 17 : 18,
+ " $You$ try to defend $your$self, but $the_c$$c_name$ "
+ "kills $you_obj$ anyhow.");
+ deadflag = 1;
+ } else /* 'Angrier' messages */
+ if (creature[i].timethresh > 0 &&
+ creature[i].timecounter > creature[i].timethresh - 3)
+ sysmsg(15, "$The_c$$c_name$ seems to be getting angrier.");
+ }
+}
+
+
+/* Wrapper for increment_turn used by exec routines below.
+ This just checks to make sure we're not one of the 1.8x versions
+ (which call increment turn from elsewhere) */
+static void exec_increment_turn(void) {
+ if (PURE_AFTER) increment_turn();
+}
+
+static void end_turn(void) {
+ if (textbold) agt_textcolor(-2);
+ textbold = 0;
+ set_statline();
+
+ if (quitflag) return;
+
+ if (notify_flag && !was_metaverb) {
+ if (old_score < tscore)
+ sysmsg(227, " [Your score just went up]");
+ else if (old_score > tscore)
+ sysmsg(228, " [Your score just went down]");
+ }
+ old_score = tscore;
+
+}
+
+
+
+static void set_pronoun(int item) {
+ if (item == 0) return;
+ switch (it_gender(item)) {
+ case 0:
+ if (it_plur(item))
+ last_they = item;
+ last_it = item; /* Change: last_it will be set even if the
+ noun is plural */
+ break;
+ case 1:
+ last_she = item;
+ break;
+ case 2:
+ last_he = item;
+ break;
+ }
+}
+
+
+/* True if the current noun is the last one in the list. */
+static rbool lastnoun(parse_rec *list) {
+ if (list->info == D_END) return 1;
+ list++;
+ while (list->info == D_AND) list++;
+ return (list->info == D_END);
+}
+
+
+
+
+static void runverbs(parse_rec *actor0, int vnum,
+ parse_rec *lnoun, word prep0, parse_rec *iobj0)
+/* The zeros are postpended simply to avoid a name conflict */
+{
+ parse_rec *currnoun;
+
+ textbold = 0;
+ do_look = 0;
+ do_autoverb = 0;
+ was_metaverb = 0;
+ actor = actor0->obj;
+ actor_rec = copy_parserec(actor0);
+ vb = vnum;
+ dobj = lnoun[0].obj;
+ dobj_rec = copy_parserec(lnoun);
+ prep = prep0;
+ iobj = iobj0->obj;
+ iobj_rec = copy_parserec(iobj0);
+ set_pronoun(actor); /* Basically the last one that isn't 0 will stick */
+ set_pronoun(iobj0->obj);
+ was_metaverb = 0; /* Most verbs are not metaverbs; assume this by default */
+ start_of_turn = 1;
+ end_of_turn = 0;
+
+ if (lnoun[0].info == D_END || lnoun[0].info == D_ALL) {
+ end_of_turn = 1;
+ exec_verb();
+ if (doing_restore) {
+ free_all_parserec();
+ return;
+ }
+ if (PURE_AND) exec_increment_turn();
+ } else for (currnoun = lnoun; currnoun->info != D_END; currnoun++)
+ if (currnoun->info != D_AND) {
+ free_all_parserec();
+ end_of_turn = lastnoun(currnoun);
+ actor = actor0->obj;
+ actor_rec = copy_parserec(actor0);
+ vb = vnum;
+ dobj = currnoun->obj;
+ dobj_rec = copy_parserec(currnoun);
+ iobj = iobj0->obj;
+ iobj_rec = copy_parserec(iobj0);
+ set_pronoun(dobj);
+ exec_verb();
+ if (doing_restore) return;
+ if (PURE_AND)
+ exec_increment_turn();
+ else
+ start_of_turn = 0;
+ if (quitflag || winflag || deadflag || endflag)
+ break;
+ }
+ assert(end_of_turn);
+ if (!PURE_AND) exec_increment_turn();
+ end_turn();
+ free_all_parserec();
+}
+
+
+/* The following store values for use by AGAIN */
+/* (so AGAIN can be implemented just by executing runverbs w/ the saved
+ values) */
+static int save_vnum;
+static word save_prep;
+static parse_rec save_actor;
+static parse_rec save_obj;
+parse_rec *save_lnoun = NULL;
+
+
+
+void exec(parse_rec *actor_, int vnum,
+ parse_rec *lnoun, word prep_, parse_rec *iobj_) {
+
+ cmd_saveable = 0;
+ pronoun_mode = !PURE_PROSUB;
+
+ if (vnum == verb_code(ext_code[wagain]) && lnoun[0].info == D_END
+ && iobj_->info == D_END &&
+ (actor_->info == D_END || actor_->obj == save_actor.obj))
+ if (save_lnoun == NULL) {
+ rfree(lnoun);
+ sysmsg(186,
+ "You can't use AGAIN until you've entered at least one command.");
+ return;
+ } else {
+ memcpy(actor_, &save_actor, sizeof(parse_rec));
+ vnum = save_vnum;
+ prep_ = save_prep;
+ memcpy(iobj_, &save_obj, sizeof(parse_rec));
+ rfree(lnoun);
+ lnoun = save_lnoun;
+ save_lnoun = NULL;
+ }
+ else
+ realverb = input[vp];
+
+
+ runverbs(actor_, vnum, lnoun, prep_, iobj_);
+
+ if (cmd_saveable) {
+ if (save_lnoun != NULL) rfree(save_lnoun);
+
+ memcpy(&save_actor, actor_, sizeof(parse_rec));
+ save_vnum = vnum;
+ save_lnoun = lnoun;
+ lnoun = NULL;
+ save_prep = prep_;
+ memcpy(&save_obj, iobj_, sizeof(parse_rec));
+ } else
+ rfree(lnoun);
+}
+
+} // End of namespace AGT
+} // End of namespace Glk
diff --git a/engines/glk/agt/exec.h b/engines/glk/agt/exec.h
new file mode 100644
index 0000000..d82ccf0
--- /dev/null
+++ b/engines/glk/agt/exec.h
@@ -0,0 +1,272 @@
+/* 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.
+ *
+ */
+
+
+namespace Glk {
+namespace AGT {
+
+#ifndef global /* Don't touch this */
+#define global extern
+#define global_defined_exec
+#endif
+
+
+
+/* This contains the decoding of the current instruction */
+struct op_rec {
+ integer op;
+ int arg1;
+ int arg2;
+ int optype;
+ int argcnt; /* Actual number of argument words present */
+ const opdef *opdata;
+ const char *errmsg;
+ rbool disambig; /* Trigger disambiguation? */
+ rbool negate; /* NOT? (cond token only) */
+ rbool failmsg; /* Run only on failure? */
+ rbool endor; /* End any OR blocks? (action tokens, mainly) */
+} ;
+
+
+
+/* The following determines if we are doing disambiguation
+ or actually executing a verb */
+global uchar do_disambig; /* 0= execution
+ 1= disambiguating noun
+ 2= disambiguating object */
+
+
+/* Flags used during turn execution */
+global rbool beforecmd; /* Only used by 1.8x games */
+global rbool supress_debug; /* Causes debugging info to _not_ be printed
+ even if debugging is on; used by disambiguator
+ and to supress ANY commands */
+global rbool was_metaverb; /* Was the verb that just executed a metaverb? */
+/* Metaverbs are commands that should not take game time
+to execute: SAVE, RESTORE, RESTART, QUIT, SCRIPT, UNSCRIPT,
+NOTIFY, SCORE, etc. */
+global integer oldloc; /* Save old location for NO_BLOCK_HOSTILE purposes */
+
+/* This is a hack to pass the subroutine number from exec_token
+ back to scan_metacommand when a DoSubroutine is done */
+global integer subcall_arg;
+
+/* This fixes a bug in the original AGT spec, causing "actor, verb ..."
+ commands to misfire if there is more than one creature of the same
+ name. */
+global integer *creat_fix;
+
+
+/* -------------------------------------------------------------------- */
+/* Defined in EXEC.C */
+/* -------------------------------------------------------------------- */
+extern void raw_lineout(const char *s, rbool do_repl,
+ int context, const char *pword);
+extern void msgout(int msgnum, rbool add_nl);
+extern void sysmsg(int msgid, const char *s);
+extern void alt_sysmsg(int msgid, const char *s, parse_rec *new_dobjrec,
+ parse_rec *new_iobjrec);
+extern void sysmsgd(int msgid, const char *s, parse_rec *new_dobj_rec);
+
+rbool ask_question(int qnum);
+extern void increment_turn(void);
+
+/* Warning: the following function rfrees <ans> */
+extern rbool match_answer(char *ans, int anum);
+
+extern void look_room(void);
+extern void runptr(int i, descr_ptr dp[], const char *msg, int msgid,
+ parse_rec *nounrec, parse_rec *objrec);
+
+extern int normalize_time(int tnum); /* Convert hhmm so mm<60 */
+extern void add_time(int dt);
+
+
+/* -------------------------------------------------------------------- */
+/* Defined in OBJECT.C */
+/* -------------------------------------------------------------------- */
+extern parse_rec *make_parserec(int obj, parse_rec *rec);
+extern parse_rec *copy_parserec(parse_rec *rec);
+extern void free_all_parserec(void); /* Freeds doj_rec, iobj_rec, and actor_rec */
+
+extern rbool in_scope(int item);
+extern rbool islit(void);
+extern rbool it_possess(int item);
+extern rbool it_proper(int item);
+extern rbool it_isweapon(int item);
+extern rbool it_door(int obj, word noun); /* Is obj a door? */
+extern rbool is_within(integer obj1, integer obj2, rbool stop_if_closed);
+
+extern integer it_room(int item); /* Returns the room that the item is in */
+
+extern int lightcheck(int parent, int roomlight, rbool active);
+/* If active is false, we don't care if the light is actually working. */
+
+#define it_move(a,b) it_reposition(a,b,0)
+#define it_destroy(item) it_move(item,0)
+#define get_obj(dobj) it_move(dobj,1)
+#define drop_obj(dobj) it_move(dobj,loc+first_room)
+
+extern void it_reposition(int item, int newloc, rbool save_pos);
+extern void goto_room(int newroom);
+
+extern void it_describe(int dobj);
+extern int print_contents(int obj, int ind_lev);
+
+extern void recompute_score(void);
+
+extern int check_fit(int obj1, int obj2);
+
+/* And its possible return values: */
+
+#define FIT_OK 0 /* Fits */
+#define FIT_WEIGHT 1 /* Too heavy [*] */
+#define FIT_NETWEIGHT 2 /* With other stuff is too heavy [*] */
+#define FIT_SIZE 3 /* Too big */
+#define FIT_NETSIZE 4 /* With other stuff is too big */
+/* [*]-- These can only occur if obj2==1 or for ME/1.5-1.7 */
+
+
+extern long getprop(int obj, int prop);
+extern void setprop(int obj, int prop, long val);
+extern rbool getattr(int obj, int prop);
+extern void setattr(int obj, int prop, rbool val);
+
+extern rbool matchclass(int obj, int oclass);
+
+/* ---------------------------------------------------------------------- */
+/* Define in RUNVERB.C */
+/* ---------------------------------------------------------------------- */
+
+/* Verbs actually used elsewhere in th interpreter */
+extern void v_inventory(void);
+extern void v_look(void);
+extern void v_listexit(void);
+
+/* The routine that actually runs the current player command */
+extern void exec_verb(void);
+
+
+/* ---------------------------------------------------------------------- */
+/* In METACOMMAND.C */
+/* ---------------------------------------------------------------------- */
+/* The main routine to search the metacommand list and run the appropriate
+ meta-commands */
+extern int scan_metacommand(integer m_actor, int vcode,
+ integer m_dobj, word m_prep, integer m_iobj,
+ int *redir_flag);
+
+/* The type checking routine */
+rbool argvalid(int argtype, int arg);
+
+/* ---------------------------------------------------------------------- */
+/* In TOKEN.C */
+/* ---------------------------------------------------------------------- */
+extern int exec_instr(op_rec *oprec); /* Execute instruction */
+extern long pop_expr_stack(void); /* Wrapper around routine to access TOS */
+
+/* ---------------------------------------------------------------------- */
+/* Defined in DEBUGCMD.C */
+/* ---------------------------------------------------------------------- */
+extern void get_debugcmd(void); /* Get and execute debugging commands */
+
+
+/* ------------------------------------------------------------------- */
+/* Macros for getting information about items */
+/* (mainly used to blackbox the difference between nouns and creatures) */
+/* -------------------------------------------------------------------- */
+
+/* A note on object codes:
+ <0 obj is a 'virtual' object, existing only as the word
+ dict[-obj], e.g. DOOR, flag nouns, global nouns
+ 0 No object (or any object)
+ 1 Self(i.e. the player)
+ first_room..last_room Rooms
+ first_noun..last_noun Nouns
+ first_creat..last_creat Creatures
+ 1000 Being worn by the player */
+
+
+/* The following macro loops over the contents of an object */
+#define contloop(i,obj) for(i=it_contents(obj);i!=0;i=it_next(i))
+#define safecontloop(i,j,obj) for(i=it_contents(obj),j=it_next(i); \
+ i!=0;i=j,j=it_next(i))
+
+#define cnt_val(c) ((c)==-1 ? 0 : (c))
+
+
+/* -------------------------------------------------------------------- */
+/* These are the macros that should usually be used to determine */
+/* information about the objects in the game, unless the object type */
+/* is definitely known */
+/* ------------------------------------------------------------------- */
+
+#define it_on(item) nounattr(item,on)
+#define it_group(item) creatattr(item,groupmemb)
+#define it_adj(item) objattr(item,adj)
+#define it_pushable(item) nounattr(item,pushable)
+#define it_pullable(item) nounattr(item,pullable)
+#define it_turnable(item) nounattr(item,turnable)
+#define it_playable(item) nounattr(item,playable)
+#define it_plur(item) nounattr(item,plural)
+#define it_gender(item) creatattr(item,gender)
+
+#define it_pict(item) objattr(item,pict)
+#define it_class(item) anyattr(item,oclass)
+#define it_next(item) objattr(item,next)
+#define it_isglobal(item) objattr(item,isglobal)
+#define it_flagnum(item) objattr(item,flagnum)
+#define it_seen(item) anyattr(item,seen)
+
+
+#define it_name(item) objattr2(item,name,(item<0) ? -item : 0)
+#define it_open(item) nounattr2(item,open, tcreat(item) || \
+ (tdoor(item) && !room[loc].locked_door))
+
+/* This checks to make sure the object isn't unmovable. */
+/* (As such, all non-nouns automatically pass) */
+#define it_canmove(item) (!tnoun(item) || noun[(item)-first_noun].movable)
+
+
+#ifdef IT_MACRO
+#define it_contents(item) objattr2(item,contents,\
+ roomattr2(item,contents,\
+ (item==1) ? player_contents : \
+ (item==1000) ? player_worn : 0))
+#define it_lockable(item) nounattr2(item,lockable, (tdoor(item) ? 1 : 0) )
+#define it_locked(item,name) nounattr2(item,locked,\
+ (tdoor(item) && room[loc].locked_door ? \
+ 1 : 0))
+#else
+extern int it_contents(integer obj);
+extern rbool it_lockable(integer obj, word noun);
+extern rbool it_locked(integer obj, word noun);
+#endif
+
+
+#ifdef global_defined_exec
+#undef global
+#undef global_defined_exec
+#endif
+
+} // End of namespace AGT
+} // End of namespace Glk
diff --git a/engines/glk/agt/filename.cpp b/engines/glk/agt/filename.cpp
new file mode 100644
index 0000000..75f18f8
--- /dev/null
+++ b/engines/glk/agt/filename.cpp
@@ -0,0 +1,692 @@
+/* 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 "glk/agt/agility.h"
+
+namespace Glk {
+namespace AGT {
+
+#ifdef force16
+#undef int
+#endif
+
+#ifdef UNIX_IO
+#include <fcntl.h>
+#include <sys/stat.h> /* Needed only for file permission bits */
+
+#ifdef __STRICT_ANSI__
+int fileno(FILE *f);
+FILE *popen(char *s, char *how);
+int pclose(FILE *p);
+#endif
+
+#ifdef MSDOS16
+#include <io.h>
+#endif
+#endif /* UNIX_IO */
+
+#ifdef force16
+#define int short
+#endif
+
+
+/*----------------------------------------------------------------------*/
+/* Filetype Data */
+/*----------------------------------------------------------------------*/
+const char *extname[] = {
+ "",
+ DA1, DA2, DA3, DA4, DA5, DA6, DSS,
+ pHNT, pOPT, pTTL,
+ pSAV, pSCR, pLOG,
+ pAGX, pINS, pVOC, pCFG,
+ pAGT, pDAT, pMSG, pCMD, pSTD, AGTpSTD
+};
+
+
+#ifdef PATH_SEP
+static const char *path_sep = PATH_SEP;
+#else
+static const char *path_sep = NULL;
+#endif
+
+/* This returns the options to use when opening the given file type */
+/* rw is true if we are writing, false if we are reading. */
+const char *filetype_info(filetype ft, rbool rw) {
+ if (ft < fTTL) return "rb";
+ if (ft == fAGX) return rw ? "wb" : "rb";
+ if (ft == fSAV) return (rw ? "wb" : "rb");
+ if (ft == fTTL || ft == fINS || ft == fVOC) return "rb";
+#ifdef OPEN_AS_TEXT
+ if (ft >= fCFG) return (open_as_binary ? "rb" : "r");
+#else
+ if (ft >= fCFG) return "rb";
+#endif
+ if (ft == fSCR) {
+ if (rw)
+ return (BATCH_MODE || make_test) ? "w" : "a";
+ else return "r";
+ }
+ if (ft == fLOG) return rw ? "w" : "r";
+ fatal("INTERNAL ERROR: Invalid filetype.");
+ return NULL;
+}
+
+
+/* Returns true if ft is a possible extension in general context ft_base */
+static rbool compat_ext(filetype ft, filetype ft_base) {
+ if (ft_base == fNONE || ft_base == fDA1 || ft_base == fAGX) { /* Game file */
+ return (ft >= fDA1 && ft <= fDSS)
+ || ft == fOPT || ft == fTTL
+ || (ft >= fAGX && ft <= fCFG);
+ }
+
+ if (ft_base == fSAV || ft_base == fSCR || ft_base == fLOG)
+ return (ft == ft_base);
+
+ if (ft_base == fAGT) { /* Source code */
+ return (ft >= fAGT && ft <= fCMD)
+ || ft == fTTL || ft == fCFG;
+ }
+
+ fatal("INTERNAL ERROR: Invalid file class.");
+ return 0;
+}
+
+
+
+/*----------------------------------------------------------------------*/
+/* Misc. utilities */
+/*----------------------------------------------------------------------*/
+
+char *assemble_filename(const char *path, const char *root,
+ const char *ext) {
+ int len1, len2, len3;
+ char *name;
+
+ len1 = len2 = len3 = 0;
+ if (path != NULL) len1 = strlen(path);
+ if (root != NULL) len2 = strlen(root);
+ if (ext != NULL) len3 = strlen(ext);
+ name = (char *)rmalloc(len1 + len2 + len3 + 1);
+ if (path != NULL) memcpy(name, path, len1);
+#ifdef PREFIX_EXT
+ if (ext != NULL) memcpy(name + len1, ext, len3);
+ if (root != NULL) memcpy(name + len1 + len3, root, len2);
+#else
+ if (root != NULL) memcpy(name + len1, root, len2);
+ if (ext != NULL) memcpy(name + len1 + len2, ext, len3);
+#endif
+ name[len1 + len2 + len3] = 0;
+ return name;
+}
+
+#ifdef PATH_SEP
+/* This works for binary files; we don't care about non-binary
+ files since this only used to search for game files. */
+static rbool file_exist(const char *fname) {
+ return Common::File::exists(fname);
+}
+#endif
+
+/* This checks to see if c matches any of the characters in matchset */
+static rbool smatch(char c, const char *matchset) {
+ for (; *matchset != 0; matchset++)
+ if (*matchset == c) return 1;
+ return 0;
+}
+
+
+
+/*----------------------------------------------------------------------*/
+/* Taking Apart the Filename */
+/*----------------------------------------------------------------------*/
+
+static int find_path_sep(const char *name) {
+ int i;
+
+ if (path_sep == NULL)
+ return -1;
+ for (i = strlen(name) - 1; i >= 0; i--)
+ if (smatch(name[i], path_sep)) break;
+ return i;
+}
+
+
+/* Checks to see if the filename (which must be path-free)
+ has an extension. Returns the length of the extensions
+ and writes the extension type in pft */
+static int search_for_ext(const char *name, filetype base_ft,
+ filetype *pft) {
+ filetype t;
+ int xlen, len;
+
+ *pft = fNONE;
+ len = strlen(name);
+ if (len == 0) return 0;
+ for (t = (filetype)(fNONE + 1); t <= fSTD; t = (filetype)((int)t + 1))
+ if (compat_ext(t, base_ft)) {
+ xlen = strlen(extname[t]);
+ if (xlen == 0 || xlen > len) continue;
+#ifdef PREFIX_EXT
+ if (strncasecmp(name, extname[t], xlen) == 0)
+#else
+ if (fnamecmp(name + len - xlen, extname[t]) == 0)
+#endif
+ {
+ *pft = t;
+ return xlen;
+ }
+ }
+#ifdef UNIX_IO
+ /* This is code to make the Unix/etc ports easier to use under
+ tab-completing shells (which often complete "gamename._" or
+ "gamename.ag_" since there are other files in the directory
+ with the same root.) */
+ assert(*pft == fNONE);
+ if (name[len - 1] == '.') return 1;
+ if (fnamecmp(name + len - 3, ".ag") == 0) {
+ if (base_ft == fDA1 || base_ft == fAGX) *pft = fAGX;
+ if (base_ft == fAGT) *pft = fAGT;
+ }
+ if (fnamecmp(name + len - 3, ".da") == 0) {
+ if (base_ft == fDA1 || base_ft == fAGX) *pft = fDA1;
+ if (base_ft == fAGT) *pft = fAGT;
+ }
+ if (*pft != fNONE) return 3;
+#endif
+ return 0;
+}
+
+
+/* Extract root filename or extension from
+ pathless name, given that the extension is of length extlen. */
+/* If isext is true, extract the extension. If isext is false,
+ then extrac the root. */
+static char *extract_piece(const char *name, int extlen, rbool isext) {
+ char *root;
+ int len, xlen;
+ rbool first; /* If true, extract from beginning; if false, extract
+ from end */
+
+ len = strlen(name) - extlen;
+ xlen = extlen;
+ if (isext) {
+ int tmp;
+ tmp = len;
+ len = xlen;
+ xlen = tmp;
+ }
+ if (len == 0) return NULL;
+ root = (char *)rmalloc((len + 1) * sizeof(char));
+#ifdef PREFIX_EXT
+ first = isext ? 1 : 0;
+#else
+ first = isext ? 0 : 1;
+#endif
+ if (first) {
+ memcpy(root, name, len);
+ root[len] = 0;
+ } else {
+ memcpy(root, name + xlen, len);
+ root[len] = 0;
+ }
+ return root;
+}
+
+
+/* This returns true if "path" is absolute, false otherwise.
+ This is _very_ platform dependent. */
+static rbool absolute_path(char *path) {
+#ifdef pathtest
+ return pathtest(path);
+#else
+ return 1;
+#endif
+}
+
+/*----------------------------------------------------------------------*/
+/* Basic routines for dealing with file contexts */
+/*----------------------------------------------------------------------*/
+
+#define FC(x) ((file_context_rec*)(x))
+
+/* formal_name is used to get filenames for diagnostic messages, etc. */
+char *formal_name(fc_type fc, filetype ft) {
+ if (FC(fc)->special) return FC(fc)->gamename;
+ if (ft == fNONE)
+ return rstrdup(FC(fc)->shortname);
+ if (ft == fAGT_STD)
+ return rstrdup(AGTpSTD);
+ return assemble_filename("", FC(fc)->shortname, extname[ft]);
+}
+
+#ifdef PATH_SEP
+static rbool test_file(const char *path, const char *root, const char *ext) {
+ char *name;
+ rbool tmp;
+
+ name = assemble_filename(path, root, ext);
+ tmp = file_exist(name);
+ rfree(name);
+ return tmp;
+}
+
+/* This does a path search for the game files. */
+static void fix_path(file_context_rec *fc) {
+ char **ppath;
+
+
+ if (gamepath == NULL) return;
+ for (ppath = gamepath; *ppath != NULL; ppath++)
+ if (test_file(*ppath, fc->shortname, fc->ext)
+ || test_file(*ppath, fc->shortname, pAGX)
+ || test_file(*ppath, fc->shortname, DA1)) {
+ fc->path = rstrdup(*ppath);
+ return;
+ }
+}
+#endif
+
+
+/* This creates a new file context based on gamename. */
+/* ft indicates the rough use it will be put towards:
+ ft=fNONE indicates it's the first pass read, before PATH has been
+ read in, and so the fc shouldn't be filled out until
+ fix_file_context() is called.
+ ft=pDA1 indicates that name refers to the game files.
+ ft=pAGX indicates the name of the AGX file to be written to.
+ ft=pSAV,pLOG,pSCR all indicate that name corresponds to the
+ related type of file. */
+fc_type init_file_context(const char *name, filetype ft) {
+ file_context_rec *fc;
+ int p, x; /* Path and extension markers */
+
+ fc = (file_context_rec *)rmalloc(sizeof(file_context_rec));
+ fc->special = 0;
+
+#ifdef UNIX
+ if (name[0] == '|') { /* Output pipe */
+ name++;
+ fc->special = 1;
+ }
+#endif
+
+ fc->gamename = rstrdup(name);
+
+#ifdef UNIX
+ x = strlen(fc->gamename);
+ if (fc->gamename[x - 1] == '|') { /* Input pipe */
+ fc->gamename[x - 1] = 0;
+ fc->special |= 2;
+ }
+ if (fc->special) {
+ fc->path = fc->shortname = fc->ext = NULL;
+ return fc;
+ }
+#endif
+
+ p = find_path_sep(fc->gamename);
+ if (p < 0)
+ fc->path = NULL;
+ else {
+ fc->path = (char *)rmalloc((p + 2) * sizeof(char));
+ memcpy(fc->path, fc->gamename, p + 1);
+ fc->path[p + 1] = '\0';
+ }
+ x = search_for_ext(fc->gamename + p + 1, ft, &fc->ft);
+ fc->shortname = extract_piece(fc->gamename + p + 1, x, 0);
+ fc->ext = extract_piece(fc->gamename + p + 1, x, 1);
+
+#ifdef PATH_SEP
+ if (fc->path == NULL && ft == fDA1)
+ fix_path(fc);
+#endif
+ return fc;
+}
+
+
+void fix_file_context(fc_type fc, filetype ft) {
+#ifdef PATH_SEP
+ if (FC(fc)->path == NULL && ft == fDA1)
+ fix_path(FC(fc));
+#endif
+}
+
+
+/* This creates new file contexts from old. */
+/* This is used to create save/log/script filenames from the game name,
+ and to create include files in the same directory as the source file. */
+fc_type convert_file_context(fc_type fc, filetype ft, const char *name) {
+ file_context_rec *nfc;
+ rbool local_ftype; /* Indicates file should be in working directory,
+ not game directory. */
+
+ local_ftype = (ft == fSAV || ft == fSCR || ft == fLOG);
+ if (BATCH_MODE || make_test) local_ftype = 0;
+
+ if (name == NULL) {
+ nfc = (file_context_rec *)rmalloc(sizeof(file_context_rec));
+ nfc->gamename = NULL;
+ nfc->path = NULL;
+ nfc->shortname = rstrdup(fc->shortname);
+ nfc->ext = NULL;
+ nfc->ft = fNONE;
+ nfc->special = 0;
+ } else {
+ nfc = init_file_context(name, ft);
+ }
+
+ /* If path already defined, then combine paths. */
+ if (!local_ftype && nfc->path != NULL && !absolute_path(nfc->path)) {
+ char *newpath;
+ newpath = nfc->path;
+ newpath = assemble_filename(fc->path, nfc->path, "");
+ rfree(nfc->path);
+ nfc->path = newpath;
+ }
+
+ /* scripts, save-games and logs should go in the working directory,
+ not the game directory, so leave nfc->path equal to NULL for them. */
+ if (!local_ftype && nfc->path == NULL)
+ nfc->path = rstrdup(fc->path); /* Put files in game directory */
+ return nfc;
+}
+
+void release_file_context(fc_type *pfc) {
+ file_context_rec *fc;
+ fc = FC(*pfc);
+ rfree(fc->gamename);
+ rfree(fc->path);
+ rfree(fc->shortname);
+ rfree(fc->ext);
+ rfree(fc);
+}
+
+
+/*----------------------------------------------------------------------*/
+/* Routines for Finding Files */
+/*----------------------------------------------------------------------*/
+
+#ifdef UNIX
+/* This requires that no two sav/scr/log files be open at the same time. */
+static int pipecnt = 0;
+static FILE *pipelist[6];
+
+static genfile try_open_pipe(fc_type fc, filetype ft, rbool rw) {
+ FILE *f;
+
+ errno = 0;
+ if (ft != fSAV && ft != fSCR && ft != fLOG) return NULL;
+ if (rw && fc->special != 1) return NULL;
+ if (!rw && fc->special != 2) return NULL;
+ if (pipecnt >= 6) return NULL;
+
+ f = popen(fc->gamename, rw ? "w" : "r"); /* Need to indicate this is a pipe */
+ pipelist[pipecnt++] = f;
+ return f;
+}
+#endif
+
+
+static genfile try_open_file(const char *path, const char *root,
+ const char *ext, const char *how,
+ rbool nofix) {
+ char *name = assemble_filename(path, root, ext);
+ genfile f = fopen(name, how);
+ rfree(name);
+
+ return f;
+}
+
+
+static genfile findread(file_context_rec *fc, filetype ft) {
+ genfile f;
+
+ f = NULL;
+
+#ifdef UNIX
+ if (fc->special) { /* It's a pipe */
+ f = try_open_pipe(fc, ft, 0);
+ return f;
+ }
+#endif
+ if (ft == fAGT_STD) {
+ f = try_open_file(fc->path, AGTpSTD, "", filetype_info(ft, 0), 0);
+ return f;
+ }
+ if (ft == fAGX || ft == fNONE) /* Try opening w/o added extension */
+ f = try_open_file(fc->path, fc->shortname, fc->ext, filetype_info(ft, 0), 0);
+ if (f == NULL)
+ f = try_open_file(fc->path, fc->shortname, extname[ft], filetype_info(ft, 0), 0);
+ return f;
+}
+
+
+/*----------------------------------------------------------------------*/
+/* File IO Routines */
+/*----------------------------------------------------------------------*/
+
+genfile readopen(fc_type fc, filetype ft, const char **errstr) {
+ genfile f;
+
+ *errstr = NULL;
+ f = findread(fc, ft);
+ if (f == NULL) {
+ *errstr = "Cannot open file";
+ }
+ return f;
+}
+
+rbool fileexist(fc_type fc, filetype ft) {
+ genfile f;
+
+ if (fc->special) return 0;
+ f = try_open_file(fc->path, fc->shortname, extname[ft], filetype_info(ft, 0), 1);
+ if (f != NULL) { /* File already exists */
+ readclose(f);
+ return 1;
+ }
+ return 0;
+}
+
+
+genfile writeopen(fc_type fc, filetype ft,
+ file_id_type *pfileid, const char **errstr) {
+ char *name;
+ genfile f;
+
+ *errstr = NULL;
+ name = NULL;
+
+#ifdef UNIX
+ if (fc->special) { /* It's a pipe */
+ f = try_open_pipe(fc, ft, 1);
+ if (f == NULL && errno == 0) {
+ *errstr = rstrdup("Invalid pipe request.");
+ return f;
+ }
+ if (f == NULL) /* For error messages */
+ name = rstrdup(fc->gamename);
+ } else
+#endif
+ {
+ name = assemble_filename(FC(fc)->path, FC(fc)->shortname, extname[ft]);
+ f = fopen(name, filetype_info(ft, 1));
+ }
+ if (f == NULL) {
+ *errstr = "Cannot open file";
+ }
+ if (pfileid == NULL)
+ rfree(name);
+ else
+ *pfileid = name;
+ return f;
+}
+
+
+rbool filevalid(genfile f, filetype ft) {
+ return (f != NULL);
+}
+
+
+
+void binseek(genfile f, long offset) {
+ assert(f != NULL);
+ assert(offset >= 0);
+#ifdef UNIX_IO
+ if (lseek(fileno(f), offset, SEEK_SET) == -1)
+#else
+ if (fseek(f, offset, SEEK_SET) != 0)
+#endif
+ fatal("binseek");
+}
+
+
+/* This returns the number of bytes read, or 0 if there was an error. */
+long varread(genfile f, void *buff, long recsize, long recnum, const char **errstr) {
+ long num;
+
+ *errstr = NULL;
+ assert(f != NULL);
+#ifdef UNIX_IO
+#ifdef MSDOS16
+ num = (unsigned int)read(fileno(f), buff, recsize * recnum);
+ if (num == (unsigned int) - 1)
+#else
+ num = read(fileno(f), buff, recsize * recnum);
+ if (num == -1)
+#endif
+ {
+ *errstr = rstrdup(strerror(errno));
+ return 0;
+ }
+#else
+ num = fread(buff, recsize, recnum, f);
+ if (num != recnum)
+ *errstr = "varread";
+ num = num * recsize;
+#endif
+ return num;
+}
+
+rbool binread(genfile f, void *buff, long recsize, long recnum, const char **errstr) {
+ long num;
+
+ num = varread(f, buff, recsize, recnum, errstr);
+ if (num < recsize * recnum && *errstr == NULL)
+ *errstr = rstrdup("Unexpected end of file.");
+ return (*errstr == NULL);
+}
+
+
+rbool binwrite(genfile f, void *buff, long recsize, long recnum, rbool ferr) {
+ assert(f != NULL);
+#ifdef UNIX_IO
+ if (write(fileno(f), buff, recsize * recnum) == -1)
+#else
+ if (fwrite(buff, recsize, recnum, f) != (size_t)recnum)
+#endif
+ {
+ if (ferr) fatal("binwrite");
+ return 0;
+ }
+ return 1;
+}
+
+#ifdef UNIX
+static rbool closepipe(genfile f) {
+ int i;
+ for (i = 0; i < pipecnt; i++)
+ if (pipelist[i] == f) {
+ pclose(f);
+ for (; i < pipecnt - 1; i++)
+ pipelist[i] = pipelist[i + 1];
+ pipecnt--;
+ return 1;
+ }
+ return 0;
+}
+#endif
+
+void readclose(genfile f) {
+ assert(f != NULL);
+#ifdef UNIX
+ if (closepipe(f)) return;
+#endif
+ fclose(f);
+}
+
+void writeclose(genfile f, file_id_type fileid) {
+ assert(f != NULL);
+ rfree(fileid);
+#ifdef UNIX
+ if (closepipe(f)) return;
+#endif
+ fclose(f);
+}
+
+void binremove(genfile f, file_id_type fileid) {
+ assert(f != NULL);
+ assert(fileid != NULL);
+ fclose(f);
+ remove((char *)fileid);
+ rfree(fileid);
+}
+
+long binsize(genfile f)
+/* Returns the size of a binary file */
+{
+ long pos, leng;
+
+ assert(f != NULL);
+#ifdef UNIX_IO
+ {
+ long fd;
+
+ fd = fileno(f);
+ pos = lseek(fd, 0, SEEK_CUR);
+ leng = lseek(fd, 0, SEEK_END);
+ lseek(fd, pos, SEEK_SET);
+ }
+#else
+ pos = ftell(f);
+ fseek(f, 0, SEEK_END);
+ leng = ftell(f);
+ fseek(f, pos, SEEK_SET);
+#endif
+ return leng;
+}
+
+rbool textrewind(genfile f) {
+ Common::SeekableReadStream *rs = dynamic_cast<Common::SeekableReadStream *>(f);
+ assert(rs);
+ rs->seek(0);
+ return 0;
+}
+
+
+genfile badfile(filetype ft) {
+ return NULL;
+}
+
+} // End of namespace AGT
+} // End of namespace Glk
diff --git a/engines/glk/agt/gamedata.cpp b/engines/glk/agt/gamedata.cpp
new file mode 100644
index 0000000..e33b594
--- /dev/null
+++ b/engines/glk/agt/gamedata.cpp
@@ -0,0 +1,1590 @@
+/* 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 "glk/agt/agility.h"
+
+namespace Glk {
+namespace AGT {
+
+/*
+ This is a mishmash of utilities and preinitialized arrays,
+ including the verblist, the metacommand token list,
+ and the dictionary routines.
+*/
+
+/* ------------------------------------------------------------------- */
+/* Preinitialized data structures */
+/* Most of the preinitialized data structures used by all of the */
+/* AGT-related programs go here . */
+/* ------------------------------------------------------------------- */
+
+
+/* ------------------------------------------------------------ */
+/* The PC --> ASCII conversion table. This converts the 8th-bit */
+/* PC characters to their nearest ASCII equivalent. */
+/* ------------------------------------------------------------ */
+
+const char trans_ibm[] =
+ "CueaaaaceeeiiiAA" /* 80 */
+ "E@@ooouuyOUc$$pf" /* 90 */
+ "aiounNao?....!<>" /* A0 */
+ "###|++|+++|\\/++\\" /* B0 */
+ "\\+++-+||\\/+++=+=" /* C0 */
+ "+=+++++++//@@@@@" /* D0 */
+ "abGpSsmtFTOd.fe^" /* E0 */
+ "=+><fj/=***/n2# "; /* F0 */
+
+
+/* ------------------------------------------------------------- */
+/* Tables of built in properties and attributes */
+/* ------------------------------------------------------------- */
+
+#define rnc(p) {#p,offsetof(room_rec,p),offsetof(noun_rec,p), \
+ offsetof(creat_rec,p)}
+#define rn(p) {#p,offsetof(room_rec,p),offsetof(noun_rec,p),-1}
+#define nc(p) {#p,-1,offsetof(noun_rec,p),offsetof(creat_rec,p)}
+#define rc(p) {#p,offsetof(room_rec,p),-1,offsetof(creat_rec,p)}
+#define r(p) {#p, offsetof(room_rec,p), -1, -1}
Commit: 88444ddc889e4faee110b637961824fcb8abc448
https://github.com/scummvm/scummvm/commit/88444ddc889e4faee110b637961824fcb8abc448
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2019-11-27T21:10:29-08:00
Commit Message:
GLK: AGT: Initialization fixes
Changed paths:
engines/glk/agt/agt.cpp
engines/glk/agt/agt.h
engines/glk/agt/filename.cpp
engines/glk/agt/interp.h
engines/glk/agt/os_glk.cpp
engines/glk/agt/util.cpp
diff --git a/engines/glk/agt/agt.cpp b/engines/glk/agt/agt.cpp
index ea28cf8..ff78e59 100644
--- a/engines/glk/agt/agt.cpp
+++ b/engines/glk/agt/agt.cpp
@@ -31,15 +31,22 @@ namespace AGT {
AGT *g_vm;
extern void glk_main();
-extern int glk_startup_code(int argc, char *argv[]);
+extern int glk_startup_code();
+extern void gagt_finalizer();
-AGT::AGT(OSystem *syst, const GlkGameDescription &gameDesc) : GlkAPI(syst, gameDesc) {
+AGT::AGT(OSystem *syst, const GlkGameDescription &gameDesc) : GlkAPI(syst, gameDesc),
+ gagt_gamefile(nullptr), gagt_game_message(nullptr) {
g_vm = this;
}
void AGT::runGame() {
- glk_startup_code(0, nullptr);
+ _gameFile.close();
+ gagt_gamefile = getFilename().c_str();
+
+ glk_startup_code();
glk_main();
+
+ gagt_finalizer();
}
Common::Error AGT::readSaveData(Common::SeekableReadStream *rs) {
diff --git a/engines/glk/agt/agt.h b/engines/glk/agt/agt.h
index 1d4ed00..1f23b2b 100644
--- a/engines/glk/agt/agt.h
+++ b/engines/glk/agt/agt.h
@@ -37,6 +37,9 @@ namespace AGT {
*/
class AGT : public GlkAPI {
public:
+ const char *gagt_gamefile = NULL; /* Name of game file. */
+ const char *gagt_game_message = NULL; /* Error message. */
+public:
/**
* Constructor
*/
diff --git a/engines/glk/agt/filename.cpp b/engines/glk/agt/filename.cpp
index 75f18f8..f3e9553 100644
--- a/engines/glk/agt/filename.cpp
+++ b/engines/glk/agt/filename.cpp
@@ -550,14 +550,10 @@ rbool filevalid(genfile f, filetype ft) {
void binseek(genfile f, long offset) {
- assert(f != NULL);
- assert(offset >= 0);
-#ifdef UNIX_IO
- if (lseek(fileno(f), offset, SEEK_SET) == -1)
-#else
- if (fseek(f, offset, SEEK_SET) != 0)
-#endif
- fatal("binseek");
+ Common::SeekableReadStream *rs = dynamic_cast<Common::SeekableReadStream *>(f);
+ assert(rs);
+
+ rs->seek(offset);
}
diff --git a/engines/glk/agt/interp.h b/engines/glk/agt/interp.h
index 872e887..9bf5d65 100644
--- a/engines/glk/agt/interp.h
+++ b/engines/glk/agt/interp.h
@@ -453,7 +453,7 @@ global volatile int screen_width, status_width;
global int screen_height;
global volatile int curr_x;
-extern void init_interface(int argc, char *argv[]);
+extern void init_interface();
extern void start_interface(fc_type fc);
extern void close_interface(void);
extern char *agt_input(int in_type); /* read line, return malloc'd string */
diff --git a/engines/glk/agt/os_glk.cpp b/engines/glk/agt/os_glk.cpp
index 1d9abd6..9f01eec 100644
--- a/engines/glk/agt/os_glk.cpp
+++ b/engines/glk/agt/os_glk.cpp
@@ -5067,7 +5067,7 @@ genfile agt_globalfile(int fid) {
* General initialization for the module; sets some variables, and creates
* the Glk windows to work in. Called from the AGiliTy main().
*/
-void init_interface(int argc, char *argv[]) {
+void init_interface() {
glui32 status_height;
/*
@@ -5147,7 +5147,6 @@ void init_interface(int argc, char *argv[]) {
}
agt_clrscr();
- gagt_debug("init_interface", "argc=%d, argv=%p", argc, argv);
}
@@ -5525,15 +5524,6 @@ int __wrap_tolower(int ch) {
extern void set_default_options();
/*
- * The following values need to be passed between the startup_code and main
- * functions.
- */
-static int gagt_saved_argc = 0; /* Recorded argc. */
-static char **gagt_saved_argv = NULL, /* Recorded argv. */
- *gagt_gamefile = NULL; /* Name of game file. */
-static const char *gagt_game_message = NULL; /* Error message. */
-
-/*
* Flag to set if we want to test for a clean exit. Without this it's a
* touch tricky sometimes to corner AGiliTy into calling exit() for us; it
* tends to require a broken game file.
@@ -5656,73 +5646,19 @@ static int gagt_parse_option(const char *option) {
* gagt_main()
*
* Together, these functions take the place of the original AGiliTy main().
- * The first one is called from glkunix_startup_code(), to parse and
- * generally handle options. The second is called from glk_main(), and
- * does the real work of running the game.
+ * The first one is called from glkunix_startup_code(). The second is called
+ * from glk_main(), and does the real work of running the game.
*/
-int gagt_startup_code(int argc, char *argv[]) {
- int argv_index;
-
- /*
- * Before doing anything else, stash argc and argv away for use by
- * gagt_main() below.
- */
- gagt_saved_argc = argc;
- gagt_saved_argv = argv;
-
+bool gagt_startup_code() {
/* Make the mandatory call for initialization. */
set_default_options();
- /* Handle command line arguments. */
- for (argv_index = 1;
- argv_index < argc && argv[argv_index][0] == '-'; argv_index++) {
- /*
- * Handle an option string coming after "-". If the options parse
- * fails, return FALSE.
- */
- if (!gagt_parse_option(argv[argv_index]))
- return FALSE;
- }
-
- /*
- * Get the name of the game file. Since we need this in our call from
- * glk_main, we need to keep it in a module static variable. If the game
- * file name is omitted, then here we'll set the pointer to NULL, and
- * complain about it later in main. Passing the message string around
- * like this is a nuisance...
- */
- if (argv_index == argc - 1) {
- gagt_gamefile = argv[argv_index];
- gagt_game_message = NULL;
-#ifdef GARGLK
- char *s;
- s = strrchr(gagt_gamefile, '\\');
- if (s) g_vm->garglk_set_story_name(s + 1);
- s = strrchr(gagt_gamefile, '/');
- if (s) g_vm->garglk_set_story_name(s + 1);
-#endif /* GARGLK */
- } else {
- gagt_gamefile = NULL;
- if (argv_index < argc - 1)
- gagt_game_message = "More than one game file was given"
- " on the command line.";
- else
- gagt_game_message = "No game file was given on the command line.";
- }
-
/* All startup options were handled successfully. */
return TRUE;
}
static void gagt_main() {
fc_type fc;
- assert(gagt_saved_argc != 0 && gagt_saved_argv);
-
- /* Ensure AGiliTy internal types have the right sizes. */
- if (sizeof(integer) < 2 || sizeof(int32) < 4 || sizeof(uint32) < 4) {
- gagt_fatal("GLK: Types sized incorrectly, recompilation is needed");
- gagt_exit();
- }
/*
* Initialize the interface. As it happens, init_interface() is in our
@@ -5734,7 +5670,7 @@ static void gagt_main() {
* window. As it doesn't return status, we have to detect this by checking
* that gagt_main_window is not NULL.
*/
- init_interface(gagt_saved_argc, gagt_saved_argv);
+ init_interface();
if (!gagt_main_window) {
gagt_fatal("GLK: Can't open main window");
gagt_exit();
@@ -5743,29 +5679,18 @@ static void gagt_main() {
g_vm->glk_set_window(gagt_main_window);
g_vm->glk_set_style(style_Normal);
- /* If there's a problem with the game file, complain now. */
- if (!gagt_gamefile) {
- assert(gagt_game_message);
- if (gagt_status_window)
- g_vm->glk_window_close(gagt_status_window, NULL);
- gagt_header_string("Glk AGiliTy Error\n\n");
- gagt_normal_string(gagt_game_message);
- gagt_normal_char('\n');
- gagt_exit();
- }
-
/*
* Create a game file context, and try to ensure it will open successfully
* in run_game().
*/
- fc = init_file_context(gagt_gamefile, fDA1);
+ fc = init_file_context(g_vm->gagt_gamefile, fDA1);
if (!(gagt_workround_fileexist(fc, fAGX)
|| gagt_workround_fileexist(fc, fDA1))) {
if (gagt_status_window)
g_vm->glk_window_close(gagt_status_window, NULL);
gagt_header_string("Glk AGiliTy Error\n\n");
gagt_normal_string("Can't find or open game '");
- gagt_normal_string(gagt_gamefile);
+ gagt_normal_string(g_vm->gagt_gamefile);
gagt_normal_char('\'');
gagt_normal_char('\n');
gagt_exit();
@@ -5860,7 +5785,7 @@ static int gagt_agility_running = FALSE;
* we do, and interpreter code is still running, it's a sign that we need
* to take actions we'd hoped not to have to take.
*/
-static void gagt_finalizer() {
+void gagt_finalizer() {
/*
* If interpreter code is still active, and we're not in a g_vm->glk_select(),
* the core interpreter code called exit(). Handle cleanup.
@@ -5983,16 +5908,6 @@ void glk_main() {
gagt_main_called = TRUE;
/*
- * Register gagt_finalizer() with atexit() to cleanup on exit. Note that
- * this module doesn't expect the atexit() handler to be called on all
- * forms of exit -- see comments in gagt_finalizer() for more.
- */
- if (atexit(gagt_finalizer) != 0) {
- gagt_fatal("GLK: Failed to register finalizer");
- gagt_exit();
- }
-
- /*
* If we're testing for a clean exit, deliberately call exit() to see what
* happens. We're hoping for a clean process termination, but our exit
* code explores "undefined" ANSI. If we get something ugly, like a core
@@ -6088,11 +6003,11 @@ glkunix_argumentlist_t glkunix_arguments[] = {
* function to parse arguments and generally set stuff up.
*/
-int glk_startup_code(int argc, char *argv[]) {
+int glk_startup_code() {
assert(!gagt_startup_called);
gagt_startup_called = TRUE;
- return gagt_startup_code(argc, argv);
+ return gagt_startup_code();
}
} // End of namespace AGT
diff --git a/engines/glk/agt/util.cpp b/engines/glk/agt/util.cpp
index 0bf9635..76b4a63 100644
--- a/engines/glk/agt/util.cpp
+++ b/engines/glk/agt/util.cpp
@@ -369,7 +369,8 @@ int fseek(genfile stream, long int offset, int whence) {
size_t fread(void *ptr, size_t size, size_t nmemb, genfile stream) {
Common::SeekableReadStream *rs = dynamic_cast<Common::SeekableReadStream *>(stream);
assert(rs);
- return rs->read(ptr, size * nmemb);
+ size_t bytesRead = rs->read(ptr, size * nmemb);
+ return bytesRead / size;
}
size_t fwrite(const void *ptr, size_t size, size_t nmemb, genfile stream) {
Commit: c0999ae5ba6800e06c35bb23a9613dd878147618
https://github.com/scummvm/scummvm/commit/c0999ae5ba6800e06c35bb23a9613dd878147618
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2019-11-27T21:10:29-08:00
Commit Message:
GLK: AGT: Startup fixes
Changed paths:
engines/glk/agt/agility.h
engines/glk/agt/agxfile.cpp
engines/glk/agt/util.cpp
diff --git a/engines/glk/agt/agility.h b/engines/glk/agt/agility.h
index 34c125a..b922430 100644
--- a/engines/glk/agt/agility.h
+++ b/engines/glk/agt/agility.h
@@ -429,25 +429,25 @@ struct prop_struct {
structures. For global variables, ptr is set to point at the variable
and offset is 0. For fields, offset is set to the offset of the field
in the structure and ptr is set internally */
-typedef struct {
+struct file_info {
int ftype; /* Type in file */
int dtype; /* Data type of field in memory; often ignored */
void *ptr; /* Pointer to variable */
size_t offset; /* Offset of field in structure */
-} file_info;
+} ;
/* This contains all of the information needed to find files. */
#ifndef REPLACE_FC
-typedef struct {
+struct file_context_rec {
char *gamename; /* Name as entered by user */
char *path; /* The path */
char *shortname; /* The filename w/o directory information */
char *ext; /* The preexisting extension/prefix */
filetype ft; /* The filetype corresponding to ext */
int special; /* Used to mark special files, such as UNIX pipes */
-} file_context_rec;
+};
typedef file_context_rec *fc_type;
diff --git a/engines/glk/agt/agxfile.cpp b/engines/glk/agt/agxfile.cpp
index 04729f5..cd544bb 100644
--- a/engines/glk/agt/agxfile.cpp
+++ b/engines/glk/agt/agxfile.cpp
@@ -751,9 +751,9 @@ static file_info fi_index[] = {
byte Extension 0
*/
-typedef struct {
- unsigned long fileid;
- unsigned long res1; /* Reserved for future use */
+struct file_head_rec {
+ uint32 fileid;
+ uint32 res1; /* Reserved for future use */
uchar res2;
uchar eol_chk1; /* Catch non-binary upload errors */
uchar eol_chk2;
@@ -763,7 +763,7 @@ typedef struct {
uchar extnum;
uchar fallback_ext; /* For non-'R' extensions, this is the 'R' extension
to fall back to. */
-} file_head_rec;
+};
static file_info fi_header[] = {
{FT_UINT32, DT_LONG, NULL, offsetof(file_head_rec, fileid)}, /* File ID */
diff --git a/engines/glk/agt/util.cpp b/engines/glk/agt/util.cpp
index 76b4a63..c91e47a 100644
--- a/engines/glk/agt/util.cpp
+++ b/engines/glk/agt/util.cpp
@@ -958,7 +958,7 @@ static const uchar zero_block[81] = {0, 0, 0, 0, 0, 0, 0, 0,
0
};
-static void read_filerec(const file_info *rec_desc, const uchar *filedata) {
+static void read_filerec(file_info *rec_desc, const uchar *filedata) {
uchar mask;
rbool past_eob; /* Are we past the end of block? */
const uchar *filebase;
@@ -986,17 +986,17 @@ static void read_filerec(const file_info *rec_desc, const uchar *filedata) {
*p(integer) = fixsign16(filedata[0], filedata[1]);
break;
case FT_UINT16:
- *p(long) = fixu16(filedata[0], filedata[1]);
+ *p(int32) = fixu16(filedata[0], filedata[1]);
break;
case FT_CMDPTR: /* cmd ptr */
case FT_INT32:
- *p(long) = fixsign32(filedata[0], filedata[1],
+ *p(int32) = fixsign32(filedata[0], filedata[1],
filedata[2], filedata[3]);
break;
case FT_UINT32:
if (filedata[3] & 0x80)
agtwarn("File value out of range", 0);
- *p(long) = fixsign32(filedata[0], filedata[1],
+ *p(uint32) = fixsign32(filedata[0], filedata[1],
filedata[2], filedata[3] & 0x7F);
break;
case FT_BYTE:
@@ -1065,7 +1065,7 @@ static void read_filerec(const file_info *rec_desc, const uchar *filedata) {
/* Here is the corresponding routien for _writing_ to files */
/* This copies the contents of a record into a buffer */
-static void write_filerec(const file_info *rec_desc, uchar *filedata) {
+static void write_filerec(file_info *rec_desc, uchar *filedata) {
uchar mask;
mask = 1;
@@ -1412,7 +1412,7 @@ char textgetc(genfile f) {
Common::ReadStream *rs = dynamic_cast<Common::ReadStream *>(f);
assert(rs);
- return rs->readByte();
+ return rs->eos() ? EOF : rs->readByte();
}
void textungetc(genfile f, char c) {
Commit: 5d7386e42b54e69b071e726dec5c3e1bfcb5298f
https://github.com/scummvm/scummvm/commit/5d7386e42b54e69b071e726dec5c3e1bfcb5298f
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2019-11-27T21:10:29-08:00
Commit Message:
GLK: AGT: Properly exit when game window is closed
Changed paths:
engines/glk/agt/agil.cpp
engines/glk/agt/interface.cpp
engines/glk/agt/os_glk.cpp
diff --git a/engines/glk/agt/agil.cpp b/engines/glk/agt/agil.cpp
index 5bf19f7..a813b84 100644
--- a/engines/glk/agt/agil.cpp
+++ b/engines/glk/agt/agil.cpp
@@ -546,6 +546,9 @@ static void mainloop(void) {
if (!menu_mode) {
prompt_out(1);
s = agt_readline(0);
+ if (g_vm->shouldQuit())
+ return;
+
agt_newline();
if (!doing_restore) tokenise(s); /* Tokenizes into input */
rfree(s);
@@ -891,6 +894,7 @@ static fc_type setup_game(fc_type fc)
pictcmd(3, 0); /* Show title image, if there is one */
print_title(fc);
have_ins = open_ins_file(fc, 0);
+
do {
if (have_ins)
writestr("Choose <I>nstructions, <A>GiliTy Information, "
@@ -898,11 +902,15 @@ static fc_type setup_game(fc_type fc)
else
writestr("Choose <A>GiliTy Information or <other> to start the game");
choice = tolower(agt_getchar()); /* Wait for keypress */
+ if (g_vm->shouldQuit())
+ return nullptr;
+
agt_clrscr();
if (have_ins && choice == 'i') print_instructions(fc);
else if (choice == 'a') print_license();
} while ((choice == 'i' && have_ins) || choice == 'a');
close_ins_file();
+
if (!intro_first && intro_ptr.size > 0) {
print_descr(intro_ptr, 1);
wait_return();
@@ -970,8 +978,13 @@ void run_game(fc_type fc) {
fc = setup_game(new_game());
} else setup_game(fc);
doing_restore = 0;
- mainloop();
+
+ if (!g_vm->shouldQuit())
+ mainloop();
close_game();
+
+ if (g_vm->shouldQuit())
+ break;
} while (doing_restore == 3);
release_file_context(&fc);
}
diff --git a/engines/glk/agt/interface.cpp b/engines/glk/agt/interface.cpp
index e787569..75763d3 100644
--- a/engines/glk/agt/interface.cpp
+++ b/engines/glk/agt/interface.cpp
@@ -275,7 +275,12 @@ char *agt_readline(int in_type) {
s = get_log();
else
s = agt_input(in_type);
- if (PURE_INPUT) agt_textcolor(-2);
+
+ if (g_vm->shouldQuit())
+ return nullptr;
+
+ if (PURE_INPUT)
+ agt_textcolor(-2);
if (logflag & 1)
put_log(s);
diff --git a/engines/glk/agt/os_glk.cpp b/engines/glk/agt/os_glk.cpp
index 9f01eec..d7f8f14 100644
--- a/engines/glk/agt/os_glk.cpp
+++ b/engines/glk/agt/os_glk.cpp
@@ -4775,6 +4775,10 @@ char *agt_input(int in_type) {
/* Set this up as a read buffer for the main window, and wait. */
g_vm->glk_request_line_event(gagt_main_window, buffer, length - 1, 0);
gagt_event_wait(evtype_LineInput, &event);
+ if (g_vm->shouldQuit()) {
+ g_vm->glk_cancel_line_event(gagt_main_window, &event);
+ return nullptr;
+ }
/* Terminate the input line with a NUL. */
assert((int)event.val1 < length);
@@ -4994,6 +4998,8 @@ static void gagt_event_wait_2(glui32 wait_type_1, glui32 wait_type_2, event_t *e
case evtype_Redraw:
gagt_status_redraw();
break;
+ case evtype_Quit:
+ return;
default:
break;
}
Commit: e3c2afe0738abd92cd05b6054b7ed3d47e6121ea
https://github.com/scummvm/scummvm/commit/e3c2afe0738abd92cd05b6054b7ed3d47e6121ea
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2019-11-27T21:10:29-08:00
Commit Message:
GLK: AGT: Remove old license information
Changed paths:
engines/glk/agt/agil.cpp
diff --git a/engines/glk/agt/agil.cpp b/engines/glk/agt/agil.cpp
index a813b84..b1fbcb7 100644
--- a/engines/glk/agt/agil.cpp
+++ b/engines/glk/agt/agil.cpp
@@ -697,105 +697,6 @@ static void fix_dummy(void) {
}
-/* char *v */
-
-static void print_license(void) {
- writeln("AGiliTy");
- writestr("The (Mostly) Universal AGT Interpreter, ");
- writeln(version_str);
- writeln(" Copyright (C) 1996-1999,2001 by Robert Masenten");
- writestr("[");
- writestr(portstr);
- writeln("]");
- writeln("-----------------------------------------------------------");
- writeln("");
- writeln(" This is an interpreter for game files created with Malmberg and "
- "Welch's _Adventure Game Toolkit_. AGiliTy is universal in the "
- "sense that it understands and interprets most of the many versions "
- "of the AGT game file format.");
- writeln(" It is *not* a port of the original interpreters but rather a "
- "completely new interpreter built around the game file format; "
- "while it follows the original interpreters on most things, there "
- "are some differences which are described in the file "
- "'readme.agility' which should have come with this program.");
- writeln("");
- writeln(" This software is copyright 1996-1999,2001 by Robert Masenten ");
- writeln(" This program is free software; you can redistribute it and/or "
- "modify it under the terms of version 2 of the GNU General "
- "Public License as published by the Free Software Foundation.");
- writeln(" 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.");
- writeln(" 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");
- writeln("");
- writeln(" Send comments and bug reports to Robert Masenten at:");
- writeln(" rcm-math at pacbell.net");
- writeln("");
- writeln("ACKNOWLEDGMENTS");
- writeln("Thanks to Jay Goemmer, who has sent me pages and pages of "
- "comments and bug reports; "
- "David Kinder, responsible for the Amiga and Windows ports and a "
- "source of much valuble feedback; "
- "David Youd, who has uncovered many subtle and complex bugs in "
- "both AGiliTy and Magx; "
- "Mitch Mlinar, who has contributed several patches and other "
- "suggestions; "
- "all of those who have sent me suggestions and bug reports, "
- "including "
- "Audrey DeLisle (responsible for the red smoke), David Doherty,"
- "Al Golden, "
- "John Hartnup, Walter Isaac, Sami Kivela, Alexander Lehmann, "
- "Grant E. Metcalf, "
- "Paul Mikell, Adam Myrow, Olav Nielsen, "
- "D.J. Picton, Kevin Soucy, Ben Straub, \"Grand Moff Tarkin\", "
- "Adam Thornton, "
- "Mark Tilford, David Turpin, and Gil Williamson; "
- "Volker Blasius,"
- " original maintainer of the Interactive Fiction Archive; "
- "Robert Pelak, who suggested the name \"AGiliTy\"; "
- "and to everyone on Rec.arts.int-fiction who suggested names for "
- "my interpreter.");
- writeln("");
- writeln("SPECIAL VERBS RECOGNIZED");
- writeln("These are all of the special verbs recognized by the interpreter:");
- writeln(" SCORE Print out your score.");
- writeln(" NOTIFY Turn score notification on and off.");
- writeln(" INSTRUCTIONS or INS Display the instructions for the game.");
- writeln(" INTRODUCTION or INTRO Repeat the introduction of the game.");
- writeln(" VIEW <picture> Views an illustration. (Not supported on all "
- "platforms.)");
- writeln(" BRIEF Don't print room descriptions for rooms you've seen.");
- writeln(" VERBOSE Print room descriptions even for rooms you've already "
- "seen.");
- writeln(" LIST EXITS List the exits from a room.");
- writeln(" LISTEXIT ON/OFF Turn on/off automatic listing of exits.");
- writeln(" SCRIPT Start sending a transcript to a file.");
- writeln(" UNSCRIPT Stop creating a transcript.");
- writeln(" SOUND ON, OFF Turn sound on/off.");
- writeln(" LOG Start sending all of your commands to a file.");
- writeln(" REPLAY <number> Replay your commands from a file, "
- "executing one every <number> seconds.");
- writeln(" REPLAY FAST Replay your commands from a file without waiting "
- "for you to read the scrolling text.");
- writeln(" REPLAY STEP Replay your commands from a file, "
- "one for every keypress.");
- writeln(" AGILDEBUG Access debugging commands.");
- writeln(" MENU Toggle menu mode on or off.");
- writeln(" OOPS Correct a word you just mistyped; must be the first "
- "command on a line.");
- writeln(" UNDO Undo your last move; must be the first command on a line.");
- writeln(" SAVE Save the game.");
- writeln(" RESTORE Restore the game.");
- writeln(" RESTART Restart the game.");
- writeln(" QUIT Quit.");
- writeln("");
-}
-
-
/* This is a hack to get rid of the "What Now?" prompt. */
static void fix_prompt(void) {
descr_line *d;
@@ -895,20 +796,19 @@ static fc_type setup_game(fc_type fc)
print_title(fc);
have_ins = open_ins_file(fc, 0);
- do {
- if (have_ins)
- writestr("Choose <I>nstructions, <A>GiliTy Information, "
- "or <other> to start the game");
- else
- writestr("Choose <A>GiliTy Information or <other> to start the game");
- choice = tolower(agt_getchar()); /* Wait for keypress */
- if (g_vm->shouldQuit())
- return nullptr;
+ if (have_ins) {
+ do {
+ writestr("Choose <I>nstructions, or <other> to start the game");
- agt_clrscr();
- if (have_ins && choice == 'i') print_instructions(fc);
- else if (choice == 'a') print_license();
- } while ((choice == 'i' && have_ins) || choice == 'a');
+ choice = tolower(agt_getchar()); /* Wait for keypress */
+ if (g_vm->shouldQuit())
+ return nullptr;
+
+ agt_clrscr();
+ if (have_ins && choice == 'i')
+ print_instructions(fc);
+ } while (choice == 'i');
+ }
close_ins_file();
if (!intro_first && intro_ptr.size > 0) {
Commit: 43579347eb7b852fb947f44d30e95450fd247936
https://github.com/scummvm/scummvm/commit/43579347eb7b852fb947f44d30e95450fd247936
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2019-11-27T21:10:29-08:00
Commit Message:
GLK: AGT: Move configuration options into AGT class
Changed paths:
engines/glk/agt/agt.cpp
engines/glk/agt/agt.h
engines/glk/agt/os_glk.cpp
diff --git a/engines/glk/agt/agt.cpp b/engines/glk/agt/agt.cpp
index ff78e59..1f0766f 100644
--- a/engines/glk/agt/agt.cpp
+++ b/engines/glk/agt/agt.cpp
@@ -23,7 +23,6 @@
#include "glk/agt/agt.h"
#include "glk/quetzal.h"
#include "common/config-manager.h"
-#include "common/translation.h"
namespace Glk {
namespace AGT {
@@ -35,18 +34,62 @@ extern int glk_startup_code();
extern void gagt_finalizer();
AGT::AGT(OSystem *syst, const GlkGameDescription &gameDesc) : GlkAPI(syst, gameDesc),
- gagt_gamefile(nullptr), gagt_game_message(nullptr) {
+ gagt_main_window(nullptr), gagt_status_window(nullptr), gagt_gamefile(nullptr),
+ gagt_game_message(nullptr), gagt_delay_mode(DELAY_SHORT), gagt_font_mode(FONT_AUTOMATIC),
+ gagt_transcript_stream(nullptr), gagt_inputlog_stream(nullptr),
+ gagt_readlog_stream(nullptr), gagt_replacement_enabled(true),
+ gagt_extended_status_enabled(true), gagt_abbreviations_enabled(true),
+ gagt_commands_enabled(true), gagt_clean_exit_test(false) {
g_vm = this;
}
void AGT::runGame() {
+ initialize();
+ glk_main();
+
+ gagt_finalizer();
+}
+
+void AGT::initialize() {
_gameFile.close();
gagt_gamefile = getFilename().c_str();
+ initializeSettings();
glk_startup_code();
- glk_main();
+}
- gagt_finalizer();
+void AGT::initializeSettings() {
+ // Delay
+ if (ConfMan.hasKey("delay")) {
+ Common::String delay = ConfMan.get("delay");
+ switch (tolower(delay.firstChar())) {
+ case 'f':
+ // Full
+ gagt_delay_mode = DELAY_FULL;
+ break;
+ case 's':
+ // Short
+ gagt_delay_mode = DELAY_SHORT;
+ break;
+ case 'n':
+ case 'o':
+ // None/off
+ gagt_delay_mode = DELAY_OFF;
+ break;
+ default:
+ break;
+ }
+ }
+
+ // Boolean flags
+ if (ConfMan.hasKey("replacement"))
+ gagt_replacement_enabled = ConfMan.getBool("replacement");
+ if (ConfMan.hasKey("abbreviations"))
+ gagt_abbreviations_enabled = ConfMan.getBool("abbreviations");
+ if (ConfMan.hasKey("extended_status"))
+ gagt_extended_status_enabled = ConfMan.getBool("extended_status");
+ if (ConfMan.hasKey("commands"))
+ gagt_commands_enabled = ConfMan.getBool("commands");
}
Common::Error AGT::readSaveData(Common::SeekableReadStream *rs) {
diff --git a/engines/glk/agt/agt.h b/engines/glk/agt/agt.h
index 1f23b2b..cf70c39 100644
--- a/engines/glk/agt/agt.h
+++ b/engines/glk/agt/agt.h
@@ -31,14 +31,65 @@
namespace Glk {
namespace AGT {
+enum DelayMode {
+ DELAY_FULL, DELAY_SHORT, DELAY_OFF
+};
+
+enum FontMode {
+ FONT_AUTOMATIC, FONT_FIXED_WIDTH, FONT_PROPORTIONAL, FONT_DEBUG
+};
/**
* AGT Adams game interpreter
*/
class AGT : public GlkAPI {
public:
- const char *gagt_gamefile = NULL; /* Name of game file. */
- const char *gagt_game_message = NULL; /* Error message. */
+ const char *gagt_gamefile; /* Name of game file. */
+ const char *gagt_game_message; /* Error message. */
+ DelayMode gagt_delay_mode;
+
+ /**
+ * We use two Glk windows; one is two lines at the top of the display area
+ * for status, and the other is the remainder of the display area, used for,
+ * well, everything else. Where a particular Glk implementation won't do
+ * more than one window, the status window remains NULL.
+ */
+ winid_t gagt_main_window, gagt_status_window;
+
+ /**
+ * Transcript stream and input log. These are NULL if there is no current
+ * collection of these strings.
+ */
+ strid_t gagt_transcript_stream, gagt_inputlog_stream;
+
+ /**
+ * Input read log stream, for reading back an input log
+ */
+ strid_t gagt_readlog_stream;
+
+ /* Options that may be turned off or set by command line flags. */
+ FontMode gagt_font_mode = FONT_AUTOMATIC;
+ bool gagt_replacement_enabled, gagt_extended_status_enabled,
+ gagt_abbreviations_enabled, gagt_commands_enabled;
+
+ /**
+ * Flag to set if we want to test for a clean exit. Without this it's a
+ * touch tricky sometimes to corner AGiliTy into calling exit() for us; it
+ * tends to require a broken game file.
+ */
+ bool gagt_clean_exit_test;
+
+
+private:
+ /**
+ * Handles initialization
+ */
+ void initialize();
+
+ /**
+ * Handles flag setup from configuration
+ */
+ void initializeSettings();
public:
/**
* Constructor
diff --git a/engines/glk/agt/os_glk.cpp b/engines/glk/agt/os_glk.cpp
index d7f8f14..c80b3b9 100644
--- a/engines/glk/agt/os_glk.cpp
+++ b/engines/glk/agt/os_glk.cpp
@@ -69,40 +69,6 @@ namespace AGT {
/* Glk AGiliTy port version number. */
static const glui32 GAGT_PORT_VERSION = 0x00010701;
-/*
- * We use two Glk windows; one is two lines at the top of the display area
- * for status, and the other is the remainder of the display area, used for,
- * well, everything else. Where a particular Glk implementation won't do
- * more than one window, the status window remains NULL.
- */
-static winid_t gagt_main_window = NULL,
- gagt_status_window = NULL;
-
-/*
- * Transcript stream and input log. These are NULL if there is no current
- * collection of these strings.
- */
-static strid_t gagt_transcript_stream = NULL,
- gagt_inputlog_stream = NULL;
-
-/* Input read log stream, for reading back an input log. */
-static strid_t gagt_readlog_stream = NULL;
-
-/* Options that may be turned off or set by command line flags. */
-enum FontMode {
- FONT_AUTOMATIC, FONT_FIXED_WIDTH, FONT_PROPORTIONAL, FONT_DEBUG
-};
-static FontMode gagt_font_mode = FONT_AUTOMATIC;
-
-enum DelayMode {
- DELAY_FULL, DELAY_SHORT, DELAY_OFF
-};
-static DelayMode gagt_delay_mode = DELAY_SHORT;
-static int gagt_replacement_enabled = TRUE,
- gagt_extended_status_enabled = TRUE,
- gagt_abbreviations_enabled = TRUE,
- gagt_commands_enabled = TRUE;
-
/* Forward declaration of event wait functions. */
static void gagt_event_wait(glui32 wait_type, event_t *event);
static void gagt_event_wait_2(glui32 wait_type_1,
@@ -132,15 +98,15 @@ static void gagt_fatal(const char *string) {
* If the failure happens too early for us to have a window, print
* the message to stderr.
*/
- if (!gagt_main_window)
+ if (!g_vm->gagt_main_window)
error("INTERNAL ERROR: %s", string);
/* Cancel all possible pending window input events. */
- g_vm->glk_cancel_line_event(gagt_main_window, NULL);
- g_vm->glk_cancel_char_event(gagt_main_window);
+ g_vm->glk_cancel_line_event(g_vm->gagt_main_window, NULL);
+ g_vm->glk_cancel_char_event(g_vm->gagt_main_window);
/* Print a message indicating the error. */
- g_vm->glk_set_window(gagt_main_window);
+ g_vm->glk_set_window(g_vm->gagt_main_window);
g_vm->glk_set_style(style_Normal);
g_vm->glk_put_string("\n\nINTERNAL ERROR: ");
g_vm->glk_put_string(string);
@@ -354,11 +320,11 @@ enum {
void start_interface(fc_type fc) {
switch (font_status) {
case GAGT_FIXED_REQUIRED:
- gagt_font_mode = FONT_FIXED_WIDTH;
+ g_vm->gagt_font_mode = FONT_FIXED_WIDTH;
break;
case GAGT_PROPORTIONAL_OKAY:
- gagt_font_mode = FONT_PROPORTIONAL;
+ g_vm->gagt_font_mode = FONT_PROPORTIONAL;
break;
default:
@@ -731,16 +697,16 @@ void agt_statline(const char *cp_string) {
*/
static void gagt_status_update_extended() {
uint width, height;
- assert(gagt_status_window);
+ assert(g_vm->gagt_status_window);
- g_vm->glk_window_get_size(gagt_status_window, &width, &height);
+ g_vm->glk_window_get_size(g_vm->gagt_status_window, &width, &height);
if (height > 1) {
uint32 index;
int exit;
/* Clear the second status line only. */
- g_vm->glk_window_move_cursor(gagt_status_window, 0, 1);
- g_vm->glk_set_window(gagt_status_window);
+ g_vm->glk_window_move_cursor(g_vm->gagt_status_window, 0, 1);
+ g_vm->glk_set_window(g_vm->gagt_status_window);
g_vm->glk_set_style(style_User1);
for (index = 0; index < width; index++)
g_vm->glk_put_char(' ');
@@ -749,7 +715,7 @@ static void gagt_status_update_extended() {
* Check bits in the compass rose, and print out exit names from
* the exitname[] array.
*/
- g_vm->glk_window_move_cursor(gagt_status_window, 0, 1);
+ g_vm->glk_window_move_cursor(g_vm->gagt_status_window, 0, 1);
g_vm->glk_put_string(" Exits: ");
for (exit = 0; exit < (int)sizeof(exitname) / (int)sizeof(exitname[0]); exit++) {
if (compass_rose & (1 << exit)) {
@@ -760,12 +726,12 @@ static void gagt_status_update_extended() {
/* If the delay flag is set, print a waiting indicator at the right. */
if (gagt_inside_delay) {
- g_vm->glk_window_move_cursor(gagt_status_window,
+ g_vm->glk_window_move_cursor(g_vm->gagt_status_window,
width - strlen("Waiting... "), 1);
g_vm->glk_put_string("Waiting... ");
}
- g_vm->glk_set_window(gagt_main_window);
+ g_vm->glk_set_window(g_vm->gagt_main_window);
}
}
@@ -783,18 +749,18 @@ static void gagt_status_update_extended() {
static void gagt_status_update() {
uint width, height;
uint32 index;
- assert(gagt_status_window);
+ assert(g_vm->gagt_status_window);
- g_vm->glk_window_get_size(gagt_status_window, &width, &height);
+ g_vm->glk_window_get_size(g_vm->gagt_status_window, &width, &height);
if (height > 0) {
- g_vm->glk_window_clear(gagt_status_window);
- g_vm->glk_window_move_cursor(gagt_status_window, 0, 0);
- g_vm->glk_set_window(gagt_status_window);
+ g_vm->glk_window_clear(g_vm->gagt_status_window);
+ g_vm->glk_window_move_cursor(g_vm->gagt_status_window, 0, 0);
+ g_vm->glk_set_window(g_vm->gagt_status_window);
g_vm->glk_set_style(style_User1);
for (index = 0; index < width; index++)
g_vm->glk_put_char(' ');
- g_vm->glk_window_move_cursor(gagt_status_window, 0, 0);
+ g_vm->glk_window_move_cursor(g_vm->gagt_status_window, 0, 0);
/* Call print_statline() to refresh status line buffer contents. */
print_statline();
@@ -812,7 +778,7 @@ static void gagt_status_update() {
? width : strlen(gagt_status_buffer);
g_vm->glk_put_buffer(gagt_status_buffer, print_width);
- if (gagt_extended_status_enabled)
+ if (g_vm->gagt_extended_status_enabled)
gagt_status_update_extended();
} else {
/*
@@ -822,7 +788,7 @@ static void gagt_status_update() {
g_vm->glk_put_string("Glk AGiliTy version 1.1.1.1");
}
- g_vm->glk_set_window(gagt_main_window);
+ g_vm->glk_set_window(g_vm->gagt_main_window);
}
}
@@ -881,7 +847,7 @@ static void gagt_status_print() {
*/
static void gagt_status_notify() {
if (!BATCH_MODE) {
- if (gagt_status_window)
+ if (g_vm->gagt_status_window)
gagt_status_update();
else
gagt_status_print();
@@ -901,7 +867,7 @@ static void gagt_status_notify() {
*/
static void gagt_status_redraw() {
if (!BATCH_MODE) {
- if (gagt_status_window) {
+ if (g_vm->gagt_status_window) {
uint width, height;
winid_t parent;
@@ -909,7 +875,7 @@ static void gagt_status_redraw() {
* Measure the status window, and update the interpreter's
* status_width variable.
*/
- g_vm->glk_window_get_size(gagt_status_window, &width, &height);
+ g_vm->glk_window_get_size(g_vm->gagt_status_window, &width, &height);
status_width = width;
/*
@@ -923,7 +889,7 @@ static void gagt_status_redraw() {
* Glk libraries other than Xglk, moreover, we're careful to
* activate it only on resize and arrange events.
*/
- parent = g_vm->glk_window_get_parent(gagt_status_window);
+ parent = g_vm->glk_window_get_parent(g_vm->gagt_status_window);
g_vm->glk_window_set_arrangement(parent,
winmethod_Above | winmethod_Fixed,
height, NULL);
@@ -949,7 +915,7 @@ static void gagt_status_in_delay(int inside_delay) {
* Update just the second line of the status window display, if
* extended status is being displayed.
*/
- if (gagt_status_window && gagt_extended_status_enabled)
+ if (g_vm->gagt_status_window && g_vm->gagt_extended_status_enabled)
gagt_status_update_extended();
}
}
@@ -1230,7 +1196,7 @@ static void gagt_init_user_styles() {
static int gagt_confirm_appearance(glui32 style, glui32 stylehint, glui32 expected) {
uint result;
- if (g_vm->glk_style_measure(gagt_main_window, style, stylehint, &result)) {
+ if (g_vm->glk_style_measure(g_vm->gagt_main_window, style, stylehint, &result)) {
/*
* Measurement succeeded, so return TRUE if the result matches the
* caller's expectation.
@@ -2848,7 +2814,7 @@ static void gagt_mark_specials() {
* Search all paragraphs for special matches, if enabled. When a special
* match is found, mark the paragraph with a pointer to the matching entry.
*/
- if (gagt_replacement_enabled) {
+ if (g_vm->gagt_replacement_enabled) {
gagt_paragraphref_t paragraph;
for (paragraph = gagt_get_first_paragraph();
@@ -3323,7 +3289,7 @@ static void gagt_output_flush() {
* routines present somewhat different output, and are responsible for
* displaying both the page buffer _and_ any buffered current line text.
*/
- switch (gagt_font_mode) {
+ switch (g_vm->gagt_font_mode) {
case FONT_AUTOMATIC:
gagt_display_auto();
break;
@@ -3365,7 +3331,7 @@ void agt_clrscr() {
/* Flush any pending buffered output, and clear the main window. */
gagt_output_flush();
- g_vm->glk_window_clear(gagt_main_window);
+ g_vm->glk_window_clear(g_vm->gagt_main_window);
/* Add a series of newlines to any script file. */
if (script_on)
@@ -3472,7 +3438,7 @@ void agt_delay(int seconds) {
* are currently temporarily suspended.
*/
if (!g_vm->glk_gestalt(gestalt_Timer, 0)
- || gagt_delay_mode == DELAY_OFF
+ || g_vm->gagt_delay_mode == DELAY_OFF
|| seconds <= 0 || gagt_delays_suspended)
return;
@@ -3482,10 +3448,10 @@ void agt_delay(int seconds) {
/* Calculate the number of milliseconds to delay. */
milliseconds = (seconds * GAGT_MS_PER_SEC)
- / (gagt_delay_mode == DELAY_SHORT ? 2 : 1);
+ / (g_vm->gagt_delay_mode == DELAY_SHORT ? 2 : 1);
/* Request timer events, and let a keypress cancel the delay. */
- g_vm->glk_request_char_event(gagt_main_window);
+ g_vm->glk_request_char_event(g_vm->gagt_main_window);
g_vm->glk_request_timer_events(GAGT_DELAY_TIMEOUT);
/*
@@ -3509,13 +3475,13 @@ void agt_delay(int seconds) {
delay_completed = FALSE;
break;
} else
- g_vm->glk_request_char_event(gagt_main_window);
+ g_vm->glk_request_char_event(g_vm->gagt_main_window);
}
}
/* Cancel any pending character input, and timer events. */
if (delay_completed)
- g_vm->glk_cancel_char_event(gagt_main_window);
+ g_vm->glk_cancel_char_event(g_vm->gagt_main_window);
g_vm->glk_request_timer_events(0);
/* Clear the waiting indicator. */
@@ -3700,7 +3666,7 @@ static void gagt_command_script(const char *argument) {
if (gagt_strcasecmp(argument, "on") == 0) {
frefid_t fileref;
- if (gagt_transcript_stream) {
+ if (g_vm->gagt_transcript_stream) {
gagt_normal_string("Glk transcript is already on.\n");
return;
}
@@ -3713,36 +3679,36 @@ static void gagt_command_script(const char *argument) {
return;
}
- gagt_transcript_stream = g_vm->glk_stream_open_file(fileref,
+ g_vm->gagt_transcript_stream = g_vm->glk_stream_open_file(fileref,
filemode_WriteAppend, 0);
g_vm->glk_fileref_destroy(fileref);
- if (!gagt_transcript_stream) {
+ if (!g_vm->gagt_transcript_stream) {
gagt_standout_string("Glk transcript failed.\n");
return;
}
- g_vm->glk_window_set_echo_stream(gagt_main_window, gagt_transcript_stream);
+ g_vm->glk_window_set_echo_stream(g_vm->gagt_main_window, g_vm->gagt_transcript_stream);
gagt_normal_string("Glk transcript is now on.\n");
}
else if (gagt_strcasecmp(argument, "off") == 0) {
- if (!gagt_transcript_stream) {
+ if (!g_vm->gagt_transcript_stream) {
gagt_normal_string("Glk transcript is already off.\n");
return;
}
- g_vm->glk_stream_close(gagt_transcript_stream, NULL);
- gagt_transcript_stream = NULL;
+ g_vm->glk_stream_close(g_vm->gagt_transcript_stream, NULL);
+ g_vm->gagt_transcript_stream = NULL;
- g_vm->glk_window_set_echo_stream(gagt_main_window, NULL);
+ g_vm->glk_window_set_echo_stream(g_vm->gagt_main_window, NULL);
gagt_normal_string("Glk transcript is now off.\n");
}
else if (strlen(argument) == 0) {
gagt_normal_string("Glk transcript is ");
- gagt_normal_string(gagt_transcript_stream ? "on" : "off");
+ gagt_normal_string(g_vm->gagt_transcript_stream ? "on" : "off");
gagt_normal_string(".\n");
}
@@ -3767,7 +3733,7 @@ static void gagt_command_inputlog(const char *argument) {
if (gagt_strcasecmp(argument, "on") == 0) {
frefid_t fileref;
- if (gagt_inputlog_stream) {
+ if (g_vm->gagt_inputlog_stream) {
gagt_normal_string("Glk input logging is already on.\n");
return;
}
@@ -3780,10 +3746,10 @@ static void gagt_command_inputlog(const char *argument) {
return;
}
- gagt_inputlog_stream = g_vm->glk_stream_open_file(fileref,
+ g_vm->gagt_inputlog_stream = g_vm->glk_stream_open_file(fileref,
filemode_WriteAppend, 0);
g_vm->glk_fileref_destroy(fileref);
- if (!gagt_inputlog_stream) {
+ if (!g_vm->gagt_inputlog_stream) {
gagt_standout_string("Glk input logging failed.\n");
return;
}
@@ -3792,20 +3758,20 @@ static void gagt_command_inputlog(const char *argument) {
}
else if (gagt_strcasecmp(argument, "off") == 0) {
- if (!gagt_inputlog_stream) {
+ if (!g_vm->gagt_inputlog_stream) {
gagt_normal_string("Glk input logging is already off.\n");
return;
}
- g_vm->glk_stream_close(gagt_inputlog_stream, NULL);
- gagt_inputlog_stream = NULL;
+ g_vm->glk_stream_close(g_vm->gagt_inputlog_stream, NULL);
+ g_vm->gagt_inputlog_stream = NULL;
gagt_normal_string("Glk input log is now off.\n");
}
else if (strlen(argument) == 0) {
gagt_normal_string("Glk input logging is ");
- gagt_normal_string(gagt_inputlog_stream ? "on" : "off");
+ gagt_normal_string(g_vm->gagt_inputlog_stream ? "on" : "off");
gagt_normal_string(".\n");
}
@@ -3830,7 +3796,7 @@ static void gagt_command_readlog(const char *argument) {
if (gagt_strcasecmp(argument, "on") == 0) {
frefid_t fileref;
- if (gagt_readlog_stream) {
+ if (g_vm->gagt_readlog_stream) {
gagt_normal_string("Glk read log is already on.\n");
return;
}
@@ -3849,9 +3815,9 @@ static void gagt_command_readlog(const char *argument) {
return;
}
- gagt_readlog_stream = g_vm->glk_stream_open_file(fileref, filemode_Read, 0);
+ g_vm->gagt_readlog_stream = g_vm->glk_stream_open_file(fileref, filemode_Read, 0);
g_vm->glk_fileref_destroy(fileref);
- if (!gagt_readlog_stream) {
+ if (!g_vm->gagt_readlog_stream) {
gagt_standout_string("Glk read log failed.\n");
return;
}
@@ -3860,20 +3826,20 @@ static void gagt_command_readlog(const char *argument) {
}
else if (gagt_strcasecmp(argument, "off") == 0) {
- if (!gagt_readlog_stream) {
+ if (!g_vm->gagt_readlog_stream) {
gagt_normal_string("Glk read log is already off.\n");
return;
}
- g_vm->glk_stream_close(gagt_readlog_stream, NULL);
- gagt_readlog_stream = NULL;
+ g_vm->glk_stream_close(g_vm->gagt_readlog_stream, NULL);
+ g_vm->gagt_readlog_stream = NULL;
gagt_normal_string("Glk read log is now off.\n");
}
else if (strlen(argument) == 0) {
gagt_normal_string("Glk read log is ");
- gagt_normal_string(gagt_readlog_stream ? "on" : "off");
+ gagt_normal_string(g_vm->gagt_readlog_stream ? "on" : "off");
gagt_normal_string(".\n");
}
@@ -3896,28 +3862,28 @@ static void gagt_command_abbreviations(const char *argument) {
assert(argument);
if (gagt_strcasecmp(argument, "on") == 0) {
- if (gagt_abbreviations_enabled) {
+ if (g_vm->gagt_abbreviations_enabled) {
gagt_normal_string("Glk abbreviation expansions are already on.\n");
return;
}
- gagt_abbreviations_enabled = TRUE;
+ g_vm->gagt_abbreviations_enabled = TRUE;
gagt_normal_string("Glk abbreviation expansions are now on.\n");
}
else if (gagt_strcasecmp(argument, "off") == 0) {
- if (!gagt_abbreviations_enabled) {
+ if (!g_vm->gagt_abbreviations_enabled) {
gagt_normal_string("Glk abbreviation expansions are already off.\n");
return;
}
- gagt_abbreviations_enabled = FALSE;
+ g_vm->gagt_abbreviations_enabled = FALSE;
gagt_normal_string("Glk abbreviation expansions are now off.\n");
}
else if (strlen(argument) == 0) {
gagt_normal_string("Glk abbreviation expansions are ");
- gagt_normal_string(gagt_abbreviations_enabled ? "on" : "off");
+ gagt_normal_string(g_vm->gagt_abbreviations_enabled ? "on" : "off");
gagt_normal_string(".\n");
}
@@ -3934,7 +3900,7 @@ static void gagt_command_abbreviations(const char *argument) {
/*
* gagt_command_fonts()
*
- * Set the value for gagt_font_mode depending on the argument from the
+ * Set the value for g_vm->gagt_font_mode depending on the argument from the
* user's command escape.
*
* Despite our best efforts, font control may still be wrong in some games.
@@ -3944,50 +3910,50 @@ static void gagt_command_fonts(const char *argument) {
assert(argument);
if (gagt_strcasecmp(argument, "fixed") == 0) {
- if (gagt_font_mode == FONT_FIXED_WIDTH) {
+ if (g_vm->gagt_font_mode == FONT_FIXED_WIDTH) {
gagt_normal_string("Glk font control is already 'fixed'.\n");
return;
}
- gagt_font_mode = FONT_FIXED_WIDTH;
+ g_vm->gagt_font_mode = FONT_FIXED_WIDTH;
gagt_normal_string("Glk font control is now 'fixed'.\n");
}
else if (gagt_strcasecmp(argument, "variable") == 0
|| gagt_strcasecmp(argument, "proportional") == 0) {
- if (gagt_font_mode == FONT_PROPORTIONAL) {
+ if (g_vm->gagt_font_mode == FONT_PROPORTIONAL) {
gagt_normal_string("Glk font control is already 'proportional'.\n");
return;
}
- gagt_font_mode = FONT_PROPORTIONAL;
+ g_vm->gagt_font_mode = FONT_PROPORTIONAL;
gagt_normal_string("Glk font control is now 'proportional'.\n");
}
else if (gagt_strcasecmp(argument, "auto") == 0
|| gagt_strcasecmp(argument, "automatic") == 0) {
- if (gagt_font_mode == FONT_AUTOMATIC) {
+ if (g_vm->gagt_font_mode == FONT_AUTOMATIC) {
gagt_normal_string("Glk font control is already 'automatic'.\n");
return;
}
- gagt_font_mode = FONT_AUTOMATIC;
+ g_vm->gagt_font_mode = FONT_AUTOMATIC;
gagt_normal_string("Glk font control is now 'automatic'.\n");
}
else if (gagt_strcasecmp(argument, "debug") == 0) {
- if (gagt_font_mode == FONT_DEBUG) {
+ if (g_vm->gagt_font_mode == FONT_DEBUG) {
gagt_normal_string("Glk font control is already 'debug'.\n");
return;
}
- gagt_font_mode = FONT_DEBUG;
+ g_vm->gagt_font_mode = FONT_DEBUG;
gagt_normal_string("Glk font control is now 'debug'.\n");
}
else if (strlen(argument) == 0) {
gagt_normal_string("Glk font control is set to '");
- switch (gagt_font_mode) {
+ switch (g_vm->gagt_font_mode) {
case FONT_AUTOMATIC:
gagt_normal_string("automatic");
break;
@@ -4027,7 +3993,7 @@ static void gagt_command_fonts(const char *argument) {
/*
* gagt_command_delays()
*
- * Set a value for gagt_delay_mode depending on the argument from
+ * Set a value for g_vm->gagt_delay_mode depending on the argument from
* the user's command escape.
*/
static void gagt_command_delays(const char *argument) {
@@ -4040,40 +4006,40 @@ static void gagt_command_delays(const char *argument) {
if (gagt_strcasecmp(argument, "full") == 0
|| gagt_strcasecmp(argument, "on") == 0) {
- if (gagt_delay_mode == DELAY_FULL) {
+ if (g_vm->gagt_delay_mode == DELAY_FULL) {
gagt_normal_string("Glk delay mode is already 'full'.\n");
return;
}
- gagt_delay_mode = DELAY_FULL;
+ g_vm->gagt_delay_mode = DELAY_FULL;
gagt_normal_string("Glk delay mode is now 'full'.\n");
}
else if (gagt_strcasecmp(argument, "short") == 0
|| gagt_strcasecmp(argument, "half") == 0) {
- if (gagt_delay_mode == DELAY_SHORT) {
+ if (g_vm->gagt_delay_mode == DELAY_SHORT) {
gagt_normal_string("Glk delay mode is already 'short'.\n");
return;
}
- gagt_delay_mode = DELAY_SHORT;
+ g_vm->gagt_delay_mode = DELAY_SHORT;
gagt_normal_string("Glk delay mode is now 'short'.\n");
}
else if (gagt_strcasecmp(argument, "none") == 0
|| gagt_strcasecmp(argument, "off") == 0) {
- if (gagt_delay_mode == DELAY_OFF) {
+ if (g_vm->gagt_delay_mode == DELAY_OFF) {
gagt_normal_string("Glk delay mode is already 'none'.\n");
return;
}
- gagt_delay_mode = DELAY_OFF;
+ g_vm->gagt_delay_mode = DELAY_OFF;
gagt_normal_string("Glk delay mode is now 'none'.\n");
}
else if (strlen(argument) == 0) {
gagt_normal_string("Glk delay mode is set to '");
- switch (gagt_delay_mode) {
+ switch (g_vm->gagt_delay_mode) {
case DELAY_FULL:
gagt_normal_string("full");
break;
@@ -4123,7 +4089,7 @@ static void gagt_command_width(const char *argument) {
char buffer[16];
assert(argument);
- if (!gagt_status_window) {
+ if (!g_vm->gagt_status_window) {
gagt_normal_string("Glk's current display width is unknown.\n");
return;
}
@@ -4145,28 +4111,28 @@ static void gagt_command_replacements(const char *argument) {
assert(argument);
if (gagt_strcasecmp(argument, "on") == 0) {
- if (gagt_replacement_enabled) {
+ if (g_vm->gagt_replacement_enabled) {
gagt_normal_string("Glk replacements are already on.\n");
return;
}
- gagt_replacement_enabled = TRUE;
+ g_vm->gagt_replacement_enabled = TRUE;
gagt_normal_string("Glk replacements are now on.\n");
}
else if (gagt_strcasecmp(argument, "off") == 0) {
- if (!gagt_replacement_enabled) {
+ if (!g_vm->gagt_replacement_enabled) {
gagt_normal_string("Glk replacements are already off.\n");
return;
}
- gagt_replacement_enabled = FALSE;
+ g_vm->gagt_replacement_enabled = FALSE;
gagt_normal_string("Glk replacements are now off.\n");
}
else if (strlen(argument) == 0) {
gagt_normal_string("Glk replacements are ");
- gagt_normal_string(gagt_replacement_enabled ? "on" : "off");
+ gagt_normal_string(g_vm->gagt_replacement_enabled ? "on" : "off");
gagt_normal_string(".\n");
}
@@ -4188,44 +4154,44 @@ static void gagt_command_replacements(const char *argument) {
static void gagt_command_statusline(const char *argument) {
assert(argument);
- if (!gagt_status_window) {
+ if (!g_vm->gagt_status_window) {
gagt_normal_string("Glk status window is not available.\n");
return;
}
if (gagt_strcasecmp(argument, "extended") == 0
|| gagt_strcasecmp(argument, "full") == 0) {
- if (gagt_extended_status_enabled) {
+ if (g_vm->gagt_extended_status_enabled) {
gagt_normal_string("Glk status line mode is already 'extended'.\n");
return;
}
/* Expand the status window down to a second line. */
- g_vm->glk_window_set_arrangement(g_vm->glk_window_get_parent(gagt_status_window),
+ g_vm->glk_window_set_arrangement(g_vm->glk_window_get_parent(g_vm->gagt_status_window),
winmethod_Above | winmethod_Fixed, 2, NULL);
- gagt_extended_status_enabled = TRUE;
+ g_vm->gagt_extended_status_enabled = TRUE;
gagt_normal_string("Glk status line mode is now 'extended'.\n");
}
else if (gagt_strcasecmp(argument, "short") == 0
|| gagt_strcasecmp(argument, "normal") == 0) {
- if (!gagt_extended_status_enabled) {
+ if (!g_vm->gagt_extended_status_enabled) {
gagt_normal_string("Glk status line mode is already 'short'.\n");
return;
}
/* Shrink the status window down to one line. */
- g_vm->glk_window_set_arrangement(g_vm->glk_window_get_parent(gagt_status_window),
+ g_vm->glk_window_set_arrangement(g_vm->glk_window_get_parent(g_vm->gagt_status_window),
winmethod_Above | winmethod_Fixed, 1, NULL);
- gagt_extended_status_enabled = FALSE;
+ g_vm->gagt_extended_status_enabled = FALSE;
gagt_normal_string("Glk status line mode is now 'short'.\n");
}
else if (strlen(argument) == 0) {
gagt_normal_string("Glk status line mode is set to '");
- gagt_normal_string(gagt_extended_status_enabled ? "extended" : "short");
+ gagt_normal_string(g_vm->gagt_extended_status_enabled ? "extended" : "short");
gagt_normal_string("'.\n");
}
@@ -4282,13 +4248,13 @@ static void gagt_command_commands(const char *argument) {
}
else if (gagt_strcasecmp(argument, "off") == 0) {
- gagt_commands_enabled = FALSE;
+ g_vm->gagt_commands_enabled = FALSE;
gagt_normal_string("Glk commands are now off.\n");
}
else if (strlen(argument) == 0) {
gagt_normal_string("Glk commands are ");
- gagt_normal_string(gagt_commands_enabled ? "on" : "off");
+ gagt_normal_string(g_vm->gagt_commands_enabled ? "on" : "off");
gagt_normal_string(".\n");
}
@@ -4743,11 +4709,11 @@ char *agt_input(int in_type) {
* If we have an input log to read from, use that until it is exhausted.
* On end of file, close the stream and resume input from line requests.
*/
- if (gagt_readlog_stream) {
+ if (g_vm->gagt_readlog_stream) {
glui32 chars;
/* Get the next line from the log stream. */
- chars = g_vm->glk_get_line_stream(gagt_readlog_stream, buffer, length);
+ chars = g_vm->glk_get_line_stream(g_vm->gagt_readlog_stream, buffer, length);
if (chars > 0) {
/* Echo the line just read in input style. */
g_vm->glk_set_style(style_Input);
@@ -4768,15 +4734,15 @@ char *agt_input(int in_type) {
* We're at the end of the log stream. Close it, and then continue
* on to request a line from Glk.
*/
- g_vm->glk_stream_close(gagt_readlog_stream, NULL);
- gagt_readlog_stream = NULL;
+ g_vm->glk_stream_close(g_vm->gagt_readlog_stream, NULL);
+ g_vm->gagt_readlog_stream = NULL;
}
/* Set this up as a read buffer for the main window, and wait. */
- g_vm->glk_request_line_event(gagt_main_window, buffer, length - 1, 0);
+ g_vm->glk_request_line_event(g_vm->gagt_main_window, buffer, length - 1, 0);
gagt_event_wait(evtype_LineInput, &event);
if (g_vm->shouldQuit()) {
- g_vm->glk_cancel_line_event(gagt_main_window, &event);
+ g_vm->glk_cancel_line_event(g_vm->gagt_main_window, &event);
return nullptr;
}
@@ -4788,7 +4754,7 @@ char *agt_input(int in_type) {
* If neither abbreviations nor local commands are enabled, use the data
* read above without further massaging.
*/
- if (gagt_abbreviations_enabled || gagt_commands_enabled) {
+ if (g_vm->gagt_abbreviations_enabled || g_vm->gagt_commands_enabled) {
char *cmd;
/*
@@ -4802,14 +4768,14 @@ char *agt_input(int in_type) {
memmove(cmd, cmd + 1, strlen(cmd));
} else {
/* Check for, and expand, any abbreviated commands. */
- if (gagt_abbreviations_enabled)
+ if (g_vm->gagt_abbreviations_enabled)
gagt_expand_abbreviations(buffer, length);
/*
* Check for standalone "help", then for Glk port special commands;
* suppress the interpreter's use of this input for Glk commands.
*/
- if (gagt_commands_enabled) {
+ if (g_vm->gagt_commands_enabled) {
int posn;
posn = strspn(buffer, "\t ");
@@ -4834,9 +4800,9 @@ char *agt_input(int in_type) {
* by logging here we get any abbreviation expansions but we won't log glk
* special commands, nor any input read from a current open input log.
*/
- if (gagt_inputlog_stream) {
- g_vm->glk_put_string_stream(gagt_inputlog_stream, buffer);
- g_vm->glk_put_char_stream(gagt_inputlog_stream, '\n');
+ if (g_vm->gagt_inputlog_stream) {
+ g_vm->glk_put_string_stream(g_vm->gagt_inputlog_stream, buffer);
+ g_vm->glk_put_char_stream(g_vm->gagt_inputlog_stream, '\n');
}
/*
@@ -4884,12 +4850,12 @@ char agt_getkey(rbool echo_char) {
* If we have an input log to read from, use that as above until it is
* exhausted. We take just the first character of a given line.
*/
- if (gagt_readlog_stream) {
+ if (g_vm->gagt_readlog_stream) {
glui32 chars;
char logbuffer[GAGT_INPUTBUFFER_LENGTH + 1];
/* Get the next line from the log stream. */
- chars = g_vm->glk_get_line_stream(gagt_readlog_stream,
+ chars = g_vm->glk_get_line_stream(g_vm->gagt_readlog_stream,
logbuffer, sizeof(logbuffer));
if (chars > 0) {
/* Take just the first character, adding a newline if necessary. */
@@ -4916,8 +4882,8 @@ char agt_getkey(rbool echo_char) {
* We're at the end of the log stream. Close it, and then continue
* on to request a character from Glk.
*/
- g_vm->glk_stream_close(gagt_readlog_stream, NULL);
- gagt_readlog_stream = NULL;
+ g_vm->glk_stream_close(g_vm->gagt_readlog_stream, NULL);
+ g_vm->gagt_readlog_stream = NULL;
}
/*
@@ -4927,7 +4893,7 @@ char agt_getkey(rbool echo_char) {
* though, and we want to pass that back as ASCII return.)
*/
do {
- g_vm->glk_request_char_event(gagt_main_window);
+ g_vm->glk_request_char_event(g_vm->gagt_main_window);
gagt_event_wait(evtype_CharInput, &event);
} while (event.val1 > BYTE_MAX_VAL && event.val1 != keycode_Return);
@@ -4940,8 +4906,8 @@ char agt_getkey(rbool echo_char) {
buffer[2] = '\0';
/* If there is an input log active, log this input string to it. */
- if (gagt_inputlog_stream)
- g_vm->glk_put_string_stream(gagt_inputlog_stream, buffer);
+ if (g_vm->gagt_inputlog_stream)
+ g_vm->glk_put_string_stream(g_vm->gagt_inputlog_stream, buffer);
/*
* No matter what echo_char says, as it happens, the output doesn't look
@@ -5095,8 +5061,8 @@ void init_interface() {
* If it fails, we'll return, and the caller can detect this by looking
* for a NULL main window.
*/
- gagt_main_window = g_vm->glk_window_open(0, 0, 0, wintype_TextBuffer, 0);
- if (!gagt_main_window)
+ g_vm->gagt_main_window = g_vm->glk_window_open(0, 0, 0, wintype_TextBuffer, 0);
+ if (!g_vm->gagt_main_window)
return;
/*
@@ -5104,7 +5070,7 @@ void init_interface() {
* this again in glk_main() -- this call is here just in case this version
* of init_interface() is ever called by AGiliTy's main.
*/
- g_vm->glk_set_window(gagt_main_window);
+ g_vm->glk_set_window(g_vm->gagt_main_window);
/*
* Screen height is something we don't use. Linux Xglk returns dimensions
@@ -5129,12 +5095,12 @@ void init_interface() {
* Create a status window, with one or two lines as selected by user
* options or flags. We can live without a status window if we have to.
*/
- status_height = gagt_extended_status_enabled ? 2 : 1;
+ status_height = g_vm->gagt_extended_status_enabled ? 2 : 1;
g_vm->glk_stylehint_set(wintype_TextGrid, style_User1, stylehint_ReverseColor, 1);
- gagt_status_window = g_vm->glk_window_open(gagt_main_window,
+ g_vm->gagt_status_window = g_vm->glk_window_open(g_vm->gagt_main_window,
winmethod_Above | winmethod_Fixed,
status_height, wintype_TextGrid, 0);
- if (gagt_status_window) {
+ if (g_vm->gagt_status_window) {
/*
* Call gagt_status_redraw() to set the interpreter's status_width
* variable initial value.
@@ -5197,7 +5163,7 @@ gagt_confirm(const char *prompt) {
/* Wait for a single 'Y' or 'N' character response. */
response = ' ';
do {
- g_vm->glk_request_char_event(gagt_main_window);
+ g_vm->glk_request_char_event(g_vm->gagt_main_window);
gagt_event_wait(evtype_CharInput, &event);
if (event.val1 <= BYTE_MAX_VAL)
@@ -5275,7 +5241,7 @@ gagt_get_user_file(glui32 usage, glui32 fmode, const char *fdtype) {
}
/* Get the path to the file from the user. */
- g_vm->glk_request_line_event(gagt_main_window, filepath, sizeof(filepath) - 1, 0);
+ g_vm->glk_request_line_event(g_vm->gagt_main_window, filepath, sizeof(filepath) - 1, 0);
gagt_event_wait(evtype_LineInput, &event);
/* Terminate the file path with a NUL. */
@@ -5529,123 +5495,6 @@ int __wrap_tolower(int ch) {
/* External declaration of interface.c's set default options function. */
extern void set_default_options();
-/*
- * Flag to set if we want to test for a clean exit. Without this it's a
- * touch tricky sometimes to corner AGiliTy into calling exit() for us; it
- * tends to require a broken game file.
- */
-static int gagt_clean_exit_test = FALSE;
-
-
-/*
- * gagt_parse_option()
- *
- * Glk-ified version of AGiliTy's parse_options() function. In practice,
- * because Glk has got to them first, most options that come in here are
- * probably going to be single-character ones, since this is what we told
- * Glk in the arguments structure above. The Glk font control and other
- * special tweaky flags will probably be the only multiple-character ones.
- */
-static int gagt_parse_option(const char *option) {
- unsigned int index;
- assert(option);
-
- assert(option[0] == '-');
- for (index = 1; option[index]; index++) {
- switch (option[index]) {
- case 'g':
- switch (option[++index]) {
- case 'f':
- gagt_font_mode = FONT_FIXED_WIDTH;
- break;
- case 'p':
- gagt_font_mode = FONT_PROPORTIONAL;
- break;
- case 'a':
- gagt_font_mode = FONT_AUTOMATIC;
- break;
- case 'd':
- gagt_delay_mode = DELAY_FULL;
- break;
- case 'h':
- gagt_delay_mode = DELAY_SHORT;
- break;
- case 'n':
- gagt_delay_mode = DELAY_OFF;
- break;
- case 'r':
- gagt_replacement_enabled = FALSE;
- break;
- case 'x':
- gagt_abbreviations_enabled = FALSE;
- break;
- case 's':
- gagt_extended_status_enabled = TRUE;
- break;
- case 'l':
- gagt_extended_status_enabled = FALSE;
- break;
- case 'c':
- gagt_commands_enabled = FALSE;
- break;
- case 'D':
- DEBUG_OUT = TRUE;
- break;
- case '#':
- gagt_clean_exit_test = TRUE;
- break;
- default:
- return FALSE;
- }
- break;
-
- case 'p':
- debug_parse = TRUE;
- break;
- case 'a':
- DEBUG_DISAMBIG = TRUE;
- break;
- case 'd':
- DEBUG_AGT_CMD = TRUE;
- break;
- case 'x':
- DEBUG_EXEC_VERB = TRUE;
- break;
- case 's':
- DEBUG_SMSG = TRUE;
- break;
-#ifdef MEM_INFO
- case 'M':
- DEBUG_MEM = TRUE;
- break;
-#endif
- case 'm':
- descr_maxmem = 0;
- break;
- case 't':
- BATCH_MODE = TRUE;
- break;
- case 'c':
- make_test = TRUE;
- break;
- case '1':
- irun_mode = TRUE;
- break;
-#ifdef OPEN_FILE_AS_TEXT
- case 'b':
- open_as_binary = TRUE;
- break;
-#endif
-
- case '?':
- default:
- return FALSE;
- }
- }
-
- return TRUE;
-}
-
/*
* gagt_startup_code()
@@ -5674,15 +5523,15 @@ static void gagt_main() {
*
* init_interface() can fail if there is a problem creating the main
* window. As it doesn't return status, we have to detect this by checking
- * that gagt_main_window is not NULL.
+ * that g_vm->gagt_main_window is not NULL.
*/
init_interface();
- if (!gagt_main_window) {
+ if (!g_vm->gagt_main_window) {
gagt_fatal("GLK: Can't open main window");
gagt_exit();
}
- g_vm->glk_window_clear(gagt_main_window);
- g_vm->glk_set_window(gagt_main_window);
+ g_vm->glk_window_clear(g_vm->gagt_main_window);
+ g_vm->glk_set_window(g_vm->gagt_main_window);
g_vm->glk_set_style(style_Normal);
/*
@@ -5692,8 +5541,8 @@ static void gagt_main() {
fc = init_file_context(g_vm->gagt_gamefile, fDA1);
if (!(gagt_workround_fileexist(fc, fAGX)
|| gagt_workround_fileexist(fc, fDA1))) {
- if (gagt_status_window)
- g_vm->glk_window_close(gagt_status_window, NULL);
+ if (g_vm->gagt_status_window)
+ g_vm->glk_window_close(g_vm->gagt_status_window, NULL);
gagt_header_string("Glk AGiliTy Error\n\n");
gagt_normal_string("Can't find or open game '");
gagt_normal_string(g_vm->gagt_gamefile);
@@ -5722,17 +5571,17 @@ static void gagt_main() {
gagt_status_cleanup();
/* Close any open transcript, input log, and/or read log. */
- if (gagt_transcript_stream) {
- g_vm->glk_stream_close(gagt_transcript_stream, NULL);
- gagt_transcript_stream = NULL;
+ if (g_vm->gagt_transcript_stream) {
+ g_vm->glk_stream_close(g_vm->gagt_transcript_stream, NULL);
+ g_vm->gagt_transcript_stream = NULL;
}
- if (gagt_inputlog_stream) {
- g_vm->glk_stream_close(gagt_inputlog_stream, NULL);
- gagt_inputlog_stream = NULL;
+ if (g_vm->gagt_inputlog_stream) {
+ g_vm->glk_stream_close(g_vm->gagt_inputlog_stream, NULL);
+ g_vm->gagt_inputlog_stream = NULL;
}
- if (gagt_readlog_stream) {
- g_vm->glk_stream_close(gagt_readlog_stream, NULL);
- gagt_readlog_stream = NULL;
+ if (g_vm->gagt_readlog_stream) {
+ g_vm->glk_stream_close(g_vm->gagt_readlog_stream, NULL);
+ g_vm->gagt_readlog_stream = NULL;
}
}
@@ -5804,7 +5653,7 @@ void gagt_finalizer() {
* status window, or to the main window) and flush any pending buffered
* output.
*/
- if (gagt_main_window) {
+ if (g_vm->gagt_main_window) {
gagt_status_notify();
gagt_output_flush();
}
@@ -5826,13 +5675,13 @@ void gagt_finalizer() {
* g_vm->glk_exit(). If we have no main window, there's no point in doing
* anything more.
*/
- if (gagt_main_window) {
- g_vm->glk_cancel_char_event(gagt_main_window);
- g_vm->glk_cancel_line_event(gagt_main_window, NULL);
+ if (g_vm->gagt_main_window) {
+ g_vm->glk_cancel_char_event(g_vm->gagt_main_window);
+ g_vm->glk_cancel_line_event(g_vm->gagt_main_window, NULL);
g_vm->glk_set_style(style_Alert);
g_vm->glk_put_string("\n\nHit any key to exit.\n");
- g_vm->glk_request_char_event(gagt_main_window);
+ g_vm->glk_request_char_event(g_vm->gagt_main_window);
gagt_event_wait(evtype_CharInput, &event);
}
}
@@ -5892,7 +5741,7 @@ void __wrap_exit(int status) {
* So, if we have a main window, flush it. This is the same cleanup as
* done by the finalizer.
*/
- if (gagt_main_window) {
+ if (g_vm->gagt_main_window) {
gagt_status_notify();
gagt_output_flush();
}
@@ -5904,7 +5753,7 @@ void __wrap_exit(int status) {
/*
- * glk_main()
+ * glk_main)
*
* Main entry point for Glk. Here, all startup is done, and we call our
* function to run the game.
@@ -5919,7 +5768,7 @@ void glk_main() {
* code explores "undefined" ANSI. If we get something ugly, like a core
* dump, we'll want to set GLK[AGIL]_CLEAN_EXIT.
*/
- if (gagt_clean_exit_test) {
+ if (g_vm->gagt_clean_exit_test) {
gagt_agility_running = TRUE;
return;
}
Commit: c3dbc2c40f68aebf04fa76cbb225bf81f43a8c41
https://github.com/scummvm/scummvm/commit/c3dbc2c40f68aebf04fa76cbb225bf81f43a8c41
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2019-11-27T21:10:29-08:00
Commit Message:
GLK: AGT: Route savegames through the engine
Changed paths:
engines/glk/agt/agil.cpp
engines/glk/agt/agt.cpp
engines/glk/agt/interface.cpp
engines/glk/agt/interp.h
engines/glk/agt/runverb.cpp
engines/glk/agt/savegame.cpp
engines/glk/agt/util.cpp
engines/glk/quetzal.cpp
diff --git a/engines/glk/agt/agil.cpp b/engines/glk/agt/agil.cpp
index b1fbcb7..23bc2fb 100644
--- a/engines/glk/agt/agil.cpp
+++ b/engines/glk/agt/agil.cpp
@@ -494,7 +494,7 @@ static void game_end(void) {
done_flag = 1;
} else writeln("Sorry, I'm unable to do that because of limited memory.");
else if (strncasecmp(s, "RESTORE", 7) == 0)
- if (loadgame()) {
+ if (g_vm->loadGame().getCode() == Common::kNoError) {
done_flag = 1;
} else writeln("(RESTORE failed)");
else if (strncasecmp(s, "UNDO", 4) == 0)
@@ -556,8 +556,10 @@ static void mainloop(void) {
} else
menu_cmd();
if (doing_restore) {
- if (doing_restore == 1) loadgame();
- else if (doing_restore == 2) restart_game();
+ if (doing_restore == 1)
+ g_vm->loadGame();
+ else if (doing_restore == 2)
+ restart_game();
else if (doing_restore == 3 || doing_restore == 4)
return; /* Quit or New game requested */
doing_restore = 0;
diff --git a/engines/glk/agt/agt.cpp b/engines/glk/agt/agt.cpp
index 1f0766f..37d1d77 100644
--- a/engines/glk/agt/agt.cpp
+++ b/engines/glk/agt/agt.cpp
@@ -23,6 +23,9 @@
#include "glk/agt/agt.h"
#include "glk/quetzal.h"
#include "common/config-manager.h"
+#include "glk/agt/agility.h"
+#include "glk/agt/interp.h"
+#include "glk/agt/exec.h"
namespace Glk {
namespace AGT {
@@ -93,11 +96,11 @@ void AGT::initializeSettings() {
}
Common::Error AGT::readSaveData(Common::SeekableReadStream *rs) {
- return Common::kReadingFailed;
+ return loadgame(rs);
}
Common::Error AGT::writeGameData(Common::WriteStream *ws) {
- return Common::kWritingFailed;
+ return savegame(ws);
}
} // End of namespace AGT
diff --git a/engines/glk/agt/interface.cpp b/engines/glk/agt/interface.cpp
index 75763d3..1e4debd 100644
--- a/engines/glk/agt/interface.cpp
+++ b/engines/glk/agt/interface.cpp
@@ -1000,7 +1000,7 @@ void replay(int delay) {
/* They're never called from the rest of the code */
void agt_save(void) {
- savegame();
+ g_vm->saveGame();
}
void agt_restore(void) {
diff --git a/engines/glk/agt/interp.h b/engines/glk/agt/interp.h
index 9bf5d65..3794a09 100644
--- a/engines/glk/agt/interp.h
+++ b/engines/glk/agt/interp.h
@@ -435,8 +435,8 @@ extern void debug_newline(integer op, rbool first_nl);
/* -------------------------------------------------------------------- */
/* In SAVEGAME.C */
/* -------------------------------------------------------------------- */
-extern void savegame(void);
-extern rbool loadgame(void);
+extern Common::Error savegame(Common::WriteStream *savefile);
+extern Common::Error loadgame(Common::SeekableReadStream *loadfile);
extern void init_state_sys(void); /* Must be called before either of the following */
extern uchar *getstate(uchar *gs);
/* Returns malloc'd block containing game state. */
diff --git a/engines/glk/agt/runverb.cpp b/engines/glk/agt/runverb.cpp
index 9f2144d..4ee3fa5 100644
--- a/engines/glk/agt/runverb.cpp
+++ b/engines/glk/agt/runverb.cpp
@@ -1393,7 +1393,7 @@ void exec_verb(void)
break;
case 45:
cmd_saveable = 0;
- savegame();
+ g_vm->saveGame();
break;
case 46:
cmd_saveable = 0;
diff --git a/engines/glk/agt/savegame.cpp b/engines/glk/agt/savegame.cpp
index 68285d2..65c4ed9 100644
--- a/engines/glk/agt/savegame.cpp
+++ b/engines/glk/agt/savegame.cpp
@@ -21,9 +21,9 @@
*/
-#include "agility.h"
-#include "interp.h"
-#include "exec.h"
+#include "glk/agt/agility.h"
+#include "glk/agt/interp.h"
+#include "glk/agt/exec.h"
namespace Glk {
namespace AGT {
@@ -337,8 +337,7 @@ void init_state_sys(void)
/*-------------------------------------------------------------------*/
/* SAVE FILE ROUTINES */
-void savegame(void) {
- genfile savefile;
+extern Common::Error savegame(Common::WriteStream *savefile) {
uchar *gs;
long size;
@@ -349,50 +348,49 @@ void savegame(void) {
#endif
if (gs == NULL) {
writeln("Insufficiant memory to support SAVE.");
- return;
+ return Common::kWritingFailed;
}
- savefile = get_user_file(1);
+
if (!filevalid(savefile, fSAV)) {
writeln("That is not a valid save file.");
- return;
+ return Common::kWritingFailed;
}
size = gs[0] + (((long)gs[1]) << 8) + (((long)gs[2]) << 16) + (((long)gs[3]) << 24);
- if (!binwrite(savefile, gs, size, 1, 0))
- writeln("Error writing save file.");
+ bool result = binwrite(savefile, gs, size, 1, 0);
#ifndef UNDO_SAVE
rfree(gs);
#endif
- writeclose(savefile, NO_FILE_ID);
+ if (!result) {
+ warning("Error writing save file.");
+ return Common::kWritingFailed;
+ } else {
+ return Common::kNoError;
+ }
}
-
-rbool loadgame(void)
/* 1=success, 0=failure */
-{
- genfile loadfile;
+Common::Error loadgame(Common::SeekableReadStream *loadfile) {
long size;
uchar *gs;
const char *errstr;
- loadfile = get_user_file(2);
if (!filevalid(loadfile, fSAV)) {
- writeln("Unable to open file.");
- return 0;
+ warning("Unable to open file.");
+ return Common::kReadingFailed;
}
size = binsize(loadfile);
if (size == -1) {
- writeln("Could not access file.");
- readclose(loadfile);
- return 0;
+ warning("Could not access file.");
+ return Common::kReadingFailed;
}
+
gs = (uchar *)rmalloc(size);
if (!binread(loadfile, gs, size, 1, &errstr)) {
- writeln("Error reading file.");
+ warning("Error reading file.");
rfree(gs);
- readclose(loadfile);
- return 0;
+ return Common::kReadingFailed;
}
- readclose(loadfile);
+
if (size != gs[0] + (((long)gs[1]) << 8) + (((long)gs[2]) << 16) + (((long)gs[3]) << 24)) {
if (size == gs[0] + (((long)gs[1]) << 8)) {
/* Old save file format; patch to look like new format */
@@ -400,16 +398,17 @@ rbool loadgame(void)
memmove(gs + 4, gs + 2, size - 2);
gs[2] = gs[3] = 0;
} else {
- writeln("Save file corrupted or invalid.");
+ warning("Save file corrupted or invalid.");
rfree(gs);
- return 0;
+ return Common::kReadingFailed;
}
}
+
putstate(gs);
rfree(gs);
set_statline();
look_room();
- return 1;
+ return Common::kNoError;
}
void restart_game(void) {
diff --git a/engines/glk/agt/util.cpp b/engines/glk/agt/util.cpp
index c91e47a..47900fc 100644
--- a/engines/glk/agt/util.cpp
+++ b/engines/glk/agt/util.cpp
@@ -376,7 +376,8 @@ size_t fread(void *ptr, size_t size, size_t nmemb, genfile stream) {
size_t fwrite(const void *ptr, size_t size, size_t nmemb, genfile stream) {
Common::WriteStream *ws = dynamic_cast<Common::WriteStream *>(stream);
assert(ws);
- return ws->write(ptr, size * nmemb);
+ size_t bytesWritten = ws->write(ptr, size * nmemb);
+ return bytesWritten / size;
}
size_t ftell(genfile f) {
diff --git a/engines/glk/quetzal.cpp b/engines/glk/quetzal.cpp
index 48e1ef0..6929a0a 100644
--- a/engines/glk/quetzal.cpp
+++ b/engines/glk/quetzal.cpp
@@ -38,6 +38,8 @@ uint32 QuetzalBase::getInterpreterTag(InterpreterType interpType) {
return MKTAG('A', 'D', 'R', 'I');
case INTERPRETER_ADVSYS:
return MKTAG('A', 'S', 'Y', 'S');
+ case INTERPRETER_AGT:
+ return MKTAG('A', 'G', 'T', ' ');
case INTERPRETER_AGILITY:
return MKTAG('A', 'G', 'I', 'L');
case INTERPRETER_ALAN2:
More information about the Scummvm-git-logs
mailing list