[Scummvm-git-logs] scummvm master -> a50d09aa31c1b1a17a07bce369f4f416f7f4c546
dreammaster
noreply at scummvm.org
Wed Apr 13 03:35:38 UTC 2022
This automated email contains information about 21 new commits which have been
pushed to the 'scummvm' repo located at https://github.com/scummvm/scummvm .
Summary:
49e26ac8bd GLK: Add typedef for glsi32.
7188ce034b GLK: SCOTT: Update scott.
7b97270214 GLK: SCOTT: Add game command parser.
fb7180e379 GLK: SCOTT: Add support for .z80 game files.
6d6d4fd434 GLK: SCOTT: Support games with compressed text data.
abecb9a388 GLK: SCOTT: Add game detection and loading for ZX Spectrum games.
4b31312480 GLK: SCOTT: Add support for different drawing methods.
5a4bda94a9 GLK: SCOTT: Add ring buffer for drawing methods.
f9521be168 GLK: SCOTT: Game specific info to load game data.
855e9b1ae6 GLK: SCOTT: Various enums for game data.
9cae726fca GLK: SCOTT: Support .z80 files.
f994bd2859 GLK: SCOTT: Add detection for 11 Mysterious Adventures.
faa4ae472d GLK: SCOTT: Class for holding global data.
7f84f25804 GLK: SCOTT: Add Questprobe: The Hulk specific functions.
a864ce872f GLK: SCOTT: Add text formatting.
ef36219245 GLK: SCOTT: Add support for saving and loading game state.
72dc87f4b4 GLK: Add new source files.
8b846ec0dd GLK: SCOTT: Import size_t.
a7c29553f7 GLK: SCOTT: Typedefs for used types.
67adecb2d7 GLK: SCOTT: Remove standard library includes.
a50d09aa31 GLK: SCOTT: Define SIZE_MAX.
Commit: 49e26ac8bdeb2bac461bed20bdc204eab3803180
https://github.com/scummvm/scummvm/commit/49e26ac8bdeb2bac461bed20bdc204eab3803180
Author: Avijeet (am388488 at gmail.com)
Date: 2022-04-12T20:35:24-07:00
Commit Message:
GLK: Add typedef for glsi32.
Changed paths:
engines/glk/glk_types.h
diff --git a/engines/glk/glk_types.h b/engines/glk/glk_types.h
index e824268edbf..11fcdb1723a 100644
--- a/engines/glk/glk_types.h
+++ b/engines/glk/glk_types.h
@@ -251,6 +251,8 @@ typedef gidispatch_intconst_struct gidispatch_intconst_t;
typedef uint32 glui32;
+typedef int32 glsi32;
+
} // End of namespace Glk
#endif
Commit: 7188ce034b50a96ee42a85091c0f373feee73534
https://github.com/scummvm/scummvm/commit/7188ce034b50a96ee42a85091c0f373feee73534
Author: Avijeet (am388488 at gmail.com)
Date: 2022-04-12T20:35:24-07:00
Commit Message:
GLK: SCOTT: Update scott.
Changed paths:
engines/glk/scott/scott.cpp
engines/glk/scott/scott.h
diff --git a/engines/glk/scott/scott.cpp b/engines/glk/scott/scott.cpp
index 8e1cd56259c..b1f08c4a51c 100644
--- a/engines/glk/scott/scott.cpp
+++ b/engines/glk/scott/scott.cpp
@@ -20,122 +20,178 @@
*/
#include "glk/scott/scott.h"
-#include "glk/quetzal.h"
#include "common/config-manager.h"
#include "common/translation.h"
#include "common/ustr.h"
+#include "glk/quetzal.h"
+#include "glk/scott/command_parser.h"
+#include "glk/scott/definitions.h"
+#include "glk/scott/detect_game.h"
+#include "glk/scott/game_info.h"
+#include "glk/scott/globals.h"
+#include "glk/scott/hulk.h"
+#include "glk/scott/layout_text.h"
+#include "glk/scott/line_drawing.h"
+#include "glk/scott/saga_draw.h"
+#include "glk/scott/restore_state.h"
namespace Glk {
namespace Scott {
-Scott::Scott(OSystem *syst, const GlkGameDescription &gameDesc) : GlkAPI(syst, gameDesc),
- _currentCounter(0), _savedRoom(0), _options(0), _width(0), _topHeight(0), _splitScreen(true),
- _bottomWindow(nullptr), _topWindow(nullptr), _bitFlags(0), _saveSlot(-1) {
+Scott *g_scott;
+
+Scott::Scott(OSystem *syst, const GlkGameDescription &gameDesc) : GlkAPI(syst, gameDesc) {
+ g_scott = this;
Common::fill(&_nounText[0], &_nounText[16], '\0');
- Common::fill(&_counters[0], &_counters[16], 0);
- Common::fill(&_roomSaved[0], &_roomSaved[16], 0);
}
void Scott::runGame() {
int vb, no;
initialize();
- _bottomWindow = glk_window_open(nullptr, 0, 0, wintype_TextBuffer, 1);
- if (_bottomWindow == nullptr) {
+ glk_stylehint_set(wintype_TextBuffer, style_User1, stylehint_Proportional, 0);
+ glk_stylehint_set(wintype_TextBuffer, style_User1, stylehint_Indentation, 20);
+ glk_stylehint_set(wintype_TextBuffer, style_User1, stylehint_ParaIndentation, 20);
+ glk_stylehint_set(wintype_TextBuffer, style_Preformatted, stylehint_Justification, stylehint_just_Centered);
+
+ _G(_bottomWindow) = glk_window_open(0, 0, 0, wintype_TextBuffer, GLK_BUFFER_ROCK);
+ if (_G(_bottomWindow) == nullptr)
glk_exit();
- return;
+ glk_set_window(_G(_bottomWindow));
+
+ for (int i = 0; i < MAX_SYSMESS; i++) {
+ _G(_sys)[i] = g_sysDict[i];
+ }
+
+ const char **dictpointer;
+
+ if (_options & YOUARE)
+ dictpointer = g_sysDict;
+ else
+ dictpointer = g_sysDictIAm;
+
+ for (int i = 0; i < MAX_SYSMESS && dictpointer[i] != nullptr; i++) {
+ _G(_sys)[i] = dictpointer[i];
+ }
+
+ GameIDType gameType = detectGame(&_gameFile);
+
+ if (gameType == SCOTTFREE)
+ loadDatabase(&_gameFile, (_options & DEBUGGING) ? 1 : 0);
+
+ if (!gameType)
+ fatal("Unsupported game!");
+
+ if (gameType != SCOTTFREE && gameType != TI994A) {
+ _options |= SPECTRUM_STYLE;
+ _splitScreen = 1;
+ } else {
+ if (gameType != TI994A)
+ _options |= TRS80_STYLE;
+ _splitScreen = 1;
+ }
+
+ if (!_titleScreen.empty()) {
+ if (_splitScreen)
+ printTitleScreenGrid();
+ else
+ printTitleScreenBuffer();
}
- glk_set_window(_bottomWindow);
if (_options & TRS80_STYLE) {
- _width = 64;
+ _topWidth = 64;
_topHeight = 11;
} else {
- _width = 80;
+ _topWidth = 80;
_topHeight = 10;
}
- if (_splitScreen) {
- _topWindow = glk_window_open(_bottomWindow, winmethod_Above | winmethod_Fixed, _topHeight, wintype_TextGrid, 0);
- if (_topWindow == nullptr) {
- _splitScreen = 0;
- _topWindow = _bottomWindow;
- }
- } else {
- _topWindow = _bottomWindow;
+ if (CURRENT_GAME == TI994A) {
+ display(_G(_bottomWindow), "In this adventure, you may abbreviate any word \
+ by typing its first %d letters, and directions by typing \
+ one letter.\n\nDo you want to restore previously saved game?\n",
+ _G(_gameHeader)->_wordLength);
+ if (yesOrNo())
+ loadGame();
+ clearScreen();
}
- output("ScummVM support adapted from Scott Free, A Scott Adams game driver in C.\n\n");
+ openTopWindow();
+
+ if (gameType == SCOTTFREE)
+ output("ScummVM support adapted from Scott Free, A Scott Adams game driver in C.\n\n");
// Check for savegame
_saveSlot = ConfMan.hasKey("save_slot") ? ConfMan.getInt("save_slot") : -1;
- // Load the game
- loadDatabase(&_gameFile, (_options & DEBUGGING) ? 1 : 0);
+ _G(_initialState) = saveCurrentState();
- // Main game loop
while (!shouldQuit()) {
glk_tick();
- performActions(0, 0);
+ if (_G(_shouldRestart))
+ restartGame();
+
+ if (!_G(_stopTime))
+ performActions(0, 0);
+
if (shouldQuit())
break;
- if (_saveSlot >= 0) {
- // Load any savegame during startup
- loadGameState(_saveSlot);
- _saveSlot = -1;
+ if (!(_G(_currentCommand) && _G(_currentCommand)->_allFlag && !(_G(_currentCommand)->_allFlag & LASTALL))) {
+ _printLookToTranscript = _shouldLookInTranscript;
+ look();
+ _printLookToTranscript = _shouldLookInTranscript = 0;
+ if (!_G(_stopTime) && !_G(_shouldRestart))
+ saveUndo();
}
- look();
+ if (_G(_shouldRestart))
+ continue;
- if (getInput(&vb, &no) == -1)
+ if (getInput(&vb, &no) == 1)
continue;
if (g_vm->shouldQuit())
break;
switch (performActions(vb, no)) {
- case -1:
- output(_("I don't understand your command. "));
+ case ER_RAN_ALL_LINES_NO_MATCH:
+ if (!recheckForExtraCommand()) {
+ output(_G(_sys)[I_DONT_UNDERSTAND]);
+ freeCommands();
+ }
break;
- case -2:
- output(_("I can't do that yet. "));
+ case ER_RAN_ALL_LINES:
+ output(_G(_sys)[YOU_CANT_DO_THAT_YET]);
+ freeCommands();
break;
default:
- break;
+ _G(_justStarted) = 0;
}
- if (shouldQuit())
- return;
-
- // Brian Howarth games seem to use -1 for forever
- if (_items[LIGHT_SOURCE]._location != DESTROYED && _gameHeader._lightTime != -1) {
- _gameHeader._lightTime--;
- if (_gameHeader._lightTime < 1) {
- _bitFlags |= (1 << LIGHTOUTBIT);
- if (_items[LIGHT_SOURCE]._location == CARRIED ||
- _items[LIGHT_SOURCE]._location == MY_LOC) {
- if (_options & SCOTTLIGHT)
- output(_("Light has run out! "));
- else
- output(_("Your light has run out. "));
+
+ /* Brian Howarth games seem to use -1 for forever */
+ if (_G(_items)[LIGHT_SOURCE]._location != DESTROYED && _G(_gameHeader)->_lightTime != -1 && !_G(_stopTime)) {
+ _G(_gameHeader)->_lightTime--;
+ if (_G(_gameHeader)->_lightTime < 1) {
+ _G(_bitFlags) |= (1 << LIGHTOUTBIT);
+ if (_G(_items)[LIGHT_SOURCE]._location == CARRIED || _G(_items)[LIGHT_SOURCE]._location == MY_LOC) {
+ output(_G(_sys)[LIGHT_HAS_RUN_OUT]);
}
- if (_options & PREHISTORIC_LAMP)
- _items[LIGHT_SOURCE]._location = DESTROYED;
- } else if (_gameHeader._lightTime < 25) {
- if (_items[LIGHT_SOURCE]._location == CARRIED ||
- _items[LIGHT_SOURCE]._location == MY_LOC) {
-
- if (_options & SCOTTLIGHT) {
- output(_("Light runs out in "));
- outputNumber(_gameHeader._lightTime);
- output(_(" turns. "));
+ if ((_options & PREHISTORIC_LAMP) || (_G(_game)->_subType & MYSTERIOUS) || CURRENT_GAME == TI994A)
+ _G(_items)[LIGHT_SOURCE]._location = DESTROYED;
+ } else if (_G(_gameHeader)->_lightTime < 25) {
+ if (_G(_items)[LIGHT_SOURCE]._location == CARRIED || _G(_items)[LIGHT_SOURCE]._location == MY_LOC) {
+ if ((_options & SCOTTLIGHT) || (_G(_game)->_subType & MYSTERIOUS)) {
+ display(_G(_bottomWindow), "%s %d %s\n", _G(_sys)[LIGHT_RUNS_OUT_IN].c_str(), _G(_gameHeader)->_lightTime, _G(_sys)[TURNS].c_str());
} else {
- if (_gameHeader._lightTime % 5 == 0)
- output(_("Your light is growing dim. "));
+ if (_G(_gameHeader)->_lightTime % 5 == 0)
+ output(_G(_sys)[LIGHT_GROWING_DIM].c_str());
}
}
}
}
+ if (_G(_stopTime))
+ _G(_stopTime)--;
}
}
@@ -164,6 +220,8 @@ void Scott::display(winid_t w, const char *fmt, ...) {
va_end(ap);
glk_put_string_stream(glk_window_get_stream(w), msg.c_str());
+ if (_G(_transcript))
+ glk_put_string_stream(_G(_transcript), msg.c_str());
}
void Scott::display(winid_t w, const Common::U32String fmt, ...) {
@@ -176,29 +234,103 @@ void Scott::display(winid_t w, const Common::U32String fmt, ...) {
va_end(ap);
glk_put_string_stream_uni(glk_window_get_stream(w), msg.u32_str());
+ if (_G(_transcript))
+ glk_put_string_stream_uni(_G(_transcript), msg.u32_str());
+}
+
+void Scott::updateSettings() {
+ if (drawingVector())
+ glk_request_timer_events(20);
+
+ PaletteType previousPal = _G(_palChosen);
+ if (_options & FORCE_PALETTE_ZX)
+ _G(_palChosen) = ZXOPT;
+ else if (_options & FORCE_PALETTE_C64) {
+ if (_G(_game)->_pictureFormatVersion == 99)
+ _G(_palChosen) = C64A;
+ else
+ _G(_palChosen) = C64B;
+ } else
+ _G(_palChosen) = _G(_game)->_palette;
+ if (_G(_palChosen) != previousPal) {
+ definePalette();
+ if (_G(_vectorState) != NO_VECTOR_IMAGE)
+ drawSomeVectorPixels(1);
+ }
+}
+
+void Scott::updates(event_t ev) {
+ if (ev.type == evtype_Arrange) {
+ updateSettings();
+
+ _G(_vectorState) = NO_VECTOR_IMAGE;
+
+ closeGraphicsWindow();
+ openGraphicsWindow();
+
+ if (_splitScreen) {
+ look();
+ }
+ } else if (ev.type == evtype_Timer) {
+ switch (_G(_game->_type)) {
+ case SHERWOOD_VARIANT:
+ // TODO
+ // UpdateRobinOfSherwoodAnimations();
+ break;
+ case GREMLINS_VARIANT:
+ // TODO
+ // UpdateGremlinsAnimations();
+ break;
+ case SECRET_MISSION_VARIANT:
+ // TODO
+ // UpdateSecretAnimations();
+ break;
+ default:
+ if (_G(_game)->_pictureFormatVersion == 99 && drawingVector())
+ drawSomeVectorPixels((_G(_vectorState) == NO_VECTOR_IMAGE));
+ break;
+ }
+ }
}
void Scott::delay(int seconds) {
+ if (_options & NO_DELAYS)
+ return;
+
event_t ev;
if (!glk_gestalt(gestalt_Timer, 0))
return;
+ glk_request_char_event(_G(_bottomWindow));
+ glk_cancel_char_event(_G(_bottomWindow));
+
+ if (drawingVector()) {
+ do {
+ glk_select(&ev);
+ updates(ev);
+ } while (drawingVector());
+ if (_G(_gliSlowDraw))
+ seconds = 0.5;
+ }
+
glk_request_timer_events(1000 * seconds);
do {
glk_select(&ev);
- } while (ev.type != evtype_Timer && ev.type != evtype_Quit);
+ updates(ev);
+ } while (ev.type != evtype_Timer);
glk_request_timer_events(0);
}
void Scott::fatal(const char *x) {
- error("%s", x);
+ display(_G(_bottomWindow), "%s\n", x);
+ cleanupAndExit();
}
void Scott::clearScreen(void) {
- glk_window_clear(_bottomWindow);
+ glk_window_clear(_G(_bottomWindow));
}
bool Scott::randomPercent(uint n) {
@@ -208,41 +340,41 @@ bool Scott::randomPercent(uint n) {
int Scott::countCarried(void) {
int ct = 0;
int n = 0;
- while (ct <= _gameHeader._numItems) {
- if (_items[ct]._location == CARRIED)
+ while (ct <= _G(_gameHeader)->_numItems) {
+ if (_G(_items)[ct]._location == CARRIED)
n++;
ct++;
}
return n;
}
-const char *Scott::mapSynonym(const char *word) {
+const char *Scott::mapSynonym(int noun) {
int n = 1;
const char *tp;
- static char lastword[16]; // Last non synonym
- while (n <= _gameHeader._numWords) {
- tp = _nouns[n].c_str();
+ static char lastword[16]; // Last non synonym
+ while (n <= _G(_gameHeader)->_numWords) {
+ tp = _G(_nouns)[n].c_str();
if (*tp == '*')
tp++;
else
strcpy(lastword, tp);
- if (scumm_strnicmp(word, tp, _gameHeader._wordLength) == 0)
+ if (n == noun)
return lastword;
n++;
}
return nullptr;
}
-int Scott::matchUpItem(const char *text, int loc) {
- const char *word = mapSynonym(text);
+int Scott::matchUpItem(int noun, int loc) {
+ const char *word = mapSynonym(noun);
int ct = 0;
if (word == nullptr)
- word = text;
+ word = _G(_nouns)[noun].c_str();
- while (ct <= _gameHeader._numItems) {
- if (!_items[ct]._autoGet.empty() && _items[ct]._location == loc &&
- scumm_strnicmp(_items[ct]._autoGet.c_str(), word, _gameHeader._wordLength) == 0)
+ while (ct <= _G(_gameHeader)->_numItems) {
+ if (!_G(_items)[ct]._autoGet.empty() && _G(_items)[ct]._location == loc &&
+ scumm_strnicmp(_G(_items)[ct]._autoGet.c_str(), word, _G(_gameHeader)->_wordLength) == 0)
return ct;
ct++;
}
@@ -302,61 +434,61 @@ void Scott::loadDatabase(Common::SeekableReadStream *f, bool loud) {
// Load the header
readInts(f, 12, &unused, &ni, &na, &nw, &nr, &mc, &pr, &tr, &wl, <, &mn, &trm);
- _gameHeader._numItems = ni;
- _items.resize(ni + 1);
- _gameHeader._numActions = na;
- _actions.resize(na + 1);
- _gameHeader._numWords = nw;
- _gameHeader._wordLength = wl;
- _verbs.resize(nw + 1);
- _nouns.resize(nw + 1);
- _gameHeader._numRooms = nr;
- _rooms.resize(nr + 1);
- _gameHeader._maxCarry = mc;
- _gameHeader._playerRoom = pr;
- _gameHeader._treasures = tr;
- _gameHeader._lightTime = lt;
- _lightRefill = lt;
- _gameHeader._numMessages = mn;
- _messages.resize(mn + 1);
- _gameHeader._treasureRoom = trm;
+ _G(_gameHeader)->_numItems = ni;
+ _G(_items).resize(ni + 1);
+ _G(_gameHeader)->_numActions = na;
+ _G(_actions).resize(na + 1);
+ _G(_gameHeader)->_numWords = nw;
+ _G(_gameHeader)->_wordLength = wl;
+ _G(_verbs).resize(nw + 1);
+ _G(_nouns).resize(nw + 1);
+ _G(_gameHeader)->_numRooms = nr;
+ _G(_rooms).resize(nr + 1);
+ _G(_gameHeader)->_maxCarry = mc;
+ _G(_gameHeader)->_playerRoom = pr;
+ _G(_gameHeader)->_treasures = tr;
+ _G(_gameHeader)->_lightTime = lt;
+ _G(_lightRefill) = lt;
+ _G(_gameHeader)->_numMessages = mn;
+ _G(_messages).resize(mn + 1);
+ _G(_gameHeader)->_treasureRoom = trm;
// Load the actions
if (loud)
debug("Reading %d actions.", na);
for (int idx = 0; idx < na + 1; ++idx) {
- Action &a = _actions[idx];
+ Action &a = _G(_actions)[idx];
readInts(f, 8,
- &a._vocab, &a._condition[0], &a._condition[1], &a._condition[2],
- &a._condition[3], &a._condition[4], &a._action[0], &a._action[1]);
+ &a._vocab, &a._condition[0], &a._condition[1], &a._condition[2],
+ &a._condition[3], &a._condition[4], &a._action[0], &a._action[1]);
}
if (loud)
debug("Reading %d word pairs.", nw);
for (int idx = 0; idx < nw + 1; ++idx) {
- _verbs[idx] = readString(f);
- _nouns[idx] = readString(f);
+ _G(_verbs)[idx] = readString(f);
+ _G(_nouns)[idx] = readString(f);
}
if (loud)
debug("Reading %d rooms.", nr);
for (int idx = 0; idx < nr + 1; ++idx) {
- Room &r = _rooms[idx];
+ Room &r = _G(_rooms)[idx];
readInts(f, 6, &r._exits[0], &r._exits[1], &r._exits[2],
&r._exits[3], &r._exits[4], &r._exits[5]);
- r._text = readString(f);
+ r._text = readString(f);
}
if (loud)
debug("Reading %d messages.", mn);
for (int idx = 0; idx < mn + 1; ++idx)
- _messages[idx] = readString(f);
+ _G(_messages)[idx] = readString(f);
if (loud)
debug("Reading %d items.", ni);
for (int idx = 0; idx < ni + 1; ++idx) {
- Item &i = _items[idx];
+ Item &i = _G(_items)[idx];
i._text = readString(f);
const char *p = strchr(i._text.c_str(), '/');
@@ -369,8 +501,9 @@ void Scott::loadDatabase(Common::SeekableReadStream *f, bool loud) {
i._autoGet.deleteChar(0);
const char *t = strchr(i._autoGet.c_str(), '/');
- if (t)
+ if (t) {
i._autoGet = Common::String(i._autoGet.c_str(), t);
+ }
}
}
@@ -394,155 +527,134 @@ void Scott::loadDatabase(Common::SeekableReadStream *f, bool loud) {
void Scott::output(const Common::String &a) {
if (_saveSlot == -1)
- display(_bottomWindow, "%s", a.c_str());
+ display(_G(_bottomWindow), "%s", a.c_str());
}
void Scott::output(const Common::U32String &a) {
if (_saveSlot == -1)
- display(_bottomWindow, Common::U32String("%S"), a.c_str());
+ display(_G(_bottomWindow), Common::U32String("%S"), a.c_str());
}
void Scott::outputNumber(int a) {
- display(_bottomWindow, "%d", a);
+ display(_G(_bottomWindow), "%d", a);
}
void Scott::look(void) {
- const char *const ExitNames[6] = {
- _s("North"), _s("South"), _s("East"), _s("West"), _s("Up"), _s("Down")
- };
+ drawRoomImage();
+
+ if (_splitScreen && _G(_topWindow) == nullptr)
+ return;
+
+ char *buf = new char[1000];
+ buf = static_cast<char *>(memset(buf, 0, 1000));
+ _roomDescriptionStream = glk_stream_open_memory(buf, 1000, filemode_Write, 0);
+
Room *r;
int ct, f;
- int pos;
- if (_splitScreen)
- glk_window_clear(_topWindow);
+ if (!_splitScreen) {
+ writeToRoomDescriptionStream("\n");
+ } else if (_G(_transcript) && _printLookToTranscript) {
+ glk_put_char_stream_uni(_G(_transcript), 10);
+ }
- if ((_bitFlags & (1 << DARKBIT)) && _items[LIGHT_SOURCE]._location != CARRIED
- && _items[LIGHT_SOURCE]._location != MY_LOC) {
- if (_options & YOUARE)
- display(_topWindow, _("You can't see. It is too dark!\n"));
- else
- display(_topWindow, _("I can't see. It is too dark!\n"));
- if (_options & TRS80_STYLE)
- display(_topWindow, TRS80_LINE);
+ if ((_G(_bitFlags) & (1 << DARKBIT)) && _G(_items)[LIGHT_SOURCE]._location != CARRIED && _G(_items)[LIGHT_SOURCE]._location != MY_LOC) {
+ writeToRoomDescriptionStream("%s", _G(_sys)[TOO_DARK_TO_SEE].c_str());
+ flushRoomDescription(buf);
return;
}
- r = &_rooms[MY_LOC];
+
+ r = &_G(_rooms)[MY_LOC];
+
+ if (r->_text.empty())
+ return;
+
if (r->_text.hasPrefix("*"))
- display(_topWindow, "%s\n", r->_text.c_str() + 1);
+ writeToRoomDescriptionStream("%s", r->_text.substr(1).c_str());
else {
- if (_options & YOUARE)
- display(_topWindow, _("You are in a %s\n"), r->_text.c_str());
- else
- display(_topWindow, _("I'm in a %s\n"), r->_text.c_str());
+ writeToRoomDescriptionStream("%s%s", _G(_sys)[YOU_ARE].c_str(), r->_text.c_str());
}
- ct = 0;
- f = 0;
- display(_topWindow, _("\nObvious exits: "));
- while (ct < 6) {
- if (r->_exits[ct] != 0) {
- if (f == 0)
- f = 1;
- else
- display(_topWindow, ", ");
- display(_topWindow, Common::U32String("%S"), _(ExitNames[ct]).c_str());
- }
- ct++;
+ if (!(_options & SPECTRUM_STYLE)) {
+ listExits();
+ writeToRoomDescriptionStream(".\n");
}
- if (f == 0)
- display(_topWindow, _("none"));
- display(_topWindow, ".\n");
ct = 0;
f = 0;
- pos = 0;
- while (ct <= _gameHeader._numItems) {
- if (_items[ct]._location == MY_LOC) {
+ while (ct <= _G(_gameHeader)->_numItems) {
+ if (_G(_items)[ct]._location == MY_LOC) {
+ if (_G(_items)[ct]._text[0] == 0) {
+ error("Invisible item in room: %d\n", ct);
+ ct++;
+ continue;
+ }
if (f == 0) {
- if (_options & YOUARE) {
- display(_topWindow, _("\nYou can also see: "));
- pos = 18;
- } else {
- display(_topWindow, _("\nI can also see: "));
- pos = 16;
- }
+ writeToRoomDescriptionStream("%s", _G(_sys)[YOU_SEE].c_str());
f++;
- } else if (!(_options & TRS80_STYLE)) {
- display(_topWindow, " - ");
- pos += 3;
- }
- if (pos + (int)_items[ct]._text.size() > (_width - 10)) {
- pos = 0;
- display(_topWindow, "\n");
+ if (_options & SPECTRUM_STYLE)
+ writeToRoomDescriptionStream("\n");
+ } else if (!(_options & (TRS80_STYLE | SPECTRUM_STYLE))) {
+ writeToRoomDescriptionStream("%s", _G(_sys)[ITEM_DELIMITER].c_str());
}
- display(_topWindow, "%s", _items[ct]._text.c_str());
- pos += _items[ct]._text.size();
- if (_options & TRS80_STYLE) {
- display(_topWindow, ". ");
- pos += 2;
+ writeToRoomDescriptionStream("%s", _G(_items)[ct]._text.c_str());
+ if (_options & (TRS80_STYLE | SPECTRUM_STYLE)) {
+ writeToRoomDescriptionStream("%s", _G(_sys)[ITEM_DELIMITER].c_str());
}
}
ct++;
}
- display(_topWindow, "\n");
- if (_options & TRS80_STYLE)
- display(_topWindow, TRS80_LINE);
+ if ((_options & TI994A_STYLE) && f) {
+ writeToRoomDescriptionStream("%s", ".");
+ }
+
+ if (_options & SPECTRUM_STYLE) {
+ listExitsSpectrumStyle();
+ } else if (f) {
+ writeToRoomDescriptionStream("\n");
+ }
+
+ if ((_G(_autoInventory) || (_options & FORCE_INVENTORY)) && !(_options & FORCE_INVENTORY_OFF))
+ listInventoryInUpperWindow();
+
+ flushRoomDescription(buf);
}
int Scott::whichWord(const char *word, const Common::StringArray &list) {
int n = 1;
int ne = 1;
const char *tp;
- while (ne <= _gameHeader._numWords) {
+ while (ne <= _G(_gameHeader)->_numWords) {
tp = list[ne].c_str();
if (*tp == '*')
tp++;
else
n = ne;
- if (scumm_strnicmp(word, tp, _gameHeader._wordLength) == 0)
+ if (scumm_strnicmp(word, tp, _G(_gameHeader)->_wordLength) == 0)
return n;
ne++;
}
return -1;
}
-void Scott::lineInput(char *buf, size_t n) {
- event_t ev;
-
- glk_request_line_event(_bottomWindow, buf, n - 1, 0);
-
- do {
- glk_select(&ev);
- if (ev.type == evtype_Quit)
- return;
- else if (ev.type == evtype_LineInput)
- break;
- else if (ev.type == evtype_Arrange && _splitScreen)
- look();
- } while (ev.type != evtype_Quit);
-
- buf[ev.val1] = 0;
-}
-
Common::Error Scott::writeGameData(Common::WriteStream *ws) {
Common::String msg;
for (int ct = 0; ct < 16; ct++) {
- msg = Common::String::format("%d %d\n", _counters[ct], _roomSaved[ct]);
+ msg = Common::String::format("%d %d\n", _G(_counters)[ct], _G(_roomSaved)[ct]);
ws->write(msg.c_str(), msg.size());
ws->writeByte(0);
}
- msg = Common::String::format("%u %d %d %d %d %d\n",
- _bitFlags, (_bitFlags & (1 << DARKBIT)) ? 1 : 0,
- MY_LOC, _currentCounter, _savedRoom, _gameHeader._lightTime);
+ msg = Common::String::format("%u %d %d %d %d %d %d\n",
+ _G(_bitFlags), (_G(_bitFlags) & (1 << DARKBIT)) ? 1 : 0,
+ MY_LOC, _G(_currentCounter), _G(_savedRoom), _G(_gameHeader)->_lightTime, _G(_autoInventory));
ws->write(msg.c_str(), msg.size());
ws->writeByte(0);
- for (int ct = 0; ct <= _gameHeader._numItems; ct++) {
- msg = Common::String::format("%hd\n", (short)_items[ct]._location);
+ for (int ct = 0; ct <= _G(_gameHeader)->_numItems; ct++) {
+ msg = Common::String::format("%hd\n", (short)_G(_items)[ct]._location);
ws->write(msg.c_str(), msg.size());
ws->writeByte(0);
}
@@ -557,199 +669,220 @@ Common::Error Scott::readSaveData(Common::SeekableReadStream *rs) {
short lo;
short darkFlag;
+ int previousAutoInventory = _G(_autoInventory);
+
+ SavedState *state = saveCurrentState();
+
+ int result = 0;
+
for (ct = 0; ct < 16; ct++) {
line = QuetzalReader::readString(rs);
- sscanf(line.c_str(), "%d %d", &_counters[ct], &_roomSaved[ct]);
+ result = sscanf(line.c_str(), "%d %d", &_G(_counters)[ct], &_G(_roomSaved)[ct]);
+ if (result != 2 || _G(_roomSaved)[ct] > _G(_gameHeader)->_numRooms) {
+ recoverFromBadRestore(state);
+ return Common::kNoError;
+ }
}
line = QuetzalReader::readString(rs);
- sscanf(line.c_str(), "%u %hd %d %d %d %d\n",
- &_bitFlags, &darkFlag, &MY_LOC, &_currentCounter, &_savedRoom,
- &_gameHeader._lightTime);
-
+ result = sscanf(line.c_str(), "%u %hd %d %d %d %d %d\n",
+ &_G(_bitFlags), &darkFlag, &MY_LOC, &_G(_currentCounter), &_G(_savedRoom),
+ &_G(_gameHeader)->_lightTime, &_G(_autoInventory));
+
+ if (result == 6)
+ _G(_autoInventory) = previousAutoInventory;
+ if ((result != 7 && result != 6) || MY_LOC > _G(_gameHeader)->_numRooms || MY_LOC < 1 || _G(_savedRoom) > _G(_gameHeader)->_numRooms) {
+ recoverFromBadRestore(state);
+ return Common::kNoError;
+ }
// Backward compatibility
if (darkFlag)
- _bitFlags |= (1 << 15);
- for (ct = 0; ct <= _gameHeader._numItems; ct++) {
+ _G(_bitFlags) |= (1 << 15);
+ for (ct = 0; ct <= _G(_gameHeader)->_numItems; ct++) {
line = QuetzalReader::readString(rs);
- sscanf(line.c_str(), "%hd\n", &lo);
- _items[ct]._location = (unsigned char)lo;
+ result = sscanf(line.c_str(), "%hd\n", &lo);
+ _G(_items)[ct]._location = (unsigned char)lo;
+ if (result != 1 || (_G(_items)[ct]._location > _G(_gameHeader)->_numRooms && _G(_items)[ct]._location != CARRIED)) {
+ recoverFromBadRestore(state);
+ return Common::kNoError;
+ }
}
+ saveUndo();
+ _G(_justStarted) = 0;
+ _G(_stopTime) = 1;
return Common::kNoError;
}
-int Scott::getInput(int *vb, int *no) {
- char buf[256];
- char verb[10], noun[10];
- int vc, nc;
- int num;
-
- do {
- do {
- output("\nTell me what to do ? ");
- lineInput(buf, sizeof buf);
- if (g_vm->shouldQuit())
- return 0;
-
- num = sscanf(buf, "%9s %9s", verb, noun);
- } while (num == 0 || *buf == '\n');
-
- if (scumm_stricmp(verb, "restore") == 0) {
- loadGame();
- return -1;
- }
- if (num == 1)
- *noun = 0;
- if (*noun == 0 && strlen(verb) == 1) {
- switch (Common::isUpper((unsigned char)*verb) ? tolower((unsigned char)*verb) : *verb) {
- case 'n':
- strcpy(verb, "NORTH");
- break;
- case 'e':
- strcpy(verb, "EAST");
- break;
- case 's':
- strcpy(verb, "SOUTH");
- break;
- case 'w':
- strcpy(verb, "WEST");
- break;
- case 'u':
- strcpy(verb, "UP");
- break;
- case 'd':
- strcpy(verb, "DOWN");
- break;
- // Brian Howarth interpreter also supports this
- case 'i':
- strcpy(verb, "INVENTORY");
- break;
- default:
- break;
- }
- }
- nc = whichWord(verb, _nouns);
- // The Scott Adams system has a hack to avoid typing 'go'
- if (nc >= 1 && nc <= 6) {
- vc = 1;
- } else {
- vc = whichWord(verb, _verbs);
- nc = whichWord(noun, _nouns);
- }
- *vb = vc;
- *no = nc;
- if (vc == -1) {
- output(_("You use word(s) I don't know! "));
- }
- } while (vc == -1);
-
- strcpy(_nounText, noun); // Needed by GET/DROP hack
- return 0;
-}
-
-int Scott::performLine(int ct) {
- int continuation = 0;
+ActionResultType Scott::performLine(int ct) {
+#ifdef DEBUG_ACTIONS
+ debug("Performing line %d: ", ct);
+#endif
+ int continuation = 0, dead = 0;
int param[5], pptr = 0;
+ int p;
int act[4];
int cc = 0;
-
while (cc < 5) {
int cv, dv;
- cv = _actions[ct]._condition[cc];
+ cv = _G(_actions)[ct]._condition[cc];
dv = cv / 20;
cv %= 20;
+#ifdef DEBUG_ACTIONS
+ debug("Testing condition %d: ", cv);
+#endif
switch (cv) {
case 0:
param[pptr++] = dv;
break;
case 1:
- if (_items[dv]._location != CARRIED)
- return 0;
+#ifdef DEBUG_ACTIONS
+ debug("Does the player carry %s?\n", _G(_items)[dv].Text);
+#endif
+ if (_G(_items)[dv]._location != CARRIED)
+ return ACT_FAILURE;
break;
case 2:
- if (_items[dv]._location != MY_LOC)
- return 0;
+#ifdef DEBUG_ACTIONS
+ debug("Is %s in location?\n", _G(_items)[dv].Text);
+#endif
+ if (_G(_items)[dv]._location != MY_LOC)
+ return ACT_FAILURE;
break;
case 3:
- if (_items[dv]._location != CARRIED &&
- _items[dv]._location != MY_LOC)
- return 0;
+#ifdef DEBUG_ACTIONS
+ debug("Is %s held or in location?\n", _G(_items)[dv].Text);
+#endif
+ if (_G(_items)[dv]._location != CARRIED && _G(_items)[dv]._location != MY_LOC)
+ return ACT_FAILURE;
break;
case 4:
+#ifdef DEBUG_ACTIONS
+ debug("Is location %s?\n", _G(_rooms)[dv].Text);
+#endif
if (MY_LOC != dv)
- return 0;
+ return ACT_FAILURE;
break;
case 5:
- if (_items[dv]._location == MY_LOC)
- return 0;
+#ifdef DEBUG_ACTIONS
+ debug("Is %s NOT in location?\n", _G(_items)[dv].Text);
+#endif
+ if (_G(_items)[dv]._location == MY_LOC)
+ return ACT_FAILURE;
break;
case 6:
- if (_items[dv]._location == CARRIED)
- return 0;
+#ifdef DEBUG_ACTIONS
+ debug("Does the player NOT carry %s?\n", _G(_items)[dv].Text);
+#endif
+ if (_G(_items)[dv]._location == CARRIED)
+ return ACT_FAILURE;
break;
case 7:
+#ifdef DEBUG_ACTIONS
+ debug("Is location NOT %s?\n", _G(_rooms)[dv].Text);
+#endif
if (MY_LOC == dv)
- return 0;
+ return ACT_FAILURE;
break;
case 8:
- if ((_bitFlags & (1 << dv)) == 0)
- return 0;
+#ifdef DEBUG_ACTIONS
+ debug("Is bitflag %d set?\n", dv);
+#endif
+ if ((_G(_bitFlags) & (1 << dv)) == 0)
+ return ACT_FAILURE;
break;
case 9:
- if (_bitFlags & (1 << dv))
- return 0;
+#ifdef DEBUG_ACTIONS
+ debug("Is bitflag %d NOT set?\n", dv);
+#endif
+ if (_G(_bitFlags) & (1 << dv))
+ return ACT_FAILURE;
break;
case 10:
+#ifdef DEBUG_ACTIONS
+ debug("Does the player carry anything?\n");
+#endif
if (countCarried() == 0)
- return 0;
+ return ACT_FAILURE;
break;
case 11:
+#ifdef DEBUG_ACTIONS
+ debug("Does the player carry nothing?\n");
+#endif
if (countCarried())
- return 0;
+ return ACT_FAILURE;
break;
case 12:
- if (_items[dv]._location == CARRIED || _items[dv]._location == MY_LOC)
- return 0;
+#ifdef DEBUG_ACTIONS
+ debug("Is %s neither carried nor in room?\n", _G(_items)[dv].Text);
+#endif
+ if (_G(_items)[dv]._location == CARRIED || _G(_items)[dv]._location == MY_LOC)
+ return ACT_FAILURE;
break;
case 13:
- if (_items[dv]._location == 0)
- return 0;
+#ifdef DEBUG_ACTIONS
+ debug("Is %s (%d) in play?\n", _G(_items)[dv].Text, dv);
+#endif
+ if (_G(_items)[dv]._location == 0)
+ return ACT_FAILURE;
break;
case 14:
- if (_items[dv]._location)
- return 0;
+#ifdef DEBUG_ACTIONS
+ debug("Is %s NOT in play?\n", _G(_items)[dv].Text);
+#endif
+ if (_G(_items)[dv]._location)
+ return ACT_FAILURE;
break;
case 15:
- if (_currentCounter > dv)
- return 0;
+#ifdef DEBUG_ACTIONS
+ debug("Is _G(_currentCounter) <= %d?\n", dv);
+#endif
+ if (_G(_currentCounter) > dv)
+ return ACT_FAILURE;
break;
case 16:
- if (_currentCounter <= dv)
- return 0;
+#ifdef DEBUG_ACTIONS
+ debug("Is _G(_currentCounter) > %d?\n", dv);
+#endif
+ if (_G(_currentCounter) <= dv)
+ return ACT_FAILURE;
break;
case 17:
- if (_items[dv]._location != _items[dv]._initialLoc)
- return 0;
+#ifdef DEBUG_ACTIONS
+ debug("Is %s still in initial room?\n", _G(_items)[dv].Text);
+#endif
+ if (_G(_items)[dv]._location != _G(_items)[dv]._initialLoc)
+ return ACT_FAILURE;
break;
case 18:
- if (_items[dv]._location == _items[dv]._initialLoc)
- return 0;
- break;
- case 19:
- // Only seen in Brian Howarth games so far
- if (_currentCounter != dv)
- return 0;
+#ifdef DEBUG_ACTIONS
+ debug("Has %s been moved?\n", _G(_items)[dv].Text);
+#endif
+ if (_G(_items)[dv]._location == _G(_items)[dv]._initialLoc)
+ return ACT_FAILURE;
break;
- default:
+ case 19: /* Only seen in Brian Howarth games so far */
+#ifdef DEBUG_ACTIONS
+ debug("Is current counter == %d?\n", dv);
+ if (_G(_currentCounter) != dv)
+ debug("Nope, current counter is %d\n", _G(_currentCounter));
+#endif
+ if (_G(_currentCounter) != dv)
+ return ACT_FAILURE;
break;
}
+#ifdef DEBUG_ACTIONS
+ debug("YES\n");
+#endif
cc++;
}
+#if defined(__clang__)
+#pragma mark Subcommands
+#endif
- // _actions
- act[0] = _actions[ct]._action[0];
- act[2] = _actions[ct]._action[1];
+ /* Actions */
+ act[0] = _G(_actions)[ct]._action[0];
+ act[2] = _G(_actions)[ct]._action[1];
act[1] = act[0] % 150;
act[3] = act[2] % 150;
act[0] /= 150;
@@ -757,426 +890,466 @@ int Scott::performLine(int ct) {
cc = 0;
pptr = 0;
while (cc < 4) {
+#ifdef DEBUG_ACTIONS
+ debug("Performing action %d: ", act[cc]);
+#endif
if (act[cc] >= 1 && act[cc] < 52) {
- output(_messages[act[cc]]);
- output("\n");
+ printMessage(act[cc]);
} else if (act[cc] > 101) {
- output(_messages[act[cc] - 50]);
- output("\n");
- } else {
+ printMessage(act[cc] - 50);
+ } else
switch (act[cc]) {
- case 0:// NOP
+ case 0: /* NOP */
break;
case 52:
- if (countCarried() == _gameHeader._maxCarry) {
- if (_options & YOUARE)
- output(_("You are carrying too much. "));
- else
- output(_("I've too much to carry! "));
- break;
+ if (countCarried() >= _G(_gameHeader)->_maxCarry) {
+ output(_G(_sys)[YOURE_CARRYING_TOO_MUCH]);
+ return ACT_SUCCESS;
}
- _items[param[pptr++]]._location = CARRIED;
+ _G(_items)[param[pptr++]]._location = CARRIED;
break;
case 53:
- _items[param[pptr++]]._location = MY_LOC;
+#ifdef DEBUG_ACTIONS
+ debug("item %d (\"%s\") is now in location.\n", param[pptr],
+ _G(_items)[param[pptr]].Text);
+#endif
+ _G(_items)[param[pptr++]]._location = MY_LOC;
+ _shouldLookInTranscript = 1;
break;
case 54:
+#ifdef DEBUG_ACTIONS
+ debug("player location is now room %d (%s).\n", param[pptr],
+ _G(_rooms)[param[pptr]].Text);
+#endif
MY_LOC = param[pptr++];
+ _shouldLookInTranscript = 1;
+ look();
break;
case 55:
- _items[param[pptr++]]._location = 0;
+#ifdef DEBUG_ACTIONS
+ fprintf(stderr,
+ "Item %d (%s) is removed from the game (put in room 0).\n",
+ param[pptr], _G(_items)[param[pptr]].Text);
+#endif
+ _G(_items)[param[pptr++]]._location = 0;
break;
case 56:
- _bitFlags |= 1 << DARKBIT;
+ _G(_bitFlags) |= 1 << DARKBIT;
break;
case 57:
- _bitFlags &= ~(1 << DARKBIT);
+ _G(_bitFlags) &= ~(1 << DARKBIT);
break;
case 58:
- _bitFlags |= (1 << param[pptr++]);
+#ifdef DEBUG_ACTIONS
+ debug("Bitflag %d is set\n", param[pptr]);
+#endif
+ _G(_bitFlags) |= (1 << param[pptr++]);
break;
case 59:
- _items[param[pptr++]]._location = 0;
+#ifdef DEBUG_ACTIONS
+ debug("Item %d (%s) is removed from play.\n", param[pptr],
+ _G(_items)[param[pptr]].Text);
+#endif
+ _G(_items)[param[pptr++]]._location = 0;
break;
case 60:
- _bitFlags &= ~(1 << param[pptr++]);
+#ifdef DEBUG_ACTIONS
+ debug("BitFlag %d is cleared\n", param[pptr]);
+#endif
+ _G(_bitFlags) &= ~(1 << param[pptr++]);
break;
case 61:
- if (_options & YOUARE)
- output(_("You are dead.\n"));
- else
- output(_("I am dead.\n"));
- _bitFlags &= ~(1 << DARKBIT);
- MY_LOC = _gameHeader._numRooms;// It seems to be what the code says!
+ playerIsDead();
break;
- case 62: {
- // Bug fix for some systems - before it could get parameters wrong */
- int i = param[pptr++];
- _items[i]._location = param[pptr++];
+ case 62:
+ p = param[pptr++];
+ putItemAInRoomB(p, param[pptr++]);
break;
- }
case 63:
-doneit:
- output(_("The game is now over.\n"));
- glk_exit();
- return 0;
+#ifdef DEBUG_ACTIONS
+ debug("Game over.\n");
+#endif
+ doneIt();
+ dead = 1;
+ break;
case 64:
break;
- case 65: {
- int i = 0;
- int n = 0;
- while (i <= _gameHeader._numItems) {
- if (_items[i]._location == _gameHeader._treasureRoom &&
- _items[i]._text.hasPrefix("*"))
- n++;
- i++;
- }
- if (_options & YOUARE)
- output(_("You have stored "));
- else
- output(_("I've stored "));
- outputNumber(n);
- output(_(" treasures. On a scale of 0 to 100, that rates "));
- outputNumber((n * 100) / _gameHeader._treasures);
- output(".\n");
- if (n == _gameHeader._treasures) {
- output(_("Well done.\n"));
- goto doneit;
- }
+ case 65:
+ dead = printScore();
+ _G(_stopTime) = 2;
break;
- }
- case 66: {
- int i = 0;
- int f = 0;
- if (_options & YOUARE)
- output(_("You are carrying:\n"));
+ case 66:
+ if (_G(_game)->_type == SEAS_OF_BLOOD_VARIANT)
+ // TODO
+ // AdventureSheet();
+ debug("case 66 not implemented\n");
else
- output(_("I'm carrying:\n"));
- while (i <= _gameHeader._numItems) {
- if (_items[i]._location == CARRIED) {
- if (f == 1) {
- if (_options & TRS80_STYLE)
- output(". ");
- else
- output(" - ");
- }
- f = 1;
- output(_items[i]._text);
- }
- i++;
- }
- if (f == 0)
- output(_("Nothing"));
- output(".\n");
+ listInventory();
+ _G(_stopTime) = 2;
break;
- }
case 67:
- _bitFlags |= (1 << 0);
+ _G(_bitFlags) |= (1 << 0);
break;
case 68:
- _bitFlags &= ~(1 << 0);
+ _G(_bitFlags) &= ~(1 << 0);
break;
case 69:
- _gameHeader._lightTime = _lightRefill;
- _items[LIGHT_SOURCE]._location = CARRIED;
- _bitFlags &= ~(1 << LIGHTOUTBIT);
+ _G(_gameHeader)->_lightTime = _G(_lightRefill);
+ _G(_items)[LIGHT_SOURCE]._location = CARRIED;
+ _G(_bitFlags) &= ~(1 << LIGHTOUTBIT);
break;
case 70:
- clearScreen(); // pdd.
+ clearScreen(); /* pdd. */
break;
case 71:
saveGame();
+ _G(_stopTime) = 2;
break;
- case 72: {
- int i1 = param[pptr++];
- int i2 = param[pptr++];
- int t = _items[i1]._location;
- _items[i1]._location = _items[i2]._location;
- _items[i2]._location = t;
+ case 72:
+ p = param[pptr++];
+ swapItemLocations(p, param[pptr++]);
break;
- }
case 73:
+#ifdef DEBUG_ACTIONS
+ debug("Continue with next line\n");
+#endif
continuation = 1;
break;
case 74:
- _items[param[pptr++]]._location = CARRIED;
+ _G(_items)[param[pptr++]]._location = CARRIED;
break;
- case 75: {
- int i1, i2;
- i1 = param[pptr++];
- i2 = param[pptr++];
- _items[i1]._location = _items[i2]._location;
+ case 75:
+ p = param[pptr++];
+ moveItemAToLocOfItemB(p, param[pptr++]);
break;
- }
- case 76:
- // Looking at adventure ..
+ case 76: /* Looking at adventure .. */
+#ifdef DEBUG_ACTIONS
+ debug("LOOK\n");
+#endif
+ if (_splitScreen)
+ look();
+ _shouldLookInTranscript = 1;
break;
case 77:
- if (_currentCounter >= 0)
- _currentCounter--;
+ if (_G(_currentCounter) >= 1)
+ _G(_currentCounter)--;
+#ifdef DEBUG_ACTIONS
+ fprintf(stderr,
+ "decrementing current counter. Current counter is now %d.\n",
+ _G(_currentCounter));
+#endif
break;
case 78:
- outputNumber(_currentCounter);
+ outputNumber(_G(_currentCounter));
+ output(" ");
break;
case 79:
- _currentCounter = param[pptr++];
+#ifdef DEBUG_ACTIONS
+ debug("_G(_currentCounter) is set to %d.\n", param[pptr]);
+#endif
+ _G(_currentCounter) = param[pptr++];
break;
- case 80: {
- int t = MY_LOC;
- MY_LOC = _savedRoom;
- _savedRoom = t;
+ case 80:
+ goToStoredLoc();
break;
- }
- case 81: {
- // This is somewhat guessed. Claymorgue always seems to do
- // select counter n, thing, select counter n, but uses one value that always
- // seems to exist. Trying a few options I found this gave sane results on ageing
- int t = param[pptr++];
- int c1 = _currentCounter;
- _currentCounter = _counters[t];
- _counters[t] = c1;
+ case 81:
+ swapCounters(param[pptr++]);
break;
- }
case 82:
- _currentCounter += param[pptr++];
+ _G(_currentCounter) += param[pptr++];
break;
case 83:
- _currentCounter -= param[pptr++];
- if (_currentCounter < -1)
- _currentCounter = -1;
- // Note: This seems to be needed. I don't yet know if there
- // is a maximum value to limit too
+ _G(_currentCounter) -= param[pptr++];
+ if (_G(_currentCounter) < -1)
+ _G(_currentCounter) = -1;
+ /* Note: This seems to be needed. I don't yet
+ know if there is a maximum value to limit too */
break;
case 84:
- output(_nounText);
+ if (_G(_currentCommand))
+ glk_put_string_stream_uni(
+ glk_window_get_stream(_G(_bottomWindow)),
+ _G(_unicodeWords)[_G(_currentCommand)->_nounWordIndex]);
break;
case 85:
- output(_nounText);
+ if (_G(_currentCommand))
+ glk_put_string_stream_uni(
+ glk_window_get_stream(_G(_bottomWindow)),
+ _G(_unicodeWords)[_G(_currentCommand)->_nounWordIndex]);
output("\n");
break;
case 86:
- output("\n");
+ if (!(_options & SPECTRUM_STYLE))
+ output("\n");
break;
- case 87: {
- // Changed this to swap location<->roomflag[x] not roomflag 0 and x
- int p = param[pptr++];
- int sr = MY_LOC;
- MY_LOC = _roomSaved[p];
- _roomSaved[p] = sr;
+ case 87:
+ swapLocAndRoomFlag(param[pptr++]);
break;
- }
case 88:
- delay(2);
+#ifdef DEBUG_ACTIONS
+ debug("Delay\n");
+#endif
+ delay(1);
break;
case 89:
- pptr++;
- // SAGA draw picture n
- // Spectrum Seas of Blood - start combat ?
- // Poking this into older spectrum games causes a crash
+#ifdef DEBUG_ACTIONS
+ debug("Action 89, parameter %d\n", param[pptr]);
+#endif
+ p = param[pptr++];
+ switch (CURRENT_GAME) {
+ case SPIDERMAN:
+ case SPIDERMAN_C64:
+ // TODO
+ // DrawBlack();
+ break;
+ case SECRET_MISSION:
+ case SECRET_MISSION_C64:
+ // TODO
+ // SecretAction(p);
+ break;
+ case ADVENTURELAND:
+ case ADVENTURELAND_C64:
+ // TODO
+ // AdventurelandAction(p);
+ break;
+ case SEAS_OF_BLOOD:
+ case SEAS_OF_BLOOD_C64:
+ // TODO
+ // BloodAction(p);
+ break;
+ case ROBIN_OF_SHERWOOD:
+ case ROBIN_OF_SHERWOOD_C64:
+ // TODO
+ // SherwoodAction(p);
+ break;
+ case GREMLINS:
+ case GREMLINS_SPANISH:
+ case GREMLINS_GERMAN:
+ case GREMLINS_GERMAN_C64:
+ // TODO
+ // GremlinsAction(p);
+ break;
+ default:
+ break;
+ }
+ break;
+
+ case 90:
+#ifdef DEBUG_ACTIONS
+ debug("Draw Hulk image, parameter %d\n", param[pptr]);
+#endif
+ if (CURRENT_GAME != HULK && CURRENT_GAME != HULK_C64) {
+ pptr++;
+ } else if (!(_G(_bitFlags) & (1 << DARKBIT)))
+ drawHulkImage(param[pptr++]);
break;
default:
- error("Unknown action %d [Param begins %d %d]\n",
- act[cc], param[pptr], param[pptr + 1]);
+ debug("Unknown action %d [Param begins %d %d]\n", act[cc],
+ param[pptr], param[pptr + 1]);
break;
}
- }
-
cc++;
}
- return 1 + continuation;
+ if (dead) {
+ return ACT_GAMEOVER;
+ } else if (continuation) {
+ return ACT_CONTINUE;
+ } else {
+ return ACT_SUCCESS;
+ }
}
-int Scott::performActions(int vb, int no) {
- static bool disableSysFunc = false; // Recursion lock
- int d = _bitFlags & (1 << DARKBIT);
-
+ExplicitResultType Scott::performActions(int vb, int no) {
+ int dark = _G(_bitFlags) & (1 << DARKBIT);
int ct = 0;
- int fl;
- int doagain = 0;
- if (vb == 1 && no == -1) {
- output(_("Give me a direction too."));
- return 0;
+ ExplicitResultType flag = ER_NO_RESULT;
+ int doAgain = 0;
+ int foundMatch = 0;
+
+ if (vb == GO && no == -1) {
+ output(_G(_sys)[DIRECTION]);
+ return ER_SUCCESS;
}
if (vb == 1 && no >= 1 && no <= 6) {
int nl;
- if (_items[LIGHT_SOURCE]._location == MY_LOC ||
- _items[LIGHT_SOURCE]._location == CARRIED)
- d = 0;
- if (d)
- output(_("Dangerous to move in the dark! "));
- nl = _rooms[MY_LOC]._exits[no - 1];
+ if (_G(_items)[LIGHT_SOURCE]._location == MY_LOC || _G(_items)[LIGHT_SOURCE]._location == CARRIED)
+ dark = 0;
+ if (dark)
+ output(_G(_sys)[DANGEROUS_TO_MOVE_IN_DARK]);
+ nl = _G(_rooms)[MY_LOC]._exits[no - 1];
if (nl != 0) {
+ /* Seas of Blood needs this to be able to flee back to the last room */
+ if (_G(_game)->_type == SEAS_OF_BLOOD_VARIANT)
+ _G(_savedRoom) = MY_LOC;
+ if (_options & (SPECTRUM_STYLE | TI994A_STYLE))
+ output(_G(_sys)[OK]);
MY_LOC = nl;
- return 0;
+ _shouldLookInTranscript = 1;
+ if (_G(_currentCommand) && _G(_currentCommand)->_next) {
+ lookWithPause();
+ }
+ return ER_SUCCESS;
}
- if (d) {
- if (_options & YOUARE)
- output(_("You fell down and broke your neck. "));
- else
- output(_("I fell down and broke my neck. "));
- glk_exit();
- return 0;
+ if (dark) {
+ _G(_bitFlags) &= ~(1 << DARKBIT);
+ MY_LOC = _G(_gameHeader)->_numRooms; /* It seems to be what the code says! */
+ output(_G(_sys)[YOU_FELL_AND_BROKE_YOUR_NECK]);
+ _G(_bitFlags) &= ~(1 << DARKBIT);
+ MY_LOC = _G(_gameHeader)->_numRooms; /* It seems to be what the code says! */
+ return ER_SUCCESS;
}
- if (_options & YOUARE)
- output(_("You can't go in that direction. "));
- else
- output(_("I can't go in that direction. "));
- return 0;
+ output(_G(_sys)[YOU_CANT_GO_THAT_WAY]);
+ return ER_SUCCESS;
}
- fl = -1;
- while (ct <= _gameHeader._numActions) {
- int vv, nv;
- vv = _actions[ct]._vocab;
- // Think this is now right. If a line we run has an action73
- // run all following lines with vocab of 0,0
- if (vb != 0 && (doagain && vv != 0))
- break;
- // Oops.. added this minor cockup fix 1.11
- if (vb != 0 && !doagain && fl == 0)
- break;
- nv = vv % 150;
- vv /= 150;
- if ((vv == vb) || (doagain && _actions[ct]._vocab == 0)) {
- if ((vv == 0 && randomPercent(nv)) || doagain ||
- (vv != 0 && (nv == no || nv == 0))) {
- int f2;
- if (fl == -1)
- fl = -2;
- if ((f2 = performLine(ct)) > 0) {
- // ahah finally figured it out !
- fl = 0;
- if (f2 == 2)
- doagain = 1;
- if (vb != 0 && doagain == 0)
- return 0;
- }
+ if ((CURRENT_GAME == HULK || CURRENT_GAME == HULK_C64) && vb == 39 && !dark) {
+ hulkShowImageOnExamine(no);
+ }
- if (shouldQuit())
- return 0;
+ if (_G(_currentCommand) && _G(_currentCommand)->_allFlag && vb == _G(_currentCommand)->_verb && !(dark && vb == TAKE)) {
+ output(_G(_items)[_G(_currentCommand)->_item]._text);
+ output("....");
+ }
+ flag = ER_RAN_ALL_LINES_NO_MATCH;
+ if (CURRENT_GAME != TI994A) {
+ while (ct <= _G(_gameHeader)->_numActions) {
+ int verbvalue, nounvalue;
+ verbvalue = _G(_actions)[ct]._vocab;
+ /* Think this is now right. If a line we run has an action73
+ run all following lines with vocab of 0,0 */
+ if (vb != 0 && (doAgain && verbvalue != 0))
+ break;
+ /* Oops.. added this minor cockup fix 1.11 */
+ if (vb != 0 && !doAgain && flag == 0)
+ break;
+ nounvalue = verbvalue % 150;
+ verbvalue /= 150;
+ if ((verbvalue == vb) || (doAgain && _G(_actions)[ct]._vocab == 0)) {
+ if ((verbvalue == 0 && randomPercent(nounvalue)) || doAgain || (verbvalue != 0 && (nounvalue == no || nounvalue == 0))) {
+ if (verbvalue == vb && vb != 0 && nounvalue == no)
+ foundMatch = 1;
+ ActionResultType flag2;
+ if (flag == ER_RAN_ALL_LINES_NO_MATCH)
+ flag = ER_RAN_ALL_LINES;
+ if ((flag2 = performLine(ct)) != ACT_FAILURE) {
+ /* ahah finally figured it out ! */
+ flag = ER_SUCCESS;
+ if (flag2 == ACT_CONTINUE)
+ doAgain = 1;
+ else if (flag2 == ACT_GAMEOVER)
+ return ER_SUCCESS;
+ if (vb != 0 && doAgain == 0)
+ return ER_SUCCESS;
+ }
+ }
}
+
+ ct++;
+
+ /* Previously this did not check ct against
+ * GameHeader.NumActions and would read past the end of
+ * Actions. I don't know what should happen on the last
+ * action, but doing nothing is better than reading one
+ * past the end.
+ * --Chris
+ */
+ if (ct <= _G(_gameHeader)->_numActions && _G(_actions)[ct]._vocab != 0)
+ doAgain = 0;
}
- ct++;
+ } else {
+ if (vb == 0) {
+ // TODO
+ // RunImplicitTI99Actions();
+ return ER_NO_RESULT;
+ } else {
+ // TODO
+ // flag = RunExplicitTI99Actions(vb, no);
+ }
+ }
- // Previously this did not check ct against _gameHeader._numActions and would read
- // past the end of _actions. I don't know what should happen on the last action,
- // but doing nothing is better than reading one past the end.
- // --Chris
- if (ct <= _gameHeader._numActions && _actions[ct]._vocab != 0)
- doagain = 0;
- }
- if (fl != 0 && disableSysFunc == 0) {
- int item;
- if (_items[LIGHT_SOURCE]._location == MY_LOC ||
- _items[LIGHT_SOURCE]._location == CARRIED)
- d = 0;
- if (vb == 10 || vb == 18) {
- // Yes they really _are_ hardcoded values
- if (vb == 10) {
- if (scumm_stricmp(_nounText, "ALL") == 0) {
- int i = 0;
- int f = 0;
-
- if (d) {
- output(_("It is dark.\n"));
- return 0;
- }
- while (i <= _gameHeader._numItems) {
- if (_items[i]._location == MY_LOC && _items[i]._autoGet != nullptr && _items[i]._autoGet[0] != '*') {
- no = whichWord(_items[i]._autoGet.c_str(), _nouns);
- disableSysFunc = true; // Don't recurse into auto get !
- performActions(vb, no); // Recursively check each items table code
- disableSysFunc = false;
- if (shouldQuit())
- return 0;
-
- if (countCarried() == _gameHeader._maxCarry) {
- if (_options & YOUARE)
- output(_("You are carrying too much. "));
- else
- output(_("I've too much to carry. "));
- return 0;
- }
- _items[i]._location = CARRIED;
- output(_items[i]._text);
- output(_(": O.K.\n"));
- f = 1;
- }
- i++;
+ if (foundMatch)
+ return flag;
+
+ if (flag != ER_SUCCESS) {
+ int item = 0;
+ if (_G(_items)[LIGHT_SOURCE]._location == MY_LOC || _G(_items)[LIGHT_SOURCE]._location == CARRIED)
+ dark = 0;
+
+ if (vb == TAKE || vb == DROP) {
+ if (_G(_currentCommand)->_allFlag) {
+ if (vb == TAKE && dark) {
+ output(_G(_sys)[TOO_DARK_TO_SEE]);
+ while (!(_G(_currentCommand)->_allFlag & LASTALL)) {
+ _G(_currentCommand) = _G(_currentCommand)->_next;
}
- if (f == 0)
- output(_("Nothing taken."));
- return 0;
+ return ER_SUCCESS;
}
+ item = _G(_currentCommand)->_item;
+ int location = CARRIED;
+ if (vb == TAKE)
+ location = MY_LOC;
+ while (_G(_items)[item]._location != location && !(_G(_currentCommand)->_allFlag & LASTALL)) {
+ _G(_currentCommand) = _G(_currentCommand)->_next;
+ }
+ if (_G(_items)[item]._location != location)
+ return ER_SUCCESS;
+ }
+
+ /* Yes they really _are_ hardcoded values */
+ if (vb == TAKE) {
if (no == -1) {
- output(_("What ? "));
- return 0;
+ output(_G(_sys)[WHAT]);
+ return ER_SUCCESS;
}
- if (countCarried() == _gameHeader._maxCarry) {
- if (_options & YOUARE)
- output(_("You are carrying too much. "));
- else
- output(_("I've too much to carry. "));
- return 0;
+ if (countCarried() >= _G(_gameHeader)->_maxCarry) {
+ output(_G(_sys)[YOURE_CARRYING_TOO_MUCH]);
+ return ER_SUCCESS;
}
- item = matchUpItem(_nounText, MY_LOC);
+ if (!item)
+ item = matchUpItem(no, MY_LOC);
if (item == -1) {
- if (_options & YOUARE)
- output(_("It is beyond your power to do that. "));
- else
- output(_("It's beyond my power to do that. "));
- return 0;
- }
- _items[item]._location = CARRIED;
- output(_("O.K. "));
- return 0;
- }
- if (vb == 18) {
- if (scumm_stricmp(_nounText, "ALL") == 0) {
- int i = 0;
- int f = 0;
- while (i <= _gameHeader._numItems) {
- if (_items[i]._location == CARRIED && !_items[i]._autoGet.empty()
- && !_items[i]._autoGet.hasPrefix("*")) {
- no = whichWord(_items[i]._autoGet.c_str(), _nouns);
- disableSysFunc = true;
- performActions(vb, no);
- disableSysFunc = false;
- if (shouldQuit())
- return 0;
-
- _items[i]._location = MY_LOC;
- output(_items[i]._text);
- output(_(": O.K.\n"));
- f = 1;
+ item = matchUpItem(no, CARRIED);
+ if (item == -1) {
+ item = matchUpItem(no, 0);
+ if (item == -1) {
+ output(_G(_sys)[THATS_BEYOND_MY_POWER]);
+ } else {
+ output(_G(_sys)[YOU_DONT_SEE_IT]);
}
- i++;
+ } else {
+ output(_G(_sys)[YOU_HAVE_IT]);
}
- if (f == 0)
- output(_("Nothing dropped.\n"));
- return 0;
+ return ER_SUCCESS;
}
+ _G(_items)[item]._location = CARRIED;
+ printTakenOrDropped(TAKEN);
+ return ER_SUCCESS;
+ }
+
+ if (vb == DROP) {
if (no == -1) {
- output(_("What ? "));
- return 0;
+ output(_G(_sys)[WHAT]);
+ return ER_SUCCESS;
}
- item = matchUpItem(_nounText, CARRIED);
+ if (!item)
+ item = matchUpItem(no, CARRIED);
if (item == -1) {
- if (_options & YOUARE)
- output(_("It's beyond your power to do that.\n"));
- else
- output(_("It's beyond my power to do that.\n"));
- return 0;
+ item = matchUpItem(no, 0);
+ if (item == -1) {
+ output(_G(_sys)[THATS_BEYOND_MY_POWER]);
+ } else {
+ output(_G(_sys)[YOU_HAVENT_GOT_IT]);
+ }
+ return ER_SUCCESS;
}
- _items[item]._location = MY_LOC;
- output("O.K. ");
- return 0;
+ _G(_items)[item]._location = MY_LOC;
+ printTakenOrDropped(DROPPED);
+ return ER_SUCCESS;
}
}
}
-
- return fl;
+ return flag;
}
void Scott::readInts(Common::SeekableReadStream *f, size_t count, ...) {
@@ -1201,11 +1374,800 @@ void Scott::readInts(Common::SeekableReadStream *f, size_t count, ...) {
c = f->readByte();
}
- *val *= factor; // Handle negatives
+ *val *= factor; // Handle negatives
}
va_end(va);
}
+void Scott::writeToRoomDescriptionStream(const char *fmt, ...) {
+ if (_roomDescriptionStream == nullptr)
+ return;
+ va_list ap;
+
+ va_start(ap, fmt);
+ Common::String msg = Common::String::vformat(fmt, ap);
+ va_end(ap);
+
+ glk_put_string_stream(_roomDescriptionStream, msg.c_str());
+}
+
+void Scott::flushRoomDescription(char *buf) {
+ glk_stream_close(_roomDescriptionStream, 0);
+
+ strid_t storedTranscript = _G(_transcript);
+ if (!_printLookToTranscript)
+ _G(_transcript) = nullptr;
+
+ int printDelimiter = (_options & (TRS80_STYLE | SPECTRUM_STYLE | TI994A_STYLE));
+
+ if (_splitScreen) {
+ glk_window_clear(_G(_topWindow));
+ glk_window_get_size(_G(_topWindow), (uint *)&_topWidth, (uint *)&_topHeight);
+ int rows, length;
+ char *textWithBreaks = lineBreakText(buf, _topWidth, &rows, &length);
+
+ glui32 bottomheight;
+ glk_window_get_size(_G(_bottomWindow), nullptr, &bottomheight);
+ winid_t o2 = glk_window_get_parent(_G(_topWindow));
+ if (!(bottomheight < 3 && _topHeight < rows)) {
+ glk_window_get_size(_G(_topWindow), (uint *)&_topWidth, (uint *)&_topHeight);
+ glk_window_set_arrangement(o2, winmethod_Above | winmethod_Fixed, rows, _G(_topWindow));
+ } else {
+ printDelimiter = 0;
+ }
+
+ int line = 0;
+ int index = 0;
+ int i;
+ char *string = new char[_topWidth + 1];
+ for (line = 0; line < rows && index < length; line++) {
+ for (i = 0; i < _topWidth; i++) {
+ string[i] = textWithBreaks[index++];
+ if (string[i] == 10 || string[i] == 13 || index >= length)
+ break;
+ }
+ if (i < _topWidth + 1) {
+ string[i++] = '\n';
+ }
+ string[i] = 0;
+ if (strlen(string) == 0)
+ break;
+ glk_window_move_cursor(_G(_topWindow), 0, line);
+ display(_G(_topWindow), "%s", string);
+ }
+
+ if (line < rows - 1) {
+ glk_window_get_size(_G(_topWindow), (uint *)&_topWidth, (uint *)&_topHeight);
+ glk_window_set_arrangement(o2, winmethod_Above | winmethod_Fixed, MIN(rows - 1, _topHeight - 1), _G(_topWindow));
+ }
+
+ delete[] textWithBreaks;
+ } else {
+ display(_G(_bottomWindow), "%s", buf);
+ }
+
+ if (printDelimiter) {
+ printWindowDelimiter();
+ }
+
+ if (_pauseNextRoomDescription) {
+ delay(0.8);
+ _pauseNextRoomDescription = 0;
+ }
+
+ _G(_transcript) = storedTranscript;
+ if (buf != nullptr) {
+ delete[] buf;
+ buf = nullptr;
+ }
+}
+
+void Scott::printWindowDelimiter() {
+ glk_window_get_size(_G(_topWindow), (uint *)&_topWidth, (uint *)&_topHeight);
+ glk_window_move_cursor(_G(_topWindow), 0, _topHeight - 1);
+ glk_stream_set_current(glk_window_get_stream(_G(_topWindow)));
+ if (_options & SPECTRUM_STYLE)
+ for (int i = 0; i < _topWidth; i++)
+ glk_put_char('*');
+ else {
+ glk_put_char('<');
+ for (int i = 0; i < _topWidth - 2; i++)
+ glk_put_char('-');
+ glk_put_char('>');
+ }
+}
+
+void Scott::listExits() {
+ int ct = 0;
+ int f = 0;
+
+ writeToRoomDescriptionStream("\n\n%s", _G(_sys)[EXITS].c_str());
+
+ while (ct < 6) {
+ if ((&_G(_rooms)[MY_LOC])->_exits[ct] != 0) {
+ if (f) {
+ writeToRoomDescriptionStream("%s", _G(_sys)[EXITS_DELIMITER].c_str());
+ }
+ /* _G(_sys)[] begins with the exit names */
+ writeToRoomDescriptionStream("%s", _G(_sys)[ct].c_str());
+ f = 1;
+ }
+ ct++;
+ }
+ if (f == 0)
+ writeToRoomDescriptionStream("%s", _G(_sys)[NONE].c_str());
+ return;
+}
+
+void Scott::listExitsSpectrumStyle() {
+ int ct = 0;
+ int f = 0;
+
+ while (ct < 6) {
+ if ((&_G(_rooms)[MY_LOC])->_exits[ct] != 0) {
+ if (f == 0) {
+ writeToRoomDescriptionStream("\n\n%s", _G(_sys)[EXITS].c_str());
+ } else {
+ writeToRoomDescriptionStream("%s", _G(_sys)[EXITS_DELIMITER].c_str());
+ }
+ /* sys[] begins with the exit names */
+ writeToRoomDescriptionStream("%s", _G(_sys)[ct].c_str());
+ f = 1;
+ }
+ ct++;
+ }
+ writeToRoomDescriptionStream("\n");
+ return;
+}
+
+void Scott::listInventoryInUpperWindow() {
+ int i = 0;
+ int lastitem = -1;
+ writeToRoomDescriptionStream("\n%s", _G(_sys)[INVENTORY].c_str());
+ while (i <= _G(_gameHeader)->_numItems) {
+ if (_G(_items)[i]._location == CARRIED) {
+ if (_G(_items)[i]._text[0] == 0) {
+ error("Invisible item in inventory: %d\n", i);
+ i++;
+ continue;
+ }
+ if (lastitem > -1 && (_options & (TRS80_STYLE | SPECTRUM_STYLE)) == 0) {
+ writeToRoomDescriptionStream("%s", _G(_sys)[ITEM_DELIMITER].c_str());
+ }
+ lastitem = i;
+ writeToRoomDescriptionStream("%s", _G(_items)[i]._text.c_str());
+ if (_options & (TRS80_STYLE | SPECTRUM_STYLE)) {
+ writeToRoomDescriptionStream("%s", _G(_sys)[ITEM_DELIMITER].c_str());
+ }
+ }
+ i++;
+ }
+ if (lastitem == -1) {
+ writeToRoomDescriptionStream("%s\n", _G(_sys)[NOTHING].c_str());
+ } else {
+ if (_options & TI994A_STYLE && !itemEndsWithPeriod(lastitem))
+ writeToRoomDescriptionStream(".");
+ writeToRoomDescriptionStream("\n");
+ }
+}
+
+int Scott::itemEndsWithPeriod(int item) {
+ if (item < 0 || item > _G(_gameHeader)->_numItems)
+ return 0;
+ Common::String desc = _G(_items)[item]._text;
+ if (!desc.empty() && desc[0] != 0) {
+ const char lastchar = desc[desc.size() - 1];
+ if (lastchar == '.' || lastchar == '!') {
+ return 1;
+ }
+ }
+ return 0;
+}
+
+void Scott::closeGraphicsWindow() {
+ if (_G(_graphics) == nullptr)
+ _G(_graphics) = findGlkWindowWithRock(GLK_GRAPHICS_ROCK);
+ if (_G(_graphics)) {
+ glk_window_close(_G(_graphics), nullptr);
+ _G(_graphics) = nullptr;
+ glk_window_get_size(_G(_topWindow), (uint *)&_topWidth, (uint *)&_topHeight);
+ }
+}
+
+winid_t Scott::findGlkWindowWithRock(glui32 rock) {
+ glui32 rockptr;
+ winid_t win = glk_window_iterate(nullptr, &rockptr);
+ while (win) {
+ if (rockptr == rock)
+ return win;
+ win = glk_window_iterate(win, &rockptr);
+ }
+ return 0;
+}
+
+void Scott::openGraphicsWindow() {
+ if (!glk_gestalt(gestalt_Graphics, 0))
+ return;
+ glui32 graphwidth, graphheight, optimalWidth, optimalHeight;
+
+ if (_G(_topWindow) == nullptr)
+ _G(_topWindow) = findGlkWindowWithRock(GLK_STATUS_ROCK);
+ if (_G(_graphics) == nullptr)
+ _G(_graphics) = findGlkWindowWithRock(GLK_GRAPHICS_ROCK);
+ if (_G(_graphics) == nullptr && _G(_topWindow) != nullptr) {
+ glk_window_get_size(_G(_topWindow), (uint *)&_topWidth, (uint *)&_topHeight);
+ glk_window_close(_G(_topWindow), nullptr);
+ _G(_graphics) = glk_window_open(_G(_bottomWindow), winmethod_Above | winmethod_Proportional, 60, wintype_Graphics, GLK_GRAPHICS_ROCK);
+ glk_window_get_size(_G(_graphics), &graphwidth, &graphheight);
+ _G(_pixelSize) = optimalPictureSize(&optimalWidth, &optimalHeight);
+ _G(_xOffset) = ((int)graphwidth - (int)optimalWidth) / 2;
+
+ if (graphheight > optimalHeight) {
+ winid_t parent = glk_window_get_parent(_G(_graphics));
+ glk_window_set_arrangement(parent, winmethod_Above | winmethod_Fixed, optimalHeight, nullptr);
+ }
+
+ // Set the graphics window background to match the main window background, best as we can, and clear the window.
+ glui32 backgroundColor;
+ if (glk_style_measure(_G(_bottomWindow), style_Normal, stylehint_BackColor, &backgroundColor)) {
+ glk_window_set_background_color(_G(_graphics), backgroundColor);
+ glk_window_clear(_G(_graphics));
+ }
+
+ _G(_topWindow) = glk_window_open(_G(_bottomWindow), winmethod_Above | winmethod_Fixed, _topHeight, wintype_TextGrid, GLK_STATUS_ROCK);
+ glk_window_get_size(_G(_topWindow), (uint *)&_topWidth, (uint *)&_topHeight);
+ } else {
+ if (!_G(_graphics))
+ _G(_graphics) = glk_window_open(_G(_bottomWindow), winmethod_Above | winmethod_Proportional, 60, wintype_Graphics, GLK_GRAPHICS_ROCK);
+ glk_window_get_size(_G(_graphics), &graphwidth, &graphheight);
+ _G(_pixelSize) = optimalPictureSize(&optimalWidth, &optimalHeight);
+ _G(_xOffset) = (graphwidth - optimalWidth) / 2;
+ winid_t parent = glk_window_get_parent(_G(_graphics));
+ glk_window_set_arrangement(parent, winmethod_Above | winmethod_Fixed, optimalHeight, nullptr);
+ }
+}
+
+glui32 Scott::optimalPictureSize(glui32 *width, glui32 *height) {
+ *width = 255;
+ *height = 96;
+ int multiplier = 1;
+ glui32 graphwidth, graphheight;
+ glk_window_get_size(_G(_graphics), &graphwidth, &graphheight);
+ multiplier = graphheight / 96;
+ if (static_cast<glui32>(255 * multiplier) > graphwidth)
+ multiplier = graphwidth / 255;
+
+ if (multiplier == 0)
+ multiplier = 1;
+
+ *width = 255 * multiplier;
+ *height = 96 * multiplier;
+
+ return multiplier;
+}
+
+void Scott::openTopWindow() {
+ _G(_topWindow) = findGlkWindowWithRock(GLK_STATUS_ROCK);
+ if (_G(_topWindow) == nullptr) {
+ if (_splitScreen) {
+ _G(_topWindow) = glk_window_open(_G(_bottomWindow), winmethod_Above | winmethod_Fixed, _topHeight, wintype_TextGrid, GLK_STATUS_ROCK);
+ if (_G(_topWindow) == nullptr) {
+ _splitScreen = 0;
+ _G(_topWindow) = _G(_bottomWindow);
+ } else {
+ glk_window_get_size(_G(_topWindow), (uint *)&_topWidth, nullptr);
+ }
+ } else {
+ _G(_topWindow) = _G(_bottomWindow);
+ }
+ }
+}
+
+void Scott::cleanupAndExit() {
+ if (_G(_transcript))
+ glk_stream_close(_G(_transcript), nullptr);
+ if (drawingVector()) {
+ _G(_gliSlowDraw) = 0;
+ drawSomeVectorPixels(0);
+ }
+ glk_exit();
+}
+
+void Scott::drawBlack() {
+ glk_window_fill_rect(_G(_graphics), 0, _G(_xOffset), 0, 32 * 8 * _G(_pixelSize), 12 * 8 * _G(_pixelSize));
+}
+
+void Scott::drawImage(int image) {
+ if (!glk_gestalt(gestalt_Graphics, 0))
+ return;
+ openGraphicsWindow();
+ if (_G(_graphics) == nullptr) {
+ error("DrawImage: Graphic window nullptr?\n");
+ return;
+ }
+ if (_G(_game)->_pictureFormatVersion == 99)
+ drawVectorPicture(image);
+ else
+ drawSagaPictureNumber(image);
+}
+
+void Scott::drawRoomImage() {
+ if (CURRENT_GAME == ADVENTURELAND || CURRENT_GAME == ADVENTURELAND_C64) {
+ // TODO
+ // AdventurelandDarkness();
+ }
+
+ int dark = ((_G(_bitFlags) & (1 << DARKBIT)) && _G(_items)[LIGHT_SOURCE]._location != CARRIED && _G(_items)[LIGHT_SOURCE]._location != MY_LOC);
+
+ if (dark && _G(_graphics) != nullptr && !(_G(_rooms)[MY_LOC]._image == 255)) {
+ _G(_vectorImageShown) = -1;
+ _G(_vectorState) = NO_VECTOR_IMAGE;
+ glk_request_timer_events(0);
+ drawBlack();
+ return;
+ }
+
+ switch (CURRENT_GAME) {
+ case SEAS_OF_BLOOD:
+ case SEAS_OF_BLOOD_C64:
+ // TODO
+ // SeasOfBloodRoomImage();
+ return;
+ case ROBIN_OF_SHERWOOD:
+ case ROBIN_OF_SHERWOOD_C64:
+ // TODO
+ // RobinOfSherwoodLook();
+ return;
+ case HULK:
+ case HULK_C64:
+ hulkLook();
+ return;
+ default:
+ break;
+ }
+
+ if (_G(_rooms)[MY_LOC]._image == 255) {
+ closeGraphicsWindow();
+ return;
+ }
+
+ if (dark)
+ return;
+
+ if (_G(_game)->_pictureFormatVersion == 99) {
+ drawImage(MY_LOC - 1);
+ return;
+ }
+
+ if (_G(_game)->_type == GREMLINS_VARIANT) {
+ // TODO
+ // GremlinsLook();
+ } else {
+ drawImage(_G(_rooms)[MY_LOC]._image & 127);
+ }
+ for (int ct = 0; ct <= _G(_gameHeader)->_numItems; ct++)
+ if (_G(_items)[ct]._image && _G(_items)[ct]._location == MY_LOC) {
+ if ((_G(_items)[ct]._flag & 127) == MY_LOC) {
+ drawImage(_G(_items)[ct]._image);
+ /* Draw the correct image of the bear on the beach */
+ } else if (_G(_game)->_type == SAVAGE_ISLAND_VARIANT && ct == 20 && MY_LOC == 8) {
+ drawImage(9);
+ }
+ }
+}
+
+void Scott::restartGame() {
+ if (_G(_currentCommand))
+ freeCommands();
+ restoreState(_G(_initialState));
+ _G(_justStarted) = 0;
+ _G(_stopTime) = 0;
+ glk_window_clear(_G(_bottomWindow));
+ openTopWindow();
+ _G(_shouldRestart) = 0;
+}
+
+void Scott::transcriptOn() {
+ frefid_t ref;
+
+ if (_G(_transcript)) {
+ output(_G(_sys)[TRANSCRIPT_ALREADY]);
+ return;
+ }
+
+ ref = glk_fileref_create_by_prompt(fileusage_TextMode | fileusage_Transcript, filemode_Write, 0);
+ if (ref == nullptr)
+ return;
+
+ _G(_transcript) = glk_stream_open_file_uni(ref, filemode_Write, 0);
+ glk_fileref_destroy(ref);
+
+ if (_G(_transcript) == nullptr) {
+ output(_G(_sys)[FAILED_TRANSCRIPT]);
+ return;
+ }
+
+ glui32 *startOfTranscript = toUnicode(_G(_sys)[TRANSCRIPT_START].c_str());
+ glk_put_string_stream_uni(_G(_transcript), startOfTranscript);
+ delete[] startOfTranscript;
+ glk_put_string_stream(glk_window_get_stream(_G(_bottomWindow)), _G(_sys)[TRANSCRIPT_ON].c_str());
+}
+
+void Scott::transcriptOff() {
+ if (_G(_transcript) == nullptr) {
+ output(_G(_sys)[NO_TRANSCRIPT]);
+ return;
+ }
+
+ glui32 *endOfTranscript = toUnicode(_G(_sys)[TRANSCRIPT_END].c_str());
+ glk_put_string_stream_uni(_G(_transcript), endOfTranscript);
+ delete[] endOfTranscript;
+
+ glk_stream_close(_G(_transcript), nullptr);
+ _G(_transcript) = nullptr;
+ output(_G(_sys)[TRANSCRIPT_OFF]);
+}
+
+int Scott::performExtraCommand(int extraStopTime) {
+ Command command = *_G(_currentCommand);
+ int verb = command._verb;
+ if (verb > _G(_gameHeader)->_numWords)
+ verb -= _G(_gameHeader)->_numWords;
+ int noun = command._noun;
+ if (noun > _G(_gameHeader)->_numWords)
+ noun -= _G(_gameHeader)->_numWords;
+ else if (noun) {
+ const char *nounWord = _G(_charWords)[_G(_currentCommand)->_nounWordIndex];
+ int newnoun = whichWord(nounWord, _G(_extraNouns));
+ newnoun = _G(_extraNounsKey)[newnoun];
+ if (newnoun)
+ noun = newnoun;
+ }
+
+ _G(_stopTime) = 1 + extraStopTime;
+
+ switch (verb) {
+ case RESTORE:
+ if (noun == 0 || noun == GAME) {
+ loadGame();
+ return 1;
+ }
+ break;
+ case RESTART:
+ if (noun == 0 || noun == GAME) {
+ output(_G(_sys)[ARE_YOU_SURE]);
+ if (yesOrNo()) {
+ _G(_shouldRestart) = 1;
+ }
+ return 1;
+ }
+ break;
+ case SAVE:
+ if (noun == 0 || noun == GAME) {
+ saveGame();
+ return 1;
+ }
+ break;
+ case UNDO:
+ if (noun == 0 || noun == COMMAND) {
+ restoreUndo();
+ return 1;
+ }
+ break;
+ case RAM:
+ if (noun == RAMLOAD) {
+ ramRestore();
+ return 1;
+ } else if (noun == RAMSAVE) {
+ ramSave();
+ return 1;
+ }
+ break;
+ case RAMSAVE:
+ if (noun == 0) {
+ ramSave();
+ return 1;
+ }
+ break;
+ case RAMLOAD:
+ if (noun == 0) {
+ ramRestore();
+ return 1;
+ }
+ break;
+ case SCRIPT:
+ if (noun == ON || noun == 0) {
+ transcriptOn();
+ return 1;
+ } else if (noun == OFF) {
+ transcriptOff();
+ return 1;
+ }
+ break;
+ case EXCEPT:
+ freeCommands();
+ }
+
+ _G(_stopTime) = 0;
+ return 0;
+}
+
+int Scott::yesOrNo() {
+ glk_request_char_event(_G(_bottomWindow));
+
+ event_t ev;
+ int result = 0;
+ const char y = tolower((unsigned char)_G(_sys)[YES][0]);
+ const char n = tolower((unsigned char)_G(_sys)[NO][0]);
+
+ do {
+ glk_select(&ev);
+ if (ev.type == evtype_CharInput) {
+ const char reply = tolower(ev.val1);
+ if (reply == y) {
+ result = 1;
+ } else if (reply == n) {
+ result = 2;
+ } else {
+ output(_G(_sys)[ANSWER_YES_OR_NO]);
+ glk_request_char_event(_G(_bottomWindow));
+ }
+ } else
+ updates(ev);
+ } while (result == 0);
+
+ return (result == 1);
+}
+
+void Scott::hitEnter() {
+ glk_request_char_event(_G(_bottomWindow));
+
+ event_t ev;
+ int result = 0;
+ do {
+ glk_select(&ev);
+ if (ev.type == evtype_CharInput) {
+ if (ev.val1 == keycode_Return) {
+ result = 1;
+ } else {
+ glk_request_char_event(_G(_bottomWindow));
+ }
+ } else
+ updates(ev);
+ } while (result == 0);
+
+ return;
+}
+
+void Scott::listInventory() {
+ int i = 0;
+ int lastitem = -1;
+ output(_G(_sys)[INVENTORY]);
+ while (i <= _G(_gameHeader)->_numItems) {
+ if (_G(_items)[i]._location == CARRIED) {
+ if (_G(_items)[i]._text[0] == 0) {
+ warning("Invisible item in inventory: %d\n", i);
+ i++;
+ continue;
+ }
+ if (lastitem > -1 && (_options & (TRS80_STYLE | SPECTRUM_STYLE)) == 0) {
+ output(_G(_sys)[ITEM_DELIMITER]);
+ }
+ lastitem = i;
+ output(_G(_items)[i]._text);
+ if (_options & (TRS80_STYLE | SPECTRUM_STYLE)) {
+ output(_G(_sys)[ITEM_DELIMITER]);
+ }
+ }
+ i++;
+ }
+ if (lastitem == -1)
+ output(_G(_sys)[NOTHING]);
+ else if (_options & TI994A_STYLE) {
+ if (!itemEndsWithPeriod(lastitem))
+ output(".");
+ output(" ");
+ }
+ if (_G(_transcript)) {
+ glk_put_char_stream_uni(_G(_transcript), 10);
+ }
+}
+
+void Scott::lookWithPause() {
+ char fc = _G(_rooms)[MY_LOC]._text[0];
+ if (_G(_rooms)[MY_LOC]._text.empty() || MY_LOC == 0 || fc == 0 || fc == '.' || fc == ' ')
+ return;
+ _shouldLookInTranscript = 1;
+ _pauseNextRoomDescription = 1;
+ look();
+}
+
+void Scott::doneIt() {
+ if (_splitScreen && _G(_topWindow))
+ look();
+ output("\n\n");
+ output(_G(_sys)[PLAY_AGAIN]);
+ output("\n");
+ if (yesOrNo()) {
+ _G(_shouldRestart) = 1;
+ } else {
+ cleanupAndExit();
+ }
+}
+
+int Scott::printScore() {
+ int i = 0;
+ int n = 0;
+ while (i <= _G(_gameHeader)->_numItems) {
+ if (_G(_items)[i]._location == _G(_gameHeader)->_treasureRoom && _G(_items)[i]._text[0] == '*')
+ n++;
+ i++;
+ }
+ display(_G(_bottomWindow), "%s %d %s%s %d.\n", _G(_sys)[IVE_STORED].c_str(), n, _G(_sys)[TREASURES].c_str(),
+ _G(_sys)[ON_A_SCALE_THAT_RATES].c_str(), (n * 100) / _G(_gameHeader)->_treasures);
+ if (n == _G(_gameHeader)->_treasures) {
+ output(_G(_sys)[YOUVE_SOLVED_IT].c_str());
+ doneIt();
+ return 1;
+ }
+ return 0;
+}
+
+void Scott::printNoun() {
+ if (_G(_currentCommand))
+ glk_put_string_stream_uni(glk_window_get_stream(_G(_bottomWindow)), _G(_unicodeWords)[_G(_currentCommand)->_nounWordIndex]);
+}
+
+void Scott::moveItemAToLocOfItemB(int itemA, int itemB) {
+ _G(_items)
+ [itemA]._location = _G(_items)[itemB]._location;
+ if (_G(_items)[itemB]._location == MY_LOC)
+ _shouldLookInTranscript = 1;
+}
+
+void Scott::goToStoredLoc() {
+#ifdef DEBUG_ACTIONS
+ debug("switch location to stored location (%d) (%s).\n",
+ SavedRoom, _G(_rooms)[SavedRoom].Text);
+#endif
+ int t = MY_LOC;
+ MY_LOC = _G(_savedRoom);
+ _G(_savedRoom) = t;
+ _shouldLookInTranscript = 1;
+}
+
+void Scott::swapLocAndRoomFlag(int index) {
+#ifdef DEBUG_ACTIONS
+ debug("swap location<->roomflag[%d]\n", index);
+#endif
+ int temp = MY_LOC;
+ MY_LOC = _G(_roomSaved)[index];
+ _G(_roomSaved)[index] = temp;
+ _shouldLookInTranscript = 1;
+ look();
+}
+
+void Scott::swapItemLocations(int itemA, int itemB) {
+ int temp = _G(_items)[itemA]._location;
+ _G(_items)[itemA]._location = _G(_items)[itemB]._location;
+ _G(_items)[itemB]._location = temp;
+ if (_G(_items)[itemA]._location == MY_LOC || _G(_items)[itemB]._location == MY_LOC)
+ _shouldLookInTranscript = 1;
+}
+
+void Scott::putItemAInRoomB(int itemA, int roomB) {
+#ifdef DEBUG_ACTIONS
+ debug("Item %d (%s) is put in room %d (%s). MY_LOC: %d (%s)\n",
+ itemA, _G(_items)[arg1].Text, roomB, _G(_rooms)[roomB].Text, MY_LOC,
+ _G(_rooms)[MY_LOC].Text);
+#endif
+ if (_G(_items)[itemA]._location == MY_LOC)
+ lookWithPause();
+ _G(_items)[itemA]._location = roomB;
+}
+
+void Scott::swapCounters(int index) {
+#ifdef DEBUG_ACTIONS
+ debug("Select a counter.Current counter is swapped with backup "
+ "counter %d\n",
+ index);
+#endif
+ if (index > 15) {
+ error("ERROR! parameter out of range. Max 15, got %d\n", index);
+ index = 15;
+ }
+ int temp = _G(_currentCounter);
+
+ _G(_currentCounter) = _G(_counters)[index];
+ _G(_counters)[index] = temp;
+#ifdef DEBUG_ACTIONS
+ debug("Value of new selected counter is %d\n", _G(_currentCounter));
+#endif
+}
+
+void Scott::printMessage(int index) {
+#ifdef DEBUG_ACTIONS
+ debug("Print message %d: \"%s\"\n", index, Messages[index]);
+#endif
+ Common::String message = _G(_messages)[index];
+ if (!message.empty() && message[0] != 0) {
+ output(message);
+ const char lastchar = message[message.size() - 1];
+ if (lastchar != 13 && lastchar != 10)
+ output(_G(_sys)[MESSAGE_DELIMITER]);
+ }
+}
+
+void Scott::playerIsDead() {
+#ifdef DEBUG_ACTIONS
+ debug("Player is dead\n");
+#endif
+ output(_G(_sys)[IM_DEAD]);
+ _G(_bitFlags) &= ~(1 << DARKBIT);
+ MY_LOC = _G(_gameHeader)->_numRooms; /* It seems to be what the code says! */
+}
+
+void Scott::printTakenOrDropped(int index) {
+ output(_G(_sys[index]));
+ int length = _G(_sys)[index].size();
+ char last = _G(_sys)[index][length - 1];
+ if (last == 10 || last == 13)
+ return;
+ output(" ");
+ if ((!(_G(_currentCommand)->_allFlag & LASTALL)) || _splitScreen == 0) {
+ output("\n");
+ }
+}
+
+void Scott::printTitleScreenBuffer() {
+ glk_stream_set_current(glk_window_get_stream(_G(_bottomWindow)));
+ glk_set_style(style_User1);
+ clearScreen();
+ output(_titleScreen);
+ glk_set_style(style_Normal);
+ hitEnter();
+ clearScreen();
+}
+
+void Scott::printTitleScreenGrid() {
+ int titleLength = _titleScreen.size();
+ int rows = 0;
+ for (int i = 0; i < titleLength; i++)
+ if (_titleScreen[i] == '\n')
+ rows++;
+ winid_t titlewin = glk_window_open(_G(_bottomWindow), winmethod_Above | winmethod_Fixed, rows + 2,
+ wintype_TextGrid, 0);
+ glui32 width, height;
+ glk_window_get_size(titlewin, &width, &height);
+ if (width < 40 || height < static_cast<glui32>(rows + 2)) {
+ glk_window_close(titlewin, nullptr);
+ printTitleScreenBuffer();
+ return;
+ }
+ int offset = (width - 40) / 2;
+ int pos = 0;
+ for (int i = 1; i <= rows; i++) {
+ glk_window_move_cursor(titlewin, offset, i);
+ while (_titleScreen[pos] != '\n' && pos < titleLength)
+ display(titlewin, "%c", _titleScreen[pos++]);
+ pos++;
+ }
+ hitEnter();
+ glk_window_close(titlewin, nullptr);
+}
+
+void writeToRoomDescriptionStream(const char *fmt, ...) {
+ if (_G(_roomDescriptionStream == nullptr))
+ return;
+ va_list ap;
+
+ va_start(ap, fmt);
+ Common::String msg = Common::String::vformat(fmt, ap);
+ va_end(ap);
+
+ g_scott->glk_put_string_stream(_G(_roomDescriptionStream), msg.c_str());
+}
+
} // End of namespace Scott
} // End of namespace Glk
diff --git a/engines/glk/scott/scott.h b/engines/glk/scott/scott.h
index f0f7d426c6c..23561404271 100644
--- a/engines/glk/scott/scott.h
+++ b/engines/glk/scott/scott.h
@@ -30,32 +30,49 @@
#include "common/scummsys.h"
#include "glk/glk_api.h"
+#include "glk/scott/definitions.h"
+#include "glk/scott/globals.h"
+#include <cstdint>
namespace Glk {
namespace Scott {
-#define LIGHT_SOURCE 9 // Always 9 how odd
-#define CARRIED 255 // Carried
-#define DESTROYED 0 // Destroyed
-#define DARKBIT 15
-#define LIGHTOUTBIT 16 // Light gone out
+struct Command;
+
+#define LIGHT_SOURCE 9 // Always 9 how odd
+#define CARRIED 255 // Carried
+#define DESTROYED 0 // Destroyed
+#define DARKBIT 15
+#define LIGHTOUTBIT 16 // Light gone out
enum GameOption {
- YOUARE = 1, ///< You are not I am
- SCOTTLIGHT = 2, ///< Authentic Scott Adams light messages
- DEBUGGING = 4, ///< Info from database load
- TRS80_STYLE = 8, ///< Display in style used on TRS-80
- PREHISTORIC_LAMP = 16 ///< Destroy the lamp (very old databases)
+ YOUARE = 1, ///< You are not I am
+ SCOTTLIGHT = 2, ///< Authentic Scott Adams light messages
+ DEBUGGING = 4, ///< Info from database load
+ TRS80_STYLE = 8, ///< Display in style used on TRS-80
+ PREHISTORIC_LAMP = 16, ///< Destroy the lamp (very old databases)
+ SPECTRUM_STYLE = 32, ///< Display in style used on ZX Spectrum
+ TI994A_STYLE = 64, ///< Display in style used on TI-99/4A
+ NO_DELAYS = 128, ///< Skip all pauses
+ FORCE_PALETTE_ZX = 256, ///< Force ZX Spectrum image palette
+ FORCE_PALETTE_C64 = 512, ///< Force CBM 64 image palette
+ FORCE_INVENTORY = 1024, ///< Inventory in upper window always on
+ FORCE_INVENTORY_OFF = 2048 ///< Inventory in upper window always off
};
-#define TRS80_LINE "\n<------------------------------------------------------------>\n"
-#define MY_LOC (_gameHeader._playerRoom)
+#define GLK_BUFFER_ROCK 1
+#define GLK_STATUS_ROCK 1010
+#define GLK_GRAPHICS_ROCK 1020
+
+#define TRS80_LINE "\n<------------------------------------------------------------>\n"
+#define MY_LOC (_G(_gameHeader)->_playerRoom)
+#define CURRENT_GAME (_G(_game->_gameID))
struct Header {
int _unknown;
int _numItems;
int _numActions;
- int _numWords; ///< Smaller of verb/noun is padded to same size
+ int _numWords; ///< Smaller of verb/noun is padded to same size
int _numRooms;
int _maxCarry;
int _playerRoom;
@@ -66,8 +83,8 @@ struct Header {
int _treasureRoom;
Header() : _unknown(0), _numItems(0), _numActions(0), _numWords(0), _numRooms(0),
- _maxCarry(0), _playerRoom(0), _treasures(0), _wordLength(0), _lightTime(0),
- _numMessages(0), _treasureRoom(0) {}
+ _maxCarry(0), _playerRoom(0), _treasures(0), _wordLength(0), _lightTime(0),
+ _numMessages(0), _treasureRoom(0) {}
};
struct Action {
@@ -84,8 +101,9 @@ struct Action {
struct Room {
Common::String _text;
int _exits[6];
+ byte _image;
- Room() {
+ Room() : _image(255) {
Common::fill(&_exits[0], &_exits[6], 0);
}
};
@@ -95,8 +113,10 @@ struct Item {
byte _location;
byte _initialLoc;
Common::String _autoGet;
+ byte _flag;
+ byte _image;
- Item() : _location(0), _initialLoc(0) {}
+ Item() : _location(0), _initialLoc(0), _flag(0), _image(0) {}
};
struct Tail {
@@ -112,55 +132,93 @@ struct Tail {
*/
class Scott : public GlkAPI {
private:
- Header _gameHeader;
- Common::Array<Item> _items;
- Common::Array<Room> _rooms;
- Common::StringArray _verbs;
- Common::StringArray _nouns;
- Common::StringArray _messages;
- Common::Array<Action> _actions;
- int _lightRefill;
+ Globals _globals;
char _nounText[16];
- int _counters[16]; ///< Range unknown
- int _currentCounter;
- int _savedRoom;
- int _roomSaved[16]; ///< Range unknown
- int _options; ///< Option flags set
- int _width; ///< Terminal width
- int _topHeight; ///< Height of top window
-
- bool _splitScreen;
- winid_t _bottomWindow, _topWindow;
- uint32 _bitFlags; ///< Might be >32 flags - I haven't seen >32 yet
- int _saveSlot; ///< Save slot when loading savegame from launcher
+ int _options = 0; ///< Option flags set
+ int _width = 0; ///< Terminal width
+ int _topHeight = 0; ///< Height of top window
+ int _topWidth = 0;
+
+ bool _splitScreen = true;
+ int _saveSlot = -1; ///< Save slot when loading savegame from launcher
+ Common::String _titleScreen;
+
+ int _shouldLookInTranscript = 0;
+ int _printLookToTranscript = 0;
+ int _pauseNextRoomDescription = 0;
+
+ strid_t _roomDescriptionStream = nullptr;
+
private:
/**
* Initialization code
*/
void initialize();
- void display(winid_t w, const char *fmt, ...);
- void display(winid_t w, const Common::U32String fmt, ...);
+ void updateSettings();
void delay(int seconds);
- void fatal(const char *x);
void clearScreen(void);
bool randomPercent(uint n);
int countCarried(void);
- const char *mapSynonym(const char *word);
- int matchUpItem(const char *text, int loc);
+ int matchUpItem(int noun, int loc);
Common::String readString(Common::SeekableReadStream *f);
void loadDatabase(Common::SeekableReadStream *f, bool loud);
- void output(const Common::String &a);
- void output(const Common::U32String &a);
void outputNumber(int a);
void look(void);
int whichWord(const char *word, const Common::StringArray &list);
- void lineInput(char *buf, size_t n);
- int getInput(int *vb, int *no);
- int performLine(int ct);
- int performActions(int vb, int no);
+
+ ActionResultType performLine(int ct);
+ ExplicitResultType performActions(int vb, int no);
void readInts(Common::SeekableReadStream *f, size_t count, ...);
+ void writeToRoomDescriptionStream(const char *fmt, ...);
+ void flushRoomDescription(char *buf);
+ void printWindowDelimiter();
+ void listExits();
+ void listExitsSpectrumStyle();
+ void listInventoryInUpperWindow();
+ int itemEndsWithPeriod(int item);
+ void closeGraphicsWindow();
+ winid_t findGlkWindowWithRock(glui32 rock);
+ void openGraphicsWindow();
+ glui32 optimalPictureSize(glui32 *width, glui32 *height);
+ void openTopWindow();
+ void cleanupAndExit();
+ void drawBlack();
+ void drawRoomImage();
+ void restartGame();
+ void transcriptOn();
+ void transcriptOff();
+ int yesOrNo();
+ void listInventory();
+ void lookWithPause();
+ void doneIt();
+ int printScore();
+ void printNoun();
+ void moveItemAToLocOfItemB(int itemA, int itemB);
+ void goToStoredLoc();
+ void swapLocAndRoomFlag(int index);
+ void swapItemLocations(int itemA, int itemB);
+ void putItemAInRoomB(int itemA, int roomB);
+ void swapCounters(int index);
+ void printMessage(int index);
+ void playerIsDead();
+ void printTakenOrDropped(int index);
+ void printTitleScreenBuffer();
+ void printTitleScreenGrid();
+
+public:
+ void drawImage(int image);
+ void output(const Common::String &a);
+ void output(const Common::U32String &a);
+ void display(winid_t w, const char *fmt, ...);
+ void display(winid_t w, const Common::U32String fmt, ...);
+ void fatal(const char *x);
+ void hitEnter();
+ void updates(event_t ev);
+ const char *mapSynonym(int noun);
+ int performExtraCommand(int extraStopTime);
+
public:
/**
* Constructor
@@ -189,6 +247,8 @@ public:
Common::Error writeGameData(Common::WriteStream *ws) override;
};
+extern Scott *g_scott;
+
} // End of namespace Scott
} // End of namespace Glk
Commit: 7b97270214c4cce5c41058799043466126236b2b
https://github.com/scummvm/scummvm/commit/7b97270214c4cce5c41058799043466126236b2b
Author: Avijeet (am388488 at gmail.com)
Date: 2022-04-12T20:35:24-07:00
Commit Message:
GLK: SCOTT: Add game command parser.
Changed paths:
A engines/glk/scott/command_parser.cpp
A engines/glk/scott/command_parser.h
diff --git a/engines/glk/scott/command_parser.cpp b/engines/glk/scott/command_parser.cpp
new file mode 100644
index 00000000000..e6a58614013
--- /dev/null
+++ b/engines/glk/scott/command_parser.cpp
@@ -0,0 +1,861 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "glk/scott/command_parser.h"
+#include "glk/scott/globals.h"
+#include "glk/scott/scott.h"
+#include "common/str.h"
+
+namespace Glk {
+namespace Scott {
+
+#define MAX_WORDLENGTH 128
+#define MAX_WORDS 128
+#define MAX_BUFFER 128
+
+void freeStrings() {
+ if (_G(_firstErrorMessage) != nullptr) {
+ delete[] _G(_firstErrorMessage);
+ _G(_firstErrorMessage) = nullptr;
+ }
+ if (_G(_wordsInInput) == 0) {
+ if (_G(_unicodeWords) != nullptr || _G(_charWords) != nullptr) {
+ g_scott->fatal("ERROR! Wordcount 0 but word arrays not empty!\n");
+ }
+ return;
+ }
+ for (int i = 0; i < _G(_wordsInInput); i++) {
+ if (_G(_unicodeWords)[i] != nullptr)
+ delete _G(_unicodeWords)[i];
+ if (_G(_charWords)[i] != nullptr)
+ delete _G(_charWords)[i];
+ }
+
+ delete _G(_unicodeWords);
+ _G(_unicodeWords) = nullptr;
+ delete _G(_charWords);
+ _G(_charWords) = nullptr;
+ _G(_wordsInInput) = 0;
+}
+
+void createErrorMessage(const char *fchar, glui32 *second, const char *tchar) {
+ if (_G(_firstErrorMessage) != nullptr)
+ return;
+ glui32 *first = toUnicode(fchar);
+ glui32 *third = toUnicode(tchar);
+ glui32 buffer[MAX_BUFFER];
+ int i, j = 0, k = 0;
+ for (i = 0; first[i] != 0 && i < MAX_BUFFER; i++)
+ buffer[i] = first[i];
+ if (second != nullptr) {
+ for (j = 0; second[j] != 0 && i + j < MAX_BUFFER; j++)
+ buffer[i + j] = second[j];
+ }
+ if (third != nullptr) {
+ for (k = 0; third[k] != 0 && i + j + k < MAX_BUFFER; k++)
+ buffer[i + j + k] = third[k];
+ delete[] third;
+ }
+ int length = i + j + k;
+ _G(_firstErrorMessage) = new glui32[(length + 1) * 4];
+ memcpy(_G(_firstErrorMessage), buffer, length * 4);
+ _G(_firstErrorMessage)[length] = 0;
+ delete[] first;
+}
+
+void printPendingError(void) {
+ if (_G(_firstErrorMessage)) {
+ g_scott->glk_put_string_stream_uni(g_scott->glk_window_get_stream(_G(_bottomWindow)), _G(_firstErrorMessage));
+ delete[] _G(_firstErrorMessage);
+ _G(_firstErrorMessage) = nullptr;
+ _G(_stopTime) = 1;
+ }
+}
+
+char **LineInput(void) {
+ event_t ev;
+ glui32 unibuf[512];
+
+ do {
+ g_scott->display(_G(_bottomWindow), "\n%s", _G(_sys)[WHAT_NOW].c_str());
+ g_scott->glk_request_line_event_uni(_G(_bottomWindow), unibuf, (glui32)511, 0);
+
+ while (ev.type != evtype_Quit) {
+ g_scott->glk_select(&ev);
+ if (ev.type == evtype_Quit)
+ return nullptr;
+
+ if (ev.type == evtype_LineInput)
+ break;
+ else
+ g_scott->updates(ev);
+ }
+
+ unibuf[ev.val1] = 0;
+
+ if (_G(_transcript)) {
+ g_scott->glk_put_string_stream_uni(_G(_transcript), unibuf);
+ g_scott->glk_put_char_stream_uni(_G(_transcript), 10);
+ }
+
+ _G(_charWords) = splitIntoWords(unibuf, ev.val1);
+
+ if (_G(_wordsInInput) == 0 || _G(_charWords) == nullptr)
+ g_scott->output(_G(_sys)[HUH]);
+ else {
+ return _G(_charWords);
+ }
+
+ } while (_G(_wordsInInput) == 0 || _G(_charWords) == nullptr);
+ return nullptr;
+}
+
+int matchYMCA(glui32 *string, int length, int index) {
+ const char *ymca = "y.m.c.a.";
+ int i;
+ for (i = 0; i < 8; i++) {
+ if (i + index > length || string[index + i] != static_cast<unsigned int>(ymca[i]))
+ return i;
+ }
+ return i;
+}
+
+char **splitIntoWords(glui32 *string, int length) {
+ if (length < 1) {
+ return nullptr;
+ }
+
+ g_scott->glk_buffer_to_lower_case_uni(string, 256, length);
+ g_scott->glk_buffer_canon_normalize_uni(string, 256, length);
+
+ int startpos[MAX_WORDS];
+ int wordlength[MAX_WORDS];
+
+ int words_found = 0;
+ int word_index = 0;
+ int foundspace = 0;
+ int foundcomma = 0;
+ startpos[0] = 0;
+ wordlength[0] = 0;
+ int lastwasspace = 1;
+ for (int i = 0; string[i] != 0 && i < length && word_index < MAX_WORDS; i++) {
+ foundspace = 0;
+ switch (string[i]) {
+ case 'y': {
+ int ymca = matchYMCA(string, length, i);
+ if (ymca > 3) {
+ /* Start a new word */
+ startpos[words_found] = i;
+ wordlength[words_found] = ymca;
+ words_found++;
+ wordlength[words_found] = 0;
+ i += ymca;
+ if (i < length)
+ foundspace = 1;
+ lastwasspace = 0;
+ }
+ } break;
+ /* Unicode space and tab variants */
+ case ' ':
+ case '\t':
+ case '!':
+ case '?':
+ case '\"':
+ case 0x83: // ¿
+ case 0x80: // ¡
+ case 0xa0: // non-breaking space
+ case 0x2000: // en quad
+ case 0x2001: // em quad
+ case 0x2003: // em
+ case 0x2004: // three-per-em
+ case 0x2005: // four-per-em
+ case 0x2006: // six-per-em
+ case 0x2007: // figure space
+ case 0x2009: // thin space
+ case 0x200A: // hair space
+ case 0x202f: // narrow no-break space
+ case 0x205f: // medium mathematical space
+ case 0x3000: // ideographic space
+ foundspace = 1;
+ break;
+ case '.':
+ case ',':
+ foundcomma = 1;
+ break;
+ default:
+ break;
+ }
+ if (!foundspace) {
+ if (lastwasspace || foundcomma) {
+ /* Start a new word */
+ startpos[words_found] = i;
+ words_found++;
+ wordlength[words_found] = 0;
+ }
+ wordlength[words_found - 1]++;
+ lastwasspace = 0;
+ } else {
+ /* Check if the last character of previous word was a period or comma */
+ lastwasspace = 1;
+ foundcomma = 0;
+ }
+ }
+
+ if (words_found == 0) {
+ return nullptr;
+ }
+
+ wordlength[words_found]--; /* Don't count final newline character */
+
+ /* Now we've created two arrays, one for starting postions
+ and one for word length. Now we convert these into an array of strings */
+ glui32 **words = new glui32 *[words_found];
+ char **words8 = new char *[words_found];
+
+ for (int i = 0; i < words_found; i++) {
+ words[i] = new glui32[(wordlength[i] + 1) * 4];
+ memcpy(words[i], string + startpos[i], wordlength[i] * 4);
+ words[i][wordlength[i]] = 0;
+ words8[i] = fromUnicode(words[i], wordlength[i]);
+ }
+ _G(_unicodeWords) = words;
+ _G(_wordsInInput) = words_found;
+
+ return words8;
+}
+
+int findVerb(const char *string, Common::StringArray *list) {
+ *list = _G(_verbs);
+ int verb = whichWord(string, *list, _G(_gameHeader)->_wordLength);
+ if (verb) {
+ return verb;
+ }
+ *list = _G(_directions);
+ verb = whichWord(string, *list, _G(_gameHeader)->_wordLength);
+ if (verb) {
+ if (verb == 13)
+ verb = 4;
+ if (verb > 6)
+ verb -= 6;
+ return verb;
+ }
+ *list = _G(_abbreviations);
+ verb = whichWord(string, *list, _G(_gameHeader)->_wordLength);
+ if (verb) {
+ verb = whichWord(_G(_abbreviationsKey)[verb].c_str(), _G(_verbs), _G(_gameHeader)->_wordLength);
+ if (verb) {
+ list = &_G(_verbs);
+ return verb;
+ }
+ }
+
+ int stringlength = strlen(string);
+
+ *list = _G(_skipList);
+ verb = whichWord(string, *list, stringlength);
+ if (verb) {
+ return 0;
+ }
+ *list = _G(_nouns);
+ verb = whichWord(string, *list, _G(_gameHeader)->_wordLength);
+ if (verb) {
+ return verb;
+ }
+
+ *list = _G(_extraCommands);
+ verb = whichWord(string, *list, stringlength);
+ if (verb) {
+ verb = _G(_extraCommandsKey)[verb];
+ return verb + _G(_gameHeader)->_numWords;
+ }
+
+ *list = _G(_extraNouns);
+ verb = whichWord(string, *list, stringlength);
+ if (verb) {
+ verb = _G(_extraNounsKey)[verb];
+ return verb + _G(_gameHeader)->_numWords;
+ }
+
+ *list = _G(_delimiterList);
+ verb = whichWord(string, *list, stringlength);
+ if (!verb)
+ *list = Common::StringArray();
+ return verb;
+}
+
+int findExtraneousWords(int *index, int noun) {
+ /* Looking for extraneous words that should invalidate the command */
+ int originalIndex = *index;
+ if (*index >= _G(_wordsInInput)) {
+ return 0;
+ }
+ Common::StringArray list;
+ int verb = 0;
+ int stringlength = strlen(_G(_charWords)[*index]);
+
+ list = _G(_skipList);
+ do {
+ verb = whichWord(_G(_charWords)[*index], _G(_skipList), stringlength);
+ if (verb)
+ *index = *index + 1;
+ } while (verb && *index < _G(_wordsInInput));
+
+ if (*index >= _G(_wordsInInput))
+ return 0;
+
+ verb = findVerb(_G(_charWords)[*index], &list);
+
+ if (list == _G(_delimiterList)) {
+ if (*index > originalIndex)
+ *index = *index - 1;
+ return 0;
+ }
+
+ if (list == _G(_nouns) && noun) {
+ if (g_scott->mapSynonym(noun) == g_scott->mapSynonym(verb)) {
+ *index = *index + 1;
+ return 0;
+ }
+ }
+
+ if (list.empty()) {
+ if (*index >= _G(_wordsInInput))
+ *index = _G(_wordsInInput) - 1;
+ createErrorMessage(_G(_sys)[I_DONT_KNOW_WHAT_A].c_str(), _G(_unicodeWords)[*index], _G(_sys)[IS].c_str());
+ } else {
+ createErrorMessage(_G(_sys)[I_DONT_UNDERSTAND].c_str(), nullptr, nullptr);
+ }
+
+ return 1;
+}
+
+Command *commandFromStrings(int index, Command *previous);
+
+Command *createCommandStruct(int verb, int noun, int verbIndex, int nounIndex, Command *previous) {
+ Command *command = new Command;
+ command->_verb = verb;
+ command->_noun = noun;
+ command->_allFlag = 0;
+ command->_item = 0;
+ command->_previous = previous;
+ command->_verbWordIndex = verbIndex;
+ if (noun && nounIndex > 0) {
+ command->_nounWordIndex = nounIndex - 1;
+ } else {
+ command->_nounWordIndex = 0;
+ }
+ command->_next = commandFromStrings(nounIndex, command);
+ return command;
+}
+
+int findNoun(const char *string, Common::StringArray *list) {
+ *list = _G(_nouns);
+ int noun = whichWord(string, *list, _G(_gameHeader)->_wordLength);
+ if (noun) {
+ return noun;
+ }
+
+ *list = _G(_directions);
+ noun = whichWord(string, *list, _G(_gameHeader)->_wordLength);
+ if (noun) {
+ if (noun > 6)
+ noun -= 6;
+ *list = _G(_nouns);
+ return noun;
+ }
+
+ int stringLength = strlen(string);
+
+ *list = _G(_extraNouns);
+
+ noun = whichWord(string, *list, stringLength);
+ if (noun) {
+ noun = _G(_extraNounsKey)[noun];
+ return noun + _G(_gameHeader)->_numWords;
+ }
+
+ *list = _G(_skipList);
+ noun = whichWord(string, *list, stringLength);
+ if (noun) {
+ return 0;
+ }
+
+ *list = _G(_verbs);
+ noun = whichWord(string, *list, _G(_gameHeader)->_wordLength);
+ if (noun) {
+ return noun;
+ }
+
+ *list = _G(_delimiterList);
+ noun = whichWord(string, *list, stringLength);
+
+ if (!noun)
+ *list = Common::StringArray();
+ return 0;
+}
+
+Command *commandFromStrings(int index, Command *previous) {
+ if (index < 0 || index >= _G(_wordsInInput)) {
+ return nullptr;
+ }
+ Common::StringArray list;
+ int verb = 0;
+ int i = index;
+
+ do {
+ /* Checking if it is a verb */
+ verb = findVerb(_G(_charWords)[i++], &list);
+ } while ((list == _G(_skipList) || list == _G(_delimiterList)) && i < _G(_wordsInInput));
+
+ int verbindex = i - 1;
+
+ if (list == _G(_directions)) {
+ /* It is a direction */
+ if (verb == 0 || findExtraneousWords(&i, 0) != 0)
+ return nullptr;
+ return createCommandStruct(GO, verb, 0, i, previous);
+ }
+
+ int found_noun_at_verb_position = 0;
+ int lastverb = 0;
+
+ if (list == _G(_nouns) || list == _G(_extraNouns)) {
+ /* It is a noun */
+ /* If we find no verb, we try copying the verb from the previous command */
+ if (previous) {
+ lastverb = previous->_verb;
+ }
+ /* Unless the game is German, where we allow the noun to come before the
+ * verb */
+ if (CURRENT_GAME != GREMLINS_GERMAN && CURRENT_GAME != GREMLINS_GERMAN_C64) {
+ if (!previous) {
+ createErrorMessage(_G(_sys)[I_DONT_KNOW_HOW_TO].c_str(), _G(_unicodeWords)[i - 1], _G(_sys)[SOMETHING].c_str());
+ return nullptr;
+ } else {
+ verbindex = previous->_verbWordIndex;
+ }
+ if (findExtraneousWords(&i, verb) != 0)
+ return nullptr;
+
+ return createCommandStruct(lastverb, verb, verbindex, i, previous);
+ } else {
+ found_noun_at_verb_position = 1;
+ }
+ }
+
+ if (list.empty() || list == _G(_skipList)) {
+ createErrorMessage(_G(_sys)[I_DONT_KNOW_HOW_TO].c_str(), _G(_unicodeWords)[i - 1], _G(_sys)[SOMETHING].c_str());
+ return nullptr;
+ }
+
+ if (i == _G(_wordsInInput)) {
+ if (lastverb) {
+ return createCommandStruct(lastverb, verb, previous->_verbWordIndex, i, previous);
+ } else if (found_noun_at_verb_position) {
+ createErrorMessage(_G(_sys)[I_DONT_KNOW_HOW_TO].c_str(), _G(_unicodeWords)[i - 1], _G(_sys)[SOMETHING].c_str());
+ return nullptr;
+ } else {
+ return createCommandStruct(verb, 0, i - 1, i, previous);
+ }
+ }
+
+ int noun = 0;
+
+ do {
+ /* Check if it is a noun */
+ noun = findNoun(_G(_charWords)[i++], &list);
+ } while (list == _G(_skipList) && i < _G(_wordsInInput));
+
+ if (list == _G(_nouns) || list == _G(_extraNouns)) {
+ /* It is a noun */
+
+ /* Check if it is an ALL followed by EXCEPT */
+ int except = 0;
+ if (list == _G(_extraNouns) && i < _G(_wordsInInput) && noun - _G(_gameHeader)->_numWords == ALL) {
+ int stringlength = strlen(_G(_charWords)[i]);
+ except = whichWord(_G(_charWords)[i], _G(_extraCommands), stringlength);
+ }
+ if (_G(_extraCommandsKey)[except] != EXCEPT && findExtraneousWords(&i, noun) != 0)
+ return nullptr;
+ if (found_noun_at_verb_position) {
+ int realverb = whichWord(_G(_charWords)[i - 1], _G(_verbs), _G(_gameHeader)->_wordLength);
+ if (realverb) {
+ noun = verb;
+ verb = realverb;
+ } else if (lastverb) {
+ noun = verb;
+ verb = lastverb;
+ }
+ }
+ return createCommandStruct(verb, noun, verbindex, i, previous);
+ }
+
+ if (list == _G(_delimiterList)) {
+ /* It is a delimiter */
+ return createCommandStruct(verb, 0, verbindex, i, previous);
+ }
+
+ if (list == _G(_verbs) && found_noun_at_verb_position) {
+ /* It is a verb */
+ /* Check if it is an ALL followed by EXCEPT */
+ int except = 0;
+ if (i < _G(_wordsInInput) && verb - _G(_gameHeader)->_numWords == ALL) {
+ int stringlength = strlen(_G(_charWords)[i]);
+ except = whichWord(_G(_charWords)[i], _G(_extraCommands), stringlength);
+ }
+ if (_G(_extraCommandsKey)[except] != EXCEPT && findExtraneousWords(&i, 0) != 0)
+ return nullptr;
+ return createCommandStruct(noun, verb, i - 1, i, previous);
+ }
+
+ createErrorMessage(_G(_sys)[I_DONT_KNOW_WHAT_A].c_str(), _G(_unicodeWords)[i - 1], _G(_sys)[IS].c_str());
+ return nullptr;
+}
+
+int createAllCommands(Command *command) {
+
+ Common::Array<int> exceptions(_G(_gameHeader)->_numItems);
+ int exceptioncount = 0;
+
+ int location = CARRIED;
+ if (command->_verb == TAKE)
+ location = MY_LOC;
+
+ Command *next = command->_next;
+ /* Check if the ALL command is followed by EXCEPT */
+ /* and if it is, build an array of items to be excepted */
+ while (next && next->_verb == _G(_gameHeader)->_numWords + EXCEPT) {
+ for (int i = 0; i <= _G(_gameHeader)->_numItems; i++) {
+ if (!_G(_items)[i]._autoGet.empty() && scumm_strnicmp(_G(_items)[i]._autoGet.c_str(), _G(_charWords)[next->_nounWordIndex], _G(_gameHeader)->_wordLength) == 0) {
+ exceptions[exceptioncount++] = i;
+ }
+ }
+ /* Remove the EXCEPT command from the linked list of commands */
+ next = next->_next;
+ delete command->_next;
+ command->_next = next;
+ }
+
+ Command *c = command;
+ int found = 0;
+ for (int i = 0; i < _G(_gameHeader)->_numItems; i++) {
+ if (!_G(_items)[i]._autoGet.empty() && _G(_items)[i]._autoGet[0] != '*' && _G(_items)[i]._location == location) {
+ int exception = 0;
+ for (int j = 0; j < exceptioncount; j++) {
+ if (exceptions[j] == i) {
+ exception = 1;
+ break;
+ }
+ }
+ if (!exception) {
+ if (found) {
+ c->_next = new Command;
+ c->_next->_previous = c;
+ c = c->_next;
+ }
+ found = 1;
+ c->_verb = command->_verb;
+ c->_noun = whichWord(_G(_items)[i]._autoGet.c_str(), _G(_nouns), _G(_gameHeader)->_wordLength);
+ c->_item = i;
+ c->_next = nullptr;
+ c->_nounWordIndex = 0;
+ c->_allFlag = 1;
+ }
+ }
+ }
+ if (found == 0) {
+ if (command->_verb == TAKE)
+ createErrorMessage(_G(_sys)[NOTHING_HERE_TO_TAKE].c_str(), nullptr, nullptr);
+ else
+ createErrorMessage(_G(_sys)[YOU_HAVE_NOTHING].c_str(), nullptr, nullptr);
+ return 0;
+ } else {
+ c->_next = next;
+ c->_allFlag = 1 | LASTALL;
+ }
+ return 1;
+}
+
+int getInput(int *vb, int *no) {
+ if (_G(_currentCommand) && _G(_currentCommand)->_next) {
+ _G(_currentCommand) = _G(_currentCommand)->_next;
+ } else {
+ printPendingError();
+ if (_G(_currentCommand))
+ freeCommands();
+ _G(_charWords) = LineInput();
+
+ if (_G(_wordsInInput) == 0 || _G(_charWords) == nullptr)
+ return 0;
+
+ _G(_currentCommand) = commandFromStrings(0, nullptr);
+ }
+
+ if (_G(_currentCommand) == nullptr) {
+ printPendingError();
+ return 1;
+ }
+
+ /* We use NumWords + verb for our extra commands */
+ /* such as UNDO and TRANSCRIPT */
+ if (_G(_currentCommand)->_verb > _G(_gameHeader)->_numWords) {
+ if (!g_scott->performExtraCommand(0)) {
+ createErrorMessage(_G(_sys)[I_DONT_UNDERSTAND].c_str(), nullptr, nullptr);
+ }
+ return 1;
+ /* And NumWords + noun for our extra nouns */
+ /* such as ALL */
+ } else if (_G(_currentCommand)->_noun > _G(_gameHeader->_numWords)) {
+ _G(_currentCommand)->_noun -= _G(_gameHeader)->_numWords;
+ if (_G(_currentCommand)->_noun == ALL) {
+ if (_G(_currentCommand)->_verb != TAKE && _G(_currentCommand)->_verb != DROP) {
+ createErrorMessage(_G(_sys)[CANT_USE_ALL].c_str(), nullptr, nullptr);
+ return 1;
+ }
+ if (!createAllCommands(_G(_currentCommand)))
+ return 1;
+ } else if (_G(_currentCommand)->_noun == IT) {
+ _G(_currentCommand)->_noun = _G(_lastNoun);
+ }
+ }
+
+ *vb = _G(_currentCommand)->_verb;
+ *no = _G(_currentCommand)->_noun;
+
+ if (*no > 6) {
+ _G(_lastNoun) = *no;
+ }
+
+ return 0;
+}
+
+void freeCommands() {
+ while (_G(_currentCommand) && _G(_currentCommand)->_previous)
+ _G(_currentCommand) = _G(_currentCommand)->_previous;
+ while (_G(_currentCommand)) {
+ Command *temp = _G(_currentCommand);
+ _G(_currentCommand) = _G(_currentCommand)->_next;
+ delete temp;
+ }
+ _G(_currentCommand) = nullptr;
+ freeStrings();
+ if (_G(_firstErrorMessage))
+ delete[] _G(_firstErrorMessage);
+ _G(_firstErrorMessage) = nullptr;
+}
+
+glui32 *toUnicode(const char *string) {
+ if (string == nullptr)
+ return nullptr;
+ glui32 unicode[2048];
+ int i;
+ int dest = 0;
+ for (i = 0; string[i] != 0 && i < 2047; i++) {
+ char c = string[i];
+ if (c == '\n')
+ c = 10;
+ glui32 unichar = (glui32)c;
+ if (_G(_game) && (CURRENT_GAME == GREMLINS_GERMAN || CURRENT_GAME == GREMLINS_GERMAN_C64)) {
+ const char d = string[i + 1];
+ if (c == 'u' && d == 'e') { // ü
+ if (!(i > 2 && string[i - 1] == 'e')) {
+ unichar = 0xfc;
+ i++;
+ }
+ } else if (c == 'o' && d == 'e') {
+ unichar = 0xf6; // ö
+ i++;
+ } else if (c == 'a' && d == 'e') {
+ unichar = 0xe4; // ä
+ i++;
+ } else if (c == 's' && d == 's') {
+ if (string[i + 2] != 'c' && string[i - 2] != 'W' && !(string[i - 1] == 'a' && string[i - 2] == 'l') && string[i + 2] != '-' && string[i - 2] != 'b') {
+ unichar = 0xdf; // Ã
+ i++;
+ }
+ } else if (c == 'U' && d == 'E') {
+ unichar = 0xdc; // Ã
+ i++;
+ }
+ if (c == '\"') {
+ unichar = 0x2019; // â
+ }
+ } else if (_G(_game) && CURRENT_GAME == GREMLINS_SPANISH) {
+ switch (c) {
+ case '\x83':
+ unichar = 0xbf; // ¿
+ break;
+ case '\x80':
+ unichar = 0xa1; // ¡
+ break;
+ case '\x82':
+ unichar = 0xfc; // ü
+ break;
+ case '{':
+ unichar = 0xe1; // á
+ break;
+ case '}':
+ unichar = 0xed; // Ã
+ break;
+ case '|':
+ unichar = 0xf3; // ó
+ break;
+ case '~':
+ unichar = 0xf1; // ñ
+ break;
+ case '\x84':
+ unichar = 0xe9; // é
+ break;
+ case '\x85':
+ unichar = 0xfa; // ú
+ break;
+ }
+ } else if (_G(_game) && CURRENT_GAME == TI994A) {
+ switch (c) {
+ case '@':
+ unicode[dest++] = 0xa9;
+ unichar = ' ';
+ break;
+ case '}':
+ unichar = 0xfc;
+ break;
+ case 12:
+ unichar = 0xf6;
+ break;
+ case '{':
+ unichar = 0xe4;
+ break;
+ }
+ }
+ unicode[dest++] = unichar;
+ }
+ unicode[dest] = 0;
+ glui32 *result = new glui32[(dest + 1) * 4];
+ memcpy(result, unicode, (dest + 1) * 4);
+ return result;
+}
+
+char *fromUnicode(glui32 *unicodeString, int origLength) {
+ int sourcepos = 0;
+ int destpos = 0;
+
+ char dest[MAX_WORDLENGTH];
+ glui32 unichar = unicodeString[sourcepos];
+ while (unichar != 0 && destpos < MAX_WORDLENGTH && sourcepos < origLength) {
+ switch (unichar) {
+ case '.':
+ if (origLength == 1) {
+ dest[destpos++] = 'a';
+ dest[destpos++] = 'n';
+ dest[destpos++] = 'd';
+ } else {
+ dest[destpos] = (char)unichar;
+ }
+ break;
+ case 0xf6: // ö
+ dest[destpos++] = 'o';
+ dest[destpos] = 'e';
+ break;
+ case 0xe4: // ä
+ dest[destpos++] = 'a';
+ dest[destpos] = 'e';
+ break;
+ case 0xfc: // ü
+ dest[destpos] = 'u';
+ if (CURRENT_GAME == GREMLINS_GERMAN || CURRENT_GAME == GREMLINS_GERMAN_C64) {
+ destpos++;
+ dest[destpos] = 'e';
+ }
+ break;
+ case 0xdf: // Ã
+ dest[destpos++] = 's';
+ dest[destpos] = 's';
+ break;
+ case 0xed: // Ã
+ dest[destpos] = 'i';
+ break;
+ case 0xe1: // á
+ dest[destpos] = 'a';
+ break;
+ case 0xf3: // ó
+ dest[destpos] = 'o';
+ break;
+ case 0xf1: // ñ
+ dest[destpos] = 'n';
+ break;
+ case 0xe9: // é
+ dest[destpos] = 'e';
+ break;
+ default:
+ dest[destpos] = (char)unichar;
+ break;
+ }
+ sourcepos++;
+ destpos++;
+ unichar = unicodeString[sourcepos];
+ }
+ if (destpos == 0)
+ return nullptr;
+ char *result = new char[destpos + 1];
+ memcpy(result, dest, destpos);
+
+ result[destpos] = 0;
+ return result;
+}
+
+int recheckForExtraCommand() {
+ const char *verbWord = _G(_charWords)[_G(_currentCommand)->_verbWordIndex];
+
+ int extraVerb = whichWord(verbWord, _G(_extraCommands), _G(_gameHeader)->_wordLength);
+ if (!extraVerb) {
+ return 0;
+ }
+ int ExtraNoun = 0;
+ if (_G(_currentCommand)->_noun) {
+ const char *nounWord = _G(_charWords)[_G(_currentCommand)->_nounWordIndex];
+ ExtraNoun = whichWord(nounWord, _G(_extraNouns), strlen(nounWord));
+ }
+ _G(_currentCommand)->_verb = _G(_extraCommandsKey)[extraVerb];
+ if (ExtraNoun)
+ _G(_currentCommand)->_noun = _G(_extraNounsKey)[ExtraNoun];
+
+ return g_scott->performExtraCommand(1);
+}
+
+int whichWord(const char *word, Common::StringArray list, int wordLength) {
+ int n = 1;
+ unsigned int ne = 1;
+ const char *tp;
+ while (ne < list.size()) {
+ tp = list[ne].c_str();
+ if (*tp == '*')
+ tp++;
+ else
+ n = ne;
+ if (scumm_strnicmp(word, tp, wordLength) == 0)
+ return (n);
+ ne++;
+ }
+ return (0);
+}
+
+} // End of namespace Scott
+} // End of namespace Glk
diff --git a/engines/glk/scott/command_parser.h b/engines/glk/scott/command_parser.h
new file mode 100644
index 00000000000..b336e95e840
--- /dev/null
+++ b/engines/glk/scott/command_parser.h
@@ -0,0 +1,78 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef GLK_SCOTT_PARSER
+#define GLK_SCOTT_PARSER
+
+#include "common/str-array.h"
+#include "glk/glk_types.h"
+
+namespace Glk {
+namespace Scott {
+
+#define NUMBER_OF_DIRECTIONS 14
+#define NUMBER_OF_SKIPPABLE_WORDS 18
+#define NUMBER_OF_DELIMITERS 5
+#define NUMBER_OF_EXTRA_COMMANDS 20
+#define NUMBER_OF_EXTRA_NOUNS 16
+
+struct Command {
+ int _verb;
+ int _noun;
+ int _item;
+ int _verbWordIndex;
+ int _nounWordIndex;
+ int _allFlag;
+ struct Command *_previous;
+ struct Command *_next;
+};
+
+enum ExtraCommand : int {
+ NO_COMMAND,
+ RESTART,
+ SAVE,
+ RESTORE,
+ SCRIPT,
+ ON,
+ OFF,
+ UNDO,
+ RAM,
+ RAMSAVE,
+ RAMLOAD,
+ GAME,
+ COMMAND,
+ ALL,
+ IT,
+ EXCEPT
+};
+
+char **splitIntoWords(glui32 *string, int length);
+int getInput(int *vb, int *no);
+void freeCommands();
+glui32 *toUnicode(const char *string);
+char *fromUnicode(glui32 *unicodeString, int origLength);
+int recheckForExtraCommand();
+int whichWord(const char *word, Common::StringArray list, int wordLength);
+
+} // End of namespace Scott
+} // End of namespace Glk
+
+#endif
Commit: fb7180e37960fbe4fb365aa0b17795e25c10ad50
https://github.com/scummvm/scummvm/commit/fb7180e37960fbe4fb365aa0b17795e25c10ad50
Author: Avijeet (am388488 at gmail.com)
Date: 2022-04-12T20:35:24-07:00
Commit Message:
GLK: SCOTT: Add support for .z80 game files.
Changed paths:
A engines/glk/scott/decompress_z80.cpp
A engines/glk/scott/decompress_z80.h
diff --git a/engines/glk/scott/decompress_z80.cpp b/engines/glk/scott/decompress_z80.cpp
new file mode 100644
index 00000000000..1586f15bb24
--- /dev/null
+++ b/engines/glk/scott/decompress_z80.cpp
@@ -0,0 +1,904 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "glk/scott/decompress_z80.h"
+#include "common/algorithm.h"
+#include "common/array.h"
+#include "common/textconsole.h"
+
+namespace Glk {
+namespace Scott {
+
+/* Sizes of some of the arrays in the snap structure */
+const int SNAPSHOT_RAM_PAGES = 16;
+const int SNAPSHOT_SLT_PAGES = 256;
+const int SNAPSHOT_ZXATASP_PAGES = 32;
+const int SNAPSHOT_ZXCF_PAGES = 64;
+const int SNAPSHOT_DOCK_EXROM_PAGES = 8;
+const int SNAPSHOT_JOYSTICKS = 7;
+const int SNAPSHOT_DIVIDE_PAGES = 4;
+
+void *libspectrumReallocN(void *ptr, size_t nmemb, size_t size) {
+ if (nmemb > SIZE_MAX / size)
+ error("Can't reallocate to required size");
+
+ return realloc(ptr, nmemb * size);
+}
+/* Ensure there is room for `requested' characters after the current
+position `ptr' in `buffer'. If not, renew() and update the
+pointers as necessary */
+void libspectrumMakeRoom(uint8_t **dest, size_t requested, uint8_t **ptr,
+ size_t *allocated) {
+ size_t current_length = 0;
+
+ if (*allocated == 0) {
+
+ (*allocated) = requested;
+ *dest = new uint8_t[requested];
+
+ } else {
+ current_length = *ptr - *dest;
+
+ /* If there's already enough room here, just return */
+ if (current_length + requested <= (*allocated))
+ return;
+
+ /* Make the new size the maximum of the new needed size and the
+ old allocated size * 2 */
+ (*allocated) = current_length + requested > 2 * (*allocated)
+ ? current_length + requested
+ : 2 * (*allocated);
+
+ *dest = static_cast<uint8_t *>(libspectrumReallocN(*dest, *allocated, sizeof(uint8_t)));
+ }
+
+ /* Update the secondary pointer to the block */
+ *ptr = *dest + current_length;
+}
+
+struct LibspectrumSnap {
+ LibspectrumSnap() : pages(SNAPSHOT_RAM_PAGES) {}
+
+ /* Which machine are we using here? */
+ int machine;
+
+ /* Registers and the like */
+ uint16_t pc;
+
+ Common::Array<uint8_t *> pages;
+};
+
+/* Error handling */
+
+/* The various errors which can occur */
+enum LibspectrumError {
+
+ LIBSPECTRUM_ERROR_NONE = 0,
+
+ LIBSPECTRUM_ERROR_WARNING,
+ LIBSPECTRUM_ERROR_MEMORY,
+ LIBSPECTRUM_ERROR_UNKNOWN,
+ LIBSPECTRUM_ERROR_CORRUPT,
+ LIBSPECTRUM_ERROR_SIGNATURE,
+ LIBSPECTRUM_ERROR_SLT, /* .slt data found at end of a .z80 file */
+ LIBSPECTRUM_ERROR_INVALID, /* Invalid parameter supplied */
+
+ LIBSPECTRUM_ERROR_LOGIC = -1,
+};
+
+enum LibspectrumMachine {
+
+ LIBSPECTRUM_MACHINE_48,
+ LIBSPECTRUM_MACHINE_TC2048,
+ LIBSPECTRUM_MACHINE_128,
+ LIBSPECTRUM_MACHINE_PLUS2,
+ LIBSPECTRUM_MACHINE_PENT,
+ LIBSPECTRUM_MACHINE_PLUS2A,
+ LIBSPECTRUM_MACHINE_PLUS3,
+
+ /* Used by libspectrum_tape_guess_hardware if we can't work out what
+ hardware should be used */
+ LIBSPECTRUM_MACHINE_UNKNOWN,
+
+ LIBSPECTRUM_MACHINE_16,
+ LIBSPECTRUM_MACHINE_TC2068,
+
+ LIBSPECTRUM_MACHINE_SCORP,
+ LIBSPECTRUM_MACHINE_PLUS3E,
+ LIBSPECTRUM_MACHINE_SE,
+
+ LIBSPECTRUM_MACHINE_TS2068,
+ LIBSPECTRUM_MACHINE_PENT512,
+ LIBSPECTRUM_MACHINE_PENT1024,
+ LIBSPECTRUM_MACHINE_48_NTSC,
+
+ LIBSPECTRUM_MACHINE_128E,
+
+};
+
+enum LibspectrumMachineCapability {
+ LIBSPECTRUM_MACHINE_CAPABILITY_128_MEMORY = (1u << 0),
+ LIBSPECTRUM_MACHINE_CAPABILITY_PLUS3_MEMORY = (1u << 1),
+ LIBSPECTRUM_MACHINE_CAPABILITY_TIMEX_MEMORY = (1u << 2),
+ LIBSPECTRUM_MACHINE_CAPABILITY_SCORP_MEMORY = (1u << 3),
+ LIBSPECTRUM_MACHINE_CAPABILITY_SE_MEMORY = (1u << 4),
+ LIBSPECTRUM_MACHINE_CAPABILITY_PENT512_MEMORY = (1u << 5),
+ LIBSPECTRUM_MACHINE_CAPABILITY_PENT1024_MEMORY = (1u << 6),
+};
+
+/* Given a machine type, what features does it have? */
+int libspectrumMachineCapabilities(LibspectrumMachine type) {
+ int capabilities = 0;
+
+ /* 128K Spectrum-style 0x7ffd memory paging */
+ switch (type) {
+ case LIBSPECTRUM_MACHINE_128:
+ case LIBSPECTRUM_MACHINE_PLUS2:
+ case LIBSPECTRUM_MACHINE_PLUS2A:
+ case LIBSPECTRUM_MACHINE_PLUS3:
+ case LIBSPECTRUM_MACHINE_PLUS3E:
+ case LIBSPECTRUM_MACHINE_128E:
+ case LIBSPECTRUM_MACHINE_PENT:
+ case LIBSPECTRUM_MACHINE_PENT512:
+ case LIBSPECTRUM_MACHINE_PENT1024:
+ case LIBSPECTRUM_MACHINE_SCORP:
+ /* FIXME: SE needs to have this capability to be considered a 128k machine
+ */
+ case LIBSPECTRUM_MACHINE_SE:
+ capabilities |= LIBSPECTRUM_MACHINE_CAPABILITY_128_MEMORY;
+ break;
+ default:
+ break;
+ }
+
+ /* +3 Spectrum-style 0x1ffd memory paging */
+ switch (type) {
+ case LIBSPECTRUM_MACHINE_PLUS2A:
+ case LIBSPECTRUM_MACHINE_PLUS3:
+ case LIBSPECTRUM_MACHINE_PLUS3E:
+ case LIBSPECTRUM_MACHINE_128E:
+ capabilities |= LIBSPECTRUM_MACHINE_CAPABILITY_PLUS3_MEMORY;
+ break;
+ default:
+ break;
+ }
+
+ /* T[CS]20[46]8-style 0x00fd memory paging */
+ switch (type) {
+ case LIBSPECTRUM_MACHINE_TC2048:
+ case LIBSPECTRUM_MACHINE_TC2068:
+ case LIBSPECTRUM_MACHINE_TS2068:
+ capabilities |= LIBSPECTRUM_MACHINE_CAPABILITY_TIMEX_MEMORY;
+ break;
+ default:
+ break;
+ }
+
+ /* Scorpion-style 0x1ffd memory paging */
+ switch (type) {
+ case LIBSPECTRUM_MACHINE_SCORP:
+ capabilities |= LIBSPECTRUM_MACHINE_CAPABILITY_SCORP_MEMORY;
+ break;
+ default:
+ break;
+ }
+
+ /* SE-style 0x7ffd and 0x00fd memory paging */
+ switch (type) {
+ case LIBSPECTRUM_MACHINE_SE:
+ capabilities |= LIBSPECTRUM_MACHINE_CAPABILITY_SE_MEMORY;
+ break;
+ default:
+ break;
+ }
+
+ /* Pentagon 512-style memory paging */
+ switch (type) {
+ case LIBSPECTRUM_MACHINE_PENT512:
+ case LIBSPECTRUM_MACHINE_PENT1024:
+ capabilities |= LIBSPECTRUM_MACHINE_CAPABILITY_PENT512_MEMORY;
+ break;
+ default:
+ break;
+ }
+
+ /* Pentagon 1024-style memory paging */
+ switch (type) {
+ case LIBSPECTRUM_MACHINE_PENT1024:
+ capabilities |= LIBSPECTRUM_MACHINE_CAPABILITY_PENT1024_MEMORY;
+ break;
+ default:
+ break;
+ }
+
+ return capabilities;
+}
+
+/* Length of the basic .z80 headers */
+static const int LIBSPECTRUM_Z80_HEADER_LENGTH = 30;
+
+/* Length of the v2 extensions */
+#define LIBSPECTRUM_Z80_V2_LENGTH 23
+
+/* Length of the v3 extensions */
+#define LIBSPECTRUM_Z80_V3_LENGTH 54
+
+/* Length of xzx's extensions */
+#define LIBSPECTRUM_Z80_V3X_LENGTH 55
+
+/* The constants used for each machine type */
+enum {
+
+ /* v2 constants */
+ Z80_MACHINE_48_V2 = 0,
+ Z80_MACHINE_48_IF1_V2 = 1,
+ Z80_MACHINE_48_SAMRAM_V2 = 2,
+ Z80_MACHINE_128_V2 = 3,
+ Z80_MACHINE_128_IF1_V2 = 4,
+
+ /* v3 constants */
+ Z80_MACHINE_48 = 0,
+ Z80_MACHINE_48_IF1 = 1,
+ Z80_MACHINE_48_SAMRAM = 2,
+ Z80_MACHINE_48_MGT = 3,
+ Z80_MACHINE_128 = 4,
+ Z80_MACHINE_128_IF1 = 5,
+ Z80_MACHINE_128_MGT = 6,
+
+ /* Extensions */
+ Z80_MACHINE_PLUS3 = 7,
+ Z80_MACHINE_PLUS3_XZX_ERROR = 8,
+ Z80_MACHINE_PENTAGON = 9,
+ Z80_MACHINE_SCORPION = 10,
+ Z80_MACHINE_PLUS2 = 12,
+ Z80_MACHINE_PLUS2A = 13,
+ Z80_MACHINE_TC2048 = 14,
+ Z80_MACHINE_TC2068 = 15,
+ Z80_MACHINE_TS2068 = 128,
+
+ /* The first extension ID; anything here or greater applies to both
+ v2 and v3 files */
+ Z80_MACHINE_FIRST_EXTENSION = Z80_MACHINE_PLUS3,
+};
+
+static LibspectrumError readHeader(const uint8_t *buffer, LibspectrumSnap *snap, const uint8_t **data, int *version, int *compressed);
+
+static LibspectrumError getMachineType(LibspectrumSnap *snap, uint8_t type, uint8_t mgt_type, int version);
+
+static LibspectrumError getMachineTypeV2(LibspectrumSnap *snap, uint8_t type);
+
+static LibspectrumError getMachineTypeV3(LibspectrumSnap *snap, uint8_t type, uint8_t mgt_type);
+
+static LibspectrumError getMachineTypeExtension(LibspectrumSnap *snap, uint8_t type);
+
+static LibspectrumError readBlocks(const uint8_t *buffer, size_t buffer_length, LibspectrumSnap *snap, int version, int compressed);
+
+static LibspectrumError readBlock(const uint8_t *buffer, LibspectrumSnap *snap, const uint8_t **next_block, const uint8_t *end, int version, int compressed);
+
+static LibspectrumError readV1Block(const uint8_t *buffer, int is_compressed, uint8_t **uncompressed, const uint8_t **next_block, const uint8_t *end);
+
+static LibspectrumError readV2Block(const uint8_t *buffer, uint8_t **block, size_t *length, int *page, const uint8_t **next_block, const uint8_t *end);
+
+static void uncompressBlock(uint8_t **dest, size_t *dest_length, const uint8_t *src, size_t src_length);
+
+void libspectrumSnapSetMachine(LibspectrumSnap *snap, int val) {
+ snap->machine = val;
+}
+
+uint8_t *libspectrumSnapPages(LibspectrumSnap *snap, int page) {
+ return snap->pages[page];
+}
+
+void libspectrumSnapSetPages(LibspectrumSnap *snap, int page, uint8_t *buf) {
+ snap->pages[page] = buf;
+}
+
+void libspectrumPrintError(LibspectrumError error) {
+ switch (error) {
+ case LIBSPECTRUM_ERROR_WARNING:
+ warning("warning");
+ break;
+ case LIBSPECTRUM_ERROR_MEMORY:
+ warning("memory error");
+ break;
+ case LIBSPECTRUM_ERROR_UNKNOWN:
+ warning("unknown error");
+ break;
+ case LIBSPECTRUM_ERROR_CORRUPT:
+ warning("corruption error");
+ break;
+ case LIBSPECTRUM_ERROR_SIGNATURE:
+ warning("signature error");
+ break;
+ case LIBSPECTRUM_ERROR_SLT:
+ warning("SLT data in Z80 error");
+ break;
+ case LIBSPECTRUM_ERROR_INVALID:
+ warning("invalid parameter error");
+ break;
+ case LIBSPECTRUM_ERROR_LOGIC:
+ warning("logic error");
+ break;
+ default:
+ warning("unhandled error");
+ break;
+ }
+}
+
+int libspectrumSnapMachine(LibspectrumSnap *snap) {
+ return snap->machine;
+}
+
+/* Read an LSB dword from buffer */
+uint32_t libspectrumReadDword(const uint8_t **buffer) {
+ uint32_t value;
+
+ value = (*buffer)[0] + (*buffer)[1] * 0x100 + (*buffer)[2] * 0x10000 + (*buffer)[3] * 0x1000000;
+
+ (*buffer) += 4;
+
+ return value;
+}
+
+static LibspectrumError getMachineTypeExtension(LibspectrumSnap *snap, uint8_t type) {
+ switch (type) {
+ case Z80_MACHINE_PLUS3:
+ case Z80_MACHINE_PLUS3_XZX_ERROR:
+ libspectrumSnapSetMachine(snap, LIBSPECTRUM_MACHINE_PLUS3);
+ break;
+ case Z80_MACHINE_PENTAGON:
+ libspectrumSnapSetMachine(snap, LIBSPECTRUM_MACHINE_PENT);
+ break;
+ case Z80_MACHINE_SCORPION:
+ libspectrumSnapSetMachine(snap, LIBSPECTRUM_MACHINE_SCORP);
+ break;
+ case Z80_MACHINE_PLUS2:
+ libspectrumSnapSetMachine(snap, LIBSPECTRUM_MACHINE_PLUS2);
+ break;
+ case Z80_MACHINE_PLUS2A:
+ libspectrumSnapSetMachine(snap, LIBSPECTRUM_MACHINE_PLUS2A);
+ break;
+ case Z80_MACHINE_TC2048:
+ libspectrumSnapSetMachine(snap, LIBSPECTRUM_MACHINE_TC2048);
+ break;
+ case Z80_MACHINE_TC2068:
+ libspectrumSnapSetMachine(snap, LIBSPECTRUM_MACHINE_TC2068);
+ break;
+ case Z80_MACHINE_TS2068:
+ libspectrumSnapSetMachine(snap, LIBSPECTRUM_MACHINE_TS2068);
+ break;
+ default:
+ libspectrumPrintError(LIBSPECTRUM_ERROR_UNKNOWN);
+ warning("%s:get_machine_type: unknown extension machine type %d", __FILE__, type);
+ return LIBSPECTRUM_ERROR_UNKNOWN;
+ }
+
+ return LIBSPECTRUM_ERROR_NONE;
+}
+
+LibspectrumError internalZ80Read(LibspectrumSnap *snap, const uint8_t *buffer, size_t buffer_length);
+
+uint8_t *decompressZ80(uint8_t *raw_data, size_t length) {
+ LibspectrumSnap *snap = new LibspectrumSnap;
+ for (int i = 0; i < SNAPSHOT_RAM_PAGES; i++)
+ libspectrumSnapSetPages(snap, i, nullptr);
+
+ if (internalZ80Read(snap, raw_data, length) != LIBSPECTRUM_ERROR_NONE) {
+ return nullptr;
+ }
+
+ uint8_t *uncompressed = new uint8_t[0xC000];
+ if (uncompressed == nullptr)
+ return nullptr;
+ Common::copy(snap->pages[5], snap->pages[5] + 0x4000, uncompressed);
+ Common::copy(snap->pages[2], snap->pages[2] + 0x4000, uncompressed + 0x4000);
+ Common::copy(snap->pages[0], snap->pages[0] + 0x4000, uncompressed + 0x8000);
+
+ for (int i = 0; i < SNAPSHOT_RAM_PAGES; i++)
+ if (snap->pages[i] != nullptr)
+ delete snap->pages[i];
+ delete snap;
+
+ return uncompressed;
+}
+
+LibspectrumError internalZ80Read(LibspectrumSnap *snap, const uint8_t *buffer, size_t buffer_length) {
+ LibspectrumError error;
+ const uint8_t *data;
+ int version, compressed = 1;
+
+ error = readHeader(buffer, snap, &data, &version, &compressed);
+ if (error != LIBSPECTRUM_ERROR_NONE)
+ return error;
+
+ error = readBlocks(data, buffer_length - (data - buffer), snap, version,
+ compressed);
+ if (error != LIBSPECTRUM_ERROR_NONE)
+ return error;
+
+ return LIBSPECTRUM_ERROR_NONE;
+}
+
+static LibspectrumError readHeader(const uint8_t *buffer, LibspectrumSnap *snap, const uint8_t **data, int *version, int *compressed) {
+ const uint8_t *header = buffer;
+ LibspectrumError error;
+
+ uint8_t header_6 = header[6];
+ uint8_t header_7 = header[7] * 0x100;
+ snap->pc = header_6 + header_7;
+
+ if (snap->pc == 0) { /* PC == 0x0000 => v2 or greater */
+
+ size_t extra_length;
+ const uint8_t *extra_header;
+
+ extra_length = header[LIBSPECTRUM_Z80_HEADER_LENGTH] + header[LIBSPECTRUM_Z80_HEADER_LENGTH + 1] * 0x100;
+
+ switch (extra_length) {
+ case LIBSPECTRUM_Z80_V2_LENGTH:
+ *version = 2;
+ break;
+ case LIBSPECTRUM_Z80_V3_LENGTH:
+ case LIBSPECTRUM_Z80_V3X_LENGTH:
+ *version = 3;
+ break;
+ default:
+ libspectrumPrintError(LIBSPECTRUM_ERROR_UNKNOWN);
+ warning("libspectrumReadZ80Header: unknown header length %d", (int)extra_length);
+ return LIBSPECTRUM_ERROR_UNKNOWN;
+ }
+
+ extra_header = buffer + LIBSPECTRUM_Z80_HEADER_LENGTH + 2;
+
+ snap->pc = extra_header[0] + extra_header[1] * 0x100;
+
+ error = getMachineType(snap, extra_header[2], extra_header[51], *version);
+ if (error)
+ return error;
+
+ if (extra_header[5] & 0x80) {
+
+ switch (libspectrumSnapMachine(snap)) {
+ case LIBSPECTRUM_MACHINE_48:
+ libspectrumSnapSetMachine(snap, LIBSPECTRUM_MACHINE_16);
+ break;
+ case LIBSPECTRUM_MACHINE_128:
+ libspectrumSnapSetMachine(snap, LIBSPECTRUM_MACHINE_PLUS2);
+ break;
+ case LIBSPECTRUM_MACHINE_PLUS3:
+ libspectrumSnapSetMachine(snap, LIBSPECTRUM_MACHINE_PLUS2A);
+ break;
+ default:
+ break; /* Do nothing */
+ }
+ }
+
+ (*data) = buffer + LIBSPECTRUM_Z80_HEADER_LENGTH + 2 + extra_length;
+
+ } else { /* v1 .z80 file */
+
+ libspectrumSnapSetMachine(snap, LIBSPECTRUM_MACHINE_48);
+ *version = 1;
+
+ /* Need to flag this for later */
+ *compressed = (header[12] & 0x20) ? 1 : 0;
+
+ (*data) = buffer + LIBSPECTRUM_Z80_HEADER_LENGTH;
+ }
+
+ return LIBSPECTRUM_ERROR_NONE;
+}
+
+static LibspectrumError getMachineType(LibspectrumSnap *snap, uint8_t type, uint8_t mgt_type, int version) {
+ LibspectrumError error;
+
+ if (type < Z80_MACHINE_FIRST_EXTENSION) {
+ switch (version) {
+ case 2:
+ error = getMachineTypeV2(snap, type);
+ if (error)
+ return error;
+ break;
+ case 3:
+ error = getMachineTypeV3(snap, type, mgt_type);
+ if (error)
+ return error;
+ break;
+ default:
+ libspectrumPrintError(LIBSPECTRUM_ERROR_LOGIC);
+ warning("%s:getMachineType: unknown version %d", __FILE__, version);
+ return LIBSPECTRUM_ERROR_LOGIC;
+ }
+
+ } else {
+ error = getMachineTypeExtension(snap, type);
+ if (error)
+ return error;
+ }
+
+ return LIBSPECTRUM_ERROR_NONE;
+}
+
+static LibspectrumError getMachineTypeV2(LibspectrumSnap *snap, uint8_t type) {
+ switch (type) {
+ case Z80_MACHINE_48_V2:
+ case Z80_MACHINE_48_SAMRAM_V2:
+ libspectrumSnapSetMachine(snap, LIBSPECTRUM_MACHINE_48);
+ break;
+ case Z80_MACHINE_48_IF1_V2:
+ libspectrumSnapSetMachine(snap, LIBSPECTRUM_MACHINE_48);
+ break;
+ case Z80_MACHINE_128_V2:
+ libspectrumSnapSetMachine(snap, LIBSPECTRUM_MACHINE_128);
+ break;
+ case Z80_MACHINE_128_IF1_V2:
+ libspectrumSnapSetMachine(snap, LIBSPECTRUM_MACHINE_128);
+ break;
+ default:
+ libspectrumPrintError(LIBSPECTRUM_ERROR_UNKNOWN);
+ warning("%s: getMachineType: unknown v2 machine type %d", __FILE__, type);
+ return LIBSPECTRUM_ERROR_UNKNOWN;
+ }
+
+ return LIBSPECTRUM_ERROR_NONE;
+}
+
+static LibspectrumError getMachineTypeV3(LibspectrumSnap *snap, uint8_t type, uint8_t mgt_type) {
+ switch (type) {
+ case Z80_MACHINE_48:
+ case Z80_MACHINE_48_SAMRAM:
+ libspectrumSnapSetMachine(snap, LIBSPECTRUM_MACHINE_48);
+ break;
+ case Z80_MACHINE_48_IF1:
+ libspectrumSnapSetMachine(snap, LIBSPECTRUM_MACHINE_48);
+ break;
+ case Z80_MACHINE_48_MGT:
+ libspectrumSnapSetMachine(snap, LIBSPECTRUM_MACHINE_48);
+ break;
+ case Z80_MACHINE_128:
+ libspectrumSnapSetMachine(snap, LIBSPECTRUM_MACHINE_128);
+ break;
+ case Z80_MACHINE_128_MGT:
+ libspectrumSnapSetMachine(snap, LIBSPECTRUM_MACHINE_128);
+ break;
+ default:
+ libspectrumPrintError(LIBSPECTRUM_ERROR_UNKNOWN);
+ warning("%s:getMachineType: unknown v3 machine type %d", __FILE__, type);
+ return LIBSPECTRUM_ERROR_UNKNOWN;
+ }
+
+ return LIBSPECTRUM_ERROR_NONE;
+}
+
+/* Given a 48K memory dump `data', place it into the
+ appropriate bits of `snap' for a 48K machine */
+LibspectrumError libspectrumSplitTo48kPages(LibspectrumSnap *snap, const uint8_t *data) {
+ uint8_t *buffer[3];
+ size_t i;
+
+ /* If any of the three pages are already occupied, barf */
+ if (libspectrumSnapPages(snap, 5) || libspectrumSnapPages(snap, 2) || libspectrumSnapPages(snap, 0)) {
+ libspectrumPrintError(LIBSPECTRUM_ERROR_LOGIC);
+ warning("libspectrumSplitTo48kPages: RAM page already in use");
+ return LIBSPECTRUM_ERROR_LOGIC;
+ }
+
+ for (i = 0; i < 3; i++) {
+ buffer[i] = new uint8_t[0x4000];
+ }
+
+ libspectrumSnapSetPages(snap, 5, buffer[0]);
+ libspectrumSnapSetPages(snap, 2, buffer[1]);
+ libspectrumSnapSetPages(snap, 0, buffer[2]);
+
+ /* Finally, do the copies... */
+ Common::copy(&data[0x0000], &data[0x0000] + 0x4000, libspectrumSnapPages(snap, 5));
+ Common::copy(&data[0x4000], &data[0x4000] + 0x4000, libspectrumSnapPages(snap, 2));
+ Common::copy(&data[0x8000], &data[0x8000] + 0x4000, libspectrumSnapPages(snap, 0));
+
+ return LIBSPECTRUM_ERROR_NONE;
+}
+
+static LibspectrumError readBlocks(const uint8_t *buffer, size_t buffer_length, LibspectrumSnap *snap, int version, int compressed) {
+ const uint8_t *end, *next_block;
+
+ end = buffer + buffer_length;
+ next_block = buffer;
+
+ while (next_block < end) {
+ LibspectrumError error;
+ error = readBlock(next_block, snap, &next_block, end, version, compressed);
+ if (error != LIBSPECTRUM_ERROR_NONE)
+ return error;
+ }
+
+ return LIBSPECTRUM_ERROR_NONE;
+}
+
+static LibspectrumError readBlock(const uint8_t *buffer, LibspectrumSnap *snap, const uint8_t **next_block, const uint8_t *end, int version, int compressed) {
+ LibspectrumError error;
+ uint8_t *uncompressed;
+
+ int capabilities = libspectrumMachineCapabilities(static_cast<LibspectrumMachine>(libspectrumSnapMachine(snap)));
+
+ if (version == 1) {
+ error = readV1Block(buffer, compressed, &uncompressed, next_block, end);
+ if (error != LIBSPECTRUM_ERROR_NONE)
+ return error;
+
+ libspectrumSplitTo48kPages(snap, uncompressed);
+
+ delete uncompressed;
+
+ } else {
+
+ size_t length;
+ int page;
+
+ error = readV2Block(buffer, &uncompressed, &length, &page, next_block, end);
+ if (error != LIBSPECTRUM_ERROR_NONE)
+ return error;
+
+ if (page <= 0 || page > 18) {
+ libspectrumPrintError(LIBSPECTRUM_ERROR_UNKNOWN);
+ warning("readBlock: unknown page %d", page);
+ delete uncompressed;
+ return LIBSPECTRUM_ERROR_UNKNOWN;
+ }
+
+ /* If it's a ROM page, just throw it away */
+ if (page < 3) {
+ delete uncompressed;
+ return LIBSPECTRUM_ERROR_NONE;
+ }
+
+ /* Page 11 is the Multiface ROM unless we're emulating something
+ Scorpion-like */
+ if (page == 11 && !(capabilities & LIBSPECTRUM_MACHINE_CAPABILITY_SCORP_MEMORY)) {
+ delete uncompressed;
+ return LIBSPECTRUM_ERROR_NONE;
+ }
+
+ /* Deal with 48K snaps -- first, throw away page 3, as it's a ROM.
+ Then remap the numbers slightly */
+ if (!(capabilities & LIBSPECTRUM_MACHINE_CAPABILITY_128_MEMORY)) {
+ switch (page) {
+ case 3:
+ delete uncompressed;
+ return LIBSPECTRUM_ERROR_NONE;
+ case 4:
+ page = 5;
+ break;
+ case 5:
+ page = 3;
+ break;
+ }
+ }
+
+ /* Now map onto RAM page numbers */
+ page -= 3;
+
+ if (libspectrumSnapPages(snap, page) == nullptr) {
+ libspectrumSnapSetPages(snap, page, uncompressed);
+ } else {
+ delete uncompressed;
+ libspectrumPrintError(LIBSPECTRUM_ERROR_UNKNOWN);
+ warning("readBlock: page %d duplicated", page);
+ return LIBSPECTRUM_ERROR_CORRUPT;
+ }
+ }
+
+ return LIBSPECTRUM_ERROR_NONE;
+}
+
+static LibspectrumError readV1Block(const uint8_t *buffer, int is_compressed, uint8_t **uncompressed, const uint8_t **next_block, const uint8_t *end) {
+ if (is_compressed) {
+ const uint8_t *ptr;
+ int state;
+ size_t uncompressed_length = 0;
+
+ state = 0;
+ ptr = buffer;
+
+ while (state != 4) {
+ if (ptr == end) {
+ libspectrumPrintError(LIBSPECTRUM_ERROR_CORRUPT);
+ warning("readV1Block: end marker not found");
+ return LIBSPECTRUM_ERROR_CORRUPT;
+ }
+
+ switch (state) {
+ case 0:
+ switch (*ptr++) {
+ case 0x00:
+ state = 1;
+ break;
+ default:
+ state = 0;
+ break;
+ }
+ break;
+ case 1:
+ switch (*ptr++) {
+ case 0x00:
+ state = 1;
+ break;
+ case 0xed:
+ state = 2;
+ break;
+ default:
+ state = 0;
+ break;
+ }
+ break;
+ case 2:
+ switch (*ptr++) {
+ case 0x00:
+ state = 1;
+ break;
+ case 0xed:
+ state = 3;
+ break;
+ default:
+ state = 0;
+ break;
+ }
+ break;
+ case 3:
+ switch (*ptr++) {
+ case 0x00:
+ state = 4;
+ break;
+ default:
+ state = 0;
+ break;
+ }
+ break;
+ default:
+ libspectrumPrintError(LIBSPECTRUM_ERROR_LOGIC);
+ warning("readV1Block: unknown state %d", state);
+ return LIBSPECTRUM_ERROR_LOGIC;
+ }
+ }
+
+ /* Length passed here is reduced by 4 to remove the end marker */
+ uncompressBlock(uncompressed, &uncompressed_length, buffer, (ptr - buffer - 4));
+
+ /* Uncompressed data must be exactly 48Kb long */
+ if (uncompressed_length != 0xc000) {
+ delete *uncompressed;
+ libspectrumPrintError(LIBSPECTRUM_ERROR_CORRUPT);
+ warning("readV1Block: data does not uncompress to 48Kb");
+ return LIBSPECTRUM_ERROR_CORRUPT;
+ }
+
+ *next_block = ptr;
+
+ } else { /* Snap isn't compressed */
+ /* Check we've got enough bytes to read */
+ if (end - *next_block < 0xc000) {
+ libspectrumPrintError(LIBSPECTRUM_ERROR_CORRUPT);
+ warning("readV1Block: not enough data in buffer");
+ return LIBSPECTRUM_ERROR_CORRUPT;
+ }
+
+ *uncompressed = new uint8_t[0xC000];
+ Common::copy(buffer, buffer + 0xC000, *uncompressed);
+ *next_block = buffer + 0xc000;
+ }
+
+ return LIBSPECTRUM_ERROR_NONE;
+}
+
+/* The signature used to designate the .slt extensions */
+static uint8_t slt_signature[] = "\0\0\0SLT";
+static size_t slt_signature_length = 6;
+
+static LibspectrumError readV2Block(const uint8_t *buffer, uint8_t **block, size_t *length, int *page, const uint8_t **next_block, const uint8_t *end) {
+ size_t length2;
+ length2 = buffer[0] + buffer[1] * 0x100;
+ (*page) = buffer[2];
+
+ if (length2 == 0 && *page == 0) {
+ if (buffer + 8 < end && !memcmp(buffer, slt_signature, slt_signature_length)) {
+ /* Ah, we have what looks like SLT data... */
+ *next_block = buffer + 6;
+ return LIBSPECTRUM_ERROR_SLT;
+ }
+ }
+
+ /* A length of 0xffff => 16384 bytes of uncompressed data */
+ if (length2 != 0xffff) {
+ /* Check we're not going to run over the end of the buffer */
+ if (buffer + 3 + length2 > end) {
+ libspectrumPrintError(LIBSPECTRUM_ERROR_CORRUPT);
+ warning("readV2Block: not enough data in buffer");
+ return LIBSPECTRUM_ERROR_CORRUPT;
+ }
+
+ *length = 0;
+ uncompressBlock(block, length, buffer + 3, length2);
+ *next_block = buffer + 3 + length2;
+
+ } else { /* Uncompressed block */
+ /* Check we're not going to run over the end of the buffer */
+ if (buffer + 3 + 0x4000 > end) {
+ libspectrumPrintError(LIBSPECTRUM_ERROR_CORRUPT);
+ warning("readV2Block: not enough data in buffer");
+ return LIBSPECTRUM_ERROR_CORRUPT;
+ }
+
+ *block = new uint8_t[0x4000];
+ Common::copy(buffer + 3, buffer + 3 + 0x4000, *block);
+
+ *length = 0x4000;
+ *next_block = buffer + 3 + 0x4000;
+ }
+
+ return LIBSPECTRUM_ERROR_NONE;
+}
+
+static void uncompressBlock(uint8_t **dest, size_t *dest_length, const uint8_t *src, size_t src_length) {
+ const uint8_t *in_ptr;
+ uint8_t *out_ptr;
+
+ /* Allocate memory for dest if requested */
+ if (*dest_length == 0) {
+ *dest_length = src_length / 2;
+ *dest = new uint8_t[*dest_length];
+ }
+
+ in_ptr = src;
+ out_ptr = *dest;
+
+ while (in_ptr < src + src_length) {
+ /* If we're pointing at the last byte, just copy it across and exit */
+ if (in_ptr == src + src_length - 1) {
+ libspectrumMakeRoom(dest, 1, &out_ptr, dest_length);
+ *out_ptr++ = *in_ptr++;
+ continue;
+ }
+
+ /* If we're pointing at two successive 0xed bytes, that's a run. If not, just copy the byte across */
+ if (*in_ptr == 0xed && *(in_ptr + 1) == 0xed) {
+ size_t run_length;
+ uint8_t repeated;
+
+ in_ptr += 2;
+ run_length = *in_ptr++;
+ repeated = *in_ptr++;
+
+ libspectrumMakeRoom(dest, run_length, &out_ptr, dest_length);
+
+ while (run_length--) {
+ *out_ptr++ = repeated;
+ }
+ } else {
+ libspectrumMakeRoom(dest, 1, &out_ptr, dest_length);
+ *out_ptr++ = *in_ptr++;
+ }
+ }
+
+ *dest_length = out_ptr - *dest;
+}
+
+} // End of namespace Scott
+} // End of namespace Glk
diff --git a/engines/glk/scott/decompress_z80.h b/engines/glk/scott/decompress_z80.h
new file mode 100644
index 00000000000..4aa8e3c88ea
--- /dev/null
+++ b/engines/glk/scott/decompress_z80.h
@@ -0,0 +1,36 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef GLK_SCOTT_DECOMPRESSZ80
+#define GLK_SCOTT_DECOMPRESSZ80
+
+#include <cstdint>
+
+namespace Glk {
+namespace Scott {
+
+// Will return nullptr on error or 0xc000 (49152) bytes of uncompressed raw data on success
+uint8_t *decompressZ80(uint8_t *rawData, size_t length);
+
+} // End of namespace Scott
+} // End of namespace Glk
+
+#endif
Commit: 6d6d4fd4345d6c91f8258216091e4065defa49c3
https://github.com/scummvm/scummvm/commit/6d6d4fd4345d6c91f8258216091e4065defa49c3
Author: Avijeet (am388488 at gmail.com)
Date: 2022-04-12T20:35:24-07:00
Commit Message:
GLK: SCOTT: Support games with compressed text data.
Changed paths:
A engines/glk/scott/decompress_text.cpp
A engines/glk/scott/decompress_text.h
diff --git a/engines/glk/scott/decompress_text.cpp b/engines/glk/scott/decompress_text.cpp
new file mode 100644
index 00000000000..a972ac09e9d
--- /dev/null
+++ b/engines/glk/scott/decompress_text.cpp
@@ -0,0 +1,111 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "glk/scott/decompress_text.h"
+#include "common/str.h"
+
+namespace Glk {
+namespace Scott {
+
+int rotateLeftWithCarry(uint8_t *byte, int lastCarry) {
+ int carry = ((*byte & 0x80) > 0);
+ *byte = *byte << 1;
+ if (lastCarry)
+ *byte = *byte | 0x01;
+ return carry;
+}
+
+int decompressOne(uint8_t *bytes) {
+ uint8_t result = 0;
+ int carry;
+ for (int i = 0; i < 5; i++) {
+ carry = 0;
+ for (int j = 0; j < 5; j++) {
+ carry = rotateLeftWithCarry(bytes + 4 - j, carry);
+ }
+ rotateLeftWithCarry(&result, carry);
+ }
+ return result;
+}
+
+char *decompressText(uint8_t *source, int stringIndex) {
+ // Lookup table
+ Common::String alphabet = " abcdefghijklmnopqrstuvwxyz'\x01,.\x00";
+
+ int pos, c, uppercase, i, j;
+ uint8_t decompressed[256];
+ uint8_t buffer[5];
+ int idx = 0;
+
+ // Find the start of the compressed message
+ for (i = 0; i < stringIndex; i++) {
+ pos = *source;
+ pos = pos & 0x7F;
+ source += pos;
+ };
+
+ uppercase = ((*source & 0x40) == 0); // Test bit 6
+
+ source++;
+ do {
+ // Get five compressed bytes
+ for (i = 0; i < 5; i++) {
+ buffer[i] = *source++;
+ }
+ for (j = 0; j < 8; j++) {
+ // Decompress one character:
+ int next = decompressOne(buffer);
+
+ c = alphabet[next];
+
+ if (c == 0x01) {
+ uppercase = 1;
+ c = ' ';
+ }
+
+ if (c >= 'a' && uppercase) {
+ c = toupper(c);
+ uppercase = 0;
+ }
+ decompressed[idx++] = c;
+
+ if (idx > 255)
+ return nullptr;
+
+ if (idx == 255)
+ c = 0; // We've gone too far, return
+
+ if (c == 0) {
+ char *result = new char[idx];
+ memcpy(result, decompressed, idx);
+ return result;
+ } else if (c == '.' || c == ',') {
+ if (c == '.')
+ uppercase = 1;
+ decompressed[idx++] = ' ';
+ }
+ }
+ } while (idx < 0xff); // Chosen arbitrarily, might be too small
+ return nullptr;
+}
+
+} // End of namespace Scott
+} // End of namespace Glk
diff --git a/engines/glk/scott/decompress_text.h b/engines/glk/scott/decompress_text.h
new file mode 100644
index 00000000000..d90342f02c8
--- /dev/null
+++ b/engines/glk/scott/decompress_text.h
@@ -0,0 +1,35 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef GLK_SCOTT_DECOMPRESSTEXT
+#define GLK_SCOTT_DECOMPRESSTEXT
+
+#include <stdint.h>
+
+namespace Glk {
+namespace Scott {
+
+char *decompressText(uint8_t *source, int stringIndex);
+
+} // End of namespace Scott
+} // End of namespace Glk
+
+#endif
Commit: abecb9a38804acb3700f30ddf1c12d32b8473d29
https://github.com/scummvm/scummvm/commit/abecb9a38804acb3700f30ddf1c12d32b8473d29
Author: Avijeet (am388488 at gmail.com)
Date: 2022-04-12T20:35:24-07:00
Commit Message:
GLK: SCOTT: Add game detection and loading for ZX Spectrum games.
Changed paths:
A engines/glk/scott/detect_game.cpp
A engines/glk/scott/detect_game.h
diff --git a/engines/glk/scott/detect_game.cpp b/engines/glk/scott/detect_game.cpp
new file mode 100644
index 00000000000..42159e878e0
--- /dev/null
+++ b/engines/glk/scott/detect_game.cpp
@@ -0,0 +1,1260 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "glk/scott/command_parser.h"
+#include "glk/scott/detect_game.h"
+#include "glk/scott/decompress_text.h"
+#include "glk/scott/decompress_z80.h"
+#include "glk/scott/detection.h"
+#include "glk/scott/detection_tables.h"
+#include "glk/scott/game_info.h"
+#include "glk/scott/globals.h"
+#include "glk/scott/hulk.h"
+#include "glk/scott/line_drawing.h"
+#include "glk/scott/saga_draw.h"
+#include "glk/scott/scott.h"
+#include "common/str.h"
+
+namespace Glk {
+namespace Scott {
+
+struct DictionaryKey {
+ DictionaryType _dict;
+ const char *_signature;
+};
+
+DictionaryKey g_dictKeys[] = {
+ {FOUR_LETTER_UNCOMPRESSED, "AUTO\0GO\0"},
+ {THREE_LETTER_UNCOMPRESSED, "AUT\0GO\0"},
+ {FIVE_LETTER_UNCOMPRESSED, "AUTO\0\0GO"},
+ {FOUR_LETTER_COMPRESSED, "aUTOgO\0"},
+ {GERMAN, "\xc7"
+ "EHENSTEIGE"},
+ {FIVE_LETTER_COMPRESSED, "gEHENSTEIGE"}, // Gremlins C64
+ {SPANISH, "ANDAENTRAVAN"},
+ {FIVE_LETTER_UNCOMPRESSED, "*CROSS*RUN\0\0"}, // Claymorgue
+ {ITALIAN, "AUTO\0VAI\0\0*ENTR"}
+};
+
+void readHeader(uint8_t *ptr) {
+ int i, value;
+ for (i = 0; i < 24; i++) {
+ value = *ptr + 256 * *(ptr + 1);
+ _G(_header)[i] = value;
+ ptr += 2;
+ }
+}
+
+int sanityCheckHeader(void) {
+ int16_t v = _G(_gameHeader)->_numItems;
+ if (v < 10 || v > 500)
+ return 0;
+ v = _G(_gameHeader)->_numActions;
+ if (v < 100 || v > 500)
+ return 0;
+ v = _G(_gameHeader)->_numWords; // word pairs
+ if (v < 50 || v > 190)
+ return 0;
+ v = _G(_gameHeader)->_numRooms; // Number of rooms
+ if (v < 10 || v > 100)
+ return 0;
+
+ return 1;
+}
+
+uint8_t *readDictionary(GameInfo info, uint8_t **pointer, int loud) {
+ uint8_t *ptr = *pointer;
+ char *dictword = new char[info._wordLength + 2];
+ char c = 0;
+ int wordnum = 0;
+ int charindex = 0;
+
+ int nw = info._numberOfWords;
+
+ int nv = info._numberOfVerbs;
+ int nn = info._numberOfNouns;
+
+ for (int i = 0; i <= MAX(nn, nw) - nv; i++) {
+ _G(_verbs)[nv + i] = ".\0";
+ }
+
+ for (int i = 0; i <= MAX(nn, nw) - nn; i++) {
+ _G(_nouns)[nn + i] = ".\0";
+ }
+
+ do {
+ for (int i = 0; i < info._wordLength; i++) {
+ c = *(ptr++);
+
+ if (info._dictionary == FOUR_LETTER_COMPRESSED || info._dictionary == FIVE_LETTER_COMPRESSED) {
+ if (charindex == 0) {
+ if (c >= 'a') {
+ c = toupper(c);
+ } else if (c != '.' && c != 0) {
+ dictword[charindex++] = '*';
+ }
+ }
+ dictword[charindex++] = c;
+ } else if (info._subType & LOCALIZED) {
+ if (charindex == 0) {
+ if (c & 0x80) {
+ c = c & 0x7f;
+ } else if (c != '.') {
+ dictword[charindex++] = '*';
+ }
+ }
+ dictword[charindex++] = c;
+ } else {
+ if (c == 0) {
+ if (charindex == 0) {
+ c = *(ptr++);
+ }
+ }
+ if (c != ' ' && charindex > 0 && dictword[charindex - 1] == ' ') {
+ i--;
+ charindex--;
+ }
+ if (c == '*') {
+ if (charindex != 0)
+ charindex = 0;
+ i = -1;
+ }
+ dictword[charindex++] = c;
+ }
+ }
+ dictword[charindex] = 0;
+ if (wordnum < nv) {
+ _G(_verbs)[wordnum] = Common::String(dictword, charindex + 1);
+ if (loud)
+ debug("Verb %d: \"%s\"\n", wordnum, _G(_verbs)[wordnum].c_str());
+ } else {
+ _G(_nouns)[wordnum - nv] = Common::String(dictword, charindex + 1);
+ if (loud)
+ debug("Noun %d: \"%s\"\n", wordnum - nv, _G(_nouns)[wordnum - nv].c_str());
+ }
+ wordnum++;
+
+ if (c != 0 && !isascii(c))
+ return ptr;
+
+ charindex = 0;
+ } while (wordnum <= nv + nn);
+
+ return ptr;
+}
+
+int parseHeader(int *h, HeaderType type, int *ni, int *na, int *nw, int *nr, int *mc, int *pr, int *tr, int *wl, int *lt, int *mn, int *trm) {
+ switch (type) {
+ case NO_HEADER:
+ return 0;
+ case EARLY:
+ *ni = h[1];
+ *na = h[2];
+ *nw = h[3];
+ *nr = h[4];
+ *mc = h[5];
+ *pr = h[6];
+ *tr = h[7];
+ *wl = h[8];
+ *lt = h[9];
+ *mn = h[10];
+ *trm = h[11];
+ break;
+ case LATE:
+ *ni = h[1];
+ *na = h[2];
+ *nw = h[3];
+ *nr = h[4];
+ *mc = h[5];
+ *wl = h[6];
+ *mn = h[7];
+ *pr = 1;
+ *tr = 0;
+ *lt = -1;
+ *trm = 0;
+ break;
+ case HULK_HEADER:
+ *ni = h[3];
+ *na = h[2];
+ *nw = h[1];
+ *nr = h[5];
+ *mc = h[6];
+ *pr = h[7];
+ *tr = h[8];
+ *wl = h[0];
+ *lt = h[9];
+ *mn = h[4];
+ *trm = h[10];
+ break;
+ case ROBIN_C64_HEADER:
+ *ni = h[1];
+ *na = h[2];
+ *nw = h[6];
+ *nr = h[4];
+ *mc = h[5];
+ *pr = 1;
+ *tr = 0;
+ *wl = h[7];
+ *lt = -1;
+ *mn = h[3];
+ *trm = 0;
+ break;
+ case GREMLINS_C64_HEADER:
+ *ni = h[1];
+ *na = h[2];
+ *nw = h[5];
+ *nr = h[3];
+ *mc = h[6];
+ *pr = h[8];
+ *tr = 0;
+ *wl = h[7];
+ *lt = -1;
+ *mn = 98;
+ *trm = 0;
+ break;
+ case SUPERGRAN_C64_HEADER:
+ *ni = h[3];
+ *na = h[1];
+ *nw = h[2];
+ *nr = h[4];
+ *mc = h[8];
+ *pr = 1;
+ *tr = 0;
+ *wl = h[6];
+ *lt = -1;
+ *mn = h[5];
+ *trm = 0;
+ break;
+ case SEAS_OF_BLOOD_C64_HEADER:
+ *ni = h[0];
+ *na = h[1];
+ *nw = 134;
+ *nr = h[3];
+ *mc = h[4];
+ *pr = 1;
+ *tr = 0;
+ *wl = h[6];
+ *lt = -1;
+ *mn = h[2];
+ *trm = 0;
+ break;
+ case MYSTERIOUS_C64_HEADER:
+ *ni = h[1];
+ *na = h[2];
+ *nw = h[3];
+ *nr = h[4];
+ *mc = h[5] & 0xff;
+ *pr = h[5] >> 8;
+ *tr = h[6];
+ *wl = h[7];
+ *lt = h[8];
+ *mn = h[9];
+ *trm = 0;
+ break;
+ case ARROW_OF_DEATH_PT_2_C64_HEADER:
+ *ni = h[3];
+ *na = h[1];
+ *nw = h[2];
+ *nr = h[4];
+ *mc = h[5] & 0xff;
+ *pr = h[5] >> 8;
+ *tr = h[6];
+ *wl = h[7];
+ *lt = h[8];
+ *mn = h[9];
+ *trm = 0;
+ break;
+ case INDIANS_C64_HEADER:
+ *ni = h[1];
+ *na = h[2];
+ *nw = h[3];
+ *nr = h[4];
+ *mc = h[5] & 0xff;
+ *pr = h[5] >> 8;
+ *tr = h[6] & 0xff;
+ *wl = h[6] >> 8;
+ *lt = h[7] >> 8;
+ *mn = h[8] >> 8;
+ *trm = 0;
+ break;
+ default:
+ warning("Unhandled header type!\n");
+ return 0;
+ }
+ return 1;
+}
+
+void printHeaderInfo(int *h, int ni, int na, int nw, int nr, int mc, int pr, int tr, int wl, int lt, int mn, int trm) {
+ uint16_t value;
+ for (int i = 0; i < 13; i++) {
+ value = h[i];
+ debug("b $%X %d: ", 0x494d + 0x3FE5 + i * 2, i);
+ debug("\t%d\n", value);
+ }
+
+ debug("Number of items =\t%d\n", ni);
+ debug("Number of actions =\t%d\n", na);
+ debug("Number of words =\t%d\n", nw);
+ debug("Number of rooms =\t%d\n", nr);
+ debug("Max carried items =\t%d\n", mc);
+ debug("Word length =\t%d\n", wl);
+ debug("Number of messages =\t%d\n", mn);
+ debug("Player start location: %d\n", pr);
+ debug("Treasure room: %d\n", tr);
+ debug("Lightsource time left: %d\n", lt);
+ debug("Number of treasures: %d\n", tr);
+}
+
+void loadVectorData(GameInfo info, uint8_t *ptr) {
+ int offset;
+
+ if (info._startOfImageData == FOLLOWS)
+ ptr++;
+ else if (seekIfNeeded(info._startOfImageData, &offset, &ptr) == 0)
+ return;
+
+ _G(_lineImages).resize(info._numberOfRooms);
+ int ct = 0;
+ LineImage *lp = &_G(_lineImages)[0];
+ uint8_t byte = *(ptr++);
+ do {
+ _G(_rooms)[ct]._image = 0;
+ if (byte == 0xff) {
+ lp->_bgColour = *(ptr++);
+ lp->_data = ptr;
+ } else {
+ error("Error! Image data does not start with 0xff!\n");
+ }
+ do {
+ byte = *(ptr++);
+ if (ptr > _G(_entireFile) && static_cast<size_t>(ptr - _G(_entireFile)) >= _G(_fileLength)) {
+ error("Error! Image data for image %d cut off!\n", ct);
+ if (_G(_gameHeader)->_numRooms - ct > 1)
+ g_scott->display(_G(_bottomWindow), "[This copy has %d broken or missing pictures. These have been patched out.]\n\n", _G(_gameHeader)->_numRooms - ct);
+ if (lp->_data >= ptr)
+ lp->_size = 0;
+ else
+ lp->_size = ptr - lp->_data - 1;
+ for (int i = ct + 2; i < _G(_gameHeader)->_numRooms; i++)
+ _G(_rooms)[i]._image = 255;
+ return;
+ }
+ } while (byte != 0xff);
+
+ lp->_size = ptr - lp->_data;
+ lp++;
+ ct++;
+ } while (ct < info._numberOfRooms);
+}
+
+GameIDType detectGame(Common::SeekableReadStream *f) {
+
+ for (int i = 0; i < NUMBER_OF_DIRECTIONS; i++)
+ _G(_directions)[i] = _G(_englishDirections)[i];
+ for (int i = 0; i < NUMBER_OF_SKIPPABLE_WORDS; i++)
+ _G(_skipList)[i] = _G(_englishSkipList)[i];
+ for (int i = 0; i < NUMBER_OF_DELIMITERS; i++)
+ _G(_delimiterList)[i] = _G(_englishDelimiterList)[i];
+ for (int i = 0; i < NUMBER_OF_EXTRA_NOUNS; i++)
+ _G(_extraNouns)[i] = _G(_englishExtraNouns)[i];
+
+ _G(_fileLength) = f->size();
+
+ _G(_game) = new GameInfo;
+
+ Common::String md5 = g_vm->getGameMD5();
+ const GlkDetectionEntry *p = SCOTT_GAMES;
+
+ while (p->_md5) {
+ if (md5.equalsC(p->_md5)) {
+ if (!scumm_stricmp(p->_extra, "")) {
+ CURRENT_GAME = SCOTTFREE;
+ }
+ if (!scumm_stricmp(p->_extra, "ZXSpectrum")) {
+ _G(_entireFile) = new uint8_t[_G(_fileLength)];
+ size_t result = f->read(_G(_entireFile), _G(_fileLength));
+ if (result != _G(_fileLength))
+ g_scott->fatal("File empty or read error!");
+
+ // ZXSpectrum Detection
+ uint8_t *uncompressed = decompressZ80(_G(_entireFile), _G(_fileLength));
+ if (uncompressed != nullptr) {
+ delete[] _G(_entireFile);
+ _G(_entireFile) = uncompressed;
+ _G(_fileLength) = 0xc000;
+ }
+
+ int offset;
+ DictionaryType dict_type = getId(&offset);
+ if (dict_type == NOT_A_GAME)
+ return UNKNOWN_GAME;
+ for (int i = 0; i < NUMGAMES; i++) {
+ if (g_games[i]._dictionary == dict_type) {
+ if (tryLoading(g_games[i], offset, 0)) {
+ delete _G(_game);
+ _G(_game) = &g_games[i];
+ break;
+ }
+ }
+ }
+ }
+ }
+ // TODO
+ // TI99/4A Detection
+
+ // TODO
+ // C64 Detection
+
+ ++p;
+ }
+
+ if (CURRENT_GAME == SCOTTFREE || CURRENT_GAME == TI994A)
+ return CURRENT_GAME;
+
+ /* Copy ZX Spectrum style system messages as base */
+ for (int i = 6; i < MAX_SYSMESS && g_sysDictZX[i] != nullptr; i++) {
+ _G(_sys)[i] = g_sysDictZX[i];
+ }
+
+ switch (CURRENT_GAME) {
+ case ROBIN_OF_SHERWOOD:
+ // TODO
+ break;
+ case ROBIN_OF_SHERWOOD_C64:
+ // TODO
+ break;
+ case SEAS_OF_BLOOD:
+ // TODO
+ break;
+ case SEAS_OF_BLOOD_C64:
+ // TODO
+ break;
+ case CLAYMORGUE:
+ // TODO
+ break;
+ case SECRET_MISSION:
+ case SECRET_MISSION_C64:
+ // TODO
+ break;
+ case ADVENTURELAND:
+ // TODO
+ break;
+ case ADVENTURELAND_C64:
+ // TODO
+ break;
+ case CLAYMORGUE_C64:
+ // TODO
+ break;
+ case GREMLINS_GERMAN_C64:
+ // TODO
+ break;
+ case SPIDERMAN_C64:
+ // TODO
+ break;
+ case SUPERGRAN_C64:
+ // TODO
+ break;
+ case SAVAGE_ISLAND_C64:
+ case SAVAGE_ISLAND2_C64:
+ // TODO
+ break;
+ case SAVAGE_ISLAND:
+ case SAVAGE_ISLAND2:
+ case GREMLINS_GERMAN:
+ case GREMLINS:
+ case SUPERGRAN:
+ // TODO
+ break;
+ case GREMLINS_SPANISH:
+ // TODO
+ break;
+ case HULK_C64:
+ case HULK:
+ for (int i = 0; i < 6; i++) {
+ _G(_sys)[i] = g_sysDictZX[i];
+ }
+ break;
+ default:
+ if (!(_G(_game)->_subType & C64)) {
+ if (_G(_game)->_subType & MYSTERIOUS) {
+ for (int i = PLAY_AGAIN; i <= YOU_HAVENT_GOT_IT; i++)
+ _G(_sys)[i] = _G(_systemMessages)[2 - PLAY_AGAIN + i];
+ for (int i = YOU_DONT_SEE_IT; i <= WHAT_NOW; i++)
+ _G(_sys)[i] = _G(_systemMessages)[15 - YOU_DONT_SEE_IT + i];
+ for (int i = LIGHT_HAS_RUN_OUT; i <= RESUME_A_SAVED_GAME; i++)
+ _G(_sys)[i] = _G(_systemMessages)[31 - LIGHT_HAS_RUN_OUT + i];
+ _G(_sys)[ITEM_DELIMITER] = " - ";
+ _G(_sys)[MESSAGE_DELIMITER] = "\n";
+ _G(_sys)[YOU_SEE] = "\nThings I can see:\n";
+ break;
+ } else {
+ for (int i = PLAY_AGAIN; i <= RESUME_A_SAVED_GAME; i++)
+ _G(_sys)[i] = _G(_systemMessages)[2 - PLAY_AGAIN + i];
+ }
+ }
+ break;
+ }
+
+ switch (CURRENT_GAME) {
+ case GREMLINS_GERMAN:
+ // TODO
+ break;
+ case GREMLINS_GERMAN_C64:
+ // TODO
+ break;
+ case PERSEUS_ITALIAN:
+ // TODO
+ break;
+ default:
+ break;
+ }
+
+ /* If it is a C64 or a Mysterious Adventures game, we have setup the graphics already */
+ if (!(_G(_game)->_subType & (C64 | MYSTERIOUS)) && _G(_game)->_numberOfPictures > 0) {
+ sagaSetup(0);
+ }
+
+ return CURRENT_GAME;
+}
+
+uint8_t *seekToPos(uint8_t *buf, size_t offset) {
+ if (offset > _G(_fileLength))
+ return 0;
+ return buf + offset;
+}
+
+int seekIfNeeded(int expectedStart, int *offset, uint8_t **ptr) {
+ if (expectedStart != FOLLOWS) {
+ *offset = expectedStart + _G(_fileBaselineOffset);
+ *ptr = seekToPos(_G(_entireFile), *offset);
+ if (*ptr == 0)
+ return 0;
+ }
+ return 1;
+}
+
+int tryLoadingOld(GameInfo info, int dictStart) {
+ int ni, na, nw, nr, mc, pr, tr, wl, lt, mn, trm;
+ int ct;
+
+ Action *ap;
+ Room *rp;
+ Item *ip;
+ /* Load the header */
+
+ uint8_t *ptr = _G(_entireFile);
+ _G(_fileBaselineOffset) = dictStart - info._startOfDictionary;
+ int offset = info._startOfHeader + _G(_fileBaselineOffset);
+
+ ptr = seekToPos(_G(_entireFile), offset);
+ if (ptr == 0)
+ return 0;
+
+ readHeader(ptr);
+
+ if (!parseHeader(_G(_header), info._headerStyle, &ni, &na, &nw, &nr, &mc, &pr, &tr, &wl, <, &mn, &trm))
+ return 0;
+
+ if (ni != info._numberOfItems || na != info._numberOfActions || nw != info._numberOfWords || nr != info._numberOfRooms || mc != info._maxCarried) {
+ return 0;
+ }
+
+ _G(_gameHeader)->_numItems = ni;
+ _G(_gameHeader)->_numActions = na;
+ _G(_gameHeader)->_numWords = nw;
+ _G(_gameHeader)->_wordLength = wl;
+ _G(_gameHeader)->_numRooms = nr;
+ _G(_gameHeader)->_maxCarry = mc;
+ _G(_gameHeader)->_playerRoom = pr;
+ _G(_gameHeader)->_treasures = tr;
+ _G(_gameHeader)->_lightTime = lt;
+ _G(_lightRefill) = lt;
+ _G(_gameHeader)->_numMessages = mn;
+ _G(_gameHeader)->_treasureRoom = trm;
+ _G(_items).resize(ni + 1);
+ _G(_actions).resize(na + 1);
+ _G(_verbs).resize(nw + 2);
+ _G(_nouns).resize(nw + 2);
+ _G(_rooms).resize(nr + 1);
+ _G(_messages).resize(mn + 1);
+
+ // actions
+ if (seekIfNeeded(info._startOfActions, &offset, &ptr) == 0)
+ return 0;
+
+ ct = 0;
+
+ uint16_t value, cond, comm;
+
+ while (ct < na + 1) {
+ ap = &_G(_actions)[ct];
+ value = *(ptr++);
+ value += *(ptr++) * 0x100; /* verb/noun */
+ ap->_vocab = value;
+
+ cond = 5;
+ comm = 2;
+
+ for (int j = 0; j < 5; j++) {
+ if (j < cond) {
+ value = *(ptr++);
+ value += *(ptr++) * 0x100;
+ } else
+ value = 0;
+ ap->_condition[j] = value;
+ }
+ for (int j = 0; j < 2; j++) {
+ if (j < comm) {
+ value = *(ptr++);
+ value += *(ptr++) * 0x100;
+ } else
+ value = 0;
+ ap->_action[j] = value;
+ }
+ ct++;
+ }
+
+ // room connections
+ if (seekIfNeeded(info._startOfRoomConnections, &offset, &ptr) == 0)
+ return 0;
+
+ ct = 0;
+
+ while (ct < nr + 1) {
+ rp = &_G(_rooms)[ct];
+ for (int j = 0; j < 6; j++) {
+ rp->_exits[j] = *(ptr++);
+ }
+ ct++;
+ }
+
+ // item locations
+ if (seekIfNeeded(info._startOfItemLocations, &offset, &ptr) == 0)
+ return 0;
+
+ ct = 0;
+ while (ct < ni + 1) {
+ ip = &_G(_items)[ct];
+ ip->_location = *(ptr++);
+ ip->_initialLoc = ip->_location;
+ ct++;
+ }
+
+ // dictionary
+ if (seekIfNeeded(info._startOfDictionary, &offset, &ptr) == 0)
+ return 0;
+
+ ptr = readDictionary(info, &ptr, 0);
+
+ // rooms
+ if (seekIfNeeded(info._startOfRoomDescriptions, &offset, &ptr) == 0)
+ return 0;
+
+ if (info._startOfRoomDescriptions == FOLLOWS)
+ ptr++;
+
+ ct = 0;
+
+ char text[256];
+ char c = 0;
+ int charindex = 0;
+
+ do {
+ c = *(ptr++);
+ text[charindex] = c;
+ rp = &_G(_rooms)[ct];
+ if (c == 0) {
+ rp->_text = text;
+ if (info._numberOfPictures > 0)
+ rp->_image = ct - 1;
+ else
+ rp->_image = 255;
+ ct++;
+ charindex = 0;
+ } else {
+ charindex++;
+ }
+ if (c != 0 && !isascii(c))
+ return 0;
+ } while (ct < nr + 1);
+
+ // messages
+ if (seekIfNeeded(info._startOfMessages, &offset, &ptr) == 0)
+ return 0;
+
+ ct = 0;
+ charindex = 0;
+
+ while (ct < mn + 1) {
+ c = *(ptr++);
+ text[charindex] = c;
+ if (c == 0) {
+ _G(_messages)[ct] = text;
+ ct++;
+ charindex = 0;
+ } else {
+ charindex++;
+ }
+ }
+
+ // items
+ if (seekIfNeeded(info._startOfItemDescriptions, &offset, &ptr) == 0)
+ return 0;
+
+ ct = 0;
+ charindex = 0;
+
+ do {
+ ip = &_G(_items)[ct];
+ c = *(ptr++);
+ text[charindex] = c;
+ if (c == 0) {
+ ip->_text = text;
+ const char *p = strchr(ip->_text.c_str(), '/');
+ /* Some games use // to mean no auto get/drop word! */
+ if (p) {
+ ip->_autoGet = Common::String(p);
+ if (!(ip->_autoGet == "//") && !(ip->_autoGet == "/*")) {
+ ip->_text = Common::String(ip->_text.c_str(), p);
+ ip->_autoGet.deleteChar(0);
+ const char *t = strchr(ip->_autoGet.c_str(), '/');
+ if (t) {
+ ip->_autoGet = Common::String(ip->_autoGet.c_str(), t);
+ }
+ }
+ }
+ ct++;
+ charindex = 0;
+ } else {
+ charindex++;
+ }
+ } while (ct < ni + 1);
+
+ // line images
+ if (info._numberOfPictures > 0) {
+ loadVectorData(info, ptr);
+ }
+
+ // system messages
+ ct = 0;
+ if (seekIfNeeded(info._startOfSystemMessages, &offset, &ptr) == 0)
+ return 1;
+
+ charindex = 0;
+
+ do {
+ c = *(ptr++);
+ text[charindex] = c;
+ if (c == 0 || c == 0x0d) {
+ if (charindex > 0) {
+ if (c == 0x0d)
+ charindex++;
+ text[charindex] = '\0';
+ _G(_systemMessages)[ct] = Common::String(text, charindex + 1);
+ ct++;
+ charindex = 0;
+ }
+ } else {
+ charindex++;
+ }
+
+ if (c != 0 && c != 0x0d && c != '\x83' && c != '\xc9' && !isascii(c))
+ break;
+ } while (ct < 40);
+
+ charindex = 0;
+
+ if (seekIfNeeded(info._startOfDirections, &offset, &ptr) == 0)
+ return 0;
+
+ ct = 0;
+ do {
+ c = *(ptr++);
+ text[charindex] = c;
+ if (c == 0 || c == 0x0d) {
+ if (charindex > 0) {
+ if (c == 0x0d)
+ charindex++;
+ text[charindex] = '\0';
+ _G(_sys)[ct] = Common::String(text, charindex + 1);
+ ct++;
+ charindex = 0;
+ }
+ } else {
+ charindex++;
+ }
+
+ if (c != 0 && c != 0x0d && !isascii(c))
+ break;
+ } while (ct < 6);
+
+ return 1;
+}
+
+int tryLoading(GameInfo info, int dictStart, int loud) {
+ // The Hulk does everything differently so it gets its own function
+ if (info._gameID == HULK || info._gameID == HULK_C64)
+ return tryLoadingHulk(info, dictStart);
+
+ if (info._type == OLD_STYLE)
+ return tryLoadingOld(info, dictStart);
+
+ int ni, na, nw, nr, mc, pr, tr, wl, lt, mn, trm;
+ int ct;
+
+ Action *ap;
+ Room *rp;
+ Item *ip;
+
+ /* Load the header */
+ uint8_t *ptr = _G(_entireFile);
+
+ if (loud) {
+ debug("dict_start:%x\n", dictStart);
+ debug(" info._start_of_dictionary:%x\n", info._startOfDictionary);
+ }
+ _G(_fileBaselineOffset) = dictStart - info._startOfDictionary;
+
+ if (loud) {
+ debug("_fileBaselineOffset:%x (%d)\n", _G(_fileBaselineOffset), _G(_fileBaselineOffset));
+ }
+
+ int offset = info._startOfHeader + _G(_fileBaselineOffset);
+
+ ptr = seekToPos(_G(_entireFile), offset);
+ if (ptr == 0)
+ return 0;
+
+ readHeader(ptr);
+
+ if (!parseHeader(_G(_header), info._headerStyle, &ni, &na, &nw, &nr, &mc, &pr, &tr, &wl, <, &mn, &trm))
+ return 0;
+
+ if (loud)
+ printHeaderInfo(_G(_header), ni, na, nw, nr, mc, pr, tr, wl, lt, mn, trm);
+
+ _G(_gameHeader)->_numItems = ni;
+ _G(_gameHeader)->_numActions = na;
+ _G(_gameHeader)->_numWords = nw;
+ _G(_gameHeader)->_wordLength = wl;
+ _G(_gameHeader)->_numRooms = nr;
+ _G(_gameHeader)->_maxCarry = mc;
+ _G(_gameHeader)->_playerRoom = pr;
+ _G(_gameHeader)->_treasures = tr;
+ _G(_gameHeader)->_lightTime = lt;
+ _G(_lightRefill) = lt;
+ _G(_gameHeader)->_numMessages = mn;
+ _G(_gameHeader)->_treasureRoom = trm;
+
+ if (sanityCheckHeader() == 0) {
+ return 0;
+ }
+
+ if (loud) {
+ debug("Found a valid header at position 0x%x\n", offset);
+ debug("Expected: 0x%x\n", info._startOfHeader + _G(_fileBaselineOffset));
+ }
+
+ if (ni != info._numberOfItems || na != info._numberOfActions || nw != info._numberOfWords || nr != info._numberOfRooms || mc != info._maxCarried) {
+ if (loud) {
+ debug("Non-matching header\n");
+ }
+ return 0;
+ }
+
+ _G(_items).resize(ni + 1);
+ _G(_actions).resize(na + 1);
+ _G(_verbs).resize(nw + 2);
+ _G(_nouns).resize(nw + 2);
+ _G(_rooms).resize(nr + 1);
+ _G(_messages).resize(mn + 1);
+
+ int compressed = (info._dictionary == FOUR_LETTER_COMPRESSED);
+
+ // room images
+ if (info._startOfRoomImageList != 0) {
+ if (seekIfNeeded(info._startOfRoomImageList, &offset, &ptr) == 0)
+ return 0;
+
+ for (ct = 0; ct <= _G(_gameHeader)->_numRooms; ct++) {
+ rp = &_G(_rooms)[ct];
+ rp->_image = *(ptr++);
+ }
+ }
+
+ // item flags
+ if (info._startOfItemFlags != 0) {
+
+ if (seekIfNeeded(info._startOfItemFlags, &offset, &ptr) == 0)
+ return 0;
+
+ for (ct = 0; ct <= _G(_gameHeader)->_numItems; ct++) {
+ ip = &_G(_items)[ct];
+ ip->_flag = *(ptr++);
+ }
+ }
+
+ // item images
+ if (info._startOfItemImageList != 0) {
+
+ if (seekIfNeeded(info._startOfItemImageList, &offset, &ptr) == 0)
+ return 0;
+
+ for (ct = 0; ct <= _G(_gameHeader)->_numItems; ct++) {
+ ip = &_G(_items)[ct];
+ ip->_image = *(ptr++);
+ }
+ if (loud)
+ debug("Offset after reading item images: %d\n", static_cast<int>(ptr - _G(_entireFile) - _G(_fileBaselineOffset)));
+ }
+
+ // actions
+ if (seekIfNeeded(info._startOfActions, &offset, &ptr) == 0)
+ return 0;
+
+ ct = 0;
+
+ uint16_t value, cond, comm;
+
+ while (ct < na + 1) {
+ ap = &_G(_actions)[ct];
+ value = *(ptr++);
+ value += *(ptr++) * 0x100; /* verb/noun */
+ ap->_vocab = value;
+
+ if (info._actionsStyle == COMPRESSED) {
+ value = *(ptr++); /* count of actions/conditions */
+ cond = value & 0x1f;
+ if (cond > 5) {
+ debug("Condition error at action %d!\n", ct);
+ cond = 5;
+ }
+ comm = (value & 0xe0) >> 5;
+ if (comm > 2) {
+ debug("Command error at action %d!\n", ct);
+ comm = 2;
+ }
+ } else {
+ cond = 5;
+ comm = 2;
+ }
+ for (int j = 0; j < 5; j++) {
+ if (j < cond) {
+ value = *(ptr++);
+ value += *(ptr++) * 0x100;
+ } else
+ value = 0;
+ ap->_condition[j] = value;
+ }
+ for (int j = 0; j < 2; j++) {
+ if (j < comm) {
+ value = *(ptr++);
+ value += *(ptr++) * 0x100;
+ } else
+ value = 0;
+ ap->_action[j] = value;
+ }
+
+ ct++;
+ }
+ if (loud)
+ debug("Offset after reading actions: %d\n", static_cast<int>(ptr - _G(_entireFile) - _G(_fileBaselineOffset)));
+
+ // dictionary
+ if (seekIfNeeded(info._startOfDictionary, &offset, &ptr) == 0)
+ return 0;
+
+ ptr = readDictionary(info, &ptr, loud);
+
+ if (loud)
+ debug("Offset after reading dictionary: %d\n", static_cast<int>(ptr - _G(_entireFile) - _G(_fileBaselineOffset)));
+
+ // rooms
+ if (info._startOfRoomDescriptions != 0) {
+ if (seekIfNeeded(info._startOfRoomDescriptions, &offset, &ptr) == 0)
+ return 0;
+
+ ct = 0;
+
+ char text[256];
+ char c = 0;
+ int charindex = 0;
+
+ if (!compressed) {
+ do {
+ rp = &_G(_rooms)[ct];
+ c = *(ptr++);
+ text[charindex] = c;
+ if (c == 0) {
+ rp->_text = text;
+ if (loud)
+ debug("Room %d: %s\n", ct, rp->_text.c_str());
+ ct++;
+ charindex = 0;
+ } else {
+ charindex++;
+ }
+ if (c != 0 && !isascii(c))
+ return 0;
+ } while (ct < nr + 1);
+ } else {
+ do {
+ rp = &_G(_rooms)[ct];
+ rp->_text = decompressText(ptr, ct);
+ if (rp->_text == nullptr)
+ return 0;
+ rp->_text.replace(0, 1, Common::String(tolower(rp->_text[0])));
+ ct++;
+ } while (ct < nr);
+ }
+ }
+
+ // room connections
+ if (seekIfNeeded(info._startOfRoomConnections, &offset, &ptr) == 0)
+ return 0;
+
+ ct = 0;
+
+ while (ct < nr + 1) {
+ rp = &_G(_rooms)[ct];
+ for (int j = 0; j < 6; j++) {
+ rp->_exits[j] = *(ptr++);
+ }
+
+ ct++;
+ }
+
+ // messages
+ if (seekIfNeeded(info._startOfMessages, &offset, &ptr) == 0)
+ return 0;
+
+ ct = 0;
+ char text[256];
+ char c = 0;
+ int charindex = 0;
+
+ if (compressed) {
+ while (ct < mn + 1) {
+ _G(_messages)[ct] = decompressText(ptr, ct);
+ if (loud)
+ debug("Message %d: \"%s\"\n", ct, _G(_messages)[ct].c_str());
+ if (_G(_messages)[ct] == nullptr)
+ return 0;
+ ct++;
+ }
+ } else {
+ while (ct < mn + 1) {
+ c = *(ptr++);
+ text[charindex] = c;
+ if (c == 0) {
+ _G(_messages)[ct] = text;
+ if (loud)
+ debug("Message %d: \"%s\"\n", ct, _G(_messages)[ct].c_str());
+ ct++;
+ charindex = 0;
+ } else {
+ charindex++;
+ }
+ }
+ }
+
+ // items
+ if (seekIfNeeded(info._startOfItemDescriptions, &offset, &ptr) == 0)
+ return 0;
+
+ ct = 0;
+ charindex = 0;
+
+ if (compressed) {
+ do {
+ ip = &_G(_items)[ct];
+ ip->_text = decompressText(ptr, ct);
+ ip->_autoGet = "";
+ if (ip->_text != "" && ip->_text[0] != '.') {
+ if (loud)
+ debug("Item %d: %s\n", ct, ip->_text.c_str());
+ const char *p = strchr(ip->_text.c_str(), '.');
+ if (p) {
+ ip->_autoGet = Common::String(p);
+ ip->_autoGet.deleteChar(0);
+ ip->_autoGet.deleteChar(0);
+ const char *t = strchr(ip->_autoGet.c_str(), '.');
+ if (t)
+ ip->_autoGet = Common::String(ip->_autoGet.c_str(), t);
+ for (int i = 1; i < _G(_gameHeader)->_wordLength; i++)
+ ip->_autoGet.replace(i, 1, Common::String(toupper(ip->_text[i])));
+ }
+ }
+ ct++;
+ } while (ct < ni + 1);
+ } else {
+ do {
+ ip = &_G(_items)[ct];
+ c = *(ptr++);
+ text[charindex] = c;
+ if (c == 0) {
+ ip->_text = text;
+ if (loud)
+ debug("Item %d: %s\n", ct, ip->_text.c_str());
+ const char *p = strchr(ip->_text.c_str(), '/');
+ /* Some games use // to mean no auto get/drop word! */
+ if (p) {
+ ip->_autoGet = Common::String(p);
+ if (!(ip->_autoGet == "//") && !(ip->_autoGet == "/*")) {
+ ip->_text = Common::String(ip->_text.c_str(), p);
+ ip->_autoGet.deleteChar(0);
+ const char *t = strchr(ip->_autoGet.c_str(), '/');
+ if (t) {
+ ip->_autoGet = Common::String(ip->_autoGet.c_str(), t);
+ }
+ }
+ }
+ ct++;
+ charindex = 0;
+ } else {
+ charindex++;
+ }
+ } while (ct < ni + 1);
+ }
+
+ // item locations
+ if (seekIfNeeded(info._startOfItemLocations, &offset, &ptr) == 0)
+ return 0;
+
+ ct = 0;
+ while (ct < ni + 1) {
+ ip = &_G(_items)[ct];
+ ip->_location = *(ptr++);
+ ip->_initialLoc = ip->_location;
+ ct++;
+ }
+
+ // vector images
+ if (info._numberOfPictures > 0 && info._pictureFormatVersion == 99) {
+ loadVectorData(info, ptr);
+ }
+
+ // system messages
+ if (seekIfNeeded(info._startOfSystemMessages, &offset, &ptr) == 0)
+ return 1;
+jumpSysMess:
+ ptr = seekToPos(_G(_entireFile), offset);
+ ct = 0;
+ charindex = 0;
+
+ do {
+ c = *(ptr++);
+ if ((ptr > _G(_entireFile) && static_cast<size_t>(ptr - _G(_entireFile)) > _G(_fileLength)) || ptr < _G(_entireFile)) {
+ debug("Read out of bounds!\n");
+ return 0;
+ }
+ if (charindex > 255)
+ charindex = 0;
+ text[charindex] = c;
+ if (c == 0 || c == 0x0d) {
+ if (charindex > 0) {
+ if (c == 0x0d)
+ charindex++;
+ if (ct == 0 && (info._subType & (C64 | ENGLISH)) == (C64 | ENGLISH) && memcmp(text, "NORTH", 5) != 0) {
+ offset--;
+ goto jumpSysMess;
+ }
+ text[charindex] = '\0';
+ _G(_systemMessages)[ct] = Common::String(text, charindex + 1);
+ if (loud)
+ debug("system_messages %d: \"%s\"\n", ct, _G(_systemMessages)[ct].c_str());
+ ct++;
+ charindex = 0;
+ }
+ } else {
+ charindex++;
+ }
+ } while (ct < 45);
+
+ if (loud)
+ debug("Offset after reading system messages: %d\n", static_cast<int>(ptr - _G(_entireFile)));
+
+ if ((info._subType & (C64 | ENGLISH)) == (C64 | ENGLISH)) {
+ return 1;
+ }
+
+ if (seekIfNeeded(info._startOfDirections, &offset, &ptr) == 0)
+ return 0;
+
+ charindex = 0;
+
+ ct = 0;
+ do {
+ c = *(ptr++);
+ text[charindex] = c;
+ if (c == 0 || c == 0x0d) {
+ if (charindex > 0) {
+ if (c == 0x0d)
+ charindex++;
+ text[charindex] = '\0';
+ _G(_sys)[ct] = Common::String(text, charindex + 1);
+ ct++;
+ charindex = 0;
+ }
+ } else {
+ charindex++;
+ }
+
+ if (c != 0 && c != 0x0d && !isascii(c))
+ break;
+ } while (ct < 6);
+
+ return 1;
+}
+
+DictionaryType getId(int *offset) {
+ for (int i = 0; i < 9; i++) {
+ *offset = findCode(g_dictKeys[i]._signature, 0);
+ if (*offset != -1) {
+ if (i == 4 || i == 5) // GERMAN
+ *offset -= 5;
+ else if (i == 6) // SPANISH
+ *offset -= 8;
+ else if (i == 7) // Claymorgue
+ *offset -= 11;
+ return g_dictKeys[i]._dict;
+ }
+ }
+
+ return NOT_A_GAME;
+}
+
+int findCode(const char *x, int base) {
+ unsigned const char *p = _G(_entireFile) + base;
+ int len = strlen(x);
+ if (len < 7)
+ len = 7;
+ while (p < _G(_entireFile) + _G(_fileLength) - len) {
+ if (memcmp(p, x, len) == 0) {
+ return p - _G(_entireFile);
+ }
+ p++;
+ }
+ return -1;
+}
+
+} // End of namespace Scott
+} // End of namespace Glk
diff --git a/engines/glk/scott/detect_game.h b/engines/glk/scott/detect_game.h
new file mode 100644
index 00000000000..cbc0643f358
--- /dev/null
+++ b/engines/glk/scott/detect_game.h
@@ -0,0 +1,44 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef GLK_SCOTT_DETECTGAME
+#define GLK_SCOTT_DETECTGAME
+
+#include "common/stream.h"
+#include "glk/scott/definitions.h"
+#include <stdint.h>
+
+namespace Glk {
+namespace Scott {
+
+void readHeader(uint8_t *ptr);
+int parseHeader(int *h, HeaderType type, int *ni, int *na, int *nw, int *nr, int *mc, int *pr, int *tr, int *wl, int *lt, int *mn, int *trm);
+GameIDType detectGame(Common::SeekableReadStream *f);
+uint8_t *seekToPos(uint8_t *buf, size_t offset);
+int seekIfNeeded(int expectedStart, int *offset, uint8_t **ptr);
+int tryLoading(GameInfo info, int dictStart, int loud);
+DictionaryType getId(int *offset);
+int findCode(const char *x, int base);
+
+} // End of namespace Scott
+} // End of namespace Glk
+
+#endif
Commit: 4b31312480697854956b77fb55b0bce937e1da51
https://github.com/scummvm/scummvm/commit/4b31312480697854956b77fb55b0bce937e1da51
Author: Avijeet (am388488 at gmail.com)
Date: 2022-04-12T20:35:24-07:00
Commit Message:
GLK: SCOTT: Add support for different drawing methods.
Changed paths:
A engines/glk/scott/line_drawing.cpp
A engines/glk/scott/line_drawing.h
A engines/glk/scott/saga_draw.cpp
A engines/glk/scott/saga_draw.h
diff --git a/engines/glk/scott/line_drawing.cpp b/engines/glk/scott/line_drawing.cpp
new file mode 100644
index 00000000000..dcae2152790
--- /dev/null
+++ b/engines/glk/scott/line_drawing.cpp
@@ -0,0 +1,235 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "glk/scott/line_drawing.h"
+#include "glk/scott/globals.h"
+#include "glk/scott/ring_buffer.h"
+#include "glk/scott/saga_draw.h"
+#include "glk/scott/scott.h"
+
+namespace Glk {
+namespace Scott {
+
+void scottLinegraphicsPlotClip(int x, int y, int colour) {
+ /*
+ * Clip the plot if the value is outside the context. Otherwise, plot the
+ * pixel as colour1 if it is currently colour2.
+ */
+ if (x >= 0 && x <= _G(_scottGraphicsWidth) && y >= 0 && y < _G(_scottGraphicsHeight)) {
+ _G(_pictureBitmap)[y * 255 + x] = colour;
+ PixelToDraw *toDraw = new PixelToDraw;
+ toDraw->_x = x;
+ toDraw->_y = y;
+ toDraw->_colour = colour;
+ _G(_pixelsToDraw)[_G(_totalDrawInstructions)++] = toDraw;
+ }
+}
+
+void scottLinegraphicsDrawLine(int x1, int y1, int x2, int y2, int colour) {
+ int x, y, dx, dy, incx, incy, balance;
+
+ /* Normalize the line into deltas and increments. */
+ if (x2 >= x1) {
+ dx = x2 - x1;
+ incx = 1;
+ } else {
+ dx = x1 - x2;
+ incx = -1;
+ }
+
+ if (y2 >= y1) {
+ dy = y2 - y1;
+ incy = 1;
+ } else {
+ dy = y1 - y2;
+ incy = -1;
+ }
+
+ /* Start at x1,y1. */
+ x = x1;
+ y = y1;
+
+ /* Decide on a direction to progress in. */
+ if (dx >= dy) {
+ dy <<= 1;
+ balance = dy - dx;
+ dx <<= 1;
+
+ /* Loop until we reach the end point of the line. */
+ while (x != x2) {
+ scottLinegraphicsPlotClip(x, y, colour);
+ if (balance >= 0) {
+ y += incy;
+ balance -= dx;
+ }
+ balance += dy;
+ x += incx;
+ }
+ scottLinegraphicsPlotClip(x, y, colour);
+ } else {
+ dx <<= 1;
+ balance = dx - dy;
+ dy <<= 1;
+
+ /* Loop until we reach the end point of the line. */
+ while (y != y2) {
+ scottLinegraphicsPlotClip(x, y, colour);
+ if (balance >= 0) {
+ x += incx;
+ balance -= dy;
+ }
+ balance += dx;
+ y += incy;
+ }
+ scottLinegraphicsPlotClip(x, y, colour);
+ }
+}
+
+void freePixels() {
+ for (int i = 0; i < _G(_totalDrawInstructions); i++)
+ if (_G(_pixelsToDraw)[i] != nullptr)
+ delete _G(_pixelsToDraw)[i];
+ delete[] _G(_pixelsToDraw);
+}
+
+int linegraphicsGetPixel(int x, int y) {
+ return _G(_pictureBitmap)[y * 255 + x];
+}
+
+void diamondFill(int x, int y, int colour) {
+ uint8_t buffer[2048];
+ cbuf_handle_t ringbuf = circularBufInit(buffer, 2048);
+ circularBufPut(ringbuf, x, y);
+ while (!circularBufEmpty(ringbuf)) {
+ circularBufGet(ringbuf, &x, &y);
+ if (x >= 0 && x < _G(_scottGraphicsWidth) && y >= 0 &&
+ y < _G(_scottGraphicsHeight) &&
+ linegraphicsGetPixel(x, y) == _G(_bgColour)) {
+ scottLinegraphicsPlotClip(x, y, colour);
+ circularBufPut(ringbuf, x, y + 1);
+ circularBufPut(ringbuf, x, y - 1);
+ circularBufPut(ringbuf, x + 1, y);
+ circularBufPut(ringbuf, x - 1, y);
+ }
+ }
+}
+
+void drawVectorPicture(int image) {
+ if (image < 0) {
+ return;
+ }
+
+ if (_G(_vectorImageShown) == image) {
+ if (_G(_vectorState) == SHOWING_VECTOR_IMAGE) {
+ return;
+ } else {
+ if (_G(_gliSlowDraw))
+ g_scott->glk_request_timer_events(20);
+ drawSomeVectorPixels(1);
+ return;
+ }
+ }
+
+ g_scott->glk_request_timer_events(0);
+ _G(_vectorImageShown) = image;
+ if (_G(_pixelsToDraw) != nullptr)
+ freePixels();
+ _G(_pixelsToDraw) = new PixelToDraw *[255 * 97];
+ _G(_totalDrawInstructions) = 0;
+ _G(_currentDrawInstruction) = 0;
+
+ if (_G(_palChosen) == NO_PALETTE) {
+ _G(_palChosen) = _G(_game)->_palette;
+ definePalette();
+ }
+ _G(_pictureBitmap) = new uint8_t[255 * 97];
+ _G(_bgColour) = _G(_lineImages)[image]._bgColour;
+ memset(_G(_pictureBitmap), _G(_bgColour), 255 * 97);
+ if (_G(_bgColour) == 0)
+ _G(_lineColour) = 7;
+ else
+ _G(_lineColour) = 0;
+ int x = 0, y = 0, y2 = 0;
+ int arg1, arg2, arg3;
+ uint8_t *p = _G(_lineImages)[image]._data;
+ uint8_t opcode = 0;
+ while (((p < _G(_lineImages)[image]._data) || static_cast<size_t>(p - _G(_lineImages)[image]._data) < _G(_lineImages)[image]._size) && opcode != 0xff) {
+ if (p > _G(_entireFile) + _G(_fileLength)) {
+ error("Out of range! Opcode: %x. Image: %d. LineImages[%d].size: %zu\n", opcode, image, image, _G(_lineImages)[image]._size);
+ break;
+ }
+ opcode = *(p++);
+ switch (opcode) {
+ case 0xc0:
+ y = 190 - *(p++);
+ x = *(p++);
+ break;
+ case 0xc1:
+ arg1 = *(p++);
+ arg2 = *(p++);
+ arg3 = *(p++);
+ diamondFill(arg3, 190 - arg2, arg1);
+ break;
+ case 0xff:
+ break;
+ default:
+ arg1 = *(p++);
+ y2 = 190 - opcode;
+ scottLinegraphicsDrawLine(x, y, arg1, y2, _G(_lineColour));
+ x = arg1;
+ y = y2;
+ break;
+ }
+ }
+ if (_G(_pictureBitmap) != nullptr) {
+ delete[] _G(_pictureBitmap);
+ _G(_pictureBitmap) = nullptr;
+ }
+ if (_G(_gliSlowDraw))
+ g_scott->glk_request_timer_events(20);
+ else
+ drawSomeVectorPixels(1);
+}
+
+void drawSomeVectorPixels(int fromStart) {
+ _G(_vectorState) = DRAWING_VECTOR_IMAGE;
+ int i = _G(_currentDrawInstruction);
+ if (fromStart)
+ i = 0;
+ if (i == 0)
+ rectFill(0, 0, _G(_scottGraphicsWidth), _G(_scottGraphicsHeight), remap(_G(_bgColour)));
+ for (; i < _G(_totalDrawInstructions) && (!_G(_gliSlowDraw) || i < _G(_currentDrawInstruction) + 50); i++) {
+ PixelToDraw toDraw = *_G(_pixelsToDraw)[i];
+ putPixel(toDraw._x, toDraw._y, remap(toDraw._colour));
+ }
+ _G(_currentDrawInstruction) = i;
+ if (_G(_currentDrawInstruction) >= _G(_totalDrawInstructions)) {
+ g_scott->glk_request_timer_events(0);
+ _G(_vectorState) = SHOWING_VECTOR_IMAGE;
+ }
+}
+
+int drawingVector() {
+ return (_G(_totalDrawInstructions) > _G(_currentDrawInstruction));
+}
+
+} // End of namespace Scott
+} // End of namespace Glk
diff --git a/engines/glk/scott/line_drawing.h b/engines/glk/scott/line_drawing.h
new file mode 100644
index 00000000000..7e87abd79b9
--- /dev/null
+++ b/engines/glk/scott/line_drawing.h
@@ -0,0 +1,56 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef GLK_SCOTT_LINEDRAWING
+#define GLK_SCOTT_LINEDRAWING
+
+#include <stdint.h>
+
+namespace Glk {
+namespace Scott {
+
+struct LineImage {
+ uint8_t *_data;
+ int _bgColour;
+ size_t _size;
+};
+
+struct PixelToDraw {
+ uint8_t _x = 0;
+ uint8_t _y = 0;
+ uint8_t _colour = 0;
+};
+
+void drawVectorPicture(int image);
+void drawSomeVectorPixels(int fromStart);
+int drawingVector();
+void freePixels();
+
+enum VectorStateType : int {
+ NO_VECTOR_IMAGE,
+ DRAWING_VECTOR_IMAGE,
+ SHOWING_VECTOR_IMAGE
+};
+
+} // End of namespace Scott
+} // End of namespace Glk
+
+#endif
diff --git a/engines/glk/scott/saga_draw.cpp b/engines/glk/scott/saga_draw.cpp
new file mode 100644
index 00000000000..79db99b7a10
--- /dev/null
+++ b/engines/glk/scott/saga_draw.cpp
@@ -0,0 +1,655 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "glk/scott/saga_draw.h"
+#include "glk/scott/definitions.h"
+#include "glk/scott/detect_game.h"
+#include "glk/scott/globals.h"
+#include "glk/scott/scott.h"
+
+namespace Glk {
+namespace Scott {
+
+#define INVALID_COLOR 16
+
+void rot90(uint8_t character[]) {
+ int32_t i, j;
+ uint8_t work2[8] = {0, 0, 0, 0, 0, 0, 0, 0};
+
+ for (i = 0; i < 8; i++)
+ for (j = 0; j < 8; j++)
+ if ((character[j] & (1 << i)) != 0)
+ work2[7 - i] += 1 << j;
+
+ for (i = 0; i < 8; i++)
+ character[i] = work2[i];
+}
+
+void rot180(uint8_t character[]) {
+ int32_t i, j;
+ uint8_t work2[8] = {0, 0, 0, 0, 0, 0, 0, 0};
+
+ for (i = 0; i < 8; i++)
+ for (j = 0; j < 8; j++)
+ if ((character[i] & (1 << j)) != 0)
+ work2[7 - i] += 1 << (7 - j);
+
+ for (i = 0; i < 8; i++)
+ character[i] = work2[i];
+}
+
+void rot270(uint8_t character[]) {
+ int32_t i, j;
+ uint8_t work2[8] = {0, 0, 0, 0, 0, 0, 0, 0};
+
+ for (i = 0; i < 8; i++)
+ for (j = 0; j < 8; j++)
+ if ((character[j] & (1 << i)) != 0)
+ work2[i] += 1 << (7 - j);
+
+ for (i = 0; i < 8; i++)
+ character[i] = work2[i];
+}
+
+void flip(uint8_t character[]) {
+ int32_t i, j;
+ uint8_t work2[8] = {0, 0, 0, 0, 0, 0, 0, 0};
+
+ for (i = 0; i < 8; i++)
+ for (j = 0; j < 8; j++)
+ if ((character[i] & (1 << j)) != 0)
+ work2[i] += 1 << (7 - j);
+ for (i = 0; i < 8; i++)
+ character[i] = work2[i];
+}
+
+void background(int32_t x, int32_t y, int32_t color) {
+ /* Draw the background */
+ rectFill(x * 8, y * 8, 8, 8, color);
+}
+
+void plotsprite(int32_t character, int32_t x, int32_t y, int32_t fg, int32_t bg) {
+ int32_t i, j;
+ background(x, y, bg);
+ for (i = 0; i < 8; i++) {
+ for (j = 0; j < 8; j++)
+ if ((_G(_screenchars)[character][i] & (1 << j)) != 0)
+ putPixel(x * 8 + j, y * 8 + i, fg);
+ }
+}
+
+void transform(int32_t character, int32_t flipMode, int32_t ptr) {
+ uint8_t work[8];
+ int32_t i;
+
+#ifdef DRAWDEBUG
+ debug("Plotting char: %d with flip: %02x (%s) at %d: %d,%d\n",
+ character, flip_mode, flipdescription[(flip_mode & 48) >> 4],
+ ptr, ptr % 0x20, ptr / 0x20);
+#endif
+
+ // first copy the character into work
+ for (i = 0; i < 8; i++)
+ work[i] = _G(_sprite)[character][i];
+
+ // Now flip it
+ if ((flipMode & 0x30) == 0x10) {
+ rot90(work);
+ }
+ if ((flipMode & 0x30) == 0x20) {
+ rot180(work);
+ }
+ if ((flipMode & 0x30) == 0x30) {
+ rot270(work);
+ }
+ if ((flipMode & 0x40) != 0) {
+ flip(work);
+ }
+ flip(work);
+
+ // Now mask it onto the previous character
+ for (i = 0; i < 8; i++) {
+ if ((flipMode & 0x0c) == 12)
+ _G(_screenchars)[ptr][i] ^= work[i];
+ else if ((flipMode & 0x0c) == 8)
+ _G(_screenchars)[ptr][i] &= work[i];
+ else if ((flipMode & 0x0c) == 4)
+ _G(_screenchars)[ptr][i] |= work[i];
+ else
+ _G(_screenchars)[ptr][i] = work[i];
+ }
+}
+
+uint8_t *drawSagaPictureFromData(uint8_t *dataptr, int xSize, int ySize, int xOff, int yOff) {
+ int32_t offset = 0, cont = 0;
+ int32_t i, x, y, mask_mode;
+ uint8_t data, data2, old = 0;
+ int32_t ink[0x22][14], paper[0x22][14];
+
+ // uint8_t *origptr = dataptr;
+ int version = _G(_game)->_pictureFormatVersion;
+
+ offset = 0;
+ int32_t character = 0;
+ int32_t count;
+ do {
+ count = 1;
+
+ /* first handle mode */
+ data = *dataptr++;
+ if (data < 0x80) {
+ if (character > 127 && version > 2) {
+ data += 128;
+ }
+ character = data;
+#ifdef DRAWDEBUG
+ debug("******* SOLO CHARACTER: %04x\n", character);
+#endif
+ transform(character, 0, offset);
+ offset++;
+ if (offset > 767)
+ break;
+ } else {
+ // first check for a count
+ if ((data & 2) == 2) {
+ count = (*dataptr++) + 1;
+ }
+
+ // Next get character and plot (count) times
+ character = *dataptr++;
+
+ // Plot the initial character
+ if ((data & 1) == 1 && character < 128)
+ character += 128;
+
+ for (i = 0; i < count; i++)
+ transform(character, (data & 0x0c) ? (data & 0xf3) : data, offset + i);
+
+ // Now check for overlays
+ if ((data & 0xc) != 0) {
+ // We have overlays so grab each member of the stream and work out what
+ // to do with it
+
+ mask_mode = (data & 0xc);
+ data2 = *dataptr++;
+ old = data;
+ do {
+ cont = 0;
+ if (data2 < 0x80) {
+ if (version == 4 && (old & 1) == 1)
+ data2 += 128;
+#ifdef DRAWDEBUG
+ debug("Plotting %d directly (overlay) at %d\n", data2, offset);
+#endif
+ for (i = 0; i < count; i++)
+ transform(data2, old & 0x0c, offset + i);
+ } else {
+ character = *dataptr++;
+ if ((data2 & 1) == 1)
+ character += 128;
+#ifdef DRAWDEBUG
+ debug("Plotting %d with flip %02x (%s) at %d %d\n", character, (data2 | mask_mode), flipdescription[((data2 | mask_mode) & 48) >> 4],
+ offset, count);
+#endif
+ for (i = 0; i < count; i++)
+ transform(character, (data2 & 0xf3) | mask_mode, offset + i);
+ if ((data2 & 0x0c) != 0) {
+ mask_mode = (data2 & 0x0c);
+ old = data2;
+ cont = 1;
+ data2 = *dataptr++;
+ }
+ }
+ } while (cont != 0);
+ }
+ offset += count;
+ }
+ } while (offset < xSize * ySize);
+
+ y = 0;
+ x = 0;
+
+ uint8_t colour = 0;
+ // Note version 3 count is inverse it is repeat previous colour
+ // Whilst version0-2 count is repeat next character
+ while (y < ySize) {
+ if (dataptr > _G(_entireFile) && static_cast<size_t>(dataptr - _G(_entireFile)) > _G(_fileLength))
+ return dataptr - 1;
+ data = *dataptr++;
+ if ((data & 0x80)) {
+ count = (data & 0x7f) + 1;
+ if (version >= 3) {
+ count--;
+ } else {
+ colour = *dataptr++;
+ }
+ } else {
+ count = 1;
+ colour = data;
+ }
+ while (count > 0) {
+ // Now split up depending on which version we're using
+
+ // For version 3+
+
+ if (_G(_drawToBuffer))
+ _G(_buffer)[(yOff + y) * 32 + (xOff + x)][8] = colour;
+ else {
+ if (version > 2) {
+ if (x > 33)
+ return nullptr;
+ // ink is 0-2, screen is 3-5, 6 in bright flag
+ ink[x][y] = colour & 0x07;
+ paper[x][y] = (colour & 0x38) >> 3;
+
+ if ((colour & 0x40) == 0x40) {
+ paper[x][y] += 8;
+ ink[x][y] += 8;
+ }
+ } else {
+ if (x > 33)
+ return nullptr;
+ paper[x][y] = colour & 0x07;
+ ink[x][y] = ((colour & 0x70) >> 4);
+
+ if ((colour & 0x08) == 0x08 || version < 2) {
+ paper[x][y] += 8;
+ ink[x][y] += 8;
+ }
+ }
+ }
+
+ x++;
+ if (x == xSize) {
+ x = 0;
+ y++;
+ }
+ count--;
+ }
+ }
+ offset = 0;
+ int32_t xoff2;
+ for (y = 0; y < ySize; y++)
+ for (x = 0; x < xSize; x++) {
+ xoff2 = xOff;
+ if (version > 0 && version < 3)
+ xoff2 = xOff - 4;
+
+ if (_G(_drawToBuffer)) {
+ for (i = 0; i < 8; i++)
+ _G(_buffer)[(y + yOff) * 32 + x + xoff2][i] = _G(_screenchars)[offset][i];
+ } else {
+ plotsprite(offset, x + xoff2, y + yOff, remap(ink[x][y]), remap(paper[x][y]));
+ }
+
+#ifdef DRAWDEBUG
+ debug("(gfx#:plotting %d,%d:paper=%s,ink=%s)\n", x + xoff2, y + yoff,
+ colortext(remap(paper[x][y])), colortext(remap(ink[x][y])));
+#endif
+ offset++;
+ if (offset > 766)
+ break;
+ }
+ return dataptr;
+}
+
+void drawSagaPictureNumber(int pictureNumber) {
+ int numgraphics = _G(_game)->_numberOfPictures;
+ if (pictureNumber >= numgraphics) {
+ error("Invalid image number % d !Last image: % d\n ", pictureNumber, numgraphics - 1);
+ return;
+ }
+
+ Image img = _G(_images)[pictureNumber];
+
+ if (img._imageData == nullptr)
+ return;
+
+ drawSagaPictureFromData(img._imageData, img._width, img._height, img._xOff, img._yOff);
+}
+
+void sagaSetup(size_t imgOffset) {
+ int32_t i, y;
+
+ Common::Array<uint16_t> imageOffsets(_G(_game)->_numberOfPictures);
+
+ if (_G(_palChosen) == NO_PALETTE) {
+ _G(_palChosen) = _G(_game)->_palette;
+ }
+
+ if (_G(_palChosen) == NO_PALETTE) {
+ error("unknown palette\n");
+ }
+
+ definePalette();
+
+ int version = _G(_game)->_pictureFormatVersion;
+
+ int32_t CHAR_START = _G(_game)->_startOfCharacters + _G(_fileBaselineOffset);
+ int32_t OFFSET_TABLE_START = _G(_game)->_startOfImageData + _G(_fileBaselineOffset);
+
+ if (_G(_game)->_startOfImageData == FOLLOWS) {
+ OFFSET_TABLE_START = CHAR_START + 0x800;
+ }
+
+ int32_t DATA_OFFSET = _G(_game)->_imageAddressOffset + _G(_fileBaselineOffset);
+ if (imgOffset)
+ DATA_OFFSET = imgOffset;
+ uint8_t *pos;
+ int numgraphics = _G(_game)->_numberOfPictures;
+
+ pos = seekToPos(_G(_entireFile), CHAR_START);
+
+#ifdef DRAWDEBUG
+ debug("Grabbing Character details\n");
+ debug("Character Offset: %04x\n", CHAR_START - _G(_fileBaselineOffset));
+#endif
+ for (i = 0; i < 256; i++) {
+ for (y = 0; y < 8; y++) {
+ _G(_sprite)[i][y] = *(pos++);
+ }
+ }
+
+ _G(_images).resize(numgraphics);
+ Image *img = &_G(_images)[0];
+
+ pos = seekToPos(_G(_entireFile), OFFSET_TABLE_START);
+ pos = seekToPos(_G(_entireFile), OFFSET_TABLE_START);
+
+ for (i = 0; i < numgraphics; i++) {
+ if (_G(_game)->_pictureFormatVersion == 0) {
+ uint16_t address;
+
+ if (i < 11) {
+ address = _G(_game)->_startOfImageData + (i * 2);
+ } else if (i < 28) {
+ address = _G(_hulkItemImageOffsets) + (i - 10) * 2;
+ } else if (i < 34) {
+ address = _G(_hulkLookImageOffsets) + (i - 28) * 2;
+ } else {
+ address = _G(_hulkSpecialImageOffsets) + (i - 34) * 2;
+ }
+
+ address += _G(_fileBaselineOffset);
+ address = _G(_entireFile)[address] + _G(_entireFile)[address + 1] * 0x100;
+
+ imageOffsets[i] = address + _G(_hulkImageOffset);
+ } else {
+ imageOffsets[i] = *(pos++);
+ imageOffsets[i] += *(pos++) * 0x100;
+ }
+ }
+
+ for (int picture_number = 0; picture_number < numgraphics; picture_number++) {
+ pos = seekToPos(_G(_entireFile), imageOffsets[picture_number] + DATA_OFFSET);
+ if (pos == 0)
+ return;
+
+ img->_width = *(pos++);
+ if (img->_width > 32)
+ img->_width = 32;
+
+ img->_height = *(pos++);
+ if (img->_height > 12)
+ img->_height = 12;
+
+ if (version > 0) {
+ img->_xOff = *(pos++);
+ if (img->_xOff > 32)
+ img->_xOff = 4;
+ img->_yOff = *(pos++);
+ if (img->_yOff > 12)
+ img->_yOff = 0;
+ } else {
+ if (picture_number > 9 && picture_number < 28) {
+ img->_xOff = _G(_entireFile)[_G(_hulkCoordinates) + picture_number - 10 + _G(_fileBaselineOffset)];
+ img->_yOff = _G(_entireFile)[_G(_hulkCoordinates) + 18 + picture_number - 10 + _G(_fileBaselineOffset)];
+ } else {
+ img->_xOff = img->_yOff = 0;
+ }
+ }
+
+ img->_imageData = pos;
+
+ img++;
+ }
+}
+
+void putPixel(glsi32 x, glsi32 y, int32_t color) {
+ int yOffset = 0;
+
+ glui32 glk_color = ((_G(_pal)[color][0] << 16)) | ((_G(_pal)[color][1] << 8)) | (_G(_pal)[color][2]);
+
+ g_scott->glk_window_fill_rect(_G(_graphics), glk_color, x * _G(_pixelSize) + _G(_xOffset),
+ y * _G(_pixelSize) + yOffset, _G(_pixelSize), _G(_pixelSize));
+}
+
+void rectFill(int32_t x, int32_t y, int32_t width, int32_t height, int32_t color) {
+ int yOffset = 0;
+
+ int bufferpos = (y / 8) * 32 + (x / 8);
+ if (bufferpos >= 0xD80)
+ return;
+ _G(_buffer)[bufferpos][8] = _G(_buffer)[bufferpos][8] | (color << 3);
+
+ glui32 glk_color = ((_G(_pal)[color][0] << 16)) | ((_G(_pal)[color][1] << 8)) | (_G(_pal)[color][2]);
+
+ g_scott->glk_window_fill_rect(_G(_graphics), glk_color, x * _G(_pixelSize) + _G(_xOffset),
+ y * _G(_pixelSize) + yOffset, width * _G(_pixelSize), height * _G(_pixelSize));
+}
+
+void setColor(int32_t index, RGB *colour) {
+ _G(_pal)[index][0] = (*colour)[0];
+ _G(_pal)[index][1] = (*colour)[1];
+ _G(_pal)[index][2] = (*colour)[2];
+}
+
+void definePalette() {
+ /* set up the palette */
+ if (_G(_palChosen) == VGA) {
+ RGB black = {0, 0, 0};
+ RGB blue = {0, 0, 255};
+ RGB red = {255, 0, 0};
+ RGB magenta = {255, 0, 255};
+ RGB green = {0, 255, 0};
+ RGB cyan = {0, 255, 255};
+ RGB yellow = {255, 255, 0};
+ RGB white = {255, 255, 255};
+ RGB brblack = {0, 0, 0};
+ RGB brblue = {0, 0, 255};
+ RGB brred = {255, 0, 0};
+ RGB brmagenta = {255, 0, 255};
+ RGB brgreen = {0, 255, 0};
+ RGB brcyan = {0, 255, 255};
+ RGB bryellow = {255, 255, 0};
+ RGB brwhite = {255, 255, 255};
+
+ setColor(0, &black);
+ setColor(1, &blue);
+ setColor(2, &red);
+ setColor(3, &magenta);
+ setColor(4, &green);
+ setColor(5, &cyan);
+ setColor(6, &yellow);
+ setColor(7, &white);
+ setColor(8, &brblack);
+ setColor(9, &brblue);
+ setColor(10, &brred);
+ setColor(11, &brmagenta);
+ setColor(12, &brgreen);
+ setColor(13, &brcyan);
+ setColor(14, &bryellow);
+ setColor(15, &brwhite);
+ } else if (_G(_palChosen) == ZX) {
+ /* corrected Sinclair ZX palette (pretty dull though) */
+ RGB black = {0, 0, 0};
+ RGB blue = {0, 0, 154};
+ RGB red = {154, 0, 0};
+ RGB magenta = {154, 0, 154};
+ RGB green = {0, 154, 0};
+ RGB cyan = {0, 154, 154};
+ RGB yellow = {154, 154, 0};
+ RGB white = {154, 154, 154};
+ RGB brblack = {0, 0, 0};
+ RGB brblue = {0, 0, 170};
+ RGB brred = {186, 0, 0};
+ RGB brmagenta = {206, 0, 206};
+ RGB brgreen = {0, 206, 0};
+ RGB brcyan = {0, 223, 223};
+ RGB bryellow = {239, 239, 0};
+ RGB brwhite = {255, 255, 255};
+
+ setColor(0, &black);
+ setColor(1, &blue);
+ setColor(2, &red);
+ setColor(3, &magenta);
+ setColor(4, &green);
+ setColor(5, &cyan);
+ setColor(6, &yellow);
+ setColor(7, &white);
+ setColor(8, &brblack);
+ setColor(9, &brblue);
+ setColor(10, &brred);
+ setColor(11, &brmagenta);
+ setColor(12, &brgreen);
+ setColor(13, &brcyan);
+ setColor(14, &bryellow);
+ setColor(15, &brwhite);
+
+ _G(_whiteColour) = 15;
+ _G(_blueColour) = 9;
+ _G(_diceColour) = 0xff0000;
+ } else if (_G(_palChosen) == ZXOPT) {
+ /* optimized but not realistic Sinclair ZX palette (SPIN emu) */
+ RGB black = {0, 0, 0};
+ RGB blue = {0, 0, 202};
+ RGB red = {202, 0, 0};
+ RGB magenta = {202, 0, 202};
+ RGB green = {0, 202, 0};
+ RGB cyan = {0, 202, 202};
+ RGB yellow = {202, 202, 0};
+ RGB white = {202, 202, 202};
+ /*
+ old David Lodge palette:
+
+ RGB black = { 0, 0, 0 };
+ RGB blue = { 0, 0, 214 };
+ RGB red = { 214, 0, 0 };
+ RGB magenta = { 214, 0, 214 };
+ RGB green = { 0, 214, 0 };
+ RGB cyan = { 0, 214, 214 };
+ RGB yellow = { 214, 214, 0 };
+ RGB white = { 214, 214, 214 };
+ */
+ RGB brblack = {0, 0, 0};
+ RGB brblue = {0, 0, 255};
+ RGB brred = {255, 0, 20};
+ RGB brmagenta = {255, 0, 255};
+ RGB brgreen = {0, 255, 0};
+ RGB brcyan = {0, 255, 255};
+ RGB bryellow = {255, 255, 0};
+ RGB brwhite = {255, 255, 255};
+
+ setColor(0, &black);
+ setColor(1, &blue);
+ setColor(2, &red);
+ setColor(3, &magenta);
+ setColor(4, &green);
+ setColor(5, &cyan);
+ setColor(6, &yellow);
+ setColor(7, &white);
+ setColor(8, &brblack);
+ setColor(9, &brblue);
+ setColor(10, &brred);
+ setColor(11, &brmagenta);
+ setColor(12, &brgreen);
+ setColor(13, &brcyan);
+ setColor(14, &bryellow);
+ setColor(15, &brwhite);
+
+ _G(_whiteColour) = 15;
+ _G(_blueColour) = 9;
+ _G(_diceColour) = 0xff0000;
+
+ } else if ((_G(_palChosen) == C64A) || (_G(_palChosen) == C64B)) {
+ /* and now: C64 palette (pepto/VICE) */
+ RGB black = {0, 0, 0};
+ RGB white = {255, 255, 255};
+ RGB red = {191, 97, 72};
+ RGB cyan = {153, 230, 249};
+ RGB purple = {177, 89, 185};
+ RGB green = {121, 213, 112};
+ RGB blue = {95, 72, 233};
+ RGB yellow = {247, 255, 108};
+ RGB orange = {186, 134, 32};
+ RGB brown = {116, 105, 0};
+ RGB lred = {180, 105, 164};
+ RGB dgrey = {69, 69, 69};
+ RGB grey = {167, 167, 167};
+ RGB lgreen = {154, 210, 134};
+ RGB lblue = {162, 143, 255};
+ RGB lgrey = {150, 150, 150};
+
+ setColor(0, &black);
+ setColor(1, &white);
+ setColor(2, &red);
+ setColor(3, &cyan);
+ setColor(4, &purple);
+ setColor(5, &green);
+ setColor(6, &blue);
+ setColor(7, &yellow);
+ setColor(8, &orange);
+ setColor(9, &brown);
+ setColor(10, &lred);
+ setColor(11, &dgrey);
+ setColor(12, &grey);
+ setColor(13, &lgreen);
+ setColor(14, &lblue);
+ setColor(15, &lgrey);
+
+ _G(_whiteColour) = 1;
+ _G(_blueColour) = 6;
+ _G(_diceColour) = 0x5f48e9;
+ }
+}
+
+int32_t remap(int32_t color) {
+ int32_t mapcol;
+
+ if ((_G(_palChosen) == ZX) || (_G(_palChosen) == ZXOPT)) {
+ /* nothing to remap here; shows that the gfx were created on a ZX */
+ mapcol = (((color >= 0) && (color <= 15)) ? color : INVALID_COLOR);
+ } else if (_G(_palChosen) == C64A) {
+ /* remap A determined from Golden Baton, applies to S1/S3/S13 too (8col) */
+ int32_t c64remap[] = {0, 6, 2, 4, 5, 3, 7, 1, 8, 1, 1, 1, 7, 12, 8, 7};
+ mapcol = (((color >= 0) && (color <= 15)) ? c64remap[color] : INVALID_COLOR);
+ } else if (_G(_palChosen) == C64B) {
+ /* remap B determined from Spiderman (16col) */
+ int32_t c64remap[] = {0, 6, 9, 4, 5, 14, 8, 12, 0, 6, 2, 4, 5, 3, 7, 1};
+ mapcol = (((color >= 0) && (color <= 15)) ? c64remap[color] : INVALID_COLOR);
+ } else
+ mapcol = (((color >= 0) && (color <= 15)) ? color : INVALID_COLOR);
+
+ return (mapcol);
+}
+
+} // End of namespace Scott
+} // End of namespace Glk
diff --git a/engines/glk/scott/saga_draw.h b/engines/glk/scott/saga_draw.h
new file mode 100644
index 00000000000..3d8ade31903
--- /dev/null
+++ b/engines/glk/scott/saga_draw.h
@@ -0,0 +1,56 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef GLK_SCOTT_SAGADRAW
+#define GLK_SCOTT_SAGADRAW
+
+#include "glk/glk_types.h"
+#include <stdint.h>
+
+namespace Glk {
+namespace Scott {
+
+struct Image {
+ uint8_t *_imageData;
+ uint8_t _xOff;
+ uint8_t _yOff;
+ uint8_t _width;
+ uint8_t _height;
+};
+
+typedef uint8_t RGB[3];
+typedef RGB PALETTE[16];
+
+uint8_t *drawSagaPictureFromData(uint8_t *dataptr, int xSize, int ySize, int xOff, int yOff);
+void drawSagaPictureNumber(int pictureNumber);
+
+void sagaSetup(size_t imgOffset);
+
+void putPixel(glsi32 x, glsi32 y, int32_t color);
+void rectFill(int32_t x, int32_t y, int32_t width, int32_t height, int32_t color);
+void definePalette();
+
+int32_t remap(int32_t color);
+
+} // End of namespace Scott
+} // End of namespace Glk
+
+#endif
Commit: 5a4bda94a97bc530b089bb51e6e6000a89bea83c
https://github.com/scummvm/scummvm/commit/5a4bda94a97bc530b089bb51e6e6000a89bea83c
Author: Avijeet (am388488 at gmail.com)
Date: 2022-04-12T20:35:24-07:00
Commit Message:
GLK: SCOTT: Add ring buffer for drawing methods.
Changed paths:
A engines/glk/scott/ring_buffer.cpp
A engines/glk/scott/ring_buffer.h
diff --git a/engines/glk/scott/ring_buffer.cpp b/engines/glk/scott/ring_buffer.cpp
new file mode 100644
index 00000000000..de2aa18ae8b
--- /dev/null
+++ b/engines/glk/scott/ring_buffer.cpp
@@ -0,0 +1,134 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "glk/scott/ring_buffer.h"
+
+namespace Glk {
+namespace Scott {
+
+// The hidden definition of our circular buffer structure
+struct CircularBuf {
+ uint8_t *_buffer;
+ size_t _head;
+ size_t _tail;
+ size_t _max; // of the buffer
+ bool _full;
+};
+
+// Return a pointer to a struct instance
+cbuf_handle_t circularBufInit(uint8_t *buffer, size_t size) {
+ cbuf_handle_t cbuf = new CircularBuf;
+ cbuf->_buffer = buffer;
+ cbuf->_max = size;
+ circularBufReset(cbuf);
+
+ return cbuf;
+}
+
+void circularBufReset(cbuf_handle_t me) {
+ me->_head = 0;
+ me->_tail = 0;
+ me->_full = false;
+}
+
+void circularBufFree(cbuf_handle_t me) {
+ delete me;
+}
+
+bool circularBufFull(cbuf_handle_t me) {
+ return me->_full;
+}
+
+bool circularBufEmpty(cbuf_handle_t me) {
+ return (!me->_full && (me->_head == me->_tail));
+}
+
+size_t circularBufCapacity(cbuf_handle_t me) {
+ return me->_max;
+}
+
+size_t circularBufSize(cbuf_handle_t me) {
+ size_t size = me->_max;
+
+ if (!me->_full) {
+ if (me->_head >= me->_tail) {
+ size = (me->_head - me->_tail);
+ } else {
+ size = (me->_max + me->_head - me->_tail);
+ }
+ }
+
+ return size;
+}
+
+static void advancePointer(cbuf_handle_t me) {
+ if (me->_full) {
+ if (++(me->_tail) == me->_max) {
+ me->_tail = 0;
+ }
+ }
+
+ if (++(me->_head) == me->_max) {
+ me->_head = 0;
+ }
+ me->_full = (me->_head == me->_tail);
+}
+
+static void retreatPointer(cbuf_handle_t me) {
+
+ me->_full = false;
+ if (++(me->_tail) == me->_max) {
+ me->_tail = 0;
+ }
+}
+
+int circularBufPut(cbuf_handle_t me, uint8_t x, uint8_t y) {
+ int r = -1;
+
+ if (!circularBufFull(me)) {
+ me->_buffer[me->_head] = x;
+ advancePointer(me);
+ if (!circularBufFull(me)) {
+ me->_buffer[me->_head] = y;
+ advancePointer(me);
+ r = 0;
+ }
+ }
+ return r;
+}
+
+int circularBufGet(cbuf_handle_t me, int *x, int *y) {
+ int r = -1;
+
+ if (!circularBufEmpty(me)) {
+ *x = me->_buffer[me->_tail];
+ retreatPointer(me);
+ if (!circularBufEmpty(me)) {
+ *y = me->_buffer[me->_tail];
+ retreatPointer(me);
+ r = 0;
+ }
+ }
+ return r;
+}
+
+} // End of namespace Scott
+} // End of namespace Glk
diff --git a/engines/glk/scott/ring_buffer.h b/engines/glk/scott/ring_buffer.h
new file mode 100644
index 00000000000..1a0a3597565
--- /dev/null
+++ b/engines/glk/scott/ring_buffer.h
@@ -0,0 +1,69 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef GLK_SCOTT_RINGBUFFER
+#define GLK_SCOTT_RINGBUFFER
+
+#include <stdint.h>
+
+namespace Glk {
+namespace Scott {
+
+// Opaque circular buffer structure
+struct CircularBuf;
+// Handle type, the way users interact with the API
+typedef CircularBuf *cbuf_handle_t;
+
+/// Pass in a storage buffer and size
+/// Returns a circular buffer handle
+cbuf_handle_t circularBufInit(uint8_t *buffer, size_t size);
+
+/// Free a circular buffer structure.
+/// Does not free data buffer; owner is responsible for that
+void circularBufFree(cbuf_handle_t me);
+
+/// Reset the circular buffer to empty, head == tail
+void circularBufReset(cbuf_handle_t me);
+
+/// Rejects new data if the buffer is full
+/// Returns 0 on success, -1 if buffer is full
+int circularBufPut(cbuf_handle_t me, uint8_t x, uint8_t y);
+
+/// Retrieve a value from the buffer
+/// Returns 0 on success, -1 if the buffer is empty
+int circularBufGet(cbuf_handle_t me, int *x, int *y);
+
+/// Returns true if the buffer is empty
+bool circularBufEmpty(cbuf_handle_t me);
+
+/// Returns true if the buffer is full
+bool circularBufFull(cbuf_handle_t me);
+
+/// Returns the maximum capacity of the buffer
+size_t circularBufCapacity(cbuf_handle_t me);
+
+/// Returns the current number of elements in the buffer
+size_t circularBufSize(cbuf_handle_t me);
+
+} // End of namespace Scott
+} // End of namespace Glk
+
+#endif
Commit: f9521be168884218a5fce85f581586b2693b824f
https://github.com/scummvm/scummvm/commit/f9521be168884218a5fce85f581586b2693b824f
Author: Avijeet (am388488 at gmail.com)
Date: 2022-04-12T20:35:24-07:00
Commit Message:
GLK: SCOTT: Game specific info to load game data.
Changed paths:
A engines/glk/scott/game_info.cpp
A engines/glk/scott/game_info.h
diff --git a/engines/glk/scott/game_info.cpp b/engines/glk/scott/game_info.cpp
new file mode 100644
index 00000000000..b44f7ec4d34
--- /dev/null
+++ b/engines/glk/scott/game_info.cpp
@@ -0,0 +1,2613 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "glk/scott/definitions.h"
+#include "common/array.h"
+#include "common/str.h"
+#include "common/str-array.h"
+
+namespace Glk {
+namespace Scott {
+
+Common::Array<GameInfo> g_games = {
+ GameInfo("Pirate Adventure",
+ PIRATE,
+ OLD_STYLE, // type
+ ENGLISH, // subtype
+ THREE_LETTER_UNCOMPRESSED, // dictionary type
+
+ 66, // Number of items
+ 177, // Number of actions
+ 79, // Number of words
+ 26, // Number of rooms
+ 6, // Max carried items
+ 3, // Word length
+ 99, // Number of messages
+ 80, // number_of_verbs
+ 79, // number_of_nouns;
+ 0x2451, // header
+ EARLY, // header style
+ 0, // no room images
+ 0, // no item flags
+ 0, // no item images
+ 0x3a50, // actions
+
+ UNCOMPRESSED,
+ 0x469a, // dictionary
+ FOLLOWS, // start_of_room_descriptions;
+ FOLLOWS, // start_of_room_connections;
+ FOLLOWS, // start_of_messages;
+ FOLLOWS, // start_of_item_descriptions;
+ FOLLOWS, // start_of_item_locations;
+
+ 0x2539, // start_of_system_messages
+ 0x28de, // start of directions
+ 0, // start_of_characters;
+ 0, // start_of_image_data;
+ 0, // image_address_offset
+ 0, // number_of_pictures;
+ NO_PALETTE, // palette;
+ 0,
+ 0),
+ GameInfo("Voodoo Castle",
+ VOODOO,
+ OLD_STYLE, // type
+ ENGLISH, // subtype
+ THREE_LETTER_UNCOMPRESSED, // dictionary type
+
+ 65, // Number of items
+ 189, // Number of actions
+ 89, // Number of words
+ 25, // Number of rooms
+ 9, // Max carried items
+ 3, // Word length
+ 99, // Number of messages
+
+ 90, // number_of_verbs
+ 89, // number_of_nouns;
+
+ 0x2451, // header
+ EARLY, // header style
+
+ 0, // no room images
+ 0, // no item flags
+ 0, // no item images
+
+ 0x3a50, // actions
+ UNCOMPRESSED,
+ 0x4752, // dictionary
+ FOLLOWS, // start_of_room_descriptions;
+ FOLLOWS, // start_of_room_connections;
+ FOLLOWS, // start_of_messages;
+ FOLLOWS, // start_of_item_descriptions;
+ FOLLOWS, // start_of_item_locations;
+
+ 0x2539, // start_of_system_messages
+ 0x28de, // start of directions
+
+ 0, // start_of_characters;
+ 0, // start_of_image_data;
+ 0, // image_address_offset
+ 0, // number_of_pictures;
+ NO_PALETTE, // palette;
+ 0, // picture_format_version;
+ 0),
+ GameInfo("Strange Odyssey",
+ STRANGE,
+ OLD_STYLE, // type
+ ENGLISH, // subtype
+ FOUR_LETTER_UNCOMPRESSED, // dictionary type
+
+ 55, // Number of items
+ 223, // Number of actions
+ 79, // Number of words
+ 35, // Number of rooms
+ 6, // Max carried items
+ 4, // Word length
+ 94, // Number of messages
+
+ 80, // number_of_verbs
+ 79, // number_of_nouns;
+
+ 0x2451, // header
+ EARLY, // header style
+
+ 0, // no room images
+ 0, // no item flags
+ 0, // no item images
+
+ 0x3a50, // actions
+ UNCOMPRESSED,
+ 0x499a, // dictionary
+ FOLLOWS, // start_of_room_descriptions;
+ FOLLOWS, // start_of_room_connections;
+ FOLLOWS, // start_of_messages;
+ FOLLOWS, // start_of_item_descriptions;
+ FOLLOWS, // start_of_item_locations;
+
+ 0x2539, // start_of_system_messages
+ 0x28de, // start of directions
+
+ 0, // start_of_characters;
+ 0, // start_of_image_data;
+ 0, // image_address_offset
+ 0, // number_of_pictures;
+ NO_PALETTE, // palette;
+ 0, // picture_format_version;
+ 0),
+ GameInfo("Buckaroo Banzai",
+ BANZAI,
+ OLD_STYLE, // type
+ ENGLISH, // subtype
+ FOUR_LETTER_UNCOMPRESSED, // dictionary type
+
+ 60, // Number of items
+ 266, // Number of actions
+ 110, // Number of words
+ 35, // Number of rooms
+ 5, // Max carried items
+ 4, // Word length
+ 95, // Number of messages
+
+ 111, // number_of_verbs
+ 110, // number_of_nouns;
+
+ 0x2451, // header
+ EARLY, // header style
+
+ 0, // no room images
+ 0, // no item flags
+ 0, // no item images
+
+ 0x3a50, // actions
+ UNCOMPRESSED,
+ 0x4c54, // dictionary
+ FOLLOWS, // start_of_room_descriptions;
+ FOLLOWS, // start_of_room_connections;
+ FOLLOWS, // start_of_messages;
+ FOLLOWS, // start_of_item_descriptions;
+ FOLLOWS, // start_of_item_locations;
+
+ 0x2539, // start_of_system_messages
+ 0x28de, // start of directions
+
+ 0, // start_of_characters;
+ 0, // start_of_image_data;
+ 0, // image_address_offset
+ 0, // number_of_pictures;
+ NO_PALETTE, // palette;
+ 0, // picture_format_version;
+ 0),
+ GameInfo("The Golden Baton",
+ BATON,
+ OLD_STYLE, // type
+ MYSTERIOUS, // subtype
+ FOUR_LETTER_UNCOMPRESSED, // dictionary type
+
+ 48, // Number of items
+ 171, // Number of actions
+ 76, // Number of words
+ 31, // Number of rooms
+ 5, // Max carried items
+ 4, // Word length
+ 82, // Number of messages
+
+ 77, // number_of_verbs
+ 76, // number_of_nouns;
+
+ 0x2349, // header
+ EARLY, // header style
+
+ 0, // no room images
+ 0, // no item flags
+ 0, // no item images
+
+ 0x3b56, // actions
+ UNCOMPRESSED,
+ 0x473a, // dictionary
+ FOLLOWS, // start_of_room_descriptions;
+ FOLLOWS, // start_of_room_connections;
+ FOLLOWS, // start_of_messages;
+ FOLLOWS, // start_of_item_descriptions;
+ FOLLOWS, // start_of_item_locations;
+
+ 0x2450, // start_of_system_messages
+ 0x277e, // start of directions
+
+ 0, // start_of_characters;
+ FOLLOWS, // start_of_image_data;
+ 0, // image_address_offset
+ 30, // number_of_pictures;
+ ZXOPT, // palette
+ 99, // picture_format_version;
+ 0),
+ GameInfo("The Golden Baton C64",
+ BATON_C64,
+ NO_TYPE, // type
+ static_cast<Subtype>(MYSTERIOUS | ENGLISH | C64), // subtype
+ FOUR_LETTER_UNCOMPRESSED, // dictionary type
+
+ 48, // Number of items
+ 166, // Number of actions
+ 78, // Number of words
+ 31, // Number of rooms
+ 6, // Max carried items
+ 4, // Word length
+ 99, // Number of messages
+
+ 80, // number_of_verbs
+ 79, // number_of_nouns;
+
+ 0x1dd9, // header
+ MYSTERIOUS_C64_HEADER, // header style
+
+ 0, // no room images
+ 0, // no item flags
+ 0, // no item images
+
+ 0x1df1, // actions
+ UNCOMPRESSED,
+ 0x2861, // dictionary
+ FOLLOWS, // start_of_room_descriptions;
+ FOLLOWS, // start_of_room_connections;
+ FOLLOWS, // start_of_messages;
+ FOLLOWS, // start_of_item_descriptions;
+ FOLLOWS, // start_of_item_locations;
+
+ 0x408, // start_of_system_messages
+ 0x408, // start of directions
+
+ 0, // start_of_characters;
+ 0x38f1, // start_of_image_data;
+ 0, // image_address_offset
+ 30, // number_of_pictures;
+ C64A, // palette
+ 99, // picture_format_version;
+ 0),
+ GameInfo("The Time Machine",
+ TIME_MACHINE,
+ OLD_STYLE, // type
+ MYSTERIOUS, // subtype
+ FOUR_LETTER_UNCOMPRESSED, // dictionary type
+
+ 62, // Number of items
+ 164, // Number of actions
+ 87, // Number of words
+ 44, // Number of rooms
+ 6, // Max carried items
+ 4, // Word length
+ 82, // Number of messages
+
+ 88, // number_of_verbs
+ 87, // number_of_nouns;
+
+ 0x2351, // header
+ EARLY, // header style
+
+ 0, // no room images
+ 0, // no item flags
+ 0, // no item images
+
+ 0x3b81, // actions
+ UNCOMPRESSED,
+ 0x475f, // dictionary
+ FOLLOWS, // start_of_room_descriptions;
+ FOLLOWS, // start_of_room_connections;
+ FOLLOWS, // start_of_messages;
+ FOLLOWS, // start_of_item_descriptions;
+ FOLLOWS, // start_of_item_locations;
+
+ 0x24c0, // start_of_system_messages
+ 0x2780, // start of directions
+
+ 0, // start_of_characters;
+ FOLLOWS,
+ 0, // image_address_offset
+ 43, // number_of_pictures;
+ ZXOPT, // palette
+ 99, // picture_format_version;
+ 0),
+ GameInfo("The Time Machine C64",
+ TIME_MACHINE_C64,
+ NO_TYPE, // type
+ static_cast<Subtype>(MYSTERIOUS | ENGLISH | C64), // subtype
+ FOUR_LETTER_UNCOMPRESSED, // dictionary type
+
+ 62, // Number of items
+ 161, // Number of actions
+ 85, // Number of words
+ 44, // Number of rooms
+ 6, // Max carried items
+ 4, // Word length
+ 73, // Number of messages
+
+ 87, // number_of_verbs
+ 85, // number_of_nouns;
+
+ 0x1dd9, // header
+ MYSTERIOUS_C64_HEADER, // header style
+
+ 0, // no room images
+ 0, // no item flags
+ 0, // no item images
+
+ 0x1df1, // actions
+ UNCOMPRESSED,
+ 0x2811, // dictionary
+ 0x2b6d, // start_of_room_descriptions;
+ FOLLOWS, // start_of_room_connections;
+ 0x2f0f, // start_of_messages;
+ FOLLOWS, // start_of_item_descriptions;
+ 0x37ce, // start_of_item_locations;
+
+ 0x408, // start_of_system_messages
+ 0x408, // start of directions
+
+ 0, // start_of_characters;
+ 0x3872, // start_of_image_data;
+ 0, // image_address_offset
+ 43, // number_of_pictures;
+ C64A, // palette
+ 99, // picture_format_version;
+ 0),
+ GameInfo("Arrow of Death part 1",
+ ARROW1,
+ OLD_STYLE, // type
+ MYSTERIOUS, // subtype
+ FOUR_LETTER_UNCOMPRESSED, // dictionary type
+
+ 64, // Number of items
+ 150, // Number of actions
+ 90, // Number of words
+ 52, // Number of rooms
+ 5, // Max carried items
+ 4, // Word length
+ 82, // Number of messages
+
+ 91, // number_of_verbs
+ 83, // number_of_nouns;
+
+ 0x2351, // header
+ EARLY, // header style
+
+ 0, // no room images
+ 0, // no item flags
+ 0, // no item images
+
+ 0x3b81, // actions
+ UNCOMPRESSED,
+ 0x46b3, // dictionary
+ 0x4a41, // start_of_room_descriptions;
+ FOLLOWS, // start_of_room_connections;
+ FOLLOWS, // start_of_messages;
+ FOLLOWS, // start_of_item_descriptions;
+ FOLLOWS, // start_of_item_locations;
+
+ 0x24c0, // start_of_system_messages
+ 0x2780, // start of directions
+
+ 0, // start_of_characters;
+ FOLLOWS, // start_of_image_data;
+ 0, // image_address_offset
+ 51, // number_of_pictures;
+ ZXOPT, // palette
+ 99, // picture_format_version;
+ 0),
+ GameInfo("Arrow of Death part 1 C64",
+ ARROW1_C64,
+ NO_TYPE, // type
+ static_cast<Subtype>(MYSTERIOUS | ENGLISH | C64), // subtype
+ FOUR_LETTER_UNCOMPRESSED, // dictionary type
+
+ 64, // Number of items
+ 150, // Number of actions
+ 90, // Number of words
+ 52, // Number of rooms
+ 5, // Max carried items
+ 4, // Word length
+ 82, // Number of messages
+
+ 91, // number_of_verbs
+ 82, // number_of_nouns;
+
+ 0x1dd9, // header
+ MYSTERIOUS_C64_HEADER, // header style
+
+ 0, // no room images
+ 0, // no item flags
+ 0, // no item images
+
+ 0x1df1, // actions
+ UNCOMPRESSED,
+ 0x2761, // dictionary
+ FOLLOWS, // start_of_room_descriptions;
+ FOLLOWS, // start_of_room_connections;
+ FOLLOWS, // start_of_messages;
+ FOLLOWS, // start_of_item_descriptions;
+ FOLLOWS, // start_of_item_locations;
+
+ 0x0408, // start_of_system_messages
+ 0x0408, // start of directions
+
+ 0, // start_of_characters;
+ 0x38e2, // start_of_image_data;
+ 0, // image_address_offset
+ 51, // number_of_pictures;
+ C64A, // palette
+ 99, // picture_format_version;
+ 0),
+ GameInfo("Arrow of Death part 2",
+ ARROW2,
+ OLD_STYLE, // type
+ MYSTERIOUS, // subtype
+ FOUR_LETTER_UNCOMPRESSED, // dictionary type
+
+ 91, // Number of items
+ 190, // Number of actions
+ 83, // Number of words
+ 65, // Number of rooms
+ 9, // Max carried items
+ 4, // Word length
+ 82, // Number of messages
+
+ 84, // number_of_verbs
+ 83, // number_of_nouns;
+
+ 0x2351, // header
+ EARLY, // header style
+
+ 0, // no room images
+ 0, // no item flags
+ 0, // no item images
+
+ 0x3b81, // actions
+ UNCOMPRESSED,
+ 0x49b7, // dictionary
+ FOLLOWS, // start_of_room_descriptions;
+ FOLLOWS, // start_of_room_connections;
+ FOLLOWS, // start_of_messages;
+ FOLLOWS, // start_of_item_descriptions;
+ FOLLOWS, // start_of_item_locations;
+
+ 0x24c0, // start_of_system_messages
+ 0x2780, // start of directions
+
+ 0, // start_of_characters;
+ FOLLOWS, // start_of_image_data;
+ 0, // image_address_offset
+ 64, // number_of_pictures;
+ ZXOPT, // palette
+ 99, // picture_format_version;
+ 0),
+ GameInfo("Arrow of Death part 2 C64",
+ ARROW2_C64,
+ NO_TYPE, // type
+ static_cast<Subtype>(MYSTERIOUS | ENGLISH | C64), // subtype
+ FOUR_LETTER_UNCOMPRESSED, // dictionary type
+
+ 90, // Number of items
+ 176, // Number of actions
+ 82, // Number of words
+ 65, // Number of rooms
+ 9, // Max carried items
+ 4, // Word length
+ 82, // Number of messages
+
+ 81, // number_of_verbs
+ 82, // number_of_nouns;
+
+ 0x1dd9, // header
+ ARROW_OF_DEATH_PT_2_C64_HEADER, // header style
+
+ 0, // no room images
+ 0, // no item flags
+ 0, // no item images
+
+ 0x1df1, // actions
+ UNCOMPRESSED,
+ 0x2901, // dictionary
+ FOLLOWS, // start_of_room_descriptions;
+ FOLLOWS, // start_of_room_connections;
+ FOLLOWS, // start_of_messages;
+ FOLLOWS, // start_of_item_descriptions;
+ FOLLOWS, // start_of_item_locations;
+
+ 0x408, // start_of_system_messages
+ 0x408, // start of directions
+
+ 0, // start_of_characters;
+ 0x3cac, // start_of_image_data;
+ 0, // image_address_offset
+ 64, // number_of_pictures;
+ C64A, // palette
+ 99, // picture_format_version;
+ 0),
+ GameInfo("Escape from Pulsar 7",
+ PULSAR7,
+ OLD_STYLE, // type
+ MYSTERIOUS, // subtype
+ FOUR_LETTER_UNCOMPRESSED, // dictionary type
+
+ 90, // Number of items
+ 220, // Number of actions
+ 145, // Number of words
+ 45, // Number of rooms
+ 6, // Max carried items
+ 4, // Word length
+ 82, // Number of messages
+
+ 146, // number_of_verbs
+ 145, // number_of_nouns;
+
+ 0x2351, // header
+ EARLY, // header style
+
+ 0, // no room images
+ 0, // no item flags
+ 0, // no item images
+
+ 0x3b81, // actions
+ UNCOMPRESSED,
+ 0x4b1d, // dictionary
+ FOLLOWS, // start_of_room_descriptions;
+ FOLLOWS, // start_of_room_connections;
+ FOLLOWS, // start_of_messages;
+ FOLLOWS, // start_of_item_descriptions;
+ FOLLOWS, // start_of_item_locations;
+
+ 0x24c0, // start_of_system_messages
+ 0x2780, // start of directions
+
+ 0, // start_of_characters;
+ FOLLOWS, // start_of_image_data;
+ 0, // image_address_offset
+ 44, // number_of_pictures;
+ ZXOPT, // palette
+ 99, // picture_format_version;
+ 0),
+ GameInfo("Escape from Pulsar 7 C64",
+ PULSAR7_C64,
+ NO_TYPE, // type
+ static_cast<Subtype>(MYSTERIOUS | ENGLISH | C64), // subtype
+ FOUR_LETTER_UNCOMPRESSED, // dictionary type
+
+ 88, // Number of items
+ 195, // Number of actions
+ 145, // Number of words
+ 45, // Number of rooms
+ 6, // Max carried items
+ 4, // Word length
+ 82, // Number of messages
+
+ 146, // number_of_verbs
+ 102, // number_of_nouns;
+
+ 0x1dd9, // header
+ EARLY, // header style
+
+ 0, // no room images
+ 0, // no item flags
+ 0, // no item images
+
+ 0x1da3, // actions
+ UNCOMPRESSED,
+ 0x29e3, // dictionary
+ FOLLOWS, // start_of_room_descriptions;
+ FOLLOWS, // start_of_room_connections;
+ FOLLOWS, // start_of_messages;
+ FOLLOWS, // start_of_item_descriptions;
+ FOLLOWS, // start_of_item_locations;
+
+ 0x408, // start_of_system_messages
+ 0x408, // start of directions
+
+ 0, // start_of_characters;
+ 0x3bf4, // start_of_image_data;
+ 0, // image_address_offset
+ 44, // number_of_pictures;
+ C64A, // palette
+ 99, // picture_format_version;
+ 0),
+ GameInfo("Circus",
+ CIRCUS,
+ OLD_STYLE, // type
+ MYSTERIOUS, // subtype
+ FOUR_LETTER_UNCOMPRESSED, // dictionary type
+
+ 65, // Number of items
+ 165, // Number of actions
+ 97, // Number of words
+ 36, // Number of rooms
+ 6, // Max carried items
+ 4, // Word length
+ 82, // Number of messages
+
+ 98, // number_of_verbs
+ 97, // number_of_nouns;
+
+ 0x2349, // header
+ EARLY, // header style
+
+ 0, // no room images
+ 0, // no item flags
+ 0, // no item images
+
+ 0x3b56, // actions
+ UNCOMPRESSED,
+ 0x471a, // dictionary
+ FOLLOWS, // start_of_room_descriptions;
+ FOLLOWS, // start_of_room_connections;
+ FOLLOWS, // start_of_messages;
+ FOLLOWS, // start_of_item_descriptions;
+ FOLLOWS, // start_of_item_locations;
+
+ 0x24c0, // start_of_system_messages
+ 0x277E, // start of directions
+
+ 0, // start_of_characters;
+ FOLLOWS, // start_of_image_data;
+ 0, // image_address_offset
+ 35, // number_of_pictures;
+ ZXOPT, // palette
+ 99, // picture_format_version;
+ 0),
+ GameInfo("Circus C64",
+ CIRCUS_C64,
+ NO_TYPE, // type
+ static_cast<Subtype>(MYSTERIOUS | ENGLISH | C64), // subtype
+ FOUR_LETTER_UNCOMPRESSED, // dictionary type
+
+ 65, // Number of items
+ 165, // Number of actions
+ 97, // Number of words
+ 36, // Number of rooms
+ 6, // Max carried items
+ 4, // Word length
+ 72, // Number of messages
+
+ 98, // number_of_verbs
+ 96, // number_of_nouns;
+
+ 0x1dd9, // header
+ MYSTERIOUS_C64_HEADER, // header style
+
+ 0, // no room images
+ 0, // no item flags
+ 0, // no item images
+
+ 0x1df1, // actions
+ UNCOMPRESSED,
+ 0x2851, // dictionary
+ FOLLOWS, // start_of_room_descriptions;
+ FOLLOWS, // start_of_room_connections;
+ FOLLOWS, // start_of_messages;
+ FOLLOWS, // start_of_item_descriptions;
+ FOLLOWS, // start_of_item_locations;
+
+ 0x408, // start_of_system_messages
+ 0x408, // start of directions
+
+ 0, // start_of_characters;
+ 0x3914, // start_of_image_data;
+ 0, // image_address_offset
+ 35, // number_of_pictures;
+ C64A, // palette
+ 99, // picture_format_version;
+ 0),
+ GameInfo("Feasibility Experiment",
+ FEASIBILITY,
+ OLD_STYLE, // type
+ MYSTERIOUS, // subtype
+ FOUR_LETTER_UNCOMPRESSED, // dictionary type
+
+ 65, // Number of items
+ 164, // Number of actions
+ 82, // Number of words
+ 59, // Number of rooms
+ 5, // Max carried items
+ 4, // Word length
+ 82, // Number of messages
+
+ 83, // number_of_verbs
+ 82, // number_of_nouns;
+
+ 0x2351, // header
+ EARLY, // header style
+
+ 0, // no room images
+ 0, // no item flags
+ 0, // no item images
+
+ 0x3b81, // actions
+ UNCOMPRESSED,
+ 0x47bf, // dictionary
+ FOLLOWS, // start_of_room_descriptions;
+ FOLLOWS, // start_of_room_connections;
+ FOLLOWS, // start_of_messages;
+ FOLLOWS, // start_of_item_descriptions;
+ FOLLOWS, // start_of_item_locations;
+
+ 0x24c0, // start_of_system_messages
+ 0x2780, // start of directions
+
+ 0, // start_of_characters;
+ FOLLOWS, // start_of_image_data;
+ 0, // image_address_offset
+ 58, // number_of_pictures;
+ ZXOPT, // palette
+ 99, // picture_format_version;
+ 0),
+ GameInfo("Feasibility Experiment C64",
+ FEASIBILITY_C64,
+ NO_TYPE, // type
+ static_cast<Subtype>(MYSTERIOUS | ENGLISH | C64), // subtype
+ FOUR_LETTER_UNCOMPRESSED, // dictionary type
+
+ 65, // Number of items
+ 156, // Number of actions
+ 79, // Number of words
+ 59, // Number of rooms
+ 6, // Max carried items
+ 4, // Word length
+ 82, // Number of messages
+
+ 56, // number_of_verbs
+ 80, // number_of_nouns;
+
+ 0x1dd9, // header
+ EARLY, // header style
+
+ 0, // no room images
+ 0, // no item flags
+ 0, // no item images
+
+ 0x1df3, // actions
+ UNCOMPRESSED,
+ 0x27c3, // dictionary
+ FOLLOWS, // start_of_room_descriptions;
+ FOLLOWS, // start_of_room_connections;
+ FOLLOWS, // start_of_messages;
+ FOLLOWS, // start_of_item_descriptions;
+ FOLLOWS, // start_of_item_locations;
+
+ 0x408, // start_of_system_messages
+ 0x408, // start of directions
+
+ 0, // start_of_characters;
+ 0x3876, // start_of_image_data;
+ 0, // image_address_offset
+ 58, // number_of_pictures;
+ C64A, // palette
+ 99, // picture_format_version;
+ 0),
+ GameInfo("The Wizard of Akyrz",
+ AKYRZ,
+ OLD_STYLE, // type
+ MYSTERIOUS, // subtype
+ FOUR_LETTER_UNCOMPRESSED, // dictionary type
+
+ 49, // Number of items
+ 201, // Number of actions
+ 85, // Number of words
+ 40, // Number of rooms
+ 6, // Max carried items
+ 4, // Word length
+ 82, // Number of messages
+
+ 86, // number_of_verbs
+ 85, // number_of_nouns;
+
+ 0x2351, // header
+ EARLY, // header style
+
+ 0, // no room images
+ 0, // no item flags
+ 0, // no item images
+
+ 0x3b81, // actions
+ UNCOMPRESSED,
+ 0x497d, // dictionary
+ FOLLOWS, // start_of_room_descriptions;
+ FOLLOWS, // start_of_room_connections;
+ FOLLOWS, // start_of_messages;
+ FOLLOWS, // start_of_item_descriptions;
+ FOLLOWS, // start_of_item_locations;
+
+ 0x24c0, // start_of_system_messages
+ 0x2780, // start of directions
+
+ 0, // start_of_characters;
+ FOLLOWS, // start_of_image_data;
+ 0, // image_address_offset
+ 39, // number_of_pictures;
+ ZXOPT, // palette
+ 99, // picture_format_version;
+ 0),
+ GameInfo("The Wizard of Akyrz C64",
+ AKYRZ_C64,
+ NO_TYPE, // type
+ static_cast<Subtype>(MYSTERIOUS | ENGLISH | C64), // subtype
+ FOUR_LETTER_UNCOMPRESSED, // dictionary type
+
+ 49, // Number of items
+ 199, // Number of actions
+ 85, // Number of words
+ 40, // Number of rooms
+ 6, // Max carried items
+ 4, // Word length
+ 82, // Number of messages
+
+ 67, // number_of_verbs
+ 85, // number_of_nouns;
+
+ 0x1dd9, // header
+ MYSTERIOUS_C64_HEADER, // header style
+
+ 0, // no room images
+ 0, // no item flags
+ 0, // no item images
+
+ 0x1df1, // actions
+ UNCOMPRESSED,
+ 0x2a71, // dictionary
+ FOLLOWS, // start_of_room_descriptions;
+ FOLLOWS, // start_of_room_connections;
+ FOLLOWS, // start_of_messages;
+ FOLLOWS, // start_of_item_descriptions;
+ FOLLOWS, // start_of_item_locations;
+
+ 0x408, // start_of_system_messages
+ 0x408, // start of directions
+
+ 0, // start_of_characters;
+ 0x3bce, // start_of_image_data;
+ 0, // image_address_offset
+ 39, // number_of_pictures;
+ C64A, // palette
+ 99, // picture_format_version;
+ 0),
+ GameInfo("Perseus and Andromeda",
+ PERSEUS,
+ OLD_STYLE, // type
+ MYSTERIOUS, // subtype
+ FOUR_LETTER_UNCOMPRESSED, // dictionary type
+
+ 60, // Number of items
+ 178, // Number of actions
+ 130, // Number of words
+ 40, // Number of rooms
+ 6, // Max carried items
+ 4, // Word length
+ 82, // Number of messages
+
+ 131, // number_of_verbs
+ 130, // number_of_nouns;
+
+ 0x2351, // header
+ EARLY, // header style
+
+ 0, // no room images
+ 0, // no item flags
+ 0, // no item images
+
+ 0x3b81, // actions
+ UNCOMPRESSED,
+ 0x4823, // dictionary
+ FOLLOWS, // start_of_room_descriptions;
+ FOLLOWS, // start_of_room_connections;
+ FOLLOWS, // start_of_messages;
+ FOLLOWS, // start_of_item_descriptions;
+ FOLLOWS, // start_of_item_locations;
+
+ 0x24c0, // start_of_system_messages
+ 0x2780, // start of directions
+
+ 0, // start_of_characters;
+ FOLLOWS, // start_of_image_data;
+ 0, // image_address_offset
+ 39, // number_of_pictures;
+ ZXOPT, // palette
+ 99, // picture_format_version;
+ 0),
+ GameInfo("Perseus and Andromeda C64",
+ PERSEUS_C64,
+ NO_TYPE, // type
+ static_cast<Subtype>(MYSTERIOUS | ENGLISH | C64), // subtype
+ FOUR_LETTER_UNCOMPRESSED, // dictionary type
+
+ 59, // Number of items
+ 165, // Number of actions
+ 130, // Number of words
+ 40, // Number of rooms
+ 6, // Max carried items
+ 4, // Word length
+ 82, // Number of messages
+
+ 131, // number_of_verbs
+ 82, // number_of_nouns;
+
+ 0x1dd9, // header
+ EARLY, // header style
+
+ 0, // no room images
+ 0, // no item flags
+ 0, // no item images
+
+ 0x1df3, // actions
+ UNCOMPRESSED,
+ 0x2853, // dictionary
+ FOLLOWS, // start_of_room_descriptions;
+ FOLLOWS, // start_of_room_connections;
+ FOLLOWS, // start_of_messages;
+ FOLLOWS, // start_of_item_descriptions;
+ FOLLOWS, // start_of_item_locations;
+
+ 0x408, // start_of_system_messages
+ 0x408, // start of directions
+
+ 0, // start_of_characters;
+ 0x3d91, // start_of_image_data;
+ 0, // image_address_offset
+ 39, // number_of_pictures;
+ C64A, // palette
+ 99, // picture_format_version;
+ 0),
+ GameInfo("Perseus and Andromeda C64",
+ PERSEUS_C64,
+ NO_TYPE, // type
+ static_cast<Subtype>(MYSTERIOUS | ENGLISH | C64), // subtype
+ FOUR_LETTER_UNCOMPRESSED, // dictionary type
+
+ 59, // Number of items
+ 165, // Number of actions
+ 130, // Number of words
+ 40, // Number of rooms
+ 6, // Max carried items
+ 4, // Word length
+ 82, // Number of messages
+
+ 131, // number_of_verbs
+ 82, // number_of_nouns;
+
+ 0x1dd9, // header
+ EARLY, // header style
+
+ 0, // no room images
+ 0, // no item flags
+ 0, // no item images
+
+ 0x1df3, // actions
+ UNCOMPRESSED,
+ 0x2853, // dictionary
+ FOLLOWS, // start_of_room_descriptions;
+ FOLLOWS, // start_of_room_connections;
+ FOLLOWS, // start_of_messages;
+ FOLLOWS, // start_of_item_descriptions;
+ FOLLOWS, // start_of_item_locations;
+
+ 0x408, // start_of_system_messages
+ 0x408, // start of directions
+
+ 0, // start_of_characters;
+ 0x3d91, // start_of_image_data;
+ 0, // image_address_offset
+ 39, // number_of_pictures;
+ C64A, // palette
+ 99, // picture_format_version;
+ 0),
+ GameInfo("Perseus and Andromeda C64",
+ PERSEUS_C64,
+ NO_TYPE, // type
+ static_cast<Subtype>(MYSTERIOUS | ENGLISH | C64), // subtype
+ FOUR_LETTER_UNCOMPRESSED, // dictionary type
+
+ 59, // Number of items
+ 165, // Number of actions
+ 130, // Number of words
+ 40, // Number of rooms
+ 6, // Max carried items
+ 4, // Word length
+ 82, // Number of messages
+
+ 131, // number_of_verbs
+ 82, // number_of_nouns;
+
+ 0x1dd9, // header
+ EARLY, // header style
+
+ 0, // no room images
+ 0, // no item flags
+ 0, // no item images
+
+ 0x1df3, // actions
+ UNCOMPRESSED,
+ 0x2853, // dictionary
+ FOLLOWS, // start_of_room_descriptions;
+ FOLLOWS, // start_of_room_connections;
+ FOLLOWS, // start_of_messages;
+ FOLLOWS, // start_of_item_descriptions;
+ FOLLOWS, // start_of_item_locations;
+
+ 0x408, // start_of_system_messages
+ 0x408, // start of directions
+
+ 0, // start_of_characters;
+ 0x3d91, // start_of_image_data;
+ 0, // image_address_offset
+ 39, // number_of_pictures;
+ C64A, // palette
+ 99, // picture_format_version;
+ 0),
+ GameInfo("Perseus and Andromeda Italian",
+ PERSEUS_ITALIAN,
+ OLD_STYLE, // type
+ MYSTERIOUS, // subtype
+ ITALIAN, // dictionary type
+
+ 60, // Number of items
+ 178, // Number of actions
+ 130, // Number of words
+ 40, // Number of rooms
+ 6, // Max carried items
+ 4, // Word length
+ 82, // Number of messages
+
+ 131, // number_of_verbs
+ 83, // number_of_nouns;
+
+ 0x045d, // header
+ EARLY, // header style
+
+ 0, // no room images
+ 0, // no item flags
+ 0, // no item images
+
+ 0x1c8d, // actions
+ UNCOMPRESSED,
+ 0x292f, // dictionary
+ 0x2e4d, // start_of_room_descriptions;
+ FOLLOWS, // start_of_room_connections;
+ FOLLOWS, // start_of_messages;
+ FOLLOWS, // start_of_item_descriptions;
+ FOLLOWS, // start_of_item_locations;
+
+ 0x5d5, // start_of_system_messages
+ 0x892, // start of directions
+
+ 0, // start_of_characters;
+ 0x3faa, // start_of_image_data;
+ 0, // image_address_offset
+ 40, // number_of_pictures;
+ ZXOPT, // palette
+ 99, // picture_format_version;
+ 0),
+ GameInfo("Ten Little Indians",
+ INDIANS,
+ OLD_STYLE, // type
+ MYSTERIOUS, // subtype
+ FOUR_LETTER_UNCOMPRESSED, // dictionary type
+
+ 73, // Number of items
+ 161, // Number of actions
+ 85, // Number of words
+ 63, // Number of rooms
+ 5, // Max carried items
+ 4, // Word length
+ 82, // Number of messages
+
+ 86, // number_of_verbs
+ 85, // number_of_nouns;
+
+ 0x2351, // header
+ EARLY, // header style
+
+ 0, // no room images
+ 0, // no item flags
+ 0, // no item images
+
+ 0x3b81, // actions
+ UNCOMPRESSED,
+ 0x47b7, // dictionary
+ FOLLOWS, // start_of_room_descriptions;
+ FOLLOWS, // start_of_room_connections;
+ FOLLOWS, // start_of_messages;
+ FOLLOWS, // start_of_item_descriptions;
+ FOLLOWS, // start_of_item_locations;
+
+ 0x24c0, // start_of_system_messages
+ 0x2780, // start of directions
+
+ 0, // start_of_characters;
+ FOLLOWS, // start_of_image_data;
+ 0, // image_address_offset
+ 62, // number_of_pictures;
+ ZXOPT, // palette
+ 99, // picture_format_version;
+ 0),
+ GameInfo("Ten Little Indians C64",
+ INDIANS_C64,
+ NO_TYPE, // type
+ static_cast<Subtype>(MYSTERIOUS | ENGLISH | C64), // subtype
+ FOUR_LETTER_UNCOMPRESSED, // dictionary type
+
+ 73, // Number of items
+ 161, // Number of actions
+ 82, // Number of words
+ 63, // Number of rooms
+ 5, // Max carried items
+ 4, // Word length
+ 82, // Number of messages
+
+ 64, // number_of_verbs
+ 82, // number_of_nouns;
+
+ 0x1dd9, // header
+ INDIANS_C64_HEADER, // header style
+
+ 0, // no room images
+ 0, // no item flags
+ 0, // no item images
+
+ 0x1df0, // actions
+ UNCOMPRESSED,
+ 0x2810, // dictionary
+ FOLLOWS, // start_of_room_descriptions;
+ FOLLOWS, // start_of_room_connections;
+ FOLLOWS, // start_of_messages;
+ FOLLOWS, // start_of_item_descriptions;
+ FOLLOWS, // start_of_item_locations;
+
+ 0x408, // start_of_system_messages
+ 0x408, // start of directions
+
+ 0, // start_of_characters;
+ 0x3a46, // start_of_image_data;
+ 0, // image_address_offset
+ 62, // number_of_pictures;
+ C64A, // palette
+ 99, // picture_format_version;
+ 0),
+ GameInfo("Waxworks",
+ WAXWORKS,
+ OLD_STYLE, // type
+ MYSTERIOUS, // subtype
+ FOUR_LETTER_UNCOMPRESSED, // dictionary type
+
+ 57, // Number of items
+ 189, // Number of actions
+ 106, // Number of words
+ 41, // Number of rooms
+ 6, // Max carried items
+ 4, // Word length
+ 82, // Number of messages
+
+ 107, // number_of_verbs
+ 106, // number_of_nouns;
+
+ 0x2351, // header
+ EARLY, // header style
+
+ 0, // no room images
+ 0, // no item flags
+ 0, // no item images
+
+ 0x3b81, // actions
+ UNCOMPRESSED,
+ 0x48d3, // dictionary
+ FOLLOWS, // start_of_room_descriptions;
+ FOLLOWS, // start_of_room_connections;
+ FOLLOWS, // start_of_messages;
+ FOLLOWS, // start_of_item_descriptions;
+ FOLLOWS, // start_of_item_locations;
+
+ 0x24c0, // start_of_system_messages
+ 0x2780, // start of directions
+
+ 0, // start_of_characters;
+ FOLLOWS, // start_of_image_data;
+ 0, // image_address_offset
+ 40, // number_of_pictures;
+ ZXOPT, // palette
+ 99, // picture_format_version;
+ 0),
+ GameInfo("Waxworks C64",
+ WAXWORKS_C64,
+ NO_TYPE, // type
+ static_cast<Subtype>(MYSTERIOUS | ENGLISH | C64), // subtype
+ FOUR_LETTER_UNCOMPRESSED, // dictionary type
+
+ 57, // Number of items
+ 189, // Number of actions
+ 105, // Number of words
+ 41, // Number of rooms
+ 6, // Max carried items
+ 4, // Word length
+ 82, // Number of messages
+
+ 91, // number_of_verbs
+ 105, // number_of_nouns;
+
+ 0x1dd9, // header
+ EARLY, // header style
+
+ 0, // no room images
+ 0, // no item flags
+ 0, // no item images
+
+ 0x1df3, // actions
+ UNCOMPRESSED,
+ 0x29d3, // dictionary
+ FOLLOWS, // start_of_room_descriptions;
+ FOLLOWS, // start_of_room_connections;
+ FOLLOWS, // start_of_messages;
+ FOLLOWS, // start_of_item_descriptions;
+ FOLLOWS, // start_of_item_locations;
+
+ 0x408, // start_of_system_messages
+ 0x408, // start of directions
+
+ 0, // start_of_characters;
+ 0x3f31, // start_of_image_data;
+ 0, // image_address_offset
+ 40, // number_of_pictures;
+ C64A, // palette
+ 99, // picture_format_version;
+ 0),
+ GameInfo("Questprobe 1: The Hulk",
+ HULK,
+ NO_TYPE, // type
+ ENGLISH, // subtype
+ FOUR_LETTER_UNCOMPRESSED, // dictionary type
+
+ 54, // Number of items
+ 261, // Number of actions
+ 128, // Number of words
+ 20, // Number of rooms
+ 10, // Max carried items
+ 4, // Word length
+ 99, // Number of messages
+
+ 128, // number_of_verbs
+ 129, // number_of_nouns;
+
+ 0x4bf4, // header
+ HULK_HEADER, // header style
+
+ 0x270c, // room images
+ 0, // item flags
+ 0x26c8, // item images
+
+ 0x6087, // actions
+ HULK_ACTIONS,
+ 0x4cc4, // dictionary
+ 0x51cd, // start_of_room_descriptions;
+ 0x7111, // start_of_room_connections;
+ 0x575e, // start_of_messages;
+ FOLLOWS, // start_of_item_descriptions;
+ 0x5f3d, // start_of_item_locations;
+
+ 0x2553, // start_of_system_messages
+ 0x28f7, // start of directions
+
+ 0x281b, // start_of_characters;
+ 0x2782, // start_of_image_data
+ 0, // image_address_offset
+ 43, // number_of_pictures;
+ ZXOPT, // palette
+ 0, // picture_format_version;
+ 0),
+ GameInfo("Questprobe 1: The Hulk C64", HULK_C64,
+ NO_TYPE, // type
+ ENGLISH, // subtype
+ FOUR_LETTER_UNCOMPRESSED, // dictionary type
+
+ 54, // Number of items
+ 261, // Number of actions
+ 128, // Number of words
+ 20, // Number of rooms
+ 10, // Max carried items
+ 4, // Word length
+ 99, // Number of messages
+
+ 128, // number_of_verbs
+ 129, // number_of_nouns;
+
+ 0x7867, // header
+ HULK_HEADER, // header style
+
+ 0x2280, // room images
+ 0, // item flags
+ 0x22ba, // item images
+
+ 0x8c47, // actions
+ HULK_ACTIONS,
+ 0x7884, // dictionary
+ 0x7d8d, // start_of_room_descriptions;
+ 0x9CD1, // start_of_room_connections;
+ 0x831E, // start_of_messages;
+ FOLLOWS, // start_of_item_descriptions;
+ 0x8AFD, // start_of_item_locations;
+
+ // These are spread out all over the code
+ 0, // start_of_system_messages
+ 0xae25, // start of directions
+
+ 0x0d01, // start_of_characters;
+ 0x2701, // start_of_image_data
+ 0, // image_address_offset
+ 43, // number_of_pictures;
+ C64B, // palette
+ 0, // picture_format_version;
+ 0),
+ GameInfo("Adventureland",
+ ADVENTURELAND,
+ NO_TYPE, // type
+ ENGLISH, // subtype
+ THREE_LETTER_UNCOMPRESSED, // dictionary type
+
+ 65, // Number of items
+ 181, // Number of actions
+ 69, // Number of words
+ 33, // Number of rooms
+ 6, // Max carried items
+ 3, // Word length
+ 75, // Number of messages
+
+ 70, // number_of_verbs
+ 69, // number_of_nouns;
+
+ 0x2473, // header
+ EARLY, // header style
+
+ 0x3ebe, // room images
+ FOLLOWS, // item flags
+ 0x3f1f, // item images
+ 0x3f5e, // actions
+ UNCOMPRESSED,
+ 0x4c10, // dictionary
+ 0x4e40, // start_of_room_descriptions;
+ 0x4abe, // start_of_room_connections;
+ 0x52c3, // start_of_messages;
+ FOLLOWS, // start_of_item_descriptions;
+ 0x4b8a, // start_of_item_locations;
+
+ 0x24eb, // start_of_system_messages
+ 0x285e, // start of directions
+
+ 0x631e, // start_of_characters;
+ FOLLOWS, // start_of_image_data
+ -0x3fe5, // image_address_offset
+ 41, // number_of_pictures;
+ ZXOPT, // palette
+ 1, // picture_format_version;
+ 0),
+ GameInfo("Adventureland C64",
+ ADVENTURELAND_C64,
+ NO_TYPE, // type
+ static_cast<Subtype>(ENGLISH | C64), // subtype
+ THREE_LETTER_UNCOMPRESSED, // dictionary type
+
+ 62, // Number of items
+ 170, // Number of actions
+ 69, // Number of words
+ 33, // Number of rooms
+ 6, // Max carried items
+ 3, // Word length
+ 75, // Number of messages
+
+ 70, // number_of_verbs
+ 69, // number_of_nouns;
+
+ 0x4146, // header
+ EARLY, // header style
+
+ 0x6364, // room images
+ 0x85a7, // item flags
+ 0x63c5, // item images
+ 0x4160, // actions
+ UNCOMPRESSED,
+ 0x4c10, // dictionary
+ 0x4e40, // start_of_room_descriptions;
+ FOLLOWS, // start_of_room_connections;
+ FOLLOWS, // start_of_messages;
+ FOLLOWS, // start_of_item_descriptions;
+ 0x6404, // start_of_item_locations;
+
+ 0x188, // start_of_system_messages
+ 0x188, // start of directions
+
+ 0x6482, // start_of_characters;
+ 0x6c82, // start_of_image_data
+ -0x1102, // image_address_offset
+ 41, // number_of_pictures;
+ C64B, // palette
+ 1, // picture_format_version;
+ 0),
+ GameInfo("Secret Mission",
+ SECRET_MISSION,
+ SECRET_MISSION_VARIANT, // type
+ ENGLISH, // subtype
+ THREE_LETTER_UNCOMPRESSED, // dictionary type
+
+ 53, // Number of items
+ 164, // Number of actions
+ 64, // Number of words
+ 23, // Number of rooms
+ 7, // Max carried items
+ 3, // Word length
+ 81, // Number of messages
+
+ 65, // number_of_verbs
+ 65, // number_of_nouns;
+
+ 0x2473, // header
+ EARLY, // header style
+ 0x3f26, // room images
+ FOLLOWS, // item flags
+ 0x3f70, // item images
+ 0x3fa2, // actions
+ UNCOMPRESSED,
+ 0x4af0, // dictionary
+ 0x4cf8, // start_of_room_descriptions;
+ 0x49f2, // start_of_room_connections;
+ 0x4edf, // start_of_messages;
+ FOLLOWS, // start_of_item_descriptions;
+ 0x4a82, // start_of_item_locations;
+
+ 0x24eb, // start_of_system_messages
+ 0x285e, // start of directions
+
+ 0x625d, // start_of_characters;
+ FOLLOWS, // start_of_image_data
+ -0x3fe5, // image_address_offset
+ 44, // number_of_pictures;
+ ZXOPT, // palette
+ 1, // picture_format_version;
+ 0),
+ GameInfo("Secret Mission C64",
+ SECRET_MISSION_C64,
+ SECRET_MISSION_VARIANT, // type
+ static_cast<Subtype>(ENGLISH | C64), // subtype
+ THREE_LETTER_UNCOMPRESSED, // dictionary type
+
+ 49, // Number of items
+ 154, // Number of actions
+ 62, // Number of words
+ 23, // Number of rooms
+ 7, // Max carried items
+ 3, // Word length
+ 81, // Number of messages
+
+ 60, // number_of_verbs
+ 62, // number_of_nouns;
+
+ 0x4572, // header
+ EARLY, // header style
+ 0x458c, // room images
+ 0x6721, // item flags
+ FOLLOWS, // item images
+ 0x45a4, // actions
+ UNCOMPRESSED,
+ 0x4f54, // dictionary
+ 0x5140, // start_of_room_descriptions;
+ FOLLOWS, // start_of_room_connections;
+ FOLLOWS, // start_of_messages;
+ FOLLOWS, // start_of_item_descriptions;
+ 0x6785, // start_of_item_locations;
+
+ 0x2502, // start_of_system_messages
+ 0x2502, // start of directions
+
+ 0x681b, // start_of_characters;
+ 0x701b, // start_of_image_data
+ -0x1CD0, // image_address_offset
+ 44, // number_of_pictures;
+ C64B, // palette
+ 1, // picture_format_version;
+ 0),
+ GameInfo("The Sorcerer of Claymorgue Castle",
+ CLAYMORGUE,
+ NO_TYPE, // type
+ ENGLISH, // subtype
+ FIVE_LETTER_UNCOMPRESSED, // dictionary type
+
+ 75, // Number of items
+ 267, // Number of actions
+ 109, // Number of words
+ 32, // Number of rooms
+ 10, // Max carried items
+ 5, // Word length
+ 79, // Number of messages
+
+ 110, // number_of_verbs
+ 108, // number_of_nouns;
+
+ 0x246c, // header
+ EARLY, // header style
+
+ 0x3bf6, // room images
+ FOLLOWS, // item flags
+ FOLLOWS, // item images
+ FOLLOWS, // actions
+ UNCOMPRESSED,
+ 0x4ecf, // dictionary
+ 0x53f7, // start_of_room_descriptions;
+ 0x4d6f, // start_of_room_connections;
+ 0x5605, // start_of_messages;
+ FOLLOWS, // start_of_item_descriptions;
+ 0x4e35, // start_of_item_locations;
+
+ 0x24e2, // start_of_system_messages
+ 0x2877, // start of directions
+
+ 0x6007, // start_of_characters;
+ 0x6807, // start_of_image_data
+ -0x3fe5, // image_address_offset
+ 37, // number_of_pictures;
+ ZXOPT, // palette
+ 1, // picture_format_version;
+ 0),
+ GameInfo("The Sorcerer of Claymorgue Castle C64",
+ CLAYMORGUE_C64,
+ NO_TYPE, // type
+ static_cast<Subtype>(ENGLISH | C64), // subtype
+ FIVE_LETTER_UNCOMPRESSED, // dictionary type
+
+ 75, // Number of items
+ 265, // Number of actions
+ 108, // Number of words
+ 32, // Number of rooms
+ 10, // Max carried items
+ 5, // Word length
+ 79, // Number of messages
+
+ 110, // number_of_verbs
+ 108, // number_of_nouns;
+
+ 0x47d6, // header
+ EARLY, // header style
+
+ 0x471f, // room images
+ FOLLOWS, // item flags
+ FOLLOWS, // item images
+ 0x47f0, // actions
+ UNCOMPRESSED,
+ 0x5891, // dictionary
+ FOLLOWS, // start_of_room_descriptions;
+ FOLLOWS, // start_of_room_connections;
+ FOLLOWS, // start_of_messages;
+ FOLLOWS, // start_of_item_descriptions;
+ FOLLOWS, // start_of_item_locations;
+
+ 0xa2c, // start_of_system_messages
+ 0xa2c, // start of directions
+
+ 0x6b1a, // start_of_characters;
+ 0x731a, // start_of_image_data
+ 0x6b1a, // image_address_offset
+ 44, // number_of_pictures;
+ C64B, // palette
+ 1, // picture_format_version;
+ 0),
+ GameInfo("Questprobe 2: Spiderman",
+ SPIDERMAN,
+ NO_TYPE, // type
+ ENGLISH, // subtype
+ FOUR_LETTER_UNCOMPRESSED, // dictionary type
+
+ 72, // Number of items
+ 257, // Number of actions
+ 124, // Number of words
+ 40, // Number of rooms
+ 12, // Max carried items
+ 4, // Word length
+ 99, // Number of messages
+
+ 125, // number_of_verbs
+ 125, // number_of_nouns;
+
+ 0x246b, // header
+ EARLY, // header style
+
+ 0x3dd1, // room images
+ FOLLOWS, // item flags
+ FOLLOWS, // item images
+
+ FOLLOWS, // actions
+ UNCOMPRESSED,
+ 0x5036, // dictionary
+ 0x5518, // start_of_room_descriptions;
+ 0x4eac, // start_of_room_connections;
+ 0x575e, // start_of_messages;
+ FOLLOWS, // start_of_item_descriptions;
+ 0x4fa2, // start_of_item_locations;
+
+ 0x2553, // start_of_system_messages
+ 0x28f7, // start of directions
+
+ 0x6296, // start_of_characters;
+ 0x6a96, // start_of_image_data
+ -0x3fe5, // image_address_offset
+ 41, // number_of_pictures;
+ ZXOPT, // palette
+ 2, // picture_format_version;
+ 0),
+ GameInfo("Questprobe 2: Spiderman C64",
+ SPIDERMAN_C64,
+ NO_TYPE, // type
+ static_cast<Subtype>(ENGLISH | C64), // subtype
+ FOUR_LETTER_UNCOMPRESSED, // dictionary type
+
+ 72, // Number of items
+ 245, // Number of actions
+ 124, // Number of words
+ 40, // Number of rooms
+ 12, // Max carried items
+ 4, // Word length
+ 98, // Number of messages
+
+ 118, // number_of_verbs
+ 124, // number_of_nouns;
+
+ 0x4baf, // header
+ EARLY, // header style
+
+ 0x4bc9, // room images
+ 0x70da, // item flags
+ FOLLOWS, // item images
+
+ 0x4bf2, // actions
+ UNCOMPRESSED,
+ 0x5b52, // dictionary
+ 0x600c, // start_of_room_descriptions;
+ FOLLOWS, // start_of_room_connections;
+ FOLLOWS, // start_of_messages;
+ 0x6b9b, // start_of_item_descriptions;
+ FOLLOWS, // start_of_item_locations;
+
+ 0x0a7b, // start_of_system_messages
+ 0x0a7b, // start of directions
+
+ 0x716c, // start_of_characters;
+ 0x796c, // start_of_image_data
+ 0x716c, // image_address_offset
+ 41, // number_of_pictures;
+ C64B, // palette
+ 2, // picture_format_version;
+ 0),
+ GameInfo("Savage Island part I",
+ SAVAGE_ISLAND,
+ SAVAGE_ISLAND_VARIANT, // type
+ ENGLISH, // subtype
+ FOUR_LETTER_UNCOMPRESSED, // dictionary type
+
+ 58, // Number of items
+ 259, // Number of actions
+ 84, // Number of words
+ 34, // Number of rooms
+ 6, // Max carried items
+ 4, // Word length
+ 99, // Number of messages
+
+ 82, // number_of_verbs
+ 84, // number_of_nouns;
+
+ 0x236d, // header
+ LATE, // header style
+ 0x390c, // room images
+ FOLLOWS, // item flags
+ FOLLOWS, // item images
+ 0x39a6, // actions
+ COMPRESSED,
+ 0x4484, // dictionary
+ 0x47c7, // start_of_room_descriptions;
+ FOLLOWS, // start_of_room_connections;
+ 0x4b91, // start_of_messages;
+ FOLLOWS, // start_of_item_descriptions;
+ FOLLOWS, // start_of_item_locations;
+
+ 0x2423, // start_of_system_messages
+ 0x25f3, // start of directions
+
+ 0x570f, // start_of_characters;
+ FOLLOWS, // start_of_image_data
+ 0x600f, // image_address_offset
+ 37, // number_of_pictures;
+ ZXOPT, // palette
+ 3, // picture_format_version;
+ 0),
+ GameInfo("Savage Island part I (C64)",
+ SAVAGE_ISLAND_C64,
+ SAVAGE_ISLAND_VARIANT, // type
+ static_cast<Subtype>(ENGLISH | C64), // subtype
+ FOUR_LETTER_UNCOMPRESSED, // dictionary type
+
+ 58, // Number of items
+ 259, // Number of actions
+ 84, // Number of words
+ 34, // Number of rooms
+ 6, // Max carried items
+ 4, // Word length
+ 99, // Number of messages
+
+ 85, // number_of_verbs
+ 84, // number_of_nouns;
+
+ 0x46dc, // header
+ EARLY, // header style
+ 0x4645, // room images
+ FOLLOWS, // item flags
+ FOLLOWS, // item images
+ 0x46f6, // actions
+ COMPRESSED,
+ 0x51d6, // dictionary
+ 0x5528, // start_of_room_descriptions;
+ FOLLOWS, // start_of_room_connections;
+ FOLLOWS, // start_of_messages;
+ // 0x58f6, // start_of_messages;
+ FOLLOWS, // start_of_item_descriptions;
+ FOLLOWS, // start_of_item_locations;
+
+ 0x0a5f, // start_of_system_messages
+ 0x0a5f, // start of directions
+
+ 0x6A02, // start_of_characters;
+ 0x7202, // start_of_image_data
+ 0x7302, // image_address_offset
+ 37, // number_of_pictures;
+ C64B, // palette
+ 3, // picture_format_version;
+ 0),
+ GameInfo("Savage Island part II",
+ SAVAGE_ISLAND2,
+ NO_TYPE, // type
+ ENGLISH, // subtype
+ FOUR_LETTER_UNCOMPRESSED, // dictionary type
+
+ 48, // Number of items
+ 241, // Number of actions
+ 79, // Number of words
+ 30, // Number of rooms
+ 6, // Max carried items
+ 4, // Word length
+ 95, // Number of messages
+
+ 74, // number_of_verbs
+ 79, // number_of_nouns;
+
+ 0x236d, // header
+ LATE, // header style
+ 0x390c, // room images
+ FOLLOWS, // item flags
+ FOLLOWS, // item images
+ 0x398e, // actions
+ COMPRESSED,
+ 0x43f0, // dictionary
+ 0x46ed, // start_of_room_descriptions;
+ FOLLOWS, // start_of_room_connections;
+ FOLLOWS, // start_of_messages;
+ FOLLOWS, // start_of_item_descriptions;
+ FOLLOWS, // start_of_item_locations;
+
+ 0x2423, // start_of_system_messages
+ 0x25f3, // start of directions
+
+ 0x57d0, // start_of_characters;
+ FOLLOWS, // start_of_image_data
+ 0x60d0, // image_address_offset
+ 21, // number_of_pictures;
+ ZXOPT, // palette
+ 3, // picture_format_version;
+ 0),
+ GameInfo("Savage Island part II (C64)",
+ SAVAGE_ISLAND2_C64,
+ NO_TYPE, // type
+ static_cast<Subtype>(ENGLISH | C64), // subtype
+ FOUR_LETTER_UNCOMPRESSED, // dictionary type
+
+ 48, // Number of items
+ 241, // Number of actions
+ 78, // Number of words
+ 30, // Number of rooms
+ 6, // Max carried items
+ 4, // Word length
+ 94, // Number of messages
+
+ 79, // number_of_verbs
+ 79, // number_of_nouns;
+
+ 0x4654, // header
+ EARLY, // header style
+ 0x6310, // room images
+ FOLLOWS, // item flags
+ FOLLOWS, // item images
+ 0x466e, // actions
+ COMPRESSED,
+ 0x50d2, // dictionary
+ 0x53e8, // start_of_room_descriptions;
+ FOLLOWS, // start_of_room_connections;
+ FOLLOWS, // start_of_messages;
+ FOLLOWS, // start_of_item_descriptions;
+ FOLLOWS, // start_of_item_locations;
+
+ 0x0a50, // start_of_system_messages
+ 0x0a50, // start of directions
+
+ 0x6A02, // start_of_characters;
+ 0x7202, // start_of_image_data
+ 0x7302, // image_address_offset
+ 21, // number_of_pictures;
+ C64B, // palette
+ 3, // picture_format_version;
+ 0),
+ GameInfo("Gremlins",
+ GREMLINS,
+ GREMLINS_VARIANT, // type
+ ENGLISH, // subtype
+ FOUR_LETTER_UNCOMPRESSED, // dictionary type
+
+ 99, // Number of items
+ 236, // Number of actions
+ 126, // Number of words
+ 42, // Number of rooms
+ 6, // Max carried items
+ 4, // Word length
+ 98, // Number of messages
+
+ 115, // number_of_verbs
+ 126, // number_of_nouns;
+
+ 0x2370, // header
+ LATE, // header style
+
+ 0x3a09, // room images
+ FOLLOWS, // item flags
+ FOLLOWS, // item images
+
+ 0x3afe, // actions
+ COMPRESSED, // actions_style;
+ 0x45e5, // dictionary
+ FOLLOWS, // start_of_room_descriptions;
+ FOLLOWS, // start_of_room_connections;
+ FOLLOWS, // start_of_messages;
+ FOLLOWS, // start_of_item_descriptions;
+ FOLLOWS, // start_of_item_locations;
+ 0x2426, // start_of_system_messages
+ 0x25f6, // start of directions
+
+ 0x5be1, // start_of_characters;
+ 0x63e1, // start_of_image_data
+ 0x64e1, // image_address_offset
+ 78, // number_of_pictures;
+ ZXOPT, // palette
+ 3, // picture_format_version;
+ 0),
+ GameInfo("Gremlins (alternative)",
+ GREMLINS_ALT,
+ GREMLINS_VARIANT, // type
+ ENGLISH, // subtype
+ FOUR_LETTER_UNCOMPRESSED, // dictionary type
+
+ 99, // Number of items
+ 236, // Number of actions
+ 126, // Number of words
+ 42, // Number of rooms
+ 6, // Max carried items
+ 4, // Word length
+ 98, // Number of messages
+
+ 115, // number_of_verbs
+ 126, // number_of_nouns;
+
+ 0x2378, // header
+ LATE, // header style
+
+ 0x3a0d, // room images
+ FOLLOWS, // item flags
+ FOLLOWS, // item images
+
+ 0x3b02, // actions
+ COMPRESSED, // actions_style;
+ 0x45e5, // dictionary
+ FOLLOWS, // start_of_room_descriptions;
+ FOLLOWS, // start_of_room_connections;
+ FOLLOWS, // start_of_messages;
+ FOLLOWS, // start_of_item_descriptions;
+ FOLLOWS, // start_of_item_locations;
+ 0x242e, // start_of_system_messages
+ 0x25FE, // start of directions
+
+ 0x5bdd, // start_of_characters;
+ 0x63dd, // start_of_image_data
+ 0x64dd, // image_address_offset
+ 78, // number_of_pictures;
+ ZXOPT, // palette
+ 3, // picture_format_version;
+ 0),
+ GameInfo("Gremlins (German)",
+ GREMLINS_GERMAN,
+ GREMLINS_VARIANT, // type
+ LOCALIZED, // subtype
+ GERMAN, // dictionary type
+
+ 99, // Number of items
+ 236, // Number of actions
+ 126, // Number of words
+ 42, // Number of rooms
+ 6, // Max carried items
+ 5, // Word length
+ 98, // Number of messages
+
+ 115, // number_of_verbs
+ 126, // number_of_nouns
+
+ 0x237d, // header
+ LATE, // header style
+
+ 0x3a07, // room images
+ FOLLOWS, // item flags
+ FOLLOWS, // item images
+
+ 0x3afc, // actions
+ COMPRESSED,
+ 0x45d9, // dictionary
+ FOLLOWS, // start_of_room_descriptions;
+ FOLLOWS, // start_of_room_connections;
+ FOLLOWS, // start_of_messages;
+ FOLLOWS, // start_of_item_descriptions;
+ FOLLOWS, // start_of_item_locations;
+
+ 0x23de, // start_of_system_messages
+ 0x2623, // start of directions
+
+ 0x643e, // start_of_characters
+ 0x6c3e, // start_of_image_data
+ 0x6d3e, // image_address_offset
+ 72, // number_of_pictures;
+ ZX, // palette
+ 3, // picture_format_version;
+ 0),
+ GameInfo("Gremlins C64",
+ GREMLINS_C64,
+ GREMLINS_VARIANT, // type
+ static_cast<Subtype>(ENGLISH | C64), // subtype
+ FOUR_LETTER_UNCOMPRESSED, // dictionary type
+
+ 99, // Number of items
+ 243, // Number of actions
+ 126, // Number of words
+ 42, // Number of rooms
+ 6, // Max carried items
+ 4, // Word length
+ 99, // Number of messages
+
+ 115, // number_of_verbs
+ 126, // number_of_nouns;
+
+ 0x4584, // header
+ GREMLINS_C64_HEADER, // header style
+
+ 0x465e, // room images
+ FOLLOWS, // item flags
+ FOLLOWS, // item images
+
+ 0x4751, // actions
+ COMPRESSED, // actions_style;
+ 0x527f, // dictionary
+ FOLLOWS, // start_of_room_descriptions;
+ FOLLOWS, // start_of_room_connections;
+ FOLLOWS, // start_of_messages;
+ FOLLOWS, // start_of_item_descriptions;
+ 0x4596, // start_of_item_locations;
+ 0x0a5e, // start_of_system_messages
+ 0x0a5e, // start of directions
+
+ 0x6a01, // start_of_characters;
+ 0x7201, // start_of_image_data
+ 0x7301, // image_address_offset
+ 91, // number_of_pictures;
+ C64B, // palette
+ 3, // picture_format_version;
+ 0),
+ GameInfo("Gremlins (German, C64)",
+ GREMLINS_GERMAN_C64,
+ GREMLINS_VARIANT, // type
+ static_cast<Subtype>(LOCALIZED | C64), // subtype
+ FIVE_LETTER_COMPRESSED, // dictionary type
+
+ 99, // Number of items
+ 243, // Number of actions
+ 125, // Number of words
+ 42, // Number of rooms
+ 6, // Max carried items
+ 5, // Word length
+ 98, // Number of messages
+
+ 125, // number_of_verbs
+ 124, // number_of_nouns
+
+ 0x4bb2, // header
+ GREMLINS_C64_HEADER, // header style
+
+ 0x4c8c, // room images
+ FOLLOWS, // item flags
+ FOLLOWS, // item images
+
+ 0x4d81, // actions
+ COMPRESSED,
+ 0x585e, // dictionary
+ FOLLOWS, // start_of_room_descriptions;
+ FOLLOWS, // start_of_room_connections;
+ FOLLOWS, // start_of_messages;
+ FOLLOWS, // start_of_item_descriptions;
+ 0x4bc4, // start_of_item_locations;
+
+ 0x1325, // start_of_system_messages
+ 0x1325, // start of directions
+
+ 0x76c5, // start_of_characters
+ 0x7cfd, // start_of_image_data
+ 0x7db3, // image_address_offset
+ 91, // number_of_pictures;
+ C64B, // palette
+ 3, // picture_format_version;
+ 0),
+ GameInfo("Gremlins (Spanish)",
+ GREMLINS_SPANISH,
+ GREMLINS_VARIANT, // type
+ LOCALIZED, // subtype
+ SPANISH, // dictionary type
+
+ 99, // Number of items
+ 236, // Number of actions
+ 126, // Number of words
+ 42, // Number of rooms
+ 6, // Max carried items
+ 4, // Word length
+ 98, // Number of messages
+
+ 115, // number_of_verbs
+ 126, // number_of_nouns
+
+ 0x23c5, // header
+ LATE, // header style
+
+ 0x3993, // room images
+ FOLLOWS, // item flags
+ FOLLOWS, // item images
+
+ 0x3a88, // actions
+ COMPRESSED,
+ 0x455f, // dictionary
+ FOLLOWS, // start_of_room_descriptions;
+ FOLLOWS, // start_of_room_connections;
+ FOLLOWS, // start_of_messages;
+ FOLLOWS, // start_of_item_descriptions;
+ FOLLOWS, // start_of_item_locations;
+
+ 0x2426, // start_of_system_messages
+ 0x25c4, // start of directions
+
+ 0x6171, // start_of_characters
+ 0x6971, // start_of_image_data
+ 0x6A71, // image_address_offset
+ 74, // number_of_pictures;
+ ZXOPT, // palette
+ 3, // picture_format_version;
+ 0),
+ GameInfo("Supergran",
+ SUPERGRAN,
+ NO_TYPE, // type
+ ENGLISH, // subtype
+ FOUR_LETTER_UNCOMPRESSED, // dictionary type
+
+ 85, // Number of items
+ 204, // Number of actions
+ 105, // Number of words
+ 39, // Number of rooms
+ 6, // Max carried items
+ 4, // Word length
+ 99, // Number of messages
+
+ 101, // number_of_verbs
+ 106, // number_of_nouns;
+
+ 0x236d, // header
+ LATE, // header style
+ 0x38c8, // room images
+ FOLLOWS, // item flags
+ FOLLOWS, // item images
+ 0x399e, // actions
+ COMPRESSED,
+ 0x42fd, // dictionary
+ 0x4708, // start_of_room_descriptions;
+ FOLLOWS, // start_of_room_connections;
+ FOLLOWS, // start_of_messages;
+ FOLLOWS, // start_of_item_descriptions;
+ FOLLOWS, // start_of_item_locations;
+
+ 0x2423, // start_of_system_messages
+ 0x25f3, // start of directions
+
+ 0x5a4e, // start_of_characters;
+ FOLLOWS, // start_of_image_data
+ 0x634e, // image_address_offset
+ 47, // number_of_pictures;
+ ZXOPT, // palette
+ 3, // picture_format_version;
+ 0),
+ GameInfo("Supergran C64",
+ SUPERGRAN_C64,
+ NO_TYPE, // type
+ static_cast<Subtype>(ENGLISH | C64), // subtype
+ FOUR_LETTER_UNCOMPRESSED, // dictionary type
+
+ 85, // Number of items
+ 204, // Number of actions
+ 105, // Number of words
+ 39, // Number of rooms
+ 6, // Max carried items
+ 4, // Word length
+ 99, // Number of messages
+
+ 101, // number_of_verbs
+ 105, // number_of_nouns;
+
+ 0x4624, // header
+ SUPERGRAN_C64_HEADER, // header style
+ 0x4636, // room images
+ FOLLOWS, // item flags
+ FOLLOWS, // item images
+ 0x47b5, // actions
+ COMPRESSED,
+ 0x5119, // dictionary
+ 0x5524, // start_of_room_descriptions;
+ FOLLOWS, // start_of_room_connections;
+ FOLLOWS, // start_of_messages;
+ FOLLOWS, // start_of_item_descriptions;
+ 0x470a, // start_of_item_locations;
+
+ 0x0a53, // start_of_system_messages
+ 0x0a53, // start of directions
+
+ 0x6a02, // start_of_characters;
+ FOLLOWS, // start_of_image_data
+ 0x7302, // image_address_offset
+ 49, // number_of_pictures;
+ C64B, // palette
+ 3, // picture_format_version;
+ 0),
+ GameInfo("Robin of Sherwood",
+ ROBIN_OF_SHERWOOD,
+ SHERWOOD_VARIANT, // type
+ ENGLISH, // subtype
+ FOUR_LETTER_COMPRESSED, // dictionary type
+
+ 87, // Number of items
+ 295, // Number of actions
+ 114, // Number of words
+ 93, // Number of rooms
+ 10, // Max carried items
+ 4, // Word length
+ 98, // Number of messages
+
+ 115, // number_of_verbs
+ 109, // number_of_nouns;
+
+ 0x3b5a, // header
+ LATE, // header style
+
+ 0, // 0x3d99 room images, zero because it needs custom handling
+ 0x3db8, // item flags
+ FOLLOWS, // item images
+
+ 0x409b, // actions
+ COMPRESSED,
+ 0x4dc3, // dictionary
+ 0, // 0x9b53 start_of_room_descriptions, zero because of custom handling
+ 0x3e67, // start_of_room_connections
+ 0x5147, // start_of_messages
+ 0x5d65, // start_of_item_descriptions
+ 0x4d6b, // start_of_item_locations
+
+ 0x250b, // start_of_system_messages
+ 0x26b5, // start of directions
+
+ 0x614f, // start_of_characters
+ 0x66bf, // start_of_image_data
+ 0x6765, // image_address_offset
+ 83, // number_of_pictures
+ ZXOPT, // palette
+ 4, // picture_format_version
+ 0),
+ GameInfo("Robin of Sherwood C64",
+ ROBIN_OF_SHERWOOD_C64,
+ SHERWOOD_VARIANT, // type
+ static_cast<Subtype>(ENGLISH | C64), // subtype
+ FOUR_LETTER_COMPRESSED, // dictionary type
+
+ 87, // Number of items
+ 296, // Number of actions
+ 114, // Number of words
+ 93, // Number of rooms
+ 10, // Max carried items
+ 4, // Word length
+ 98, // Number of messages
+
+ 115, // number_of_verbs
+ 109, // number_of_nouns;
+
+ 0x1f85, // header
+ ROBIN_C64_HEADER, // header style
+
+ 0, // room images, zero because it needs custom handling
+ 0x201c, // item flags
+ FOLLOWS, // item images
+
+ 0x252b, // actions
+ COMPRESSED,
+ 0x320a, // dictionary
+ 0, // 0x9b53 start_of_room_descriptions, zero because of custom handling
+ 0x20cc, // start_of_room_connections
+ 0x358e, // start_of_messages
+ 0x4215, // start_of_item_descriptions
+ 0x1fa5, // start_of_item_locations
+
+ 0x428, // start_of_system_messages
+ 0x428, // start of directions
+
+ 0x45ff, // start_of_characters
+ 0x4b6f, // start_of_image_data
+ 0x4c23, // image_address_offset
+ 90, // number_of_pictures
+ C64B, // palette
+ 4, // picture_format_version
+ 0),
+ GameInfo("Seas of Blood",
+ SEAS_OF_BLOOD,
+ SEAS_OF_BLOOD_VARIANT, // type
+ ENGLISH, // subtype
+ FOUR_LETTER_COMPRESSED, // dictionary type
+
+ 125, // Number of items
+ 344, // Number of actions
+ 134, // Number of words
+ 83, // Number of rooms
+ 10, // Max carried items
+ 4, // Word length
+ 99, // Number of messages
+
+ 69, // number_of_verbs
+ 134, // number_of_nouns;
+
+ 0x494d, // header
+ LATE, // header style
+
+ 0, // no room images
+ 0x4961, // item flags
+ FOLLOWS, // item images
+
+ FOLLOWS, // actions
+ COMPRESSED,
+ 0x591b, // dictionary
+ 0x67cb, // start_of_room_descriptions;
+ 0x5c4b, // start_of_room_connections;
+ 0x5ebb, // start_of_messages;
+ 0x6ce0, // start_of_item_descriptions;
+ 0x5e3d, // start_of_item_locations;
+
+ 0x24ed, // start_of_system_messages
+ 0x26fc, // start of directions
+
+ 0x7389, // start_of_characters;
+ 0x7a89, // start_of_image_data;
+ 0x7b9f, // image_address_offset
+ 139, // number_of_pictures;
+ ZXOPT, // palette
+ 4, // picture_format_version;
+ 0),
+ GameInfo("Seas of Blood C64",
+ SEAS_OF_BLOOD_C64,
+ SEAS_OF_BLOOD_VARIANT, // type
+ static_cast<Subtype>(ENGLISH | C64), // subtype
+ FOUR_LETTER_COMPRESSED, // dictionary type
+
+ 125, // Number of items
+ 347, // Number of actions
+ 134, // Number of words
+ 82, // Number of rooms
+ 10, // Max carried items
+ 4, // Word length
+ 99, // Number of messages
+
+ 69, // number_of_verbs
+ 134, // number_of_nouns;
+
+ 0x527d, // header
+ SEAS_OF_BLOOD_C64_HEADER, // header style
+
+ 0, // no room images
+ 0x5a73, // item flags
+ FOLLOWS, // item images
+
+ 0x5b69, // actions
+ COMPRESSED,
+ 0x6a2d, // dictionary
+ 0x78dd, // start_of_room_descriptions;
+ 0x6d5d, // start_of_room_connections;
+ 0x6fcd, // start_of_messages;
+ 0x7df2, // start_of_item_descriptions;
+ 0x6f4f, // start_of_item_locations;
+
+ 0x0b9d, // start_of_system_messages
+ 0x0b9d, // start of directions
+
+ 0x84a5, // start_of_characters;
+ 0x8ba5, // start_of_image_data;
+ 0x8CBB, // image_address_offset
+ 139, // number_of_pictures;
+ C64B, // palette
+ 4, // picture_format_version;
+ 0)
+};
+
+/* This is supposed to be the original ScottFree system
+ messages in second person, as far as possible */
+const char *g_sysDict[] = {
+ "North",
+ "South",
+ "East",
+ "West",
+ "Up",
+ "Down",
+ "The game is now over. Play again?",
+ "You have stored",
+ "treasures. ",
+ "On a scale of 0 to 100 that rates",
+ "O.K.",
+ "O.K.",
+ "O.K. ",
+ "Well done.\n",
+ "I don't understand ",
+ "You can't do that yet. ",
+ "Huh ? ",
+ "Give me a direction too. ",
+ "You haven't got it. ",
+ "You have it. ",
+ "You don't see it. ",
+ "It is beyond your power to do that. ",
+ "\nDangerous to move in the dark! ",
+ "You fell down and broke your neck. ",
+ "You can't go in that direction. ",
+ "I don't know how to \"",
+ "\" something. ",
+ "I don't know what a \"",
+ "\" is. ",
+ "You can't see. It is too dark!\n",
+ "You are in a ",
+ "\nYou can also see: ",
+ "Obvious exits: ",
+ "You are carrying:\n",
+ "Nothing.\n",
+ "Tell me what to do ? ",
+ "<HIT ENTER>",
+ "Light has run out. ",
+ "Light runs out in",
+ "turns! ",
+ "You are carrying too much. \n",
+ "You're dead. ",
+ "Resume a saved game? ",
+ "None",
+ "There's nothing here to take. ",
+ "You carry nothing. ",
+ "Your light is growing dim. ",
+ ", ",
+ "\n",
+ " - ",
+ "What ?",
+ "yes",
+ "no",
+ "Answer yes or no.\n",
+ "Are you sure? ",
+ "Move undone. ",
+ "Can't undo on first turn. ",
+ "No more undo states stored. ",
+ "Saved. ",
+ "You can't use ALL with that verb. ",
+ "Transcript is now off.\n",
+ "Transcript is now on.\n",
+ "No transcript is currently running.\n",
+ "A transcript is already running.\n",
+ "Failed to create transcript file. ",
+ "Start of transcript\n\n",
+ "\n\nEnd of transcript\n",
+ "BAD DATA! Invalid save file.\n",
+ "State saved.\n",
+ "State restored.\n",
+ "No saved state exists.\n"
+};
+
+/* These are supposed to be the original ScottFree system
+ messages in first person, as far as possible */
+const char *g_sysDictIAm[] = {
+ "North",
+ "South",
+ "East",
+ "West",
+ "Up",
+ "Down",
+ "The game is now over. Play again?",
+ "You have stored",
+ "treasures. ",
+ "On a scale of 0 to 100 that rates",
+ "O.K.",
+ "O.K.",
+ "O.K. ",
+ "Well done.\n",
+ "I don't understand ",
+ "I can't do that yet. ",
+ "Huh ? ",
+ "Give me a direction too.",
+ "I'm not carrying it. ",
+ "I already have it. ",
+ "I don't see it here. ",
+ "It is beyond my power to do that. ",
+ "Dangerous to move in the dark! ",
+ "\nI fell and broke my neck.",
+ "I can't go in that direction. ",
+ "I don't know how to \"",
+ "\" something. ",
+ "I don't know what a \"",
+ "\" is. ",
+ "I can't see. It is too dark!\n",
+ "I'm in a ",
+ "\nI can also see: ",
+ "Obvious exits: ",
+ "I'm carrying: \n",
+ "Nothing.\n",
+ "Tell me what to do ? ",
+ "<HIT ENTER>",
+ "Light has run out. ",
+ "Light runs out in",
+ "turns! ",
+ "I've too much to carry. \n",
+ "I'm dead. ",
+ "Resume a saved game? ",
+ "None",
+ "There's nothing here to take. ",
+ "I have nothing to drop. ",
+ "My light is growing dim. ",
+ nullptr
+};
+
+/* These are supposed to be the original TI-99/4A system
+ messages in first person, as far as possible */
+const char *g_sysDictTI994A[] = {
+ "North",
+ "South",
+ "East",
+ "West",
+ "Up",
+ "Down",
+ "This adventure is over. Play again?",
+ "You have stored",
+ "treasures. ",
+ "On a scale of 0 to 100 that rates",
+ "OK. ",
+ "OK. ",
+ "OK. ",
+ "Well done.\n",
+ "I don't understand the command. ",
+ "I can't do that yet. ",
+ "Huh? ",
+ "Give me a direction too.",
+ "I'm not carrying it. ",
+ "I already have it. ",
+ "I don't see it here. ",
+ "It is beyond my power to do that. ",
+ "Dangerous to move in the dark!\n",
+ "\nI fell down and broke my neck.",
+ "I can't go in that direction. ",
+ "I don't know how to \"",
+ "\" something. ",
+ "I don't know what a \"",
+ "\" is. ",
+ "I can't see. It is too dark!\n",
+ "I am in a ",
+ "\nVisible items are : ",
+ "Obvious exits : ",
+ "I am carrying : ",
+ "Nothing. ",
+ "What shall I do? ",
+ "<HIT ENTER>",
+ "Light went out! ",
+ "Light runs out in",
+ "turns! ",
+ "I am carrying too much.\n",
+ "I'm dead... ",
+ "Resume a saved game? ",
+ "None",
+ "There's nothing here to take. ",
+ "I have nothing to drop. ",
+ "Light is growing dim ",
+ ", ",
+ " ",
+ ", ",
+ nullptr,
+};
+
+const char *g_sysDictZX[] = {
+ "NORTH",
+ "SOUTH",
+ "EAST",
+ "WEST",
+ "UP",
+ "DOWN",
+ "The Adventure is over. Want to try this Adventure again? ",
+ "I've stored",
+ "Treasures. ",
+ "On a scale of 0 to 100 that rates",
+ "Dropped.",
+ "Taken.",
+ "O.K. ",
+ "FANTASTIC! You've solved it ALL! \n",
+ "I must be stupid, but I just don't understand what you mean ",
+ "I can't do that...yet! ",
+ "Huh? ",
+ "I need a direction too. ",
+ "I'm not carrying it. ",
+ "I already have it. ",
+ "I don't see it here. ",
+ "It's beyond my Power to do that. ",
+ "It's dangerous to move in the dark! ",
+ "\nI fell and broke my neck! I'm DEAD! ",
+ "I can't go in that direction. ",
+ "I don't know how to \"",
+ "\" something. ",
+ "I don't know what a \"",
+ "\" is. ",
+ "It's too dark to see!\n",
+ "I am in a ",
+ ". Visible items:\n",
+ "Exits: ",
+ "I'm carrying the following: ",
+ "Nothing at all. ",
+ "---TELL ME WHAT TO DO ? ",
+ "<HIT ENTER> ",
+ "Light has run out. ",
+ "Light runs out in",
+ "turns! ",
+ "I'm carrying too much! Try: TAKE INVENTORY. ",
+ "I'm DEAD!! ",
+ "Restore a previously saved game ? ",
+ "None",
+ "There's nothing here to take. ",
+ "I have nothing to drop. ",
+ "My light is growing dim. ",
+ " ",
+ " ",
+ ". ",
+ "What ? ",
+ nullptr
+};
+
+} // End of namespace Scott
+} // End of namespace Glk
diff --git a/engines/glk/scott/game_info.h b/engines/glk/scott/game_info.h
new file mode 100644
index 00000000000..9c741aa6edd
--- /dev/null
+++ b/engines/glk/scott/game_info.h
@@ -0,0 +1,40 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef GLK_SCOTT_GAMEINFO
+#define GLK_SCOTT_GAMEINFO
+
+#include "glk/scott/definitions.h"
+#include "common/array.h"
+
+namespace Glk {
+namespace Scott {
+
+extern Common::Array<GameInfo> g_games;
+extern const char *g_sysDict[];
+extern const char *g_sysDictIAm[];
+extern const char *g_sysDictZX[];
+extern const char *g_sysDictTI994A[];
+
+} // End of namespace Scott
+} // End of namespace Glk
+
+#endif
Commit: 855e9b1ae604071aaeef125bcc6d790d1059a0bd
https://github.com/scummvm/scummvm/commit/855e9b1ae604071aaeef125bcc6d790d1059a0bd
Author: Avijeet (am388488 at gmail.com)
Date: 2022-04-12T20:35:24-07:00
Commit Message:
GLK: SCOTT: Various enums for game data.
Changed paths:
A engines/glk/scott/definitions.cpp
A engines/glk/scott/definitions.h
diff --git a/engines/glk/scott/definitions.cpp b/engines/glk/scott/definitions.cpp
new file mode 100644
index 00000000000..51fc3829307
--- /dev/null
+++ b/engines/glk/scott/definitions.cpp
@@ -0,0 +1,78 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "glk/scott/definitions.h"
+
+namespace Glk {
+namespace Scott {
+
+GameInfo::GameInfo() {}
+
+GameInfo::GameInfo(
+ Common::String title,
+ GameIDType gameID,
+ GameType type,
+ Subtype subType,
+ DictionaryType dictionary,
+ int numberOfItems,
+ int numberOfActions,
+ int numberOfWords,
+ int numberOfRooms,
+ int maxCarried,
+ int wordLength,
+ int numberOfMessages,
+ int numberOfVerbs,
+ int numberOfNouns,
+ int startOfHeader,
+ HeaderType headerStyle,
+ int startOfRoomImageList,
+ int startOfItemFlags,
+ int startOfItemImageList,
+ int startOfActions,
+ ActionTableType actionsStyle,
+ int startOfDictionary,
+ int startOfRoomDescriptions,
+ int startOfRoomConnections,
+ int startOfMessages,
+ int startOfItemDescriptions,
+ int startOfItemLocations,
+ int startOfSystemMessages,
+ int startOfDirections,
+ int startOfCharacters,
+ int startOfImageData,
+ int imageAddressOffset,
+ int numberOfPictures,
+ PaletteType palette,
+ int pictureFormatVersion,
+ int startOfIntroText)
+ : _title(title), _gameID(gameID), _type(type), _subType(subType), _dictionary(dictionary), _numberOfItems(numberOfItems),
+ _numberOfActions(numberOfActions), _numberOfWords(numberOfWords), _numberOfRooms(numberOfRooms), _maxCarried(maxCarried),
+ _wordLength(wordLength), _numberOfMessages(numberOfMessages), _numberOfVerbs(numberOfVerbs), _numberOfNouns(numberOfNouns),
+ _startOfHeader(startOfHeader), _headerStyle(headerStyle), _startOfRoomImageList(startOfRoomImageList),
+ _startOfItemFlags(startOfItemFlags), _startOfItemImageList(startOfItemImageList), _startOfActions(startOfActions),
+ _actionsStyle(actionsStyle), _startOfDictionary(startOfDictionary), _startOfRoomDescriptions(startOfRoomDescriptions),
+ _startOfRoomConnections(startOfRoomConnections), _startOfMessages(startOfMessages), _startOfItemDescriptions(startOfItemDescriptions),
+ _startOfItemLocations(startOfItemLocations), _startOfSystemMessages(startOfSystemMessages), _startOfDirections(startOfDirections),
+ _startOfCharacters(startOfCharacters), _startOfImageData(startOfImageData), _imageAddressOffset(imageAddressOffset),
+ _numberOfPictures(numberOfPictures), _palette(palette), _pictureFormatVersion(pictureFormatVersion), _startOfIntroText(startOfIntroText) {}
+
+} // End of namespace Scott
+} // End of namespace Glk
diff --git a/engines/glk/scott/definitions.h b/engines/glk/scott/definitions.h
new file mode 100644
index 00000000000..9ee66c56c2c
--- /dev/null
+++ b/engines/glk/scott/definitions.h
@@ -0,0 +1,309 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef GLK_SCOTT_DEFINITIONS
+#define GLK_SCOTT_DEFINITIONS
+
+#include "common/str.h"
+
+namespace Glk {
+namespace Scott {
+
+const int FOLLOWS = 0xFFFF;
+
+const int GO = 1;
+const int TAKE = 10;
+const int DROP = 18;
+
+const int LASTALL = 128;
+
+enum GameIDType {
+ UNKNOWN_GAME,
+ SCOTTFREE,
+ TI994A,
+ PIRATE,
+ VOODOO,
+ STRANGE,
+ BANZAI,
+ BATON,
+ BATON_C64,
+ TIME_MACHINE,
+ TIME_MACHINE_C64,
+ ARROW1,
+ ARROW1_C64,
+ ARROW2,
+ ARROW2_C64,
+ PULSAR7,
+ PULSAR7_C64,
+ CIRCUS,
+ CIRCUS_C64,
+ FEASIBILITY,
+ FEASIBILITY_C64,
+ AKYRZ,
+ AKYRZ_C64,
+ PERSEUS,
+ PERSEUS_C64,
+ PERSEUS_ITALIAN,
+ INDIANS,
+ INDIANS_C64,
+ WAXWORKS,
+ WAXWORKS_C64,
+ HULK,
+ HULK_C64,
+ ADVENTURELAND,
+ ADVENTURELAND_C64,
+ SECRET_MISSION,
+ SECRET_MISSION_C64,
+ CLAYMORGUE,
+ CLAYMORGUE_C64,
+ SPIDERMAN,
+ SPIDERMAN_C64,
+ SAVAGE_ISLAND,
+ SAVAGE_ISLAND_C64,
+ SAVAGE_ISLAND2,
+ SAVAGE_ISLAND2_C64,
+ GREMLINS,
+ GREMLINS_ALT,
+ GREMLINS_C64,
+ GREMLINS_GERMAN,
+ GREMLINS_GERMAN_C64,
+ GREMLINS_SPANISH,
+ SUPERGRAN,
+ SUPERGRAN_C64,
+ ROBIN_OF_SHERWOOD,
+ ROBIN_OF_SHERWOOD_C64,
+ SEAS_OF_BLOOD,
+ SEAS_OF_BLOOD_C64,
+ NUMGAMES
+};
+
+enum ExplicitResultType {
+ ER_NO_RESULT,
+ ER_SUCCESS = 0,
+ ER_RAN_ALL_LINES_NO_MATCH = -1,
+ ER_RAN_ALL_LINES = -2
+};
+
+enum ActionResultType {
+ ACT_SUCCESS = 0,
+ ACT_FAILURE = 1,
+ ACT_CONTINUE,
+ ACT_GAMEOVER
+};
+
+enum SysMessageType {
+ NORTH,
+ SOUTH,
+ EAST,
+ WEST,
+ UP,
+ DOWN,
+ PLAY_AGAIN,
+ IVE_STORED,
+ TREASURES,
+ ON_A_SCALE_THAT_RATES,
+ DROPPED,
+ TAKEN,
+ OK,
+ YOUVE_SOLVED_IT,
+ I_DONT_UNDERSTAND,
+ YOU_CANT_DO_THAT_YET,
+ HUH,
+ DIRECTION,
+ YOU_HAVENT_GOT_IT,
+ YOU_HAVE_IT,
+ YOU_DONT_SEE_IT,
+ THATS_BEYOND_MY_POWER,
+ DANGEROUS_TO_MOVE_IN_DARK,
+ YOU_FELL_AND_BROKE_YOUR_NECK,
+ YOU_CANT_GO_THAT_WAY,
+ I_DONT_KNOW_HOW_TO,
+ SOMETHING,
+ I_DONT_KNOW_WHAT_A,
+ IS,
+ TOO_DARK_TO_SEE,
+ YOU_ARE,
+ YOU_SEE,
+ EXITS,
+ INVENTORY,
+ NOTHING,
+ WHAT_NOW,
+ HIT_ENTER,
+ LIGHT_HAS_RUN_OUT,
+ LIGHT_RUNS_OUT_IN,
+ TURNS,
+ YOURE_CARRYING_TOO_MUCH,
+ IM_DEAD,
+ RESUME_A_SAVED_GAME,
+ NONE,
+ NOTHING_HERE_TO_TAKE,
+ YOU_HAVE_NOTHING,
+ LIGHT_GROWING_DIM,
+ EXITS_DELIMITER,
+ MESSAGE_DELIMITER,
+ ITEM_DELIMITER,
+ WHAT,
+ YES,
+ NO,
+ ANSWER_YES_OR_NO,
+ ARE_YOU_SURE,
+ MOVE_UNDONE,
+ CANT_UNDO_ON_FIRST_TURN,
+ NO_UNDO_STATES,
+ SAVED,
+ CANT_USE_ALL,
+ TRANSCRIPT_OFF,
+ TRANSCRIPT_ON,
+ NO_TRANSCRIPT,
+ TRANSCRIPT_ALREADY,
+ FAILED_TRANSCRIPT,
+ TRANSCRIPT_START,
+ TRANSCRIPT_END,
+ BAD_DATA,
+ STATE_SAVED,
+ STATE_RESTORED,
+ NO_SAVED_STATE,
+ LAST_SYSTEM_MESSAGE
+};
+
+const SysMessageType MAX_SYSMESS = LAST_SYSTEM_MESSAGE;
+
+enum DictionaryType {
+ NOT_A_GAME,
+ FOUR_LETTER_UNCOMPRESSED,
+ THREE_LETTER_UNCOMPRESSED,
+ FIVE_LETTER_UNCOMPRESSED,
+ FOUR_LETTER_COMPRESSED,
+ FIVE_LETTER_COMPRESSED,
+ GERMAN,
+ SPANISH,
+ ITALIAN
+};
+
+enum GameType {
+ NO_TYPE,
+ GREMLINS_VARIANT,
+ SHERWOOD_VARIANT,
+ SAVAGE_ISLAND_VARIANT,
+ SECRET_MISSION_VARIANT,
+ SEAS_OF_BLOOD_VARIANT,
+ OLD_STYLE,
+};
+
+enum Subtype {
+ ENGLISH = 0x1,
+ MYSTERIOUS = 0x2,
+ LOCALIZED = 0x4,
+ C64 = 0x8
+};
+
+enum PaletteType {
+ NO_PALETTE,
+ ZX,
+ ZXOPT,
+ C64A,
+ C64B,
+ VGA
+};
+
+enum HeaderType {
+ NO_HEADER,
+ EARLY,
+ LATE,
+ HULK_HEADER,
+ GREMLINS_C64_HEADER,
+ ROBIN_C64_HEADER,
+ SUPERGRAN_C64_HEADER,
+ SEAS_OF_BLOOD_C64_HEADER,
+ MYSTERIOUS_C64_HEADER,
+ ARROW_OF_DEATH_PT_2_C64_HEADER,
+ INDIANS_C64_HEADER
+};
+
+enum ActionTableType {
+ UNKNOWN_ACTIONS_TYPE,
+ COMPRESSED,
+ UNCOMPRESSED,
+ HULK_ACTIONS
+};
+
+struct GameInfo {
+ GameInfo();
+ GameInfo(Common::String title, GameIDType gameID, GameType type, Subtype subType, DictionaryType dictionary,
+ int numberOfItems, int numberOfActions, int numberOfWords, int numberOfRooms, int maxCarried,
+ int wordLength, int numberOfMessages, int numberOfVerbs, int numberOfNouns, int startOfHeader,
+ HeaderType headerStyle, int startOfRoomImageList, int startOfItemFlags, int startOfItemImageList,
+ int startOfActions, ActionTableType actionsStyle, int startOfDictionary, int startOfRoomDescriptions,
+ int startOfRoomConnections, int startOfMessages, int startOfItemDescriptions, int startOfItemLocations,
+ int startOfSystemMessages, int startOfDirections, int startOfCharacters, int startOfImageData,
+ int imageAddressOffset, int numberOfPictures, PaletteType palette, int pictureFormatVersion,
+ int startOfIntroText);
+ Common::String _title;
+
+ GameIDType _gameID = UNKNOWN_GAME;
+ GameType _type = NO_TYPE;
+ Subtype _subType = ENGLISH;
+ DictionaryType _dictionary = NOT_A_GAME;
+
+ int _numberOfItems = 0;
+ int _numberOfActions = 0;
+ int _numberOfWords = 0;
+ int _numberOfRooms = 0;
+ int _maxCarried = 0;
+ int _wordLength = 0;
+ int _numberOfMessages = 0;
+
+ int _numberOfVerbs = 0;
+ int _numberOfNouns = 0;
+
+ int _startOfHeader = 0;
+ HeaderType _headerStyle = NO_HEADER;
+
+ int _startOfRoomImageList = 0;
+ int _startOfItemFlags = 0;
+ int _startOfItemImageList = 0;
+
+ int _startOfActions = 0;
+ ActionTableType _actionsStyle = UNKNOWN_ACTIONS_TYPE;
+ int _startOfDictionary = 0;
+ int _startOfRoomDescriptions = 0;
+ int _startOfRoomConnections = 0;
+ int _startOfMessages = 0;
+ int _startOfItemDescriptions = 0;
+ int _startOfItemLocations = 0;
+
+ int _startOfSystemMessages = 0;
+ int _startOfDirections = 0;
+
+ int _startOfCharacters = 0;
+ int _startOfImageData = 0;
+ int _imageAddressOffset = 0; /* This is the difference between the value given by
+ the image data lookup table and a usable file
+ offset */
+ int _numberOfPictures = 0;
+ PaletteType _palette = NO_PALETTE;
+ int _pictureFormatVersion = 0;
+ int _startOfIntroText = 0;
+};
+
+} // End of namespace Scott
+} // End of namespace Glk
+#endif /* definitions_h */
Commit: 9cae726fcaa2ff630e87c1367162006dd5ca73fa
https://github.com/scummvm/scummvm/commit/9cae726fcaa2ff630e87c1367162006dd5ca73fa
Author: Avijeet (am388488 at gmail.com)
Date: 2022-04-12T20:35:24-07:00
Commit Message:
GLK: SCOTT: Support .z80 files.
Changed paths:
engines/glk/scott/detection.cpp
diff --git a/engines/glk/scott/detection.cpp b/engines/glk/scott/detection.cpp
index 3efc4dbdbc9..d936d316b64 100644
--- a/engines/glk/scott/detection.cpp
+++ b/engines/glk/scott/detection.cpp
@@ -44,7 +44,7 @@ GameDescriptor ScottMetaEngine::findGame(const char *gameId) {
}
bool ScottMetaEngine::detectGames(const Common::FSList &fslist, DetectedGames &gameList) {
- const char *const EXTENSIONS[] = { ".saga", ".dat", nullptr };
+ const char *const EXTENSIONS[] = { ".z80", ".saga", ".dat", nullptr };
// Loop through the files of the folder
for (Common::FSList::const_iterator file = fslist.begin(); file != fslist.end(); ++file) {
Commit: f994bd285906037cbacba9af68a09c01572b7b17
https://github.com/scummvm/scummvm/commit/f994bd285906037cbacba9af68a09c01572b7b17
Author: Avijeet (am388488 at gmail.com)
Date: 2022-04-12T20:35:24-07:00
Commit Message:
GLK: SCOTT: Add detection for 11 Mysterious Adventures.
Changed paths:
engines/glk/scott/detection_tables.h
diff --git a/engines/glk/scott/detection_tables.h b/engines/glk/scott/detection_tables.h
index 0f26b55bb92..eb682232955 100644
--- a/engines/glk/scott/detection_tables.h
+++ b/engines/glk/scott/detection_tables.h
@@ -147,6 +147,19 @@ const GlkDetectionEntry SCOTT_GAMES[] = {
DT_ENTRY0("romulanadv", "d97b5cb5ed66eb276ef9f1c1bae0b8dd", 13959),
DT_ENTRY0("topsecretadv", "effb411e74dfe3a8d69b57b9bc3a2cef", 15575),
+ //ZX Spectrum games
+ //11 Mysterious Adventures
+ DT_ENTRY1("goldenbaton", "ZXSpectrum", "cb7dadc9d5f8bce453b9139265e4dd7d", 32060),
+ DT_ENTRY1("timemachine", "ZXSpectrum", "b22d1f4d46c99ff4443d541d3fe424c1", 30928),
+ DT_ENTRY1("arrowofdeath1", "ZXSpectrum", "3a5c3f4079c1c0347f03420db8ad4596", 34105),
+ DT_ENTRY1("arrowofdeath2", "ZXSpectrum", "d3f8943c4f5f71ce00139065055a72ee", 38043),
+ DT_ENTRY1("pulsar7", "ZXSpectrum", "441edd90fc7f9ff39a5eebe035a974e9", 29961),
+ DT_ENTRY1("circus", "ZXSpectrum", "ed99306a2fb23bf6579068a4d74034ee", 27746),
+ DT_ENTRY1("feasibility", "ZXSpectrum", "5e381e83f15d77e3542be4a4cffc8e25", 37395),
+ DT_ENTRY1("akyrz", "ZXSpectrum", "b0f8676817475753f1edd7f1eeea31fb", 33753),
+ DT_ENTRY1("perseus", "ZXSpectrum", "84d5fbb16a37e495abf09d191fd8b1a2", 31504),
+ DT_ENTRY1("10indians", "ZXSpectrum", "afde056c152de79ea20453c42a2d08af", 31664),
+ DT_ENTRY1("waxworks11", "ZXSpectrum", "6c6fbbbb50032463a6ea71c6750ea1f5", 32662),
DT_END_MARKER
};
Commit: faa4ae472dfee8bd537c5a85fcd0e11e2950376c
https://github.com/scummvm/scummvm/commit/faa4ae472dfee8bd537c5a85fcd0e11e2950376c
Author: Avijeet (am388488 at gmail.com)
Date: 2022-04-12T20:35:24-07:00
Commit Message:
GLK: SCOTT: Class for holding global data.
Changed paths:
A engines/glk/scott/globals.cpp
A engines/glk/scott/globals.h
diff --git a/engines/glk/scott/globals.cpp b/engines/glk/scott/globals.cpp
new file mode 100644
index 00000000000..22df5b1659c
--- /dev/null
+++ b/engines/glk/scott/globals.cpp
@@ -0,0 +1,74 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "glk/scott/globals.h"
+#include "glk/scott/scott.h"
+#include "glk/scott/command_parser.h"
+#include "glk/scott/line_drawing.h"
+#include "glk/scott/saga_draw.h"
+#include "common/algorithm.h"
+
+namespace Glk {
+namespace Scott {
+
+Globals *g_globals;
+
+Globals::Globals() : _sys(MAX_SYSMESS), _directions(NUMBER_OF_DIRECTIONS), _extraNouns(NUMBER_OF_EXTRA_NOUNS),
+ _skipList(NUMBER_OF_SKIPPABLE_WORDS), _delimiterList(NUMBER_OF_DELIMITERS), _systemMessages(60),
+ _vectorState(NO_VECTOR_IMAGE) {
+
+ g_globals = this;
+
+ _gameHeader = new Header;
+
+ _englishDirections = {nullptr, "north", "south", "east", "west", "up", "down", "n", "s", "e", "w", "u", "d", " "};
+
+ _extraCommands = {nullptr, "restart", "save", "restore", "load", "transcript", "script", "oops", "undo", "ram",
+ "ramload", "ramrestore", "ramsave", "except", "but", " ", " ", " ", " ", " "};
+
+ _extraCommandsKey = {NO_COMMAND, RESTART, SAVE, RESTORE, RESTORE, SCRIPT, SCRIPT, UNDO, UNDO, RAM,
+ RAMLOAD, RAMLOAD, RAMSAVE, EXCEPT, EXCEPT, RESTORE, RESTORE, SCRIPT, UNDO, RESTART};
+
+ _englishExtraNouns = {nullptr, "game", "story", "on", "off", "load", "restore", "save", "move",
+ "command", "turn", "all", "everything", "it", " ", " "};
+
+ _extraNounsKey = {NO_COMMAND, GAME, GAME, ON, OFF, RAMLOAD, RAMLOAD, RAMSAVE, COMMAND, COMMAND, COMMAND,
+ ALL, ALL, IT, ON, OFF};
+
+ _abbreviations = {nullptr, "i", "l", "x", "z", "q"};
+
+ _abbreviationsKey = {nullptr, "inventory", "look", "examine", "wait", "quit"};
+
+ _englishSkipList = {nullptr, "at", "to", "in", "into", "the", "a", "an", "my", "quickly",
+ "carefully", "quietly", "slowly", "violently", "fast", "hard", "now", "room"};
+
+ _englishDelimiterList = {nullptr, ",", "and", "then", " "};
+
+ Common::fill(&_counters[0], &_counters[16], 0);
+ Common::fill(&_roomSaved[0], &_roomSaved[16], 0);
+}
+
+Globals::~Globals() {
+ delete _gameHeader;
+}
+
+} // End of namespace Scott
+} // End of namespace Glk
diff --git a/engines/glk/scott/globals.h b/engines/glk/scott/globals.h
new file mode 100644
index 00000000000..7a6fe86073c
--- /dev/null
+++ b/engines/glk/scott/globals.h
@@ -0,0 +1,162 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef GLK_SCOTT_GLOBALS
+#define GLK_SCOTT_GLOBALS
+
+#include "glk/glk_types.h"
+#include "glk/windows.h"
+#include "glk/scott/definitions.h"
+#include "common/array.h"
+#include "common/str-array.h"
+#include <cstdint>
+
+namespace Glk {
+namespace Scott {
+
+struct Command;
+enum ExtraCommand : int;
+struct LineImage;
+enum VectorStateType : int;
+
+struct PixelToDraw;
+struct Image;
+
+typedef uint8_t RGB[3];
+typedef RGB PALETTE[16];
+
+struct Header;
+struct Item;
+struct Room;
+struct Action;
+
+struct SavedState;
+
+class Globals {
+public:
+ // scott
+ Header *_gameHeader;
+ Common::Array<Item> _items;
+ Common::Array<Room> _rooms;
+ Common::StringArray _verbs;
+ Common::StringArray _nouns;
+ Common::StringArray _messages;
+ Common::Array<Action> _actions;
+ Common::StringArray _sys;
+ Common::StringArray _systemMessages;
+ GameInfo *_game = nullptr;
+ winid_t _graphics = nullptr;
+ uint8_t *_entireFile = nullptr;
+ size_t _fileLength = 0;
+ strid_t _roomDescriptionStream = nullptr;
+ int _fileBaselineOffset = 0;
+ int _header[24];
+ int _lightRefill = 0;
+ winid_t _bottomWindow = nullptr, _topWindow = nullptr;
+ Command *_currentCommand = nullptr;
+ int _justStarted = 1;
+ int _shouldRestart = 0;
+ int _stopTime = 0;
+ strid_t _transcript = nullptr;
+ int _counters[16]; ///< Range unknown
+ int _currentCounter = 0;
+ int _savedRoom = 0;
+ int _roomSaved[16]; ///< Range unknown
+ uint32 _bitFlags = 0; ///< Might be >32 flags - I haven't seen >32 yet
+ int _autoInventory = 0;
+
+ // sagadraw
+ int _drawToBuffer = 0;
+ uint8_t _sprite[256][8];
+ uint8_t _screenchars[768][8];
+ uint8_t _buffer[384][9];
+ Common::Array<Image> _images;
+ int _pixelSize = 0;
+ int _xOffset = 0;
+ PALETTE _pal;
+ int _whiteColour = 15;
+ int _blueColour = 9;
+ glui32 _diceColour = 0xFF0000;
+ int32_t _errorCount = 0;
+ PaletteType _palChosen = NO_PALETTE;
+ size_t _hulkCoordinates = 0x26DB;
+ size_t _hulkItemImageOffsets = 0x2798;
+ size_t _hulkLookImageOffsets = 0x27BC;
+ size_t _hulkSpecialImageOffsets = 0x276E;
+ size_t _hulkImageOffset = 0x441B;
+
+ // line_drawing
+ Common::Array<LineImage> _lineImages;
+ VectorStateType _vectorState;
+ PixelToDraw **_pixelsToDraw = nullptr;
+ int _totalDrawInstructions = 0;
+ int _currentDrawInstruction = 0;
+ int _vectorImageShown = -1;
+ uint8_t *_pictureBitmap = nullptr;
+ int _lineColour = 15;
+ int _bgColour = 0;
+ int _scottGraphicsWidth = 255;
+ int _scottGraphicsHeight = 94;
+
+ // connect
+ int _gliSlowDraw = 0;
+
+ // parser
+ int _lastNoun = 0;
+ glui32 *_firstErrorMessage = nullptr;
+ glui32 **_unicodeWords = nullptr;
+ char **_charWords = nullptr;
+ int _wordsInInput = 0;
+ Common::StringArray _directions;
+ Common::StringArray _englishDirections;
+ Common::StringArray _skipList;
+ Common::StringArray _englishSkipList;
+ Common::StringArray _delimiterList;
+ Common::StringArray _englishDelimiterList;
+ Common::StringArray _extraCommands;
+ Common::StringArray _extraNouns;
+ Common::StringArray _englishExtraNouns;
+ Common::Array<ExtraCommand> _extraNounsKey;
+ Common::Array<ExtraCommand> _extraCommandsKey;
+ Common::StringArray _abbreviations;
+ Common::StringArray _abbreviationsKey;
+
+ //restore state
+ int _justUndid = 0;
+ SavedState *_initialState = nullptr;
+ SavedState *_ramSave = nullptr;
+ SavedState *_lastUndo = nullptr;
+ SavedState *_oldestUndo = nullptr;
+ int _numberOfUndos = 0;
+
+public:
+ Globals();
+ ~Globals();
+};
+
+extern Globals *g_globals;
+
+#define _G(FIELD) (g_globals->FIELD)
+
+} // End of namespace Scott
+} // End of namespace Glk
+
+#endif
Commit: 7f84f25804f70fe135f4df2a8bcea8c39a84ff57
https://github.com/scummvm/scummvm/commit/7f84f25804f70fe135f4df2a8bcea8c39a84ff57
Author: Avijeet (am388488 at gmail.com)
Date: 2022-04-12T20:35:24-07:00
Commit Message:
GLK: SCOTT: Add Questprobe: The Hulk specific functions.
Changed paths:
A engines/glk/scott/hulk.cpp
A engines/glk/scott/hulk.h
diff --git a/engines/glk/scott/hulk.cpp b/engines/glk/scott/hulk.cpp
new file mode 100644
index 00000000000..580b463e353
--- /dev/null
+++ b/engines/glk/scott/hulk.cpp
@@ -0,0 +1,409 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "glk/scott/hulk.h"
+#include "glk/scott/definitions.h"
+#include "glk/scott/detect_game.h"
+#include "glk/scott/globals.h"
+#include "glk/scott/scott.h"
+
+namespace Glk {
+namespace Scott {
+
+void hulkShowImageOnExamine(int noun) {
+ int image = 0;
+ switch (noun) {
+ case 55: // Dome
+ if (_G(_items)[11]._location == MY_LOC)
+ image = 28;
+ break;
+ case 108: // Natter energy egg
+ if (_G(_items)[17]._location == MY_LOC || _G(_items)[17]._location == CARRIED)
+ image = 30;
+ break;
+ case 124: // Bio-Gem
+ case 41:
+ if (_G(_items)[18]._location == MY_LOC || _G(_items)[18]._location == CARRIED)
+ image = 29;
+ break;
+ case 21: // Killer Bees
+ if (_G(_items)[24]._location == MY_LOC)
+ image = 31;
+ break;
+ case 83: // Iron ring
+ if (_G(_items)[33]._location == MY_LOC)
+ image = 32;
+ break;
+ case 121: // Cage
+ if (_G(_items)[47]._location == MY_LOC)
+ image = 33;
+ break;
+ default:
+ break;
+ }
+ if (image) {
+ g_scott->drawImage(image);
+ g_scott->output(_G(_sys)[HIT_ENTER]);
+ g_scott->hitEnter();
+ }
+}
+
+void hulkLook() {
+ g_scott->drawImage(_G(_rooms)[MY_LOC]._image);
+ for (int ct = 0; ct <= _G(_gameHeader)->_numItems; ct++) {
+ int image = _G(_items)[ct]._image;
+ if (_G(_items)[ct]._location == MY_LOC && image != 255) {
+ /* Don't draw bio gem in fuzzy area */
+ if ((ct == 18 && MY_LOC != 15) ||
+ /* Don't draw Dr. Strange until outlet is plugged */
+ (ct == 26 && _G(_items)[28]._location != MY_LOC))
+ continue;
+ g_scott->drawImage(image);
+ }
+ }
+}
+
+void drawHulkImage(int p) {
+ int image = 0;
+ switch (p) {
+ case 85:
+ image = 34;
+ break;
+ case 86:
+ image = 35;
+ break;
+ case 83:
+ image = 36;
+ break;
+ case 84:
+ image = 37;
+ break;
+ case 87:
+ image = 38;
+ break;
+ case 88:
+ image = 39;
+ break;
+ case 89:
+ image = 40;
+ break;
+ case 82:
+ image = 41;
+ break;
+ case 81:
+ image = 42;
+ break;
+ default:
+ error("Unhandled image number %d!\n", p);
+ break;
+ }
+
+ if (image != 0) {
+ g_scott->drawImage(image);
+ g_scott->output(_G(_sys)[HIT_ENTER]);
+ g_scott->hitEnter();
+ }
+}
+
+uint8_t *readHulkDictionary(GameInfo info, uint8_t **pointer) {
+ uint8_t *ptr = *pointer;
+ char *dictword = new char[info._wordLength + 2];
+ char c = 0;
+ int wordnum = 0;
+ int charindex = 0;
+
+ int nv = info._numberOfVerbs;
+ int nn = info._numberOfNouns;
+
+ for (int i = 0; i < nn - nv; i++)
+ _G(_verbs)[nv + i] = ".\0";
+
+ for (int i = 0; i < nv - nn; i++)
+ _G(_nouns)[nn + i] = ".\0";
+
+ do {
+ for (int i = 0; i < info._wordLength; i++) {
+ c = *(ptr++);
+ if (c == 0) {
+ if (charindex == 0) {
+ c = *(ptr++);
+ }
+ }
+ dictword[charindex] = c;
+ if (c == '*')
+ i--;
+ charindex++;
+
+ dictword[charindex] = 0;
+ }
+
+ if (wordnum < nn) {
+ _G(_nouns)[wordnum] = Common::String(dictword, charindex + 1);
+ } else {
+ _G(_verbs)[wordnum - nn] = Common::String(dictword, charindex + 1);
+ }
+ wordnum++;
+
+ if (c != 0 && !isascii(c))
+ return ptr;
+
+ charindex = 0;
+ } while (wordnum <= nv + nn);
+
+ delete[] dictword;
+ return ptr;
+}
+
+int tryLoadingHulk(GameInfo info, int dictStart) {
+ int ni, na, nw, nr, mc, pr, tr, wl, lt, mn, trm;
+ int ct;
+
+ Action *ap;
+ Room *rp;
+ Item *ip;
+
+ /* Load the header */
+ uint8_t *ptr = _G(_entireFile);
+
+ _G(_fileBaselineOffset) = dictStart - info._startOfDictionary - 645;
+
+ int offset = info._startOfHeader + _G(_fileBaselineOffset);
+ ptr = seekToPos(_G(_entireFile), offset);
+
+ if (ptr == 0)
+ return 0;
+
+ readHeader(ptr);
+
+ parseHeader(_G(_header), info._headerStyle, &ni, &na, &nw, &nr, &mc, &pr, &tr, &wl, <, &mn, &trm);
+
+ _G(_gameHeader)->_numItems = ni;
+ _G(_items).resize(ni + 1);
+ _G(_gameHeader)->_numActions = na;
+ _G(_actions).resize(na + 1);
+ _G(_gameHeader)->_numWords = nw;
+ _G(_gameHeader)->_wordLength = wl;
+ _G(_verbs).resize(nw + 1);
+ _G(_nouns).resize(nw + 1);
+ _G(_gameHeader)->_numRooms = nr;
+ _G(_rooms).resize(nr + 1);
+ _G(_gameHeader)->_maxCarry = mc;
+ _G(_gameHeader)->_playerRoom = pr;
+ _G(_gameHeader)->_treasures = tr;
+ _G(_gameHeader)->_lightTime = lt;
+ _G(_lightRefill) = lt;
+ _G(_gameHeader)->_numMessages = mn;
+ _G(_messages).resize(mn + 1);
+ _G(_gameHeader)->_treasureRoom = trm;
+
+ if (_G(_header)[0] != info._wordLength || _G(_header)[1] != info._numberOfWords || _G(_header)[2] != info._numberOfActions || _G(_header)[3] != info._numberOfItems || _G(_header)[4] != info._numberOfMessages || _G(_header)[5] != info._numberOfRooms || _G(_header)[6] != info._maxCarried) {
+ return 0;
+ }
+
+#pragma mark Dictionary
+
+ if (seekIfNeeded(info._startOfDictionary, &offset, &ptr) == 0)
+ return 0;
+
+ readHulkDictionary(info, &ptr);
+
+#pragma mark Rooms
+
+ if (seekIfNeeded(info._startOfRoomDescriptions, &offset, &ptr) == 0)
+ return 0;
+
+ ct = 0;
+
+ uint8_t string_length = 0;
+ do {
+ rp = &_G(_rooms)[ct];
+ string_length = *(ptr++);
+ if (string_length == 0) {
+ rp->_text = ".\0";
+ } else {
+ for (int i = 0; i < string_length; i++) {
+ rp->_text += *(ptr++);
+ }
+ }
+ ct++;
+ } while (ct < nr + 1);
+
+#pragma mark Messages
+
+ ct = 0;
+ const char *string;
+
+ do {
+ string_length = *(ptr++);
+ if (string_length == 0) {
+ string = ".\0";
+ _G(_messages)[ct] = string;
+ } else {
+ char *s = new char[string_length + 1];
+ for (int i = 0; i < string_length; i++) {
+ s[i] = *(ptr++);
+ }
+ s[string_length] = 0;
+ _G(_messages)[ct] = s;
+ }
+ ct++;
+ } while (ct < mn + 1);
+
+#pragma mark Items
+
+ if (seekIfNeeded(info._startOfItemDescriptions, &offset, &ptr) == 0)
+ return 0;
+
+ ct = 0;
+
+ do {
+ ip = &_G(_items)[ct];
+ string_length = *(ptr++);
+ if (string_length == 0) {
+ ip->_text = ".\0";
+ } else {
+ for (int i = 0; i < string_length; i++) {
+ ip->_text += *(ptr++);
+ }
+ const char *p = strchr(ip->_text.c_str(), '/');
+ if (p) {
+ ip->_autoGet = Common::String(p);
+
+ /* Some games use // to mean no auto get/drop word! */
+ if (!(ip->_autoGet == "//") && !(ip->_autoGet == "/*")) {
+ ip->_text = Common::String(ip->_text.c_str(), p);
+ ip->_autoGet.deleteChar(0);
+
+ const char *t = strchr(ip->_autoGet.c_str(), '/');
+ if (t) {
+ ip->_autoGet = Common::String(ip->_autoGet.c_str(), t);
+ }
+ ptr++;
+ }
+ }
+ }
+
+ ct++;
+ } while (ct < ni + 1);
+
+#pragma mark Room connections
+
+ if (seekIfNeeded(info._startOfRoomConnections, &offset, &ptr) == 0)
+ return 0;
+
+ /* The room connections are ordered by direction, not room, so all the North
+ * connections for all the rooms come first, then the South connections, and
+ * so on. */
+ for (int j = 0; j < 6; j++) {
+ ct = 0;
+
+ while (ct < nr + 1) {
+ rp = &_G(_rooms)[ct];
+ rp->_exits[j] = *(ptr++);
+ ptr++;
+ ct++;
+ }
+ }
+
+#pragma mark item locations
+
+ if (seekIfNeeded(info._startOfItemLocations, &offset, &ptr) == 0)
+ return 0;
+
+ ct = 0;
+ while (ct < ni + 1) {
+ ip = &_G(_items)[ct];
+ ip->_location = *(ptr++);
+ ip->_location += *(ptr++) * 0x100;
+ ip->_initialLoc = ip->_location;
+ ct++;
+ }
+
+#pragma mark room images
+
+ if (seekIfNeeded(info._startOfRoomImageList, &offset, &ptr) == 0)
+ return 0;
+
+ for (ct = 0; ct <= _G(_gameHeader)->_numRooms; ct++) {
+ rp = &_G(_rooms)[ct];
+ rp->_image = *(ptr++);
+ }
+
+#pragma mark item images
+
+ if (seekIfNeeded(info._startOfItemImageList, &offset, &ptr) == 0)
+ return 0;
+
+ for (ct = 0; ct <= _G(_gameHeader)->_numItems; ct++) {
+ ip = &_G(_items)[ct];
+ ip->_image = 255;
+ }
+
+ int index, image = 10;
+
+ for (index = (*ptr++); index != 255; index = (*ptr++)) {
+ _G(_items)[index]._image = image++;
+ }
+
+#pragma mark item flags
+
+ /* Hulk does not seem to use item flags */
+
+#pragma mark Actions
+
+ if (seekIfNeeded(info._startOfActions, &offset, &ptr) == 0)
+ return 0;
+
+ ct = 0;
+
+ int verb, noun, value, value2, plus;
+ while (ct < na + 1) {
+ ap = &_G(_actions)[ct];
+ plus = na + 1;
+ verb = _G(_entireFile)[offset + ct];
+ noun = _G(_entireFile)[offset + ct + plus];
+
+ ap->_vocab = verb * 150 + noun;
+
+ for (int j = 0; j < 2; j++) {
+ plus += na + 1;
+ value = _G(_entireFile)[offset + ct + plus];
+ plus += na + 1;
+ value2 = _G(_entireFile)[offset + ct + plus];
+ ap->_action[j] = 150 * value + value2;
+ }
+
+ int offset2 = offset + 0x624;
+ plus = 0;
+
+ for (int j = 0; j < 5; j++) {
+ value = _G(_entireFile)[offset2 + ct * 2 + plus];
+ value2 = _G(_entireFile)[offset2 + ct * 2 + plus + 1];
+ ap->_condition[j] = value + value2 * 0x100;
+ plus += (na + 1) * 2;
+ }
+ ct++;
+ }
+ return 1;
+}
+
+} // End of namespace Scott
+} // End of namespace Glk
diff --git a/engines/glk/scott/hulk.h b/engines/glk/scott/hulk.h
new file mode 100644
index 00000000000..f69f2cb8398
--- /dev/null
+++ b/engines/glk/scott/hulk.h
@@ -0,0 +1,38 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef GLK_SCOTT_HULK
+#define GLK_SCOTT_HULK
+
+struct GameInfo;
+
+namespace Glk {
+namespace Scott {
+
+void hulkShowImageOnExamine(int noun);
+void hulkLook();
+void drawHulkImage(int p);
+int tryLoadingHulk(GameInfo info, int dictStart);
+
+} // End of namespace Scott
+} // End of namespace Glk
+
+#endif
Commit: a864ce872f17b4fbb15c78c3986a03521a61f734
https://github.com/scummvm/scummvm/commit/a864ce872f17b4fbb15c78c3986a03521a61f734
Author: Avijeet (am388488 at gmail.com)
Date: 2022-04-12T20:35:24-07:00
Commit Message:
GLK: SCOTT: Add text formatting.
Changed paths:
A engines/glk/scott/layout_text.cpp
A engines/glk/scott/layout_text.h
diff --git a/engines/glk/scott/layout_text.cpp b/engines/glk/scott/layout_text.cpp
new file mode 100644
index 00000000000..1ba4700c960
--- /dev/null
+++ b/engines/glk/scott/layout_text.cpp
@@ -0,0 +1,108 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "glk/scott/layout_text.h"
+#include "common/util.h"
+
+namespace Glk {
+namespace Scott {
+
+int findBreak(const char *buf, int pos, int columns) {
+ int diff = 0;
+
+ while (diff < columns && !Common::isSpace((unsigned char)buf[pos])) {
+ pos--;
+ diff++;
+ }
+
+ if (diff >= columns || diff < 1) /* Found no space */ {
+ return -1;
+ }
+
+ return diff;
+}
+
+/* Breaks a null-terminated string up by inserting newlines, moving words
+ down to the next line when reaching the end of the line */
+char *lineBreakText(char *source, int columns, int *rows, int *length) {
+ columns -= 1;
+
+ char *result = nullptr;
+ char buf[768];
+ int col = 0;
+ int row = 0;
+ int sourcepos = 0;
+ int destpos = 0;
+ int diff = 0;
+ *rows = 0;
+ while (source[sourcepos] != '\0') {
+ while (col < columns && source[sourcepos] != '\0') {
+ if (source[sourcepos] == 10 || source[sourcepos] == 13) {
+ /* Found a line break. */
+ /* Any spaces before a line break may cause trouble, */
+ /* so we delete them */
+ while (destpos && buf[destpos - 1] == ' ') {
+ destpos--;
+ }
+ col = 0;
+ row++;
+ } else {
+ col++;
+ }
+
+ buf[destpos++] = source[sourcepos++];
+
+ if (source[sourcepos] == 10 || source[sourcepos] == 13)
+ col--;
+ }
+
+ /* We have reached the end of a line */
+ row++;
+ col = 0;
+
+ if (source[sourcepos] == '\0') {
+ break;
+ }
+
+ diff = findBreak(source, sourcepos, columns);
+ if (diff > -1) { /* We found a suitable break */
+ sourcepos = sourcepos - diff;
+ destpos = destpos - diff;
+ buf[destpos++] = '\n';
+
+ if (Common::isSpace((unsigned char)source[sourcepos])) {
+ sourcepos++;
+ }
+ }
+ }
+ *rows = row;
+ *length = 0;
+ result = new char[destpos + 1];
+ if (result == nullptr)
+ return nullptr;
+ memcpy(result, buf, destpos);
+ result[destpos] = '\0';
+ *length = destpos;
+ return result;
+}
+
+} // End of namespace Scott
+} // End of namespace Glk
diff --git a/engines/glk/scott/layout_text.h b/engines/glk/scott/layout_text.h
new file mode 100644
index 00000000000..54d3125558f
--- /dev/null
+++ b/engines/glk/scott/layout_text.h
@@ -0,0 +1,34 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef GLK_SCOTT_LAYOUTTEXT
+#define GLK_SCOTT_LAYOUTTEXT
+
+namespace Glk {
+namespace Scott {
+
+/* Breaks a null-terminated string up by inserting newlines,*/
+/* moving words down to the next line when reaching the end of the line */
+char *lineBreakText(char *source, int columns, int *rows, int *length);
+
+} // End of namespace Scott
+} // End of namespace Glk
+#endif
Commit: ef3621924539d0e7ef5fa1abc491e165f394e8a2
https://github.com/scummvm/scummvm/commit/ef3621924539d0e7ef5fa1abc491e165f394e8a2
Author: Avijeet (am388488 at gmail.com)
Date: 2022-04-12T20:35:24-07:00
Commit Message:
GLK: SCOTT: Add support for saving and loading game state.
Changed paths:
A engines/glk/scott/restore_state.cpp
A engines/glk/scott/restore_state.h
diff --git a/engines/glk/scott/restore_state.cpp b/engines/glk/scott/restore_state.cpp
new file mode 100644
index 00000000000..1fd46e0da16
--- /dev/null
+++ b/engines/glk/scott/restore_state.cpp
@@ -0,0 +1,158 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "glk/scott/restore_state.h"
+#include "glk/scott/globals.h"
+#include "glk/scott/definitions.h"
+#include "glk/scott/scott.h"
+
+namespace Glk {
+namespace Scott {
+
+#define MAX_UNDOS 100
+
+void saveUndo() {
+ if (_G(_justUndid)) {
+ _G(_justUndid) = 0;
+ return;
+ }
+ if (_G(_lastUndo) == nullptr) {
+ _G(_lastUndo) = saveCurrentState();
+ _G(_oldestUndo) = _G(_lastUndo);
+ _G(_numberOfUndos) = 1;
+ return;
+ }
+
+ if (_G(_numberOfUndos) == 0)
+ g_scott->fatal("Number of undos == 0 but _G(_lastUndo) != nullptr!");
+
+ _G(_lastUndo)->_nextState = saveCurrentState();
+ SavedState *current = _G(_lastUndo)->_nextState;
+ current->_previousState = _G(_lastUndo);
+ _G(_lastUndo) = current;
+ if (_G(_numberOfUndos) == MAX_UNDOS) {
+ SavedState *oldest = _G(_oldestUndo);
+ _G(_oldestUndo) = _G(_oldestUndo)->_nextState;
+ _G(_oldestUndo)->_previousState = nullptr;
+ delete[] oldest->_itemLocations;
+ delete oldest;
+ } else {
+ _G(_numberOfUndos)++;
+ }
+}
+
+void restoreUndo() {
+ if (_G(_justStarted)) {
+ g_scott->output(_G(_sys)[CANT_UNDO_ON_FIRST_TURN]);
+ return;
+ }
+ if (_G(_lastUndo) == nullptr || _G(_lastUndo)->_previousState == nullptr) {
+ g_scott->output(_G(_sys)[NO_UNDO_STATES]);
+ return;
+ }
+ SavedState *current = _G(_lastUndo);
+ _G(_lastUndo) = current->_previousState;
+ if (_G(_lastUndo)->_previousState == nullptr)
+ _G(_oldestUndo) = _G(_lastUndo);
+ restoreState(_G(_lastUndo));
+ g_scott->output(_G(_sys)[MOVE_UNDONE]);
+ delete[] current->_itemLocations;
+ delete current;
+ _G(_numberOfUndos)--;
+ _G(_justUndid) = 1;
+}
+
+void ramSave() {
+ if (_G(_ramSave) != nullptr) {
+ delete[] _G(_ramSave)->_itemLocations;
+ delete _G(_ramSave);
+ }
+
+ _G(_ramSave) = saveCurrentState();
+ g_scott->output(_G(_sys)[STATE_SAVED]);
+}
+
+void ramRestore() {
+ if (_G(_ramSave) == nullptr) {
+ g_scott->output(_G(_sys)[NO_SAVED_STATE]);
+ return;
+ }
+
+ restoreState(_G(_ramSave));
+ g_scott->output(_G(_sys)[STATE_RESTORED]);
+ saveUndo();
+}
+
+SavedState *saveCurrentState() {
+ SavedState *s = new SavedState;
+ for (int ct = 0; ct < 16; ct++) {
+ s->_counters[ct] = _G(_counters)[ct];
+ s->_roomSaved[ct] = _G(_roomSaved)[ct];
+ }
+
+ s->_bitFlags = _G(_bitFlags);
+ s->_currentLoc = MY_LOC;
+ s->_currentCounter = _G(_currentCounter);
+ s->_savedRoom = _G(_savedRoom);
+ s->_lightTime = _G(_gameHeader)->_lightTime;
+ s->_autoInventory = _G(_autoInventory);
+
+ s->_itemLocations = new uint8_t[_G(_gameHeader)->_numItems + 1];
+
+ for (int ct = 0; ct <= _G(_gameHeader)->_numItems; ct++) {
+ s->_itemLocations[ct] = _G(_items)[ct]._location;
+ }
+
+ s->_previousState = nullptr;
+ s->_nextState = nullptr;
+
+ return s;
+}
+
+void recoverFromBadRestore(SavedState *state) {
+ g_scott->output(_G(_sys)[BAD_DATA]);
+ restoreState(state);
+ delete state;
+}
+
+void restoreState(SavedState *state) {
+ for (int ct = 0; ct < 16; ct++) {
+ _G(_counters)[ct] = state->_counters[ct];
+ _G(_roomSaved)[ct] = state->_roomSaved[ct];
+ }
+
+ _G(_bitFlags) = state->_bitFlags;
+
+ MY_LOC = state->_currentLoc;
+ _G(_currentCounter) = state->_currentCounter;
+ _G(_savedRoom) = state->_savedRoom;
+ _G(_gameHeader)->_lightTime = state->_lightTime;
+ _G(_autoInventory) = state->_autoInventory;
+
+ for (int ct = 0; ct <= _G(_gameHeader)->_numItems; ct++) {
+ _G(_items)[ct]._location = state->_itemLocations[ct];
+ }
+
+ _G(_stopTime) = 1;
+}
+
+} // End of namespace Scott
+} // End of namespace Glk
diff --git a/engines/glk/scott/restore_state.h b/engines/glk/scott/restore_state.h
new file mode 100644
index 00000000000..7f5c835484e
--- /dev/null
+++ b/engines/glk/scott/restore_state.h
@@ -0,0 +1,55 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef GLK_SCOTT_RESTORESTATE
+#define GLK_SCOTT_RESTORESTATE
+
+#include <stdint.h>
+
+namespace Glk {
+namespace Scott {
+
+struct SavedState {
+ int _counters[16];
+ int _roomSaved[16];
+ uint32_t _bitFlags = 0;
+ int _currentLoc = 0;
+ int _currentCounter = 0;
+ int _savedRoom = 0;
+ int _lightTime = 0;
+ int _autoInventory = 0;
+ uint8_t *_itemLocations = nullptr;
+ SavedState *_previousState = nullptr;
+ SavedState *_nextState = nullptr;
+};
+
+void saveUndo();
+void restoreUndo();
+void ramSave();
+void ramRestore();
+SavedState *saveCurrentState();
+void recoverFromBadRestore(SavedState *state);
+void restoreState(SavedState *state);
+
+} // End of namespace Scott
+} // End of namespace Glk
+
+#endif
Commit: 72dc87f4b43b08993efcbb96752ba2b7086c79c9
https://github.com/scummvm/scummvm/commit/72dc87f4b43b08993efcbb96752ba2b7086c79c9
Author: Avijeet (am388488 at gmail.com)
Date: 2022-04-12T20:35:24-07:00
Commit Message:
GLK: Add new source files.
Changed paths:
engines/glk/module.mk
diff --git a/engines/glk/module.mk b/engines/glk/module.mk
index cab4fac4aa0..b3b2d99a28b 100644
--- a/engines/glk/module.mk
+++ b/engines/glk/module.mk
@@ -234,6 +234,19 @@ MODULE_OBJS := \
quest/read_file.o \
quest/string.o \
quest/streams.o \
+ scott/command_parser.o \
+ scott/decompress_text.o \
+ scott/decompress_z80.o \
+ scott/definitions.o \
+ scott/detect_game.o \
+ scott/game_info.o \
+ scott/globals.o \
+ scott/hulk.o \
+ scott/layout_text.o \
+ scott/line_drawing.o \
+ scott/restore_state.o \
+ scott/ring_buffer.o \
+ scott/saga_draw.o \
scott/scott.o \
tads/os_banners.o \
tads/os_buffer.o \
Commit: 8b846ec0dd44590195579648a245294fc935aaf9
https://github.com/scummvm/scummvm/commit/8b846ec0dd44590195579648a245294fc935aaf9
Author: Avijeet (am388488 at gmail.com)
Date: 2022-04-12T20:35:24-07:00
Commit Message:
GLK: SCOTT: Import size_t.
Changed paths:
engines/glk/scott/decompress_z80.h
engines/glk/scott/detect_game.h
engines/glk/scott/globals.h
engines/glk/scott/hulk.cpp
engines/glk/scott/line_drawing.h
engines/glk/scott/ring_buffer.h
engines/glk/scott/saga_draw.h
engines/glk/scott/scott.h
diff --git a/engines/glk/scott/decompress_z80.h b/engines/glk/scott/decompress_z80.h
index 4aa8e3c88ea..2769ecf4f9a 100644
--- a/engines/glk/scott/decompress_z80.h
+++ b/engines/glk/scott/decompress_z80.h
@@ -22,7 +22,8 @@
#ifndef GLK_SCOTT_DECOMPRESSZ80
#define GLK_SCOTT_DECOMPRESSZ80
-#include <cstdint>
+#include <stdint.h>
+#include <stddef.h>
namespace Glk {
namespace Scott {
diff --git a/engines/glk/scott/detect_game.h b/engines/glk/scott/detect_game.h
index cbc0643f358..28a33d0b182 100644
--- a/engines/glk/scott/detect_game.h
+++ b/engines/glk/scott/detect_game.h
@@ -25,6 +25,7 @@
#include "common/stream.h"
#include "glk/scott/definitions.h"
#include <stdint.h>
+#include <stddef.h>
namespace Glk {
namespace Scott {
diff --git a/engines/glk/scott/globals.h b/engines/glk/scott/globals.h
index 7a6fe86073c..3d3bbad0918 100644
--- a/engines/glk/scott/globals.h
+++ b/engines/glk/scott/globals.h
@@ -27,7 +27,8 @@
#include "glk/scott/definitions.h"
#include "common/array.h"
#include "common/str-array.h"
-#include <cstdint>
+#include <stdint.h>
+#include <stddef.h>
namespace Glk {
namespace Scott {
diff --git a/engines/glk/scott/hulk.cpp b/engines/glk/scott/hulk.cpp
index 580b463e353..2cf037d5d98 100644
--- a/engines/glk/scott/hulk.cpp
+++ b/engines/glk/scott/hulk.cpp
@@ -24,6 +24,8 @@
#include "glk/scott/detect_game.h"
#include "glk/scott/globals.h"
#include "glk/scott/scott.h"
+#include <stddef.h>
+#include <stdint.h>
namespace Glk {
namespace Scott {
diff --git a/engines/glk/scott/line_drawing.h b/engines/glk/scott/line_drawing.h
index 7e87abd79b9..f2d1810899d 100644
--- a/engines/glk/scott/line_drawing.h
+++ b/engines/glk/scott/line_drawing.h
@@ -22,6 +22,7 @@
#ifndef GLK_SCOTT_LINEDRAWING
#define GLK_SCOTT_LINEDRAWING
+#include <stddef.h>
#include <stdint.h>
namespace Glk {
diff --git a/engines/glk/scott/ring_buffer.h b/engines/glk/scott/ring_buffer.h
index 1a0a3597565..8c0c68ff973 100644
--- a/engines/glk/scott/ring_buffer.h
+++ b/engines/glk/scott/ring_buffer.h
@@ -22,6 +22,7 @@
#ifndef GLK_SCOTT_RINGBUFFER
#define GLK_SCOTT_RINGBUFFER
+#include <stddef.h>
#include <stdint.h>
namespace Glk {
diff --git a/engines/glk/scott/saga_draw.h b/engines/glk/scott/saga_draw.h
index 3d8ade31903..b9319b7d8d0 100644
--- a/engines/glk/scott/saga_draw.h
+++ b/engines/glk/scott/saga_draw.h
@@ -23,6 +23,7 @@
#define GLK_SCOTT_SAGADRAW
#include "glk/glk_types.h"
+#include <stddef.h>
#include <stdint.h>
namespace Glk {
diff --git a/engines/glk/scott/scott.h b/engines/glk/scott/scott.h
index 23561404271..4d5b1c96185 100644
--- a/engines/glk/scott/scott.h
+++ b/engines/glk/scott/scott.h
@@ -32,7 +32,8 @@
#include "glk/glk_api.h"
#include "glk/scott/definitions.h"
#include "glk/scott/globals.h"
-#include <cstdint>
+#include <stddef.h>
+#include <stdint.h>
namespace Glk {
namespace Scott {
Commit: a7c29553f73df5cefef571066c4c0b2ed7df74f6
https://github.com/scummvm/scummvm/commit/a7c29553f73df5cefef571066c4c0b2ed7df74f6
Author: Avijeet (am388488 at gmail.com)
Date: 2022-04-12T20:35:24-07:00
Commit Message:
GLK: SCOTT: Typedefs for used types.
Changed paths:
A engines/glk/scott/types.h
diff --git a/engines/glk/scott/types.h b/engines/glk/scott/types.h
new file mode 100644
index 00000000000..43abcde29d0
--- /dev/null
+++ b/engines/glk/scott/types.h
@@ -0,0 +1,42 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef GLK_SCOTT_TYPES
+#define GLK_SCOTT_TYPES
+
+#include "common/scummsys.h"
+
+namespace Glk {
+namespace Scott {
+
+typedef uint8 uint8_t;
+typedef uint16 uint16_t;
+typedef uint32 uint32_t;
+typedef int8 int8_t;
+typedef int16 int16_t;
+typedef int32 int32_t;
+typedef int64 int64_t;
+typedef unsigned long long size_t;
+
+} // End of namespace Scott
+} // End of namespace Glk
+
+#endif
Commit: 67adecb2d782021cf43304d65e6fa8d7d9a45b6f
https://github.com/scummvm/scummvm/commit/67adecb2d782021cf43304d65e6fa8d7d9a45b6f
Author: Avijeet (am388488 at gmail.com)
Date: 2022-04-12T20:35:24-07:00
Commit Message:
GLK: SCOTT: Remove standard library includes.
Changed paths:
engines/glk/scott/decompress_text.h
engines/glk/scott/decompress_z80.h
engines/glk/scott/detect_game.h
engines/glk/scott/globals.h
engines/glk/scott/hulk.cpp
engines/glk/scott/line_drawing.h
engines/glk/scott/restore_state.h
engines/glk/scott/ring_buffer.h
engines/glk/scott/saga_draw.h
engines/glk/scott/scott.h
diff --git a/engines/glk/scott/decompress_text.h b/engines/glk/scott/decompress_text.h
index d90342f02c8..61756631f46 100644
--- a/engines/glk/scott/decompress_text.h
+++ b/engines/glk/scott/decompress_text.h
@@ -22,7 +22,7 @@
#ifndef GLK_SCOTT_DECOMPRESSTEXT
#define GLK_SCOTT_DECOMPRESSTEXT
-#include <stdint.h>
+#include "glk/scott/types.h"
namespace Glk {
namespace Scott {
diff --git a/engines/glk/scott/decompress_z80.h b/engines/glk/scott/decompress_z80.h
index 2769ecf4f9a..ae80faac43e 100644
--- a/engines/glk/scott/decompress_z80.h
+++ b/engines/glk/scott/decompress_z80.h
@@ -22,8 +22,7 @@
#ifndef GLK_SCOTT_DECOMPRESSZ80
#define GLK_SCOTT_DECOMPRESSZ80
-#include <stdint.h>
-#include <stddef.h>
+#include "glk/scott/types.h"
namespace Glk {
namespace Scott {
diff --git a/engines/glk/scott/detect_game.h b/engines/glk/scott/detect_game.h
index 28a33d0b182..b038fa3e786 100644
--- a/engines/glk/scott/detect_game.h
+++ b/engines/glk/scott/detect_game.h
@@ -24,8 +24,7 @@
#include "common/stream.h"
#include "glk/scott/definitions.h"
-#include <stdint.h>
-#include <stddef.h>
+#include "glk/scott/types.h"
namespace Glk {
namespace Scott {
diff --git a/engines/glk/scott/globals.h b/engines/glk/scott/globals.h
index 3d3bbad0918..7d02c0f01bc 100644
--- a/engines/glk/scott/globals.h
+++ b/engines/glk/scott/globals.h
@@ -25,10 +25,9 @@
#include "glk/glk_types.h"
#include "glk/windows.h"
#include "glk/scott/definitions.h"
+#include "glk/scott/types.h"
#include "common/array.h"
#include "common/str-array.h"
-#include <stdint.h>
-#include <stddef.h>
namespace Glk {
namespace Scott {
diff --git a/engines/glk/scott/hulk.cpp b/engines/glk/scott/hulk.cpp
index 2cf037d5d98..33073c86c5b 100644
--- a/engines/glk/scott/hulk.cpp
+++ b/engines/glk/scott/hulk.cpp
@@ -24,8 +24,7 @@
#include "glk/scott/detect_game.h"
#include "glk/scott/globals.h"
#include "glk/scott/scott.h"
-#include <stddef.h>
-#include <stdint.h>
+#include "glk/scott/types.h"
namespace Glk {
namespace Scott {
diff --git a/engines/glk/scott/line_drawing.h b/engines/glk/scott/line_drawing.h
index f2d1810899d..86e7bf3ada0 100644
--- a/engines/glk/scott/line_drawing.h
+++ b/engines/glk/scott/line_drawing.h
@@ -22,8 +22,7 @@
#ifndef GLK_SCOTT_LINEDRAWING
#define GLK_SCOTT_LINEDRAWING
-#include <stddef.h>
-#include <stdint.h>
+#include "glk/scott/types.h"
namespace Glk {
namespace Scott {
diff --git a/engines/glk/scott/restore_state.h b/engines/glk/scott/restore_state.h
index 7f5c835484e..93edf6acc10 100644
--- a/engines/glk/scott/restore_state.h
+++ b/engines/glk/scott/restore_state.h
@@ -22,7 +22,7 @@
#ifndef GLK_SCOTT_RESTORESTATE
#define GLK_SCOTT_RESTORESTATE
-#include <stdint.h>
+#include "glk/scott/types.h"
namespace Glk {
namespace Scott {
diff --git a/engines/glk/scott/ring_buffer.h b/engines/glk/scott/ring_buffer.h
index 8c0c68ff973..b8a3e2e8157 100644
--- a/engines/glk/scott/ring_buffer.h
+++ b/engines/glk/scott/ring_buffer.h
@@ -22,8 +22,7 @@
#ifndef GLK_SCOTT_RINGBUFFER
#define GLK_SCOTT_RINGBUFFER
-#include <stddef.h>
-#include <stdint.h>
+#include "glk/scott/types.h"
namespace Glk {
namespace Scott {
diff --git a/engines/glk/scott/saga_draw.h b/engines/glk/scott/saga_draw.h
index b9319b7d8d0..11fa5be0f6e 100644
--- a/engines/glk/scott/saga_draw.h
+++ b/engines/glk/scott/saga_draw.h
@@ -23,8 +23,7 @@
#define GLK_SCOTT_SAGADRAW
#include "glk/glk_types.h"
-#include <stddef.h>
-#include <stdint.h>
+#include "glk/scott/types.h"
namespace Glk {
namespace Scott {
diff --git a/engines/glk/scott/scott.h b/engines/glk/scott/scott.h
index 4d5b1c96185..f81b5fa8ccf 100644
--- a/engines/glk/scott/scott.h
+++ b/engines/glk/scott/scott.h
@@ -32,8 +32,7 @@
#include "glk/glk_api.h"
#include "glk/scott/definitions.h"
#include "glk/scott/globals.h"
-#include <stddef.h>
-#include <stdint.h>
+#include "glk/scott/types.h"
namespace Glk {
namespace Scott {
Commit: a50d09aa31c1b1a17a07bce369f4f416f7f4c546
https://github.com/scummvm/scummvm/commit/a50d09aa31c1b1a17a07bce369f4f416f7f4c546
Author: Avijeet (am388488 at gmail.com)
Date: 2022-04-12T20:35:24-07:00
Commit Message:
GLK: SCOTT: Define SIZE_MAX.
Changed paths:
engines/glk/scott/types.h
diff --git a/engines/glk/scott/types.h b/engines/glk/scott/types.h
index 43abcde29d0..2bc5f4427f2 100644
--- a/engines/glk/scott/types.h
+++ b/engines/glk/scott/types.h
@@ -27,6 +27,8 @@
namespace Glk {
namespace Scott {
+#define SIZE_MAX 0xFFFFFFFF
+
typedef uint8 uint8_t;
typedef uint16 uint16_t;
typedef uint32 uint32_t;
More information about the Scummvm-git-logs
mailing list