Initial commit

This commit is contained in:
2026-02-02 04:50:13 +01:00
commit 5b11698731
22592 changed files with 7677434 additions and 0 deletions

View File

@@ -0,0 +1,101 @@
/* 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/hugo/detection.h"
#include "glk/hugo/detection_tables.h"
#include "common/debug.h"
#include "common/file.h"
#include "common/md5.h"
#include "engines/game.h"
namespace Glk {
namespace Hugo {
void HugoMetaEngine::getSupportedGames(PlainGameList &games) {
for (const PlainGameDescriptor *pd = HUGO_GAME_LIST; pd->gameId; ++pd) {
games.push_back(*pd);
}
}
const GlkDetectionEntry* HugoMetaEngine::getDetectionEntries() {
return HUGO_GAMES;
}
GameDescriptor HugoMetaEngine::findGame(const char *gameId) {
for (const PlainGameDescriptor *pd = HUGO_GAME_LIST; pd->gameId; ++pd) {
if (!strcmp(gameId, pd->gameId))
return *pd;
}
return PlainGameDescriptor::empty();
}
bool HugoMetaEngine::detectGames(const Common::FSList &fslist, DetectedGames &gameList) {
const char *const EXTENSIONS[] = { ".hex", nullptr };
// Loop through the files of the folder
for (Common::FSList::const_iterator file = fslist.begin(); file != fslist.end(); ++file) {
// Check for a recognised filename
if (file->isDirectory())
continue;
Common::String filename = file->getName();
bool hasExt = false;
for (const char *const *ext = &EXTENSIONS[0]; *ext && !hasExt; ++ext)
hasExt = filename.hasSuffixIgnoreCase(*ext);
if (!hasExt)
continue;
// Open up the file and calculate the md5
Common::File gameFile;
if (!gameFile.open(*file))
continue;
Common::String md5 = Common::computeStreamMD5AsString(gameFile, 5000);
size_t filesize = gameFile.size();
gameFile.close();
// Check for known games
const GlkDetectionEntry *p = HUGO_GAMES;
while (p->_gameId && (md5 != p->_md5 || filesize != p->_filesize))
++p;
if (!p->_gameId) {
const PlainGameDescriptor &desc = HUGO_GAME_LIST[0];
gameList.push_back(GlkDetectedGame(desc.gameId, desc.description, filename, md5, filesize));
} else {
PlainGameDescriptor gameDesc = findGame(p->_gameId);
gameList.push_back(GlkDetectedGame(p->_gameId, gameDesc.description, p->_extra, filename, p->_language));
}
}
return !gameList.empty();
}
void HugoMetaEngine::detectClashes(Common::StringMap &map) {
for (const PlainGameDescriptor *pd = HUGO_GAME_LIST; pd->gameId; ++pd) {
if (map.contains(pd->gameId))
error("Duplicate game Id found - %s", pd->gameId);
map[pd->gameId] = "";
}
}
} // End of namespace Hugo
} // End of namespace Glk

View File

@@ -0,0 +1,67 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 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_HUGO_DETECTION
#define GLK_HUGO_DETECTION
#include "common/fs.h"
#include "common/hash-str.h"
#include "engines/game.h"
#include "glk/detection.h"
namespace Glk {
namespace Hugo {
/**
* Meta engine for Hugo interpreter
*/
class HugoMetaEngine {
public:
/**
* Get a list of supported games
*/
static void getSupportedGames(PlainGameList &games);
/**
* Get the detection entries
*/
static const GlkDetectionEntry* getDetectionEntries();
/**
* Returns a game description for the given game Id, if it's supported
*/
static GameDescriptor findGame(const char *gameId);
/**
* Detect supported games
*/
static bool detectGames(const Common::FSList &fslist, DetectedGames &gameList);
/**
* Check for game Id clashes with other sub-engines
*/
static void detectClashes(Common::StringMap &map);
};
} // End of namespace Hugo
} // End of namespace Glk
#endif

View File

@@ -0,0 +1,179 @@
/* 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 "engines/game.h"
#include "common/language.h"
namespace Glk {
namespace Hugo {
const PlainGameDescriptor HUGO_GAME_LIST[] = {
{ "hugo", "Hugo IF Game" },
{ "acs", "A Crimson Spring" },
{ "adv350h", "Adventure, 350 point Colossal Cave" },
{ "annoyotron2", "Aggravatron: Annoyotron II" },
{ "babyuncleny", "Baby Uncle New Year" },
{ "captainspeedo14", "Captain Speedo: Victim of the Vacuum" },
{ "captainspeedo16", "Captain Speedo: Let Them Heat cake!" },
{ "captainspeedo42", "Captain Speedo: So Long, and Thanks for All the Books!" },
{ "captainspeedo112", "Captain Speedo: Alert on Aleph V!" },
{ "captainspeedotng", "Captain Speedo: The New Generation" },
{ "cb2", "The Clockwork Boy 2" },
{ "chicken", "The Chicken's Dilemma" },
{ "clockworkboy", "Tales of a Clockwork Boy"},
{ "cryptozookeeper", "Cryptozookeeper" },
{ "dino", "The Loneliness of the Long Distance Runner" },
{ "distress", "Distress" },
{ "dddhugo", "Doom, Death, Destruction and All That" },
{ "down", "Down" },
{ "dragonhunt", "Dragon Hunt" },
{ "eastofeastwood", "East of Eastwood" },
{ "enceladus", "Enceladus" },
{ "fallacyofdawn", "Fallacy of Dawn" },
{ "futureboy", "Future Boy!" },
{ "guiltybastards", "Guilty Bastards" },
{ "halloweenhorror1", "The Halloween Horror - part 1" },
{ "halloweenhorror2", "The Halloween Horror - part 2" },
{ "hammurabi_rme", "Hammurabi (by Rick Merrill)" },
{ "htgessay", "Hauning the Ghosts" },
{ "hugoclock", "The Hugo Clock" },
{ "hugozork", "Hugo Zork 1" },
{ "ish", "Escape from Ice Station Hippo" },
{ "leather", "Leather" },
{ "madrigal", "Madrigals of War and Love" },
{ "marjorie", "Will the Real Marjorie Hopkirk Please Stand Up?" },
{ "ndrift", "Necrotic Drift" },
{ "nextday", "The Next Day" },
{ "nmnl", "Nothing More, Nothing Less" },
{ "overbrook", "Overbrook, an Interactive House Tour" },
{ "pantomime", "Pantomime" },
{ "partyarty", "Party Arty, Man of La Munchies" },
{ "paxless", "PAXLess, Quest to the IF Suite" },
{ "pirateadv", "Pirate Adventure" },
{ "pom", "Persistence of Memory" },
{ "renga", "Renga in Four Parts" },
{ "retronemesis", "Retro-Nemesis" },
{ "scavhunt", "Scavenger Hunt" },
{ "spinning", "Spinning" },
{ "spur", "Spur, A Western Misadventure" },
{ "squest", "SceptreQuest" },
{ "stormoverlondon", "Storm Over London" },
{ "teleporttest", "Teleport Test"},
{ "tetrish", "Tetris" },
{ "theoniondestiny", "The Onion of Destiny" },
{ "theprofile", "The Profile" },
{ "tradingpunches", "Trading Punches" },
{ "travellingswordsman", "Tales of the Travelling Swordsman" },
{ "tripkey", "Tripkey" },
{ "trollface", "Trollface" },
{ "vaulthugoii", "Vault of Hugo II: Electric Boogaloo" },
{ "wfte", "Waiting for The End" },
{ "worldbuilder", "Word Builder" },
{ nullptr, nullptr }
};
const GlkDetectionEntry HUGO_GAMES[] = {
DT_ENTRY1("acs", "0.09.0 Beta", "8788f1e28a4cd875f220155b2aeae8c8", 356450),
DT_ENTRY1("acs", "1.0.04 Text Only", "37682bc8cbcc6706b8bd81d0d0b6745e", 371811),
DT_ENTRY1("acs", "1.0.04", "1daad86bee94f4519681c441d4f0f2bc", 371658),
DT_ENTRY0("adv350h", "d6735640ca21797f24e3cadb12be4ae2", 124148),
DT_ENTRY0("annoyotron2", "944056721054fd1c9af9d1e95e63ce52", 66762),
DT_ENTRY0("babyuncleny", "e2bedee095297428c8043c35c4fdca17", 71128),
DT_ENTRY0("captainspeedo14", "c4ee09af9c66d9322adeaa599c97a591", 59700),
DT_ENTRY0("captainspeedo16", "0d11e711150f08d0c04f9b414ba5c71b", 67984),
DT_ENTRY0("captainspeedo42", "5a704cc50805400416f25105162025b6", 65544),
DT_ENTRY0("captainspeedo112", "6ccbc38144b5b83627e022ec83f7ced6", 59706),
DT_ENTRY0("captainspeedotng", "bc38464151bbba4dd37795ea42f041e8", 63837),
DT_ENTRY0("cb2", "232827c10abd45c98d77ceffaf9ac9fa", 155732),
DT_ENTRY0("chicken", "eed5770a3f88d95987d018271ffa2342", 48718),
DT_ENTRY0("clockworkboy", "c42a897dc519597fb41ed2d310fc67be", 73139),
DT_ENTRY1("clockworkboy", "Competition Release", "c3c24c9b98933b0e47b229b50c53d501", 63451),
DT_ENTRY0("cryptozookeeper", "1b8301b969882c46ffa9f635e1695c41", 840567),
DT_ENTRY1("cryptozookeeper", "1.05", "411494b65bf491ab3dd7237eaa7387a7", 1136856),
DT_ENTRY0("dddhugo", "8d15ee9bd41d0cc1ffa97b1d3397a2f7", 50908),
DT_ENTRY0("dino", "e465ea9b77192432c6af82715ef59ef7", 59056),
DT_ENTRY0("distress", "fe61c8722d8625d3d196b3d390346a55", 188317),
DT_ENTRY1("down", "r1", "7daf198e81a92b152bf6a0969210aa77", 94323),
DT_ENTRY1("down", "r4", "4bc119c61d3cdf5d796c36b1d9a023c6", 99561),
DT_ENTRY0("dragonhunt", "93db9cdf1d2d2800715c93fff0d48a59", 68944),
DT_ENTRY0("eastofeastwood", "ebc4e37c66fca8a07b5782b57686ce07", 42445),
DT_ENTRY1("enceladus", "1.00", "49b3e2a3087455c31ce929427f9510d8", 233986),
DT_ENTRY1("enceladus", "1.01", "092ffa4ca5d35d83013a1094b58ce22c", 234191),
DT_ENTRY1("fallacyofdawn", "1.05", "8821566e2d3b301c6dc705f2bea54eb1", 729176),
DT_ENTRY1("fallacyofdawn", "1.07", "bebb4427004a6cded72068b0ea04b7b6", 730694),
DT_ENTRY1("futureboy", "Demo", "cfce7ee7893bb5adc9ba4ea198f38201", 827396),
DT_ENTRY1("guiltybastards", "Text Only", "77efc9a102a406a3b123172bb37e87e7", 246971),
DT_ENTRY0("guiltybastards", "77efc9a102a406a3b123172bb37e87e7", 246971),
DT_ENTRY0("halloweenhorror1", "db76f8a419767ebe6d1ad304e8001cba", 68923),
DT_ENTRY0("halloweenhorror2", "34bb57521acd33f1f985f0898d8104a8", 55574),
DT_ENTRY0("hammurabi_rme", "738739f9dc7ffa041a13445b23e77e37", 46905),
DT_ENTRY0("htgessay", "c3b5bce395f3f54097077f830dad70ac", 142921),
DT_ENTRY1("hugoclock", "v1", "9a1ab9fd3fcb52ed99751e8525020739", 105469),
DT_ENTRY1("hugoclock", "v2", "53a0b99011ccb14ddc45cfeb8c23d417", 120887),
DT_ENTRY0("hugozork", "b525f8bc83bc735fb5c62edd1b486499", 172150),
DT_ENTRY0("ish", "023c9083378fcd1a08d97e60910b54da", 113958),
DT_ENTRY0("leather", "4b8dc6050cec83b304f1eed39f4c6c24", 58409),
DT_ENTRY0("madrigal", "e63550ddff05b06d8148ec4f05d4eabc", 66902),
DT_ENTRY1("marjorie", "Demo", "826ff07155c2334a7d26aa826cae82e2", 55205),
DT_ENTRY1("ndrift", "1.03", "fea92564f4ae4c626841aa4c93fcb31e", 556591),
DT_ENTRY1("ndrift", "1.04", "8e23d867e3fc13157c1b31195550244d", 556990),
DT_ENTRY0("nextday", "2c5a9b95f6fb079986f8a4b178c9fcb4", 136844),
DT_ENTRY0("nmnl", "cee4e1ffae1fd562d507d65c143739ef", 171732),
DT_ENTRY0("overbrook", "0ed7b671ed9cd3b6362419b5159366e3", 70752),
DT_ENTRY1("pantomime", "Release 1", "874d6dab0820fd4b550d5d8c87cb8783", 247545),
DT_ENTRY1("pantomime", "Release 2", "1067b09fda08eafb09d53b51ffe73e7d", 248466),
DT_ENTRY1("partyarty", "v1", "7047bc315ad1410ef38a771e539c40b3", 84396),
DT_ENTRY1("partyarty", "v2", "62dac43addf6ea21e5759e098998773b", 86845),
DT_ENTRY1("paxless", "Release 1", "db00b1242a4a0898c2d0d2d1c77103f4", 61973),
DT_ENTRY1("paxless", "Release 2", "c4038b711d9f0ecb4ca2586623471a52", 61973),
DT_ENTRY0("pirateadv", "81c961f121e4465adb4592eee2bcc2d5", 53915),
DT_ENTRY1("pom", "r1", "9a3724529fce408c2f4a2a1bbb635748", 92889),
DT_ENTRY1("pom", "r2", "ba86e162ba30e6dbe82abc96648486da", 95577),
DT_ENTRY0("renga", "c79032bdc349863f02a4fab30beafd35", 64373),
DT_ENTRY0("retronemesis", "517908b4503c653a0d9bb326d00b22ab", 101526),
DT_ENTRY0("scavhunt", "665dce1a0f552e95590b983a3f2106da", 129514),
DT_ENTRY0("spinning", "487d0a0cdcf55407a09cafdf6ca32237", 99140),
DT_ENTRY0("spur", "77968cf043ecc012b4938c690b81227c", 185338),
DT_ENTRY0("squest", "3e91849c6e8ea3072aa58e96010b6078", 36322),
DT_ENTRY0("stormoverlondon", "13767bc8e9ca0795b6084f13da904d32", 119420),
DT_ENTRY0("teleporttest", "4cc54ad4d5b8f3628aa3925272311efb", 100723),
DT_ENTRY0("tetrish", "da1299e86f9fcded1f9a41979685ec02", 6464),
DT_ENTRY0("theoniondestiny", "29435113419753447900f644e8858ed1", 52942),
DT_ENTRY1("theprofile", "v1.00", "2231e5789fc6d1a956a40d630478ef4a", 78569),
DT_ENTRY1("theprofile", "v1.02", "618c8c3702f98bc58a0a100b7380d51a", 80015),
DT_ENTRY1("tradingpunches", "1.6", "e7ca0323da847c364ad12e160af6c494", 294441),
DT_ENTRY1("tradingpunches", "1.9", "b31b2eed49e788429cd9c5c641a3e713", 315934),
DT_ENTRY0("travellingswordsman", "37378ff2cfe75d0cfb581b7777036040", 431432),
DT_ENTRY0("tripkey", "f76297d8ff7658752aa5a29417bbb274", 188058),
DT_ENTRY0("trollface", "ff47b6d8f36cc2a4c2d41fa878631e77", 90620),
DT_ENTRY1("trollface", "Strict Mode Release", "acffc9c316749a2d96f452a1feb5c788", 86182),
DT_ENTRY0("vaulthugoii", "632054772b73993e5d7672c430d04d36", 61530),
DT_ENTRY0("wfte", "695233c271d6e355652bd11d0cc8da5e", 73099),
DT_ENTRY1("worldbuilder", "v1", "ca41b45835288f694871769ab22b8a5a", 82732),
DT_ENTRY1("worldbuilder", "v2", "031ff1a1364cd0d42600dc1bac967255", 90382),
DT_END_MARKER
};
} // End of namespace Hugo
} // End of namespace Glk

1243
engines/glk/hugo/heexpr.cpp Normal file

File diff suppressed because it is too large Load Diff

625
engines/glk/hugo/heglk.cpp Normal file
View File

@@ -0,0 +1,625 @@
/* 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/hugo/hugo.h"
namespace Glk {
namespace Hugo {
void Hugo::hugo_init_screen() {
// Open the main window...
mainwin = currentwin = glk_window_open(nullptr, 0, 0, wintype_TextBuffer, 1);
assert(mainwin);
// ...and set it up for default output
glk_set_window(mainwin);
// By setting the width and height so high, we're basically forcing the Glk library
// to deal with text-wrapping and page ends
SCREENWIDTH = 0x7fff;
SCREENHEIGHT = 0x7fff;
FIXEDCHARWIDTH = 1;
FIXEDLINEHEIGHT = 1;
hugo_settextwindow(1, 1,
SCREENWIDTH/FIXEDCHARWIDTH, SCREENHEIGHT/FIXEDLINEHEIGHT);
}
void Hugo::hugo_getline(const char *prmpt) {
event_t ev;
char gotline = 0;
/* Just in case we try to get line input from a Glk-illegal
window that hasn't been created, switch as a failsafe
to mainwin
*/
if (currentwin == nullptr)
glk_set_window(currentwin = mainwin);
/* Print prompt */
glk_put_string(prmpt);
/* Request line input */
glk_request_line_event(currentwin, buffer, MAXBUFFER, 0);
while (!gotline) {
if (shouldQuit())
return;
/* Grab an event */
glk_select(&ev);
switch (ev.type)
{
case evtype_LineInput:
/* (Will always be currentwin, but anyway) */
if (ev.window == currentwin) {
gotline = true;
}
break;
default:
break;
}
}
/* The line we have received in commandbuf is not null-terminated */
buffer[ev.val1] = '\0'; /* i.e., the length */
/* Copy the input to the script file (if open) */
if (script) {
Common::String text = Common::String::format("%s%s\n", prmpt, buffer);
script->putBuffer(text.c_str(), text.size());
}
}
int Hugo::hugo_waitforkey() {
event_t ev;
char gotchar = 0;
/* Just in case we try to get key input from a Glk-illegal
window that hasn't been created, switch as a failsafe
to mainwin
*/
if (currentwin == nullptr)
glk_set_window(currentwin = mainwin);
#if defined (NO_KEYPRESS_CURSOR)
if (currentwin != mainwin)
{
glk_window_move_cursor(currentwin, currentpos / CHARWIDTH, currentline - 1);
hugo_print("*");
glk_window_move_cursor(currentwin, currentpos / CHARWIDTH, currentline - 1);
}
#endif
glk_request_char_event(currentwin);
while (!gotchar)
{
/* Grab an event */
glk_select(&ev);
switch (ev.type) {
case evtype_CharInput:
/* (Will always be mainwin, but anyway) */
if (ev.window == currentwin) {
gotchar = true;
}
break;
case evtype_Quit:
return 0;
default:
break;
}
}
/* Convert Glk special keycodes: */
switch (ev.val1)
{
case keycode_Left: ev.val1 = 8; break;
case keycode_Right: ev.val1 = 21; break;
case keycode_Up: ev.val1 = 11; break;
case keycode_Down: ev.val1 = 10; break;
case keycode_Return: ev.val1 = 13; break;
case keycode_Escape: ev.val1 = 27; break;
}
#if defined (NO_KEYPRESS_CURSOR)
if (currentwin != mainwin)
{
glk_window_move_cursor(currentwin, currentpos / CHARWIDTH, currentline - 1);
hugo_print(" ");
glk_window_move_cursor(currentwin, currentpos / CHARWIDTH, currentline - 1);
}
#endif
return ev.val1;
}
int Hugo::hugo_iskeywaiting() {
var[system_status] = STAT_UNAVAILABLE;
return 0;
}
int Hugo::hugo_timewait(int n) {
uint32 millisecs;
event_t ev;
if (!glk_gestalt(gestalt_Timer, 0))
return false;
if (n == 0) return true;
millisecs = 1000 / n;
if (millisecs == 0)
millisecs = 1;
// For the time being, we're going to disallow
// millisecond delays in Glk (1) because there's no
// point, and (2) so that we can tell we're running
// under Glk.
if (millisecs < 1000) return false;
glk_request_timer_events(millisecs);
while (1)
{
glk_select(&ev);
if (ev.type == evtype_Timer)
break;
}
glk_request_timer_events(0);
return true;
}
void Hugo::hugo_clearfullscreen() {
glk_window_clear(mainwin);
if (secondwin) glk_window_clear(secondwin);
if (auxwin) glk_window_clear(auxwin);
/* See hugo_print() for the need for this */
if (currentwin == mainwin) mainwin_bgcolor = glk_bgcolor;
/* Must be set: */
currentpos = 0;
currentline = 1;
if (!inwindow) just_cleared_screen = true;
}
void Hugo::hugo_clearwindow() {
/* Clears the currently defined window, moving the cursor to the top-left
corner of the window */
/* If the engine thinks we're in a window, but Glk was
unable to comply, don't clear the window, because it's
not really a window
*/
if (inwindow && currentwin == mainwin) return;
if (currentwin == nullptr) return;
glk_window_clear(currentwin);
/* See hugo_print() for the need for this */
if (currentwin == mainwin) mainwin_bgcolor = glk_bgcolor;
/* If we're in a fixed-font (i.e., textgrid) auxiliary
window when we call for a clear, close auxwin and reset
the current window to mainwin
*/
if (auxwin)
{
stream_result_t sr;
glk_window_close(auxwin, &sr);
auxwin = nullptr;
glk_set_window(currentwin = mainwin);
}
/* Must be set: */
currentpos = 0;
currentline = 1;
if (!inwindow) just_cleared_screen = true;
}
void Hugo::hugo_settextmode() {
charwidth = FIXEDCHARWIDTH;
lineheight = FIXEDLINEHEIGHT;
}
void Hugo::hugo_settextwindow(int left, int top, int right, int bottom) {
/* Hugo's arbitrarily positioned windows don't currently
mesh with what Glk has to offer, so we have to ignore any
non-Glk-ish Windows and just maintain the current
parameters
*/
if ((top != 1 || bottom >= physical_windowbottom / FIXEDLINEHEIGHT + 1)
/* Pre-v2.4 didn't support proper windowing */
&& (game_version >= 24 || !inwindow))
{
in_valid_window = false;
/* Glk-illegal floating window; setting currentwin
to nullptr will tell hugo_print() not to print in it:
*/
if (bottom<physical_windowbottom / FIXEDLINEHEIGHT + 1)
{
currentwin = nullptr;
glk_set_window(mainwin);
return;
}
else
glk_set_window(currentwin = mainwin);
}
/* Otherwise this is a valid window (positioned along the
top of the screen a la a status window), so... */
else
{
/* Arbitrary height of 4 lines for pre-v2.4 windows */
if (game_version < 24) bottom = 4;
/* ...either create a new window if none exists... */
if (!secondwin)
{
glk_stylehint_set(wintype_TextGrid, style_Normal, stylehint_ReverseColor, 1);
glk_stylehint_set(wintype_TextGrid, style_Subheader, stylehint_ReverseColor, 1);
glk_stylehint_set(wintype_TextGrid, style_Emphasized, stylehint_ReverseColor, 1);
//winid_t p = glk_window_get_parent(mainwin);
secondwin = glk_window_open(mainwin,//p,
winmethod_Above | winmethod_Fixed,
bottom,
wintype_TextGrid,
0);
}
/* ...or resize the existing one if necessary */
else if (bottom != secondwin_bottom)
{
winid_t p;
p = glk_window_get_parent(secondwin);
glk_window_set_arrangement(p,
winmethod_Above | winmethod_Fixed,
bottom,
secondwin);
}
if (secondwin)
{
if (game_version < 24)
glk_window_clear(secondwin);
glk_set_window(currentwin = secondwin);
in_valid_window = true;
secondwin_bottom = bottom;
}
else
{
currentwin = nullptr;
glk_set_window(mainwin);
secondwin_bottom = 0;
return;
}
}
physical_windowleft = (left - 1)*FIXEDCHARWIDTH;
physical_windowtop = (top - 1)*FIXEDLINEHEIGHT;
physical_windowright = right*FIXEDCHARWIDTH - 1;
physical_windowbottom = bottom*FIXEDLINEHEIGHT - 1;
physical_windowwidth = (right - left + 1)*FIXEDCHARWIDTH;
physical_windowheight = (bottom - top + 1)*FIXEDLINEHEIGHT;
}
int Hugo::heglk_get_linelength() {
static uint width;
// Try to use whatever fixed-width linelength is available
if (secondwin)
glk_window_get_size(secondwin, &width, nullptr);
else if (auxwin)
glk_window_get_size(auxwin, &width, nullptr);
// Otherwise try to approximate it by the proportionally spaced linelength
else
glk_window_get_size(mainwin, &width, nullptr);
// -1 to override automatic line wrapping
return width - 1;
}
int Hugo::heglk_get_screenheight() {
static uint height = 0, mainheight = 0;
if (secondwin)
glk_window_get_size(secondwin, nullptr, &height);
else if (auxwin)
glk_window_get_size(auxwin, nullptr, &height);
glk_window_get_size(mainwin, nullptr, &mainheight);
return height + mainheight;
}
void Hugo::hugo_settextpos(int x, int y) {
if (currentwin == nullptr) return;
// Try to determine if we're trying to position fixed-width text in the main window,
// as in a menu, for example
if (!just_cleared_screen && !inwindow &&
!(glk_current_font & PROP_FONT)
&& y != 1 /* not just cls */
&& y < SCREENHEIGHT - 0x0f) /* 0x0f is arbitrary */
{
/* See if we're already in the auxiliary window */
if (currentwin != auxwin)
{
/* If not, create it, making it 100% of
mainwin's height
*/
if (auxwin == nullptr)
{
auxwin = glk_window_open(mainwin,
winmethod_Below | winmethod_Proportional,
100,
wintype_TextGrid,
0);
}
else
glk_window_clear(auxwin);
glk_set_window(currentwin = auxwin);
}
}
/* On the other hand, if we were in a textgrid window and
no longer need to be, get out
*/
else if (auxwin)
{
stream_result_t sr;
/* Close auxwin */
glk_window_close(auxwin, &sr);
auxwin = nullptr;
/* Clear the screen (both windows) */
glk_window_clear(mainwin);
glk_window_clear(secondwin);
glk_set_window(currentwin = mainwin);
}
just_cleared_screen = false;
/* Can only move the Glk cursor in a textgrid window */
if (currentwin != mainwin)
glk_window_move_cursor(currentwin, x - 1, y - 1);
/* Must be set: */
currentline = y;
currentpos = (x - 1)*CHARWIDTH; /* Note: zero-based */
}
void Hugo::hugo_print(const char *a) {
static char just_printed_linefeed = false;
/* static already_modified_style = false; */
/* Can't print in a Glk-illegal window since it hasn't been
created
*/
if (currentwin == nullptr) return;
/* In lieu of colors, in case we're highlighting something
such as a menu selection:
*/
/*
if (!inwindow and glk_bgcolor!=mainwin_bgcolor)
{
if (!already_modified_style)
{
if (glk_current_font & BOLD_FONT)
glk_set_style(style_Normal);
else
glk_set_style(style_Emphasized);
}
already_modified_style = true;
}
else
already_modified_style = false;
*/
if (a[0] == '\n')
{
if (!just_printed_linefeed)
{
glk_put_string("\n");
}
else
just_printed_linefeed = false;
}
else if (a[0] == '\r')
{
if (!just_printed_linefeed)
{
glk_put_string("\n");
just_printed_linefeed = true;
}
else
just_printed_linefeed = false;
}
else
{
glk_put_string(a);
just_printed_linefeed = false;
}
}
void Hugo::hugo_font(int f) {
static char using_prop_font = false;
glk_current_font = f;
glk_set_style(style_Normal);
if (f & BOLD_FONT)
glk_set_style(style_Subheader);
if (f & UNDERLINE_FONT)
glk_set_style(style_Emphasized);
if (f & ITALIC_FONT)
glk_set_style(style_Emphasized);
if (f & PROP_FONT)
using_prop_font = true;
/* Have to comment this out, it seems, because it will mess up the
alignment of the input in the main window
if (!(f & PROP_FONT))
glk_set_style(style_Preformatted);
*/
/* Workaround to decide if we have to open auxwin for positioned
non-proportional text:
*/
if (!(f & PROP_FONT))
{
/* If at top of screen, and changing to a fixed-
width font (a situation which wouldn't normally
be adjusted for by hugo_settextpos())
*/
if (!inwindow && currentline == 1 && currentpos == 0 && using_prop_font)
{
just_cleared_screen = false;
hugo_settextpos(1, 2);
glk_window_move_cursor(currentwin, 0, 0);
}
}
}
void Hugo::hugo_settextcolor(int c) {
// Set the foreground color to hugo_color(c)
glk_fcolor = hugo_color(c);
}
void Hugo::hugo_setbackcolor(int c) {
// Set the background color to hugo_color(c)
glk_bgcolor = hugo_color(c);
}
int Hugo::hugo_color(int c) {
if (c == 16) c = DEF_FCOLOR;
else if (c == 17) c = DEF_BGCOLOR;
else if (c == 18) c = DEF_SLFCOLOR;
else if (c == 19) c = DEF_SLBGCOLOR;
else if (c == 20) c = hugo_color(fcolor); /* match foreground */
/* Uncomment this block of code and change "c = ..." values if the system
palette differs from the Hugo palette.
If colors are unavailable on the system in question, it may suffice
to have black, white, and brightwhite (i.e. boldface). It is expected
that colored text will be visible on any other-colored background.
switch (c)
{
case HUGO_BLACK: c = 0; break;
case HUGO_BLUE: c = 1; break;
case HUGO_GREEN: c = 2; break;
case HUGO_CYAN: c = 3; break;
case HUGO_RED: c = 4; break;
case HUGO_MAGENTA: c = 5; break;
case HUGO_BROWN: c = 6; break;
case HUGO_WHITE: c = 7; break;
case HUGO_DARK_GRAY: c = 8; break;
case HUGO_LIGHT_BLUE: c = 9; break;
case HUGO_LIGHT_GREEN: c = 10; break;
case HUGO_LIGHT_CYAN: c = 11; break;
case HUGO_LIGHT_RED: c = 12; break;
case HUGO_LIGHT_MAGENTA: c = 13; break;
case HUGO_YELLOW: c = 14; break;
case HUGO_BRIGHT_WHITE: c = 15; break;
*/
return c;
}
int Hugo::hugo_charwidth(char a) const {
if (a == FORCED_SPACE)
return CHARWIDTH; /* same as ' ' */
else if ((unsigned char)a >= ' ') /* alphanumeric characters */
return CHARWIDTH; /* for non-proportional */
return 0;
}
int Hugo::hugo_textwidth(const char *a) const {
int i, slen, len = 0;
slen = (int)strlen(a);
for (i = 0; i<slen; i++)
{
if (a[i] == COLOR_CHANGE) i += 2;
else if (a[i] == FONT_CHANGE) i++;
else
len += hugo_charwidth(a[i]);
}
return len;
}
int Hugo::hugo_strlen(const char *a) const {
int i, slen, len = 0;
slen = (int)strlen(a);
for (i = 0; i<slen; i++)
{
if (a[i] == COLOR_CHANGE) i += 2;
else if (a[i] == FONT_CHANGE) i++;
else len++;
}
return len;
}
/*
* Replacements for things the Glk port doesn't support:
*
*/
void Hugo::hugo_setgametitle(const char *t) {}
int Hugo::hugo_hasvideo() const { return false; }
int Hugo::hugo_playvideo(HUGO_FILE infile, long reslength, char loop_flag, char background, int volume) {
delete infile;
return true;
}
void Hugo::hugo_stopvideo(void) {}
} // End of namespace Hugo
} // End of namespace Glk

View File

@@ -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/hugo/hugo.h"
namespace Glk {
namespace Hugo {
int Hugo::loadres(HUGO_FILE infile, int reslen, int type) {
char buf[4096];
frefid_t fileref;
strid_t stream;
long offset;
int idVal;
int i, n;
offset = hugo_ftell(infile);
for (i = 0; i < numres[type]; i++)
if (resids[type][i] == offset)
return i;
/* Too many resources loaded... */
if (numres[type] + 1 == MAXRES)
return -1;
idVal = numres[type]++;
Common::sprintf_s(buf, "%s%d", type == PIC ? "PIC" : "SND", idVal);
resids[type][idVal] = offset;
fileref = glk_fileref_create_by_name(fileusage_Data, buf, 0);
if (!fileref)
{
return -1;
}
stream = glk_stream_open_file(fileref, filemode_Write, 0);
if (!stream)
{
glk_fileref_destroy(fileref);
return -1;
}
glk_fileref_destroy(fileref);
while (reslen > 0)
{
n = hugo_fread(buf, 1, reslen < (int)sizeof(buf) ? reslen : sizeof(buf), infile);
if (n <= 0)
break;
glk_put_buffer_stream(stream, buf, n);
reslen -= n;
}
glk_stream_close(stream, nullptr);
return idVal;
}
int Hugo::hugo_hasgraphics() {
/* Returns true if the current display is capable of graphics display */
return glk_gestalt(gestalt_Graphics, 0)
&& glk_gestalt(gestalt_DrawImage, glk_window_get_type(mainwin));
}
void Hugo::initsound() {
if (!glk_gestalt(gestalt_Sound, 0))
return;
schannel = glk_schannel_create(0);
}
void Hugo::initmusic() {
if (!glk_gestalt(gestalt_Sound, 0) || !glk_gestalt(gestalt_SoundMusic, 0))
return;
mchannel = glk_schannel_create(0);
}
int Hugo::hugo_playmusic(HUGO_FILE infile, long reslen, char loop_flag) {
int idVal;
if (!mchannel)
initmusic();
if (mchannel)
{
idVal = loadres(infile, reslen, SND);
if (idVal < 0)
{
hugo_fclose(infile);
return false;
}
glk_schannel_play_ext(mchannel, idVal, loop_flag ? -1 : 1, 0);
}
hugo_fclose(infile);
return true;
}
void Hugo::hugo_musicvolume(int vol) {
if (!mchannel) initmusic();
if (!mchannel) return;
glk_schannel_set_volume(mchannel, (vol * 0x10000) / 100);
}
void Hugo::hugo_stopmusic() {
if (!mchannel) initmusic();
if (!mchannel) return;
glk_schannel_stop(mchannel);
}
int Hugo::hugo_playsample(HUGO_FILE infile, long reslen, char loop_flag) {
int idVal;
if (schannel)
{
idVal = loadres(infile, reslen, SND);
if (idVal < 0)
{
hugo_fclose(infile);
return false;
}
glk_schannel_play_ext(schannel, idVal, loop_flag ? -1 : 1, 0);
}
hugo_fclose(infile);
return true;
}
void Hugo::hugo_samplevolume(int vol) {
if (!schannel) initsound();
if (!schannel) return;
glk_schannel_set_volume(schannel, (vol * 0x10000) / 100);
}
void Hugo::hugo_stopsample() {
if (!schannel) initsound();
if (!schannel) return;
glk_schannel_stop(schannel);
}
} // End of namespace Hugo
} // End of namespace Glk

2391
engines/glk/hugo/hemisc.cpp Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,670 @@
/* 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/hugo/hugo.h"
namespace Glk {
namespace Hugo {
#if defined (DEBUGGER)
int Hugo::CheckObjectRange(int obj) {
if (runtime_warnings) {
return CheckinRange((unsigned)obj, (unsigned)objects, "object");
} else
return true;
}
#endif
int Hugo::Child(int obj) {
int c;
#if defined (DEBUGGER)
if (!CheckObjectRange(obj)) return 0;
#endif
if (obj<0 || obj>=objects) return 0;
defseg = objtable;
c = PeekWord(2 + obj*object_size + object_size - 4);
defseg = gameseg;
return c;
}
int Hugo::Children(int obj) {
int count = 0;
int nextobj;
if (obj<0 || obj>=objects) return 0;
nextobj = Child(obj);
while (nextobj)
{count++;
nextobj = Sibling(nextobj);}
return count;
}
int Hugo::Elder(int obj) {
int lastobj;
int p, cp;
if (obj<0 || obj>=objects) return 0;
p = Parent(obj);
cp = Child(p);
if (p==0 || cp==obj)
return 0;
lastobj = cp;
while (Sibling(lastobj) != obj)
lastobj = Sibling(lastobj);
return lastobj;
}
unsigned long Hugo::GetAttributes(int obj, int attribute_set) {
unsigned long a;
defseg = objtable;
#if defined (DEBUGGER)
if (!CheckObjectRange(obj)) return 0;
#endif
if (obj<0 || obj>=objects) return 0;
a = (unsigned long)PeekWord(2 + obj*object_size + attribute_set*4)
+ (unsigned long)PeekWord(2 + obj*object_size + attribute_set*4 + 2)*65536L;
defseg = gameseg;
return a;
}
int Hugo::GetProp(int obj, int p, int n, char s) {
char objonly, /* no verbroutine given in before, etc. */
isadditive = 0, /* before, after, etc. */
gotone = 0, /* when a match has been made */
getpropaddress = 0; /* when getting &object.property */
int i;
int tempself,
objtype, /* i.e., what we're matching to */
flag = 0;
int g = 0;
int templocals[MAXLOCALS];
int temp_stack_depth;
char tempinexpr = inexpr;
unsigned int pa, /* property address */
offset = 0;
long inprop, /* code position in complex property */
returnaddr;
#if defined (DEBUGGER)
long orig_inprop;
int tempdbnest;
/* Don't check a possible non-existent display object (-1) */
if (obj!=-1 || display_object!=-1) CheckObjectRange(obj);
#endif
/* This way either -1 (the non-existent display object) or a too-high
object will fail
*/
if (obj<0 || obj>=objects) return 0;
/* The display object, which is automatically created by the compiler,
did not exist pre-v2.4
*/
if ((obj==display_object) && game_version>=24)
{
/* There are no actual default "properties" per se on the
display object--but reading them returns certain data about
the current state of display affairs
*/
/* no display.<prop> #2, etc. */
if (n==1 && p<=pointer_y)
{
if (p==screenwidth)
#if defined (GLK) && defined (ACTUAL_LINELENGTH)
g = ACTUAL_LINELENGTH();
#else
g = SCREENWIDTH/FIXEDCHARWIDTH;
#endif
else if (p==screenheight)
/* ACTUAL_SCREENHEIGHT can be set to a non-portable function if
SCREENHEIGHT and SCREENHEIGHT have been set to large values in
order to force the non-portable layer to handle wrapping and
scrolling (as in the Glk port).
*/
#if defined (ACTUAL_SCREENHEIGHT)
g = ACTUAL_SCREENHEIGHT();
#else
g = SCREENHEIGHT/FIXEDLINEHEIGHT;
#endif
else if (p==linelength)
/* ACTUAL_LINELENGTH functions similarly to ACTUAL_SCREENWIDTH,
above.
*/
#if defined (ACTUAL_LINELENGTH)
g = ACTUAL_LINELENGTH();
#else
g = physical_windowwidth/FIXEDCHARWIDTH;
#endif
else if (p==windowlines)
g = physical_windowheight/FIXEDLINEHEIGHT;
else if (p==cursor_column)
g = (currentpos+1+hugo_textwidth(pbuffer))/FIXEDCHARWIDTH;
else if (p==cursor_row)
g = currentline;
else if (p==hasgraphics)
g = hugo_hasgraphics();
else if (p==title_caption)
g = FindWord(game_title);
else if (p==hasvideo)
#if !defined (COMPILE_V25)
g = hugo_hasvideo();
#else
g = 0;
#endif
else if (p==needs_repaint)
g = display_needs_repaint;
else if (p==pointer_x)
g = display_pointer_x;
else if (p==pointer_y)
g = display_pointer_y;
else
g = 0;
return g;
}
}
/* To avoid prematurely getting an address in &obj.prop.prop */
if (getaddress && MEM(codeptr)!=DECIMAL_T)
getpropaddress = true;
tempself = var[self];
if (!s) var[self] = obj;
temp_stack_depth = stack_depth;
GetNextProp:
pa = PropAddr(obj, p, offset);
defseg = proptable;
/* If the object doesn't have property p, see if there's a
default value.
*/
if (!pa)
{
if (offset) goto NoMorePropMatches;
if (getpropaddress) /* if an &value */
g = 0;
else
g = PeekWord(p * 2 + 2);
}
else
{
/* Property is a value... */
if (Peek(pa+1) < PROP_ROUTINE)
{
if (getaddress || (int)Peek(pa+1) < n || n<=0)
{
#if defined (DEBUGGER)
if (n!=1)
CheckinRange(n, (int)Peek(pa+1), "property element");
#endif
g = 0;
}
else
g = PeekWord(pa + n * 2);
}
/* ...or a property routine */
else
{
/* Check if this is an additive property */
defseg = proptable;
if (Peek(2 + Peek(0)*2 + p)&ADDITIVE_FLAG)
isadditive = true;
/* If an &value, return the address of the
property routine.
*/
if (getpropaddress)
{
g = PeekWord(pa+2);
goto NoMorePropMatches;
}
else
{
#if defined (DEBUGGER)
if (debug_eval)
{
debug_eval_error = true;
DebugMessageBox("Expression Error",
"Property routine illegal in watch/assignment");
defseg = gameseg;
return 0;
}
#endif
/* If not a complex property such as
before or after:
*/
if ((game_version>=22 && (Peek(2 + Peek(0)*2 + p)&COMPLEX_FLAG)==0) || (game_version<22 && p!=before && p!=after))
{
ret = 1;
returnaddr = codeptr;
/* Check to see if this is a valid tail-recursive return... */
if (tail_recursion==TAIL_RECURSION_PROPERTY && MEM(codeptr)==EOL_T)
{
PassLocals(0);
tail_recursion_addr = (long)PeekWord(pa+2)*address_scale;
return 0;
}
/* ...but if we're not immediately followed by and end-of-line marker,
or another property value, cancel the pending tail-recursion
*/
else if (MEM(codeptr)!=DECIMAL_T)
{
tail_recursion = 0;
}
for (i=0; i<MAXLOCALS; i++)
templocals[i] = var[MAXGLOBALS+i];
PassLocals(0);
SetStackFrame(stack_depth+1, RUNROUTINE_BLOCK, 0, 0);
#if defined (DEBUGGER)
tempdbnest = dbnest;
DebugRunRoutine((long)PeekWord(pa+2)*address_scale);
dbnest = tempdbnest;
#else
RunRoutine((long)PeekWord(pa+2)*address_scale);
#endif
retflag = 0;
codeptr = returnaddr;
g = ret;
ret = 0;
}
/* Complex property: */
else
{
for (i=0; i<MAXLOCALS; i++)
templocals[i] = var[MAXGLOBALS+i];
inprop = (long)PeekWord(pa + 2)*address_scale;
#ifdef DEBUGGER
orig_inprop = inprop;
#endif
defseg = gameseg;
while (Peek(inprop)!=CLOSE_BRACE_T)
{
returnaddr = codeptr;
codeptr = inprop;
objonly = false;
objtype = GetValue();
inprop = codeptr;
codeptr = returnaddr;
flag = 0;
/* If only an object (or other variable) is
given, and no verbroutine
*/
if (Peek(inprop)==JUMP_T)
{
objonly = true;
if (!gotone && obj==objtype) flag = 1;
}
/* Otherwise, one or more verbroutines are
specified
*/
else
{
while (Peek(inprop)!=JUMP_T)
{
if (PeekWord(inprop+1)==(unsigned int)var[verbroutine] ||
/* This is necessary because of the awkward way the pre-v2.2
differentiated non-verbroutine blocks, i.e., with Parse
*/
((game_version<22) && PeekWord(inprop+1)==(unsigned int)parseaddr && !gotone))
{
if (obj==objtype) flag = 1;
}
inprop += 3;
}
}
if (flag==1)
{
gotone = true;
ret = 1;
returnaddr = codeptr;
SetStackFrame(stack_depth, RUNROUTINE_BLOCK, 0, 0);
PassLocals(0);
#if defined (DEBUGGER)
/* Prevent premature stopping */
if (debugger_step_over && !debugger_finish)
debugger_run = true;
if (IsBreakpoint(orig_inprop))
complex_prop_breakpoint = true;
Common::sprintf_s(debug_line, "Calling: %s.%s", objectname[obj], propertyname[p]);
trace_complex_prop_routine = true;
tempdbnest = dbnest;
DebugRunRoutine(inprop+3);
dbnest = tempdbnest;
#else
RunRoutine(inprop+3);
#endif
retflag = 0;
codeptr = returnaddr;
g = ret;
ret = 0;
}
/* The following used to read "if (!flag || objonly..."
meaning that any non-verbroutine related routines
would fall through regardless of whether they returned
true or false. I don't recall the rationale for this,
and have therefore removed it.
*/
if (!flag || (objonly && !g) || ((game_version<22) && PeekWord(inprop-2)==(unsigned int)parseaddr))
inprop = (long)PeekWord(inprop+1)*address_scale;
else break;
}
}
for (i=0; i<MAXLOCALS; i++)
var[MAXGLOBALS+i] = templocals[i];
if (isadditive && !g)
{
offset = pa + 4;
gotone = false;
goto GetNextProp;
}
}
}
}
NoMorePropMatches:
defseg = gameseg;
var[self] = tempself;
inexpr = tempinexpr;
stack_depth = temp_stack_depth;
return g;
}
int Hugo::GrandParent(int obj) {
int nextobj;
#if defined (DEBUGGER)
if (!CheckObjectRange(obj)) return 0;
#endif
if (obj<0 || obj>=objects) return 0;
defseg = objtable;
while ((nextobj = PeekWord(2 + obj*object_size + object_size-8)) != 0)
obj = nextobj;
defseg = gameseg;
return obj;
}
void Hugo::MoveObj(int obj, int p) {
int oldparent, prevobj, s;
unsigned int objaddr, parentaddr, lastobjaddr;
if (obj==p) return;
if (obj<0 || obj>=objects) return;
oldparent = Parent(obj);
/* if (oldparent==p) return; */
objaddr = 2 + obj*object_size;
/* First, detach the object from its old parent and siblings... */
prevobj = Elder(obj);
s = Sibling(obj);
defseg = objtable;
if (prevobj) /* sibling */
PokeWord(2 + prevobj*object_size + object_size-6, s);
else /* child */
PokeWord(2 + oldparent*object_size + object_size-4, s);
/* Then move it to the new parent... */
defseg = objtable;
PokeWord(objaddr + object_size-8, p); /* new parent */
PokeWord(objaddr + object_size-6, 0); /* erase old sibling */
/* Only operate on the new parent if it isn't object 0 */
if (p!=0)
{
/* Object is sole child, or... */
if (Child(p)==0)
{
parentaddr = 2 + p*object_size;
defseg = objtable;
PokeWord(parentaddr + object_size-4, obj);
}
/* ...object is next sibling. */
else
{
lastobjaddr = 2 + Youngest(p)*object_size;
defseg = objtable;
PokeWord(lastobjaddr + object_size-6, obj);
}
}
}
const char *Hugo::Name(int obj) {
int p;
p = GetProp(obj, 0, 1, 0);
if (p)
return GetWord((unsigned int)p);
else
return nullptr;
}
int Hugo::Parent(int obj) {
int p;
#if defined (DEBUGGER)
if (!CheckObjectRange(obj)) return 0;
#endif
if (obj<0 || obj>=objects) return 0;
defseg = objtable;
p = PeekWord(2 + obj*object_size + object_size-8);
defseg = gameseg;
return p;
}
unsigned int Hugo::PropAddr(int obj, int p, unsigned int offset) {
unsigned char c;
int proplen;
unsigned int ptr;
#if defined (DEBUGGER)
/* Don't check any non-existent display object (-1) */
if (p!=-1) CheckinRange(p, properties, "property");
CheckObjectRange(obj);
#endif
/* This way either -1 (the non-existent display object) or a too-high
object will fail
*/
if (obj<0 || obj>=objects) return 0;
defseg = objtable;
/* Position in the property table...
i.e., ptr = PeekWord(2 + obj*object_size + (object_size-2));
*/
ptr = PeekWord(object_size*(obj+1));
/* ...unless a position has already been given */
if (offset) ptr = offset;
defseg = proptable;
c = Peek(ptr);
while (c != PROP_END && c != (unsigned char)p)
{
proplen = Peek(ptr + 1);
/* Property routine address is 1 word */
if (proplen==PROP_ROUTINE) proplen = 1;
ptr += proplen * 2 + 2;
c = Peek(ptr);
}
defseg = gameseg;
if (c==PROP_END)
return 0;
else
return ptr;
}
void Hugo::PutAttributes(int obj, unsigned long a, int attribute_set) {
unsigned int lword, hword;
hword = (unsigned int)(a/65536L);
lword = (unsigned int)(a%65536L);
defseg = objtable;
PokeWord(2 + obj*object_size + attribute_set*4, lword);
PokeWord(2 + obj*object_size + attribute_set*4 + 2, hword);
defseg = gameseg;
}
void Hugo::SetAttribute(int obj, int attr, int c) {
unsigned long a, mask;
#if defined (DEBUGGER)
if (!CheckObjectRange(obj)) return;
#endif
if (obj<0 || obj>=objects) return;
a = GetAttributes(obj, attr/32);
mask = 1L<<(long)(attr%32);
if (c==1)
a = a | mask;
else
{
if (a & mask)
a = a ^ mask;
}
PutAttributes(obj, a, attr/32);
}
int Hugo::Sibling(int obj) {
int s;
#if defined (DEBUGGER)
if (!CheckObjectRange(obj)) return 0;
#endif
if (obj<0 || obj>=objects) return 0;
defseg = objtable;
s = PeekWord(2+ obj*object_size + object_size-6);
defseg = gameseg;
return s;
}
int Hugo::TestAttribute(int obj, int attr, int nattr) {
unsigned long a, mask, ta;
#if defined (DEBUGGER)
if (!CheckObjectRange(obj)) return 0;
#endif
if (obj<0 || obj>=objects) return 0;
a = GetAttributes(obj, attr/32);
mask = 1L<<(attr%32);
ta = a & mask;
if (ta) ta = 1;
if (nattr) ta = ta ^ 1;
return (int)ta;
}
int Hugo::Youngest(int obj) {
int nextobj;
if (Child(obj)==0) return 0;
nextobj = Child(obj);
while (Sibling(nextobj))
nextobj = Sibling(nextobj);
return nextobj;
}
} // End of namespace Hugo
} // End of namespace Glk

2583
engines/glk/hugo/heparse.cpp Normal file

File diff suppressed because it is too large Load Diff

458
engines/glk/hugo/heres.cpp Normal file
View File

@@ -0,0 +1,458 @@
/* 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/hugo/hugo.h"
namespace Glk {
namespace Hugo {
void Hugo::DisplayPicture() {
if (!hugo_hasgraphics()) {
var[system_status] = STAT_UNAVAILABLE;
return;
}
char filename[MAX_RES_PATH], resname[MAX_RES_PATH];
g_vm->GetResourceParameters(filename, resname, PICTURE_T);
Common::String picName = Common::String::format("%s,%s",
filename, resname);
// Draw game's picture then move cursor down to the next line
if (glk_image_draw(mainwin, picName, imagealign_InlineUp, 0)) {
glk_put_char('\n');
} else {
var[system_status] = STAT_LOADERROR;
}
}
void Hugo::PlayMusic() {
char filename[MAX_RES_PATH], resname[MAX_RES_PATH];
char loop_flag = 0;
long resstart, reslength;
if (MEM(codeptr+1)==REPEAT_T) loop_flag = true, codeptr++;
hugo_stopmusic();
/* If a 0 parameter is passed, i.e. "music 0" */
if (!GetResourceParameters(filename, resname, MUSIC_T))
{
return;
}
if (extra_param>=0)
{
if (extra_param > 100) extra_param = 100;
hugo_musicvolume(extra_param);
}
if (!(reslength = FindResource(filename, resname)))
return;
/* Find out what type of music resource this is */
resstart = hugo_ftell(resource_file);
/* Check for MIDI */
hugo_fseek(resource_file, resstart, SEEK_SET);
hugo_fread(resname, 4, 1, resource_file);
if (!memcmp(resname, "MThd", 4))
{
resource_type = MIDI_R;
goto Identified;
}
/* Check for XM */
hugo_fseek(resource_file, resstart, SEEK_SET);
hugo_fread(resname, 17, 1, resource_file);
if (!memcmp(resname, "Extended Module: ", 17))
{
resource_type = XM_R;
goto Identified;
}
/* Check for S3M */
hugo_fseek(resource_file, resstart+0x2c, SEEK_SET);
hugo_fread(resname, 4, 1, resource_file);
if (!memcmp(resname, "SCRM", 4))
{
resource_type = S3M_R;
goto Identified;
}
/* Check for MOD */
hugo_fseek(resource_file, resstart+1080, SEEK_SET);
hugo_fread(resname, 4, 1, resource_file);
resname[4] = '\0';
/* There are a whole bunch of different MOD identifiers: */
if (!strcmp(resname+1, "CHN") || /* 4CHN, 6CHN, 8CHN */
!strcmp(resname+2, "CN") || /* 16CN, 32CN */
!strcmp(resname, "M.K.") || !strcmp(resname, "M!K!") ||
!strcmp(resname, "FLT4") || !strcmp(resname, "CD81") ||
!strcmp(resname, "OKTA") || !strcmp(resname, " "))
{
resource_type = MOD_R;
goto Identified;
}
/* Check for MP3 */
/* Assume for now that otherwise unidentified is MP3 */
else
{
resource_type = MP3_R;
goto Identified;
}
/* No file type identified */
resource_type = UNKNOWN_R;
Identified:
hugo_fseek(resource_file, resstart, SEEK_SET);
if (!hugo_playmusic(resource_file, reslength, loop_flag))
var[system_status] = STAT_LOADERROR;
}
void Hugo::PlaySample() {
char filename[MAX_RES_PATH], resname[MAX_RES_PATH];
char loop_flag = 0;
long reslength;
if (MEM(codeptr+1)==REPEAT_T) loop_flag = true, codeptr++;
hugo_stopsample();
/* If a 0 parameter is passed, i.e. "sound 0" */
if (!GetResourceParameters(filename, resname, SOUND_T))
{
return;
}
if (extra_param>=0)
{
if (extra_param > 100) extra_param = 100;
hugo_samplevolume(extra_param);
}
if (!(reslength = FindResource(filename, resname)))
return;
/* Find out what kind of audio sample this is */
hugo_fread(resname, 4, 1, resource_file);
if (!memcmp(resname, "WAVE", 4))
resource_type = WAVE_R;
else
resource_type = UNKNOWN_R;
hugo_fseek(resource_file, -4, SEEK_CUR);
if (!hugo_playsample(resource_file, reslength, loop_flag))
var[system_status] = STAT_LOADERROR;
}
void Hugo::PlayVideo() {
char filename[MAX_RES_PATH], resname[MAX_RES_PATH];
char loop_flag = 0, background = 0;
int volume = 100;
long resstart, reslength;
#if defined (COMPILE_V25)
var[system_status] = STAT_UNAVAILABLE;
#endif
if (MEM(codeptr+1)==REPEAT_T) loop_flag = true, codeptr++;
#if !defined (COMPILE_V25)
hugo_stopvideo();
#endif
/* If a 0 parameter is passed, i.e. "video 0" */
if (!GetResourceParameters(filename, resname, VIDEO_T))
{
return;
}
if (MEM(codeptr-1)==COMMA_T)
{
background = (char)GetValue();
codeptr++; /* eol */
}
if (extra_param>=0)
{
if (extra_param > 100) extra_param = 100;
volume = extra_param;
}
if (!(reslength = FindResource(filename, resname)))
return;
/* Find out what type of video resource this is */
resstart = hugo_ftell(resource_file);
/* Check for MPEG */
hugo_fseek(resource_file, resstart, SEEK_SET);
hugo_fread(resname, 4, 1, resource_file);
if (resname[2]==0x01 && (unsigned char)resname[3]==0xba)
{
resource_type = MPEG_R;
goto Identified;
}
/* Check for AVI */
hugo_fseek(resource_file, resstart+8, SEEK_SET);
hugo_fread(resname, 4, 1, resource_file);
if (!memcmp(resname, "AVI ", 4))
{
resource_type = AVI_R;
goto Identified;
}
/* No file type identified */
resource_type = UNKNOWN_R;
Identified:
hugo_fseek(resource_file, resstart, SEEK_SET);
#if !defined (COMPILE_V25)
if (!hugo_playvideo(resource_file, reslength, loop_flag, background, volume))
var[system_status] = STAT_LOADERROR;
#else
fclose(resource_file);
resource_file = nullptr;
#endif
}
long Hugo::FindResource(const char *filename, const char *resname) {
char resource_in_file[MAX_RES_PATH];
int i, len;
int rescount;
unsigned int startofdata;
long resposition, reslength;
#if defined (GLK)
frefid_t fref;
#endif
/* Previously, resource positions were written as 24 bits, which meant that
a given resource couldn't start after 16,777,216 bytes or be more than
that length. The new resource file format (designated by 'r') corrects this. */
int res_32bits = true;
resource_file = nullptr;
Common::strcpy_s(loaded_filename, filename);
Common::strcpy_s(loaded_resname, resname);
if (!strcmp(filename, "")) Common::strcpy_s(loaded_filename, resname);
/* See if the file is supposed to be in a resourcefile to
begin with
*/
if (!strcmp(filename, ""))
goto NotinResourceFile;
/* Open the resourcefile */
//hugo_strupr(filename);
#if !defined (GLK)
/* stdio implementation */
if (!(resource_file = TrytoOpen(filename, "rb", "games")))
if (!(resource_file = TrytoOpen(filename, "rb", "object")))
{
var[system_status] = STAT_NOFILE;
return 0;
}
#else
/* Glk implementation */
fref = glk_fileref_create_by_name(fileusage_Data | fileusage_BinaryMode,
filename, 0);
if (glk_fileref_does_file_exist(fref))
resource_file = glk_stream_open_file(fref, filemode_Read, 0);
else
resource_file = nullptr;
glk_fileref_destroy(fref);
if (!resource_file)
{
var[system_status] = STAT_NOFILE;
return 0;
}
#endif
/* Read the resourcefile header */
/* if (fgetc(resource_file)!='R') goto ResfileError; */
i = hugo_fgetc(resource_file);
if (i=='r')
res_32bits = true;
else if (i=='R')
res_32bits = false;
else
goto ResfileError;
/* Read and ignore the resource file version. */
hugo_fgetc(resource_file);
rescount = hugo_fgetc(resource_file);
rescount += hugo_fgetc(resource_file)*256;
startofdata = hugo_fgetc(resource_file);
startofdata += (unsigned int)hugo_fgetc(resource_file)*256;
if (hugo_ferror(resource_file))
goto ResfileError;
/* Now skim through the list of resources in the resourcefile to
see if we have a match
*/
for (i=1; i<=rescount; i++)
{
len = hugo_fgetc(resource_file);
if (hugo_ferror(resource_file))
goto ResfileError;
if (!(hugo_fgets(resource_in_file, len+1, resource_file)))
goto ResfileError;
resposition = (long)hugo_fgetc(resource_file);
resposition += (long)hugo_fgetc(resource_file)*256L;
resposition += (long)hugo_fgetc(resource_file)*65536L;
if (res_32bits)
{
resposition += (long)hugo_fgetc(resource_file)*16777216L;
}
reslength = (long)hugo_fgetc(resource_file);
reslength += (long)hugo_fgetc(resource_file)*256L;
reslength += (long)hugo_fgetc(resource_file)*65536L;
if (res_32bits)
{
reslength += (long)hugo_fgetc(resource_file)*16777216L;
}
if (hugo_ferror(resource_file)) goto ResfileError;
if (!strcmp(resname, resource_in_file))
{
if (hugo_fseek(resource_file, (long)startofdata+resposition, SEEK_SET))
goto ResfileError;
return reslength;
}
}
ResfileError:
var[system_status] = STAT_NORESOURCE;
#if defined (DEBUGGER)
SwitchtoDebugger();
Common::sprintf_s(debug_line, "Unable to find \"%s\" in \"%s\"", resname, filename);
DebugMessageBox("Resource Error", debug_line);
SwitchtoGame();
#endif
hugo_fclose(resource_file);
resource_file = nullptr;
/* If we get here, we've either been unable to find the named
resource in the given resourcefile, or no resourcefile was
given
*/
NotinResourceFile:
#if !defined (GLK)
/* stdio implementation */
if (!(resource_file = TrytoOpen(resname, "rb", "resource")))
if (!(resource_file = TrytoOpen(resname, "rb", "source")))
{
if (!strcmp(filename, ""))
var[system_status] = STAT_NOFILE;
else
var[system_status] = STAT_NORESOURCE;
return 0;
}
#else
/* Glk implementation */
fref = glk_fileref_create_by_name(fileusage_Data | fileusage_BinaryMode,
resname, 0);
if (glk_fileref_does_file_exist(fref))
resource_file = glk_stream_open_file(fref, filemode_Read, 0);
else
resource_file = nullptr;
glk_fileref_destroy(fref);
if (!resource_file)
{
if (!strcmp(filename, ""))
var[system_status] = STAT_NOFILE;
else
var[system_status] = STAT_NORESOURCE;
return 0;
}
#endif
/* resource_file here refers to a resource in an individual
on-disk file, not a consolidated resource file
*/
hugo_fseek(resource_file, 0, SEEK_END);
reslength = hugo_ftell(resource_file);
hugo_fseek(resource_file, 0, SEEK_SET);
if (hugo_ferror(resource_file))
{
hugo_fclose(resource_file);
resource_file = nullptr;
return false;
}
return reslength;
}
int Hugo::GetResourceParameters(char *filename, char *resname, int restype) {
int f;
var[system_status] = 0;
extra_param = -1;
codeptr++; /* token--i.e., 'picture', etc. */
f = GetValue();
/* If a 0 parameter is passed for "music 0", etc. */
if (!f && MEM(codeptr)!=COMMA_T)
{
++codeptr;
return 0;
}
Common::strcpy_s(filename, MAX_RES_PATH, GetWord((unsigned int)f));
if (MEM(codeptr++)!=EOL_T) /* two or more parameters */
{
hugo_strupr(filename);
Common::strcpy_s(resname, MAX_RES_PATH, GetWord(GetValue()));
if (MEM(codeptr++)==COMMA_T)
{
extra_param = GetValue();
codeptr++;
}
}
else /* only one parameter */
{
Common::strcpy_s(resname, MAX_RES_PATH, filename);
filename[0] = '\0';
}
return true;
}
} // End of namespace Hugo
} // End of namespace Glk

2631
engines/glk/hugo/herun.cpp Normal file

File diff suppressed because it is too large Load Diff

582
engines/glk/hugo/heset.cpp Normal file
View File

@@ -0,0 +1,582 @@
/* 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/hugo/hugo.h"
namespace Glk {
namespace Hugo {
void Hugo::RunSet(int gotvalue) {
char inc = 0; /* increment/decrement */
char temparrexpr, propval = 0;
int a = 0, t = 0, obj = 0;
int newl = 0; /* new length */
int newp = 0; /* new property val */
unsigned int element = 0; /* of an array */
unsigned short n, m, v; /* must be 16 bits */
inobj = 0;
if (gotvalue!=-1)
{
obj = gotvalue;
t = SetCompound(t);
goto StoreVal;
}
t = MEM(codeptr);
switch (t)
{
case OBJECTNUM_T:
{
codeptr++;
obj = PeekWord(codeptr);
codeptr += 2;
t = SetCompound(t);
break;
}
case VAR_T:
{
a = MEM(codeptr + 1);
/* Check for ++, --, +=, etc. */
inc = IsIncrement(codeptr+2);
if (MEM(codeptr + 2)==EQUALS_T || inc)
{
if (a < MAXGLOBALS) SaveUndo(VAR_T, a, var[a], 0, 0);
/* anonymous function */
if (!inc && MEM(codeptr+3)==EOL_T)
{
var[a] = GetAnonymousFunction(codeptr+4);
return;
}
if (inc)
{
var[a] = (Increment(var[a], inc)) + incdec;
/* backward-compatibility tweak */
if ((game_version<23) && MEM(codeptr)!=CLOSE_BRACE_T) codeptr--;
codeptr++; /* eol */
}
else
{
codeptr += 3;
inexpr = 1;
SetupExpr();
inexpr = 0;
v = EvalExpr(0);
var[a] = v;
}
/* If a global variable */
if (a < MAXGLOBALS)
{
if (a==wordcount) words = var[wordcount];
}
return;
}
obj = var[a];
codeptr += 2;
t = SetCompound(t);
break;
}
case WORD_T: /* "word" */
{
codeptr += 2; /* skip "[" */
n = GetValue();
#if defined (DEBUGGER)
if ((debug_eval) && debug_eval_error)
return;
#endif
codeptr += 2; /* skip "] =" */
inexpr = 1;
SetupExpr();
inexpr = 0;
GetNextWord:
if (n >= MAXWORDS) n = MAXWORDS-1;
SaveUndo(WORD_T, n, wd[n], 0, 0);
wd[n] = EvalExpr(0);
if (MEM(codeptr)==COMMA_T)
{
codeptr++;
n++;
goto GetNextWord;
}
/* Have to (rather unfortunately) rebuild the entire
input buffer and word array here
*/
buffer[0] = '\0';
t = 0;
for (a=1; a<=(int)MAXWORDS; a++)
{
if ((unsigned short)wd[a]!=UNKNOWN_WORD)
Common::strcpy_s(buffer+t, sizeof(buffer) - t, GetWord(wd[a]));
else
hugo_itoa(parsed_number, buffer+t, 10, sizeof(buffer) - t);
word[a] = buffer + t;
t+=strlen(word[a])+1;
}
if (n>(unsigned)var[wordcount])
var[wordcount] = n;
return;
}
case ARRAYDATA_T:
case ARRAY_T:
{
char af_flag = false;
/* array[n]... */
if (t==ARRAYDATA_T)
{
m = PeekWord(codeptr + 1);
codeptr += 4; /* "[" */
n = GetValue();
#if defined (DEBUGGER)
if ((debug_eval) && debug_eval_error)
return;
#endif
codeptr++; /* "]" */
}
/* ...or array val[n] */
else
{
codeptr++;
m = GetValue();
#if defined (DEBUGGER)
if ((debug_eval) && debug_eval_error)
return;
#endif
codeptr++; /* "[" */
n = GetValue();
#if defined (DEBUGGER)
if ((debug_eval) && debug_eval_error)
return;
#endif
codeptr++; /* "]" */
}
if (game_version>=22)
{
/* Convert to word value */
m*=2;
if (game_version>=23)
/* Space for array length */
a = 2;
}
#if defined (DEBUGGER)
CheckinRange(m+a+n*2, debug_workspace, "array data");
#endif
/* Check for ++, --, +=, etc. */
inc = IsIncrement(codeptr);
if (inc)
{
defseg = arraytable;
v = PeekWord(m+a+n*2);
defseg = gameseg;
v = (Increment(v, inc)) + incdec;
codeptr++; /* eol */
element = m+a+n*2;
goto WriteArrayValue;
}
if (MEM(codeptr)==EQUALS_T)
{
codeptr++;
do
{
element = m+a+n*2;
temparrexpr = arrexpr;
arrexpr = true;
/* anonymous function */
if (!inc && MEM(codeptr)==EOL_T)
{
v = GetAnonymousFunction(codeptr+1);
af_flag = true;
}
else
{
v = GetValue();
}
#if defined (DEBUGGER)
if ((debug_eval) && debug_eval_error)
return;
#endif
if (arrexpr==false && MEM(codeptr-1)==76)
codeptr--;
arrexpr = temparrexpr;
if (!af_flag && (MEM(codeptr)==COMMA_T || MEM(codeptr)==CLOSE_BRACKET_T))
codeptr++;
WriteArrayValue:
defseg = arraytable;
/* Make sure the value to be written is within range */
if ((element>0) && (element < (unsigned)(dicttable-arraytable)*16))
{
SaveUndo(ARRAYDATA_T, m+a, n, PeekWord(element), 0);
PokeWord(element, (unsigned int)v);
}
defseg = gameseg;
if (inc || af_flag) return;
n++;
}
while (MEM(codeptr)!=EOL_T);
codeptr++;
return;
}
defseg = arraytable;
obj = PeekWord((unsigned int)(m+a + n*2));
defseg = gameseg;
t = SetCompound(t);
break;
}
}
StoreVal:
/* Now store the evaluated expression in the appropriate place... */
/*
t = 1: property
t = 2: attribute
t = 3: not attribute
t = 4: property reference
*/
n = 1;
if (t==4)
{
inobj = true;
n = GetValue();
#if defined (DEBUGGER)
if ((debug_eval) && debug_eval_error)
return;
#endif
inobj = false;
LoopBack:
if (MEM(codeptr)==IS_T || MEM(codeptr)==DECIMAL_T)
{
obj = GetProp(obj, set_value, n, 0);
t = SetCompound(t);
goto LoopBack;
}
/* Don't set t = 1 if it changed above before going back
to LoopBack:
*/
else if (t==4)
t = 1; /* Just a property */
}
else if (t==1)
{
while (MEM(codeptr)==IS_T || MEM(codeptr)==DECIMAL_T)
{
obj = GetProp(obj, set_value, n, 0);
t = SetCompound(t);
}
}
switch (t)
{
case 1:
{
incdec = 0;
if (MEM(codeptr) != EQUALS_T)
{
/* Check for ++, --, +=, etc. */
if (!(inc = IsIncrement(codeptr)))
{
#if defined (DEBUGGER)
if (debug_eval)
{
debug_eval_error = true;
return;
}
#endif
FatalError(ILLEGAL_OP_E);
}
else if (MEM(codeptr)==EOL_T)
{
goto GetNextPropVal;
}
}
else
codeptr++;
/* Property routine (anonymous function)... */
if (MEM(codeptr)==EOL_T)
{
/* m = skipptr to the end of the property
routine block (i.e., the next statement
following it)
*/
m = PeekWord(codeptr + 1);
newl = PROP_ROUTINE;
newp =(unsigned int)(((codeptr + 4)+(codeptr + 4)%address_scale)/address_scale);
codeptr = (long)m*address_scale;
m = PropAddr(obj, set_value, 0);
}
/* ...or not */
else
{
GetNextPropVal:
inexpr = false;
temparrexpr = multiprop;
multiprop = true;
propval = true;
if (!inc) newp = GetValue();
#if defined (DEBUGGER)
if ((debug_eval) && debug_eval_error)
return;
#endif
if (!multiprop)
codeptr--;
multiprop = temparrexpr;
m = PropAddr(obj, set_value, 0);
if (m)
{
defseg = proptable;
newl = Peek((unsigned int)m + 1);
if (newl==PROP_ROUTINE) newl = 1;
}
/* Deal with setting built-in display object
properties
*/
else if ((obj==display_object) && n==1)
{
if (set_value==title_caption)
{
Common::strlcpy(game_title, GetWord(newp), MAX_GAME_TITLE);
hugo_setgametitle(game_title);
}
else if (set_value==needs_repaint)
{
display_needs_repaint = (char)newp;
}
}
#if defined (DEBUGGER)
/*
else if (runtime_warnings)
{
RuntimeWarning("Setting non-existent property");
}
*/
#endif
}
/* Write property obj.z = newl words of newp */
if (m && (int)n <= 0)
{
#if defined (DEBUGGER)
RuntimeWarning("Property element <= 0");
#endif
if (inc) codeptr++;
}
else if (m && (int)n <= newl)
{
defseg = proptable;
#if defined (DEBUGGER)
CheckinRange((unsigned)n, (unsigned)Peek(m+1), "property element");
#endif
/* Check to make sure this property value is within range */
if ((unsigned)(m+2+(n-1)*2)<(unsigned)(eventtable-proptable)*16)
{
SaveUndo(PROP_T, obj, (unsigned int)set_value, n, PeekWord((unsigned int)(m+2+(n-1)*2)));
/* Save the (possibly changed) length) */
Poke((unsigned int)m + 1, (unsigned char)newl);
/* An assignment such as obj.prop++ or
obj.prop += ...
*/
if (inc)
{
PokeWord((unsigned int)(m+2+(n-1)*2), Increment(PeekWord((unsigned int)(m+2+(n-1)*2)), inc) + incdec);
codeptr++; /* eol */
}
/* A regular obj.prop = ... assignment */
else
PokeWord((unsigned int)(m+2+(n-1)*2), newp);
}
}
else if (inc) codeptr++; /* eol */
defseg = gameseg;
if (inc) return;
if (propval && MEM(codeptr)==COMMA_T)
{n++;
codeptr++;
goto GetNextPropVal;}
if (propval) codeptr++;
if (MEM(codeptr)==EOL_T) codeptr++;
return;
}
case 2:
case 3:
{
ModifyAttribute:
#if defined (DEBUGGER)
CheckinRange((unsigned int)set_value, (unsigned)attributes, "attribute");
#endif
SaveUndo(ATTR_T, obj, (unsigned int)set_value, TestAttribute(obj, (unsigned int)set_value, 0), 0);
SetAttribute(obj, set_value, (t==2));
t = 2; /* reset after 'not' */
if (MEM(codeptr++)==EOL_T) return;
/* Allow multiple attributes, comma-separated */
if (MEM(codeptr)==COMMA_T)
codeptr++;
if (MEM(codeptr)==NOT_T)
{
t = 3;
codeptr++;
}
set_value = GetValue();
goto ModifyAttribute;
}
default:
{
#if defined (DEBUGGER)
if (debug_eval)
{
debug_eval_error = true;
return;
}
#endif
/* Not any sort of variable data type */
FatalError(ILLEGAL_OP_E);
}
}
}
unsigned int Hugo::GetAnonymousFunction(long addr) {
long skipaddr;
unsigned int af_addr;
skipaddr = PeekWord(addr);
/* The address of the anonymous function is the next address boundary,
calculated as:
(((addr+2)/address_scale+1)*address_scale)/address_scale */
af_addr =(unsigned int)((addr+2)/address_scale+1);
codeptr = (long)skipaddr*address_scale;
return af_addr;
}
int Hugo::SetCompound(int t) {
if (Peek(codeptr)==DECIMAL_T) /* obj.property */
{
codeptr++;
inobj = 1;
set_value = GetValue(); /* the prop. # */
inobj = 0;
if (Peek(codeptr)==POUND_T) /* if obj.prop #... */
{
codeptr++;
return 4;
}
return 1;
}
if (Peek(codeptr)==IS_T) /* obj is ... */
{
inobj = 1;
if (Peek(codeptr+1)==NOT_T)
{
codeptr += 2;
set_value = GetValue(); /* the attr. # */
inobj = 0;
return 3;
}
codeptr++;
set_value = GetValue(); /* the attr. # */
inobj = 0;
return 2;
}
#if defined (DEBUGGER)
if (debug_eval)
debug_eval_error = true;
else
#endif
FatalError(ILLEGAL_OP_E);
return 0;
}
} // End of namespace Hugo
} // End of namespace Glk

View File

@@ -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/hugo/htokens.h"
#include "common/algorithm.h"
namespace Glk {
namespace Hugo {
const char *const HTokens::token[] = {
/* 0x00 - 0x0f */
"", "(", ")", ".", ":", "=", "-", "+",
"*", "/", "|", ";", "{", "}", "[", "]",
/* 0x10 - 0x1f */
"#", "~", ">=", "<=", "~=", "&", ">", "<",
"if", ",", "else", "elseif", "while", "do", "select", "case",
/* 0x20 - 0x2f */
"for", "return", "break", "and", "or", "jump", "run", "is",
"not", "true", "false", "local", "verb", "xverb", "held", "multi",
/* 0x30 - 0x3f */
"multiheld", "newline", "anything", "print",
"number", "capital", "text", "graphics",
"color", "remove", "move", "to",
"parent", "sibling", "child", "youngest",
/* 0x40 - 0x4f */
"eldest", "younger", "elder", "prop#",
"attr#", "var#", "dictentry#", "textdata#",
"routine#","debugdata#","objectnum#", "value#",
"eol#", "system", "notheld", "multinotheld",
/* 0x50 - 0x5f */
"window", "random", "word", "locate",
"parse$", "children", "in", "pause",
"runevents", "arraydata#", "call", "stringdata#",
"save", "restore", "quit", "input",
/* 0x60 - 0x6f */
"serial$", "cls", "scripton", "scriptoff",
"restart", "hex", "object", "xobject",
"string", "array", "printchar", "undo",
"dict", "recordon", "recordoff", "writefile",
/* 0x70 - */
"readfile", "writeval", "readval", "playback",
"colour", "picture", "label#", "sound",
"music", "repeat", "addcontext", "video"
};
int HTokens::token_hash[TOKENS + 1];
HTokens::HTokens() {
Common::fill(&token_hash[0], &token_hash[TOKENS + 1], 0);
}
} // End of namespace Hugo
} // End of namespace Glk

View File

@@ -0,0 +1,99 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 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_HUGO_HTOKENS
#define GLK_HUGO_HTOKENS
namespace Glk {
namespace Hugo {
/*
* This file contains token definitions for the Hugo Compiler and Engine
* The enum constants of type TOKEN_T reflect the token names given in the token array.
* Token names followed by a # are for system use.
*/
/* i.e., highest numbered token */
#define TOKENS 0x7B
/* arbitrary */
#define HASH_KEY 1023
enum TOKEN_T {
/* 0x00 - 0x0f */
NULL_T, OPEN_BRACKET_T, CLOSE_BRACKET_T, DECIMAL_T,
COLON_T, EQUALS_T, MINUS_T, PLUS_T,
ASTERISK_T, FORWARD_SLASH_T, PIPE_T, SEMICOLON_T,
OPEN_BRACE_T, CLOSE_BRACE_T, OPEN_SQUARE_T, CLOSE_SQUARE_T,
/* 0x10 - 0x1f */
POUND_T, TILDE_T, GREATER_EQUAL_T, LESS_EQUAL_T,
NOT_EQUAL_T, AMPERSAND_T, GREATER_T, LESS_T,
IF_T, COMMA_T, ELSE_T, ELSEIF_T,
WHILE_T, DO_T, SELECT_T, CASE_T,
/* 0x20 - 0x2f */
FOR_T, RETURN_T, BREAK_T, AND_T,
OR_T, JUMP_T, RUN_T, IS_T,
NOT_T, TRUE_T, FALSE_T, LOCAL_T,
VERB_T, XVERB_T, HELD_T, MULTI_T,
/* 0x30 - 0x3f */
MULTIHELD_T, NEWLINE_T, ANYTHING_T, PRINT_T,
NUMBER_T, CAPITAL_T, TEXT_T, GRAPHICS_T,
COLOR_T, REMOVE_T, MOVE_T, TO_T,
PARENT_T, SIBLING_T, CHILD_T, YOUNGEST_T,
/* 0x40 - 0x4f */
ELDEST_T, YOUNGER_T, ELDER_T, PROP_T,
ATTR_T, VAR_T, DICTENTRY_T, TEXTDATA_T,
ROUTINE_T, DEBUGDATA_T, OBJECTNUM_T, VALUE_T,
EOL_T, SYSTEM_T, NOTHELD_T, MULTINOTHELD_T,
/* 0x50 - 0x5f */
WINDOW_T, RANDOM_T, WORD_T, LOCATE_T,
PARSE_T, CHILDREN_T, IN_T, PAUSE_T,
RUNEVENTS_T, ARRAYDATA_T, CALL_T, STRINGDATA_T,
SAVE_T, RESTORE_T, QUIT_T, INPUT_T,
/* 0x60 - 0x6f */
SERIAL_T, CLS_T, SCRIPTON_T, SCRIPTOFF_T,
RESTART_T, HEX_T, OBJECT_T, XOBJECT_T,
STRING_T, ARRAY_T, PRINTCHAR_T, UNDO_T,
DICT_T, RECORDON_T, RECORDOFF_T, WRITEFILE_T,
/* 0x70 - */
READFILE_T, WRITEVAL_T, READVAL_T, PLAYBACK_T,
COLOUR_T, PICTURE_T, LABEL_T, SOUND_T,
MUSIC_T, REPEAT_T, ADDCONTEXT_T, VIDEO_T
};
struct HTokens {
static const char *const token[];
static int token_hash[];
HTokens();
};
} // End of namespace Hugo
} // End of namespace Glk
#endif

331
engines/glk/hugo/hugo.cpp Normal file
View File

@@ -0,0 +1,331 @@
/* 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/hugo/hugo.h"
#include "glk/hugo/resource_archive.h"
#include "common/config-manager.h"
#include "common/translation.h"
namespace Glk {
namespace Hugo {
Hugo *g_vm;
Hugo::Hugo(OSystem *syst, const GlkGameDescription &gameDesc) : GlkAPI(syst, gameDesc),
mainwin(nullptr), currentwin(nullptr), secondwin(nullptr), auxwin(nullptr),
runtime_warnings(false), dbnest(0), address_scale(16),
SCREENWIDTH(0), SCREENHEIGHT(0), FIXEDCHARWIDTH(0), FIXEDLINEHEIGHT(0),
// heexpr
evalcount(0), incdec(0), getaddress(0), inexpr(0), inobj(0), last_precedence(0),
// hemedia
mchannel(nullptr), schannel(nullptr),
// hemisc
game_version(0), object_size(24), game(nullptr), script(nullptr),
playback(nullptr), record(nullptr), io(nullptr), ioblock('\0'), ioerror('\0'),
codestart(0), objtable(0), eventtable(0), proptable(0), arraytable(0), dicttable(0),
syntable(0), initaddr(0), mainaddr(0), parseaddr(0), parseerroraddr(0),
findobjectaddr(0), endgameaddr(0), speaktoaddr(0), performaddr(0),
objects(0), events(0), dictcount(0), syncount(0), mem(nullptr), loaded_in_memory(true),
defseg(0), gameseg(0), codeptr(0), codeend(0), currentpos(0), currentline(0), full(0),
def_fcolor(0), def_bgcolor(0), def_slfcolor(0), def_slbgcolor(0), fcolor(0), bgcolor(0),
icolor(0), default_bgcolor(0), currentfont(0), capital(0), textto(0),
physical_windowwidth(0), physical_windowheight(0), physical_windowtop(0),
physical_windowleft(0), physical_windowbottom(0), physical_windowright(0),
inwindow(0), charwidth(0), lineheight(0), current_text_x(0), current_text_y(0),
skipping_more(false), undoptr(0), undoturn(0), undoinvalid(0), undorecord(0),
context_commands(0), in_valid_window(false), glk_fcolor(DEF_FCOLOR), glk_bgcolor(DEF_BGCOLOR),
mainwin_bgcolor(0), glk_current_font(0), just_cleared_screen(false), secondwin_bottom(0),
// heobject
display_object(-1), display_needs_repaint(0), display_pointer_x(0), display_pointer_y(0),
// heparse
words(0), parsed_number(0), remaining(0), xverb(0), starts_with_verb(0),
grammaraddr(0), obj_parselist(nullptr), domain(0), odomain(0), objcount(0),
parse_allflag(false), pobjcount(0), pobj(0), obj_match_state(0), object_is_number(0),
objgrammar(0), objstart(0), objfinish(0), addflag(false), speaking(0), oopscount(0),
parse_called_twice(0), reparse_everything(0), full_buffer(0), recursive_call(false),
parse_location(0),
// heres
resource_file(nullptr), extra_param(0), resource_type(0),
// herun
arguments_passed(0), ret(0), retflag(0), during_player_input(false), override_full(0),
game_reset(false), stack_depth(0), tail_recursion(0), tail_recursion_addr(0),
last_window_top(0), last_window_bottom(0), last_window_left(0), last_window_right(0),
lowest_windowbottom(0), physical_lowest_windowbottom(0), just_left_window(false),
// heset
arrexpr(0), multiprop(0), set_value(0)
#if defined (DEBUGGER)
, debug_eval(false), debug_eval_error(false), debugger_step_over(false),
debugger_finish(false), debugger_run(false), debugger_interrupt(false),
debugger_skip(false), runtime_error(false), currentroutine(false),
complex_prop_breakpoint(false), trace_complex_prop_routine(false), routines(0),
properties(0), current_locals(0), this_codeptr(0), debug_workspace(0), attributes(0),
original_dictcount(0), buffered_code_lines(0), debugger_has_stepped_back(false),
debugger_step_back(false), debugger_collapsing(0), runaway_counter(0), history_count(0),
active_screen(0), step_nest(0), history_last(0)
#endif
{
g_vm = this;
gamefile[0] = '\0';
// heexpr
Common::fill(&eval[0], &eval[MAX_EVAL_ELEMENTS], 0);
Common::fill(&var[0], &var[MAXLOCALS + MAXGLOBALS], 0);
// hemedia
Common::fill(&resids[0][0], &resids[1][MAXRES], 0);
numres[0] = numres[1] = 0;
// hemisc
Common::fill(&context_command[0][0], &context_command[MAX_CONTEXT_COMMANDS - 1][64], 0);
Common::fill(&id[0], &id[3], '\0');
Common::fill(&serial[0], &serial[9], '\0');
Common::fill(&pbuffer[0], &pbuffer[MAXBUFFER * 2 + 1], 0);
Common::fill(&undostack[0][0], &undostack[MAXUNDO - 1][5], 0);
// heparse
Common::fill(&buffer[0], &buffer[MAXBUFFER + MAXWORDS], '\0');
Common::fill(&errbuf[0], &errbuf[MAXBUFFER + 1], 0);
Common::fill(&line[0], &line[1025], 0);
Common::fill(&word[0], &word[MAXWORDS + 1], (char *)nullptr);
Common::fill(&wd[0], &wd[MAXWORDS + 1], 0);
Common::fill(&parseerr[0], &parseerr[MAXBUFFER + 1], '\0');
Common::fill(&parsestr[0], &parsestr[MAXBUFFER + 1], '\0');
Common::fill(&objlist[0], &objlist[MAXOBJLIST], 0);
Common::fill(&objword_cache[0], &objword_cache[MAXWORDS], 0);
Common::fill(&oops[0], &oops[MAXBUFFER + 1], '\0');
Common::fill(&punc_string[0], &punc_string[64], '\0');
// heres
Common::fill(&loaded_filename[0], &loaded_filename[MAX_RES_PATH], '\0');
Common::fill(&loaded_resname[0], &loaded_resname[MAX_RES_PATH], '\0');
// herun
Common::fill(&passlocal[0], &passlocal[MAXLOCALS], 0);
// heset
game_title[0] = '\0';
// Miscellaneous
_savegameSlot = ConfMan.hasKey("save_slot") ? ConfMan.getInt("save_slot") : -1;
#ifdef DEBUGGER
debug_line[0] = '\0';
Common::fill(&objectname[0], &objectname[MAX_OBJECT], (char *)nullptr);
Common::fill(&propertyname[0], &propertyname[MAX_PROPERTY], (char *)nullptr);
Common::fill(&codeline[0][0], &codeline[9][0], 0);
Common::fill(&localname[0][0], &localname[9][0], 0);
Common::fill(&code_history[0], &code_history[MAX_CODE_HISTORY], 0);
Common::fill(&dbnest_history[0], &dbnest_history[MAX_CODE_HISTORY], 0);
#endif
}
Hugo::~Hugo() {
g_vm = nullptr;
}
void Hugo::runGame() {
hugo_init_screen();
SetupDisplay();
Common::strcpy_s(gamefile, getFilename().c_str());
pbuffer[0] = '\0';
ResourceArchive *res = new ResourceArchive();
SearchMan.add("Resources", res);
gameseg = 0;
LoadGame();
playGame();
hugo_cleanup_screen();
hugo_blockfree(mem);
mem = nullptr;
hugo_closefiles();
}
Common::Error Hugo::readSaveData(Common::SeekableReadStream *rs) {
char testid[3], testserial[9];
int lbyte, hbyte;
int j;
unsigned int k, undosize;
long i;
/* Check ID */
testid[0] = (char)hugo_fgetc(rs);
testid[1] = (char)hugo_fgetc(rs);
testid[2] = '\0';
if (hugo_ferror(rs)) goto RestoreError;
if (strcmp(testid, id)) {
GUIErrorMessage(_("Incorrect rs file."));
goto RestoreError;
}
/* Check serial number */
if (!hugo_fgets(testserial, 9, rs)) goto RestoreError;
if (strcmp(testserial, serial)) {
GUIErrorMessage(_("Save file created by different version."));
goto RestoreError;
}
/* Restore variables */
for (k = 0; k<MAXGLOBALS + MAXLOCALS; k++) {
if ((lbyte = hugo_fgetc(rs))==EOF || (hbyte = hugo_fgetc(rs))==EOF)
goto RestoreError;
var[k] = lbyte + hbyte * 256;
}
/* Restore objtable and above */
if (hugo_fseek(game, objtable*16L, SEEK_SET)) goto RestoreError;
i = 0;
while (i<codeend-(long)(objtable*16L)) {
if ((hbyte = hugo_fgetc(rs))==EOF && hugo_ferror(rs)) goto RestoreError;
if (hbyte == 0) {
if ((lbyte = hugo_fgetc(rs))==EOF && hugo_ferror(rs)) goto RestoreError;
SETMEM(objtable*16L+i, (unsigned char)lbyte);
i++;
/* Skip byte in game file */
if (hugo_fgetc(game)==EOF) goto RestoreError;
} else {
while (hbyte--) {
/* Get unchanged game file byte */
if ((lbyte = hugo_fgetc(game))==EOF) goto RestoreError;
SETMEM(objtable*16L+i, (unsigned char)lbyte);
i++;
}
}
}
/* Restore undo data */
if ((lbyte = hugo_fgetc(rs))==EOF || (hbyte = hugo_fgetc(rs))==EOF)
goto RestoreError;
undosize = lbyte + hbyte*256;
/* We can only restore undo data if it was saved by a port with
the same MAXUNDO as us */
if (undosize == MAXUNDO) {
for (k = 0; k < MAXUNDO; k++) {
for (j=0; j<5; j++) {
if ((lbyte = hugo_fgetc(rs))==EOF || (hbyte = hugo_fgetc(rs))==EOF)
goto RestoreError;
undostack[k][j] = lbyte + hbyte*256;
}
}
if ((lbyte = hugo_fgetc(rs))==EOF || (hbyte = hugo_fgetc(rs))==EOF) goto RestoreError;
undoptr = lbyte + hbyte*256;
if ((lbyte = hugo_fgetc(rs))==EOF || (hbyte = hugo_fgetc(rs))==EOF) goto RestoreError;
undoturn = lbyte + hbyte*256;
if ((lbyte = hugo_fgetc(rs))==EOF || (hbyte = hugo_fgetc(rs))==EOF) goto RestoreError;
undoinvalid = (unsigned char)lbyte, undorecord = (unsigned char)hbyte;
}
else undoinvalid = true;
return Common::kNoError;
RestoreError:
return Common::kReadingFailed;
}
Common::Error Hugo::writeGameData(Common::WriteStream *ws) {
int c, j;
int lbyte, hbyte;
long i;
int samecount = 0;
/* Write ID */
if (hugo_fputc(id[0], ws) == EOF || hugo_fputc(id[1], ws) == EOF) goto SaveError;
/* Write serial number */
if (hugo_fputs(serial, ws) == EOF) goto SaveError;
/* Save variables */
for (c = 0; c<MAXGLOBALS + MAXLOCALS; c++) {
hbyte = (unsigned int)var[c] / 256;
lbyte = (unsigned int)var[c] - hbyte * 256;
if (hugo_fputc(lbyte, ws) == EOF || hugo_fputc(hbyte, ws) == EOF) goto SaveError;
}
/* Save objtable to end of code space */
if (hugo_fseek(game, objtable * 16L, SEEK_SET)) goto SaveError;
for (i = 0; i <= codeend - (long)(objtable * 16L); i++) {
if ((lbyte = hugo_fgetc(game)) == EOF) goto SaveError;
hbyte = MEM(objtable * 16L + i);
/* If memory same as original game file */
if (lbyte == hbyte && samecount < 255)
samecount++;
/* If memory differs (or samecount exceeds 1 byte) */
else {
if (samecount)
if (hugo_fputc(samecount, ws) == EOF) goto SaveError;
if (lbyte != hbyte) {
if (hugo_fputc(0, ws) == EOF) goto SaveError;
if (hugo_fputc(hbyte, ws) == EOF) goto SaveError;
samecount = 0;
}
else samecount = 1;
}
}
if (samecount)
if (hugo_fputc(samecount, ws) == EOF) goto SaveError;
/* Save undo data */
/* Save the number of turns in this port's undo stack */
hbyte = (unsigned int)MAXUNDO / 256;
lbyte = (unsigned int)MAXUNDO - hbyte * 256;
if (hugo_fputc(lbyte, ws) == EOF || hugo_fputc(hbyte, ws) == EOF)
goto SaveError;
for (c = 0; c < MAXUNDO; c++) {
for (j = 0; j < 5; j++) {
hbyte = (unsigned int)undostack[c][j] / 256;
lbyte = (unsigned int)undostack[c][j] - hbyte * 256;
if (hugo_fputc(lbyte, ws) == EOF || hugo_fputc(hbyte, ws) == EOF)
goto SaveError;
}
}
if (hugo_fputc(undoptr - (undoptr / 256) * 256, ws) == EOF || hugo_fputc(undoptr / 256, ws) == EOF)
goto SaveError;
if (hugo_fputc(undoturn - (undoturn / 256) * 256, ws) == EOF || hugo_fputc(undoturn / 256, ws) == EOF)
goto SaveError;
if (hugo_fputc(undoinvalid, ws) == EOF || hugo_fputc(undorecord, ws) == EOF)
goto SaveError;
return Common::kNoError;
SaveError:
return Common::kWritingFailed;
}
} // End of namespace Hugo
} // End of namespace Glk

1217
engines/glk/hugo/hugo.h Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,194 @@
/* 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_HUGO_DEFINES
#define GLK_HUGO_DEFINES
#include "common/scummsys.h"
namespace Glk {
namespace Hugo {
#define HEVERSION 3
#define HEREVISION 3
#define HEINTERIM ".0"
#define GLK
#define GRAPHICS_SUPPORTED
#define SOUND_SUPPORTED
#define SETTITLE_SUPPORTED
#define SAVEGAMEDATA_REPLACED
#define RESTOREGAMEDATA_REPLACED
// There's a bunch of debugging code in the original Hugo sources behind DEBUGGER defines,
// but doesn't actually have any implementation of them. I've put in some stub methods,
// with the idea that debugger code could eventually be hooked up to the ScummVM debugger.
// So for now the debugger defined is commented out, since with debugger enabled the games
// don't work properly
//#define DEBUGGER 1
#define MAXOBJLIST 32
#define MAX_CONTEXT_COMMANDS 32
#define MAX_EVAL_ELEMENTS 256
#define MAX_GAME_TITLE 64
#define MAX_DEBUG_LINE 256
#define MAX_OBJECT 999
#define MAX_PROPERTY 999
// maximum number of matchable object words
#define MAX_MOBJ 16
// Larger than normal since Glk doesn't break up paragraphs (1024+256)
#define MAXBUFFER 1280
#define MAXUNDO 1024
#define MAXCALLS 99
#define MAXBREAKPOINTS 99
#define MAX_CODE_HISTORY 99
#define MAX_RES_PATH 255
#define MAXRES 1024
#define CHARWIDTH 1
#define HUGO_FILE strid_t
#define MAXPATH 256
#define MAXFILENAME 256
#define MAXDRIVE 256
#define MAXDIR 256
#define MAXEXT 256
#define DEF_PRN ""
#define DEF_FCOLOR 0
#define DEF_BGCOLOR 15
#define DEF_SLFCOLOR 15
#define DEF_SLBGCOLOR 1
/* These static values are not changeable--they depend largely on internals of the Engine. */
#define MAXATTRIBUTES 128
#define MAXGLOBALS 240
#define MAXLOCALS 16
#define MAXPOBJECTS 256 /* contenders for disambiguation */
#define MAXWORDS 32 /* in an input line */
#define MAXSTACKDEPTH 256 /* for nesting {...} */
/* The positions of various data in the header: */
#define H_GAMEVERSION 0x00
#define H_ID 0x01
#define H_SERIAL 0x03
#define H_CODESTART 0x0B
#define H_OBJTABLE 0x0D /* data tables */
#define H_PROPTABLE 0x0F
#define H_EVENTTABLE 0x11
#define H_ARRAYTABLE 0x13
#define H_DICTTABLE 0x15
#define H_SYNTABLE 0x17
#define H_INIT 0x19 /* junction routines */
#define H_MAIN 0x1B
#define H_PARSE 0x1D
#define H_PARSEERROR 0x1F
#define H_FINDOBJECT 0x21
#define H_ENDGAME 0x23
#define H_SPEAKTO 0x25
#define H_PERFORM 0x27
#define H_TEXTBANK 0x29
/* additional debugger header information */
#define H_DEBUGGABLE 0x3A
#define H_DEBUGDATA 0x3B
#define H_DEBUGWORKSPACE 0x3E
/* Printing control codes--embedded in strings printed by AP(). */
#define FONT_CHANGE 1
#define COLOR_CHANGE 2
#define NO_CONTROLCHAR 3
#define NO_NEWLINE 30
#define FORCED_SPACE 31 /* Can't be <= # colors/font codes + 1
(See AP() for the reason) */
/* Font control codes--these bitmasks follow FONT_CHANGE codes. */
#define NORMAL_FONT 0
#define BOLD_FONT 1
#define ITALIC_FONT 2
#define UNDERLINE_FONT 4
#define PROP_FONT 8
/* CHAR_TRANSLATION is simply a value that is added to an ASCII character
in order to encode the text, i.e., make it unreadable to casual
browsing.
*/
#define CHAR_TRANSLATION 0x14
/* Passed to GetWord() */
#define PARSE_STRING_VAL 0xFFF0
#define SERIAL_STRING_VAL 0xFFF1
/* Returned by FindWord() */
#define UNKNOWN_WORD 0xFFFF
/* Bitmasks for certain qualities of properties */
#define ADDITIVE_FLAG 1
#define COMPLEX_FLAG 2
/* Property-table indicators */
#define PROP_END 255
#define PROP_ROUTINE 255
#define MEM(addr) (mem[addr])
#define SETMEM(addr, n) (mem[addr] = n)
#define GETMEMADDR(addr) (&mem[addr])
#define HUGO_PTR
#define RESET_STACK_DEPTH (-1)
#define RUNROUTINE_BLOCK 1
#define CONDITIONAL_BLOCK 2
#define DOWHILE_BLOCK 3
#define TAIL_RECURSION_ROUTINE (-1)
#define TAIL_RECURSION_PROPERTY (-2)
/* For system_status: */
#define STAT_UNAVAILABLE ((short)-1)
#define STAT_NOFILE 101
#define STAT_NORESOURCE 102
#define STAT_LOADERROR 103
#define PRINTFATALERROR(a) error("%s", a)
#define PIC 0
#define SND 1
#if defined (DEBUGGER)
#define VIEW_CALLS 0
#define VIEW_LOCALS 1
#define CODE_WINDOW 2
#define VIEW_BREAKPOINTS 3
#define VIEW_WATCH 4
#define FORCE_REDRAW 1
#endif
} // End of namespace Hugo
} // End of namespace Glk
#endif

View File

@@ -0,0 +1,174 @@
/* 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_HUGO_TYPES
#define GLK_HUGO_TYPES
#include "common/scummsys.h"
namespace Glk {
namespace Hugo {
/**
* Library/engine globals
*/
enum EngineGlobals {
object = 0,
xobject = 1,
self = 2,
wordcount = 3,
player = 4,
actor = 5,
location = 6,
verbroutine = 7,
endflag = 8,
prompt = 9,
objectcount = 10,
system_status = 11
};
/**
* Library/engine properties
*/
enum EngineProperties {
before = 1,
after = 2,
noun = 3,
adjective = 4,
article = 5
};
/**
* "display" object properties
*/
enum ObjectProperties {
screenwidth = 1,
screenheight = 2,
linelength = 3,
windowlines = 4,
cursor_column = 5,
cursor_row = 6,
hasgraphics = 7,
title_caption = 8,
hasvideo = 9,
needs_repaint = 10,
pointer_x = 11,
pointer_y = 12
};
/**
* Fatal errors
*/
enum ERROR_TYPE {
MEMORY_E = 1, ///< out of memory
OPEN_E, ///< error opening file
READ_E, ///< error reading from file
WRITE_E, ///< error writing to file
EXPECT_VAL_E, ///< expecting value
UNKNOWN_OP_E, ///< unknown operation
ILLEGAL_OP_E, ///< illegal operation
OVERFLOW_E, ///< overflow
DIVIDE_E ///< divide by zero
};
enum RESOURCE_TYPE {
JPEG_R, ///< JPEG image
WAVE_R, ///< RIFF WAVE audio sample
MOD_R, ///< MOD music module
S3M_R, ///< S3M music module
XM_R, ///< XM music module
MIDI_R, ///< MIDI music
MP3_R, ///< MP3 audio layer
AVI_R, ///< Video for Windows
MPEG_R, ///< MPEG video
UNKNOWN_R
};
/**
* A structure used for disambiguation in MatchObject()
*/
struct pobject_structure {
int obj; ///< the actual object number
char type; ///< referred to by noun or adjective
pobject_structure() : obj(0), type(0) {}
};
struct SAVED_WINDOW_DATA {
int left, top, right, bottom;
int width, height, charwidth, lineheight;
int currentpos, currentline;
int currentfont;
};
/**
* Structure used for navigating {...} blocks:
*/
struct CODE_BLOCK {
int type; ///< see #defines, below
long brk; ///< break address, or 0 to indicate NOP
long returnaddr; ///< used only for do-while loops
#if defined (DEBUGGER)
int dbnest; ///< for recovering from 'break'
#endif
CODE_BLOCK() : type(0), brk(0), returnaddr(0)
#if defined (DEBUGGER)
, dbnest(0)
#endif
{
}
};
#if defined (DEBUGGER)
enum DEBUGGER_ERROR {
D_MEMORY_ERROR
};
struct CALL {
long addr;
bool param;
CALL() : addr(0), param(false) {}
};
struct WINDOW {
int count;
bool changed;
WINDOW() : count(99), changed(false) {}
};
struct BREAKPOINT {
bool isbreak;
long addr;
const char *in;
int count;
BREAKPOINT() : isbreak(false), addr(0), in(nullptr), count(0) {
}
};
#endif
} // End of namespace Hugo
} // End of namespace Glk
#endif

View File

@@ -0,0 +1,93 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 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/hugo/resource_archive.h"
#include "glk/hugo/hugo.h"
#include "common/algorithm.h"
#include "common/memstream.h"
namespace Glk {
namespace Hugo {
bool ResourceArchive::splitName(const Common::String &name,
Common::String &filename, Common::String &resName) {
size_t commaIndex = name.findFirstOf(',');
if (commaIndex == Common::String::npos)
return false;
filename = Common::String(name.c_str(), commaIndex);
resName = Common::String(name.c_str() + commaIndex + 1);
if (resName.hasSuffixIgnoreCase(".jpg"))
resName = Common::String(resName.c_str(), resName.size() - 4);
else if (resName.hasSuffixIgnoreCase(".jpeg"))
resName = Common::String(resName.c_str(), resName.size() - 5);
else if (resName.contains("."))
return false;
return true;
}
bool ResourceArchive::hasFile(const Common::Path &path) const {
Common::String filename, resName;
if (!splitName(path.baseName(), filename, resName))
return false;
size_t resLength = g_vm->FindResource(filename.c_str(), resName.c_str());
g_vm->hugo_fclose(g_vm->resource_file);
return resLength != 0;
}
const Common::ArchiveMemberPtr ResourceArchive::getMember(const Common::Path &path) const {
if (!hasFile(path))
return Common::ArchiveMemberPtr();
return Common::ArchiveMemberPtr(new Common::GenericArchiveMember(path, *this));
}
Common::SeekableReadStream *ResourceArchive::createReadStreamForMember(const Common::Path &path) const {
Common::String filename, resName;
// Split up the file and resource entry; return if it's not one
if (!splitName(path.baseName(), filename, resName))
return nullptr;
// Try and get the entry details from the given file
size_t resLength = g_vm->FindResource(filename.c_str(), resName.c_str());
if (!resLength) {
g_vm->hugo_fclose(g_vm->resource_file);
return nullptr;
}
// Otherwise, load the specified resource
byte *buffer = (byte *)malloc(resLength);
g_vm->glk_get_buffer_stream(g_vm->resource_file, (char *)buffer, resLength);
g_vm->hugo_fclose(g_vm->resource_file);
// Return a stream to allow access to the data
return new Common::MemoryReadStream(buffer, resLength, DisposeAfterUse::YES);
}
} // End of namespace Hugo
} // End of namespace Glk

View File

@@ -0,0 +1,77 @@
/* 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_HUGO_RESOURCE_ARCHIVE_H
#define GLK_HUGO_RESOURCE_ARCHIVE_H
#include "common/archive.h"
namespace Glk {
namespace Hugo {
/**
* ScummVM archive that provides virtual access to the Hugo
* resource files
*/
class ResourceArchive : public Common::Archive {
private:
/**
* Splits a resource file and entry pair into individual names
*/
static bool splitName(const Common::String &name,
Common::String &filename, Common::String &resName);
public:
~ResourceArchive() override {}
/**
* Check if a member with the given name is present in the Archive.
* Patterns are not allowed, as this is meant to be a quick File::exists()
* replacement.
*/
bool hasFile(const Common::Path &path) const override;
/**
* Add all members of the Archive to list.
* Must only append to list, and not remove elements from it.
*
* @return the number of names added to list
*/
int listMembers(Common::ArchiveMemberList &list) const override {
return 0;
}
/**
* Returns a ArchiveMember representation of the given file.
*/
const Common::ArchiveMemberPtr getMember(const Common::Path &path) const override;
/**
* Create a stream bound to a member with the specified name in the
* archive. If no member with this name exists, 0 is returned.
* @return the newly created input stream
*/
Common::SeekableReadStream *createReadStreamForMember(const Common::Path &path) const override;
};
} // End of namespace Hugo
} // End of namespace Glk
#endif

View File

@@ -0,0 +1,119 @@
/* 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/hugo/stringfn.h"
namespace Glk {
namespace Hugo {
char *StringFunctions::Left(char a[], int l) {
static char *temp;
int i;
temp = GetTempString();
if (l > (int)strlen(a))
l = strlen(a);
for (i = 0; i<l; i++)
temp[i] = a[i];
temp[i] = '\0';
return temp;
}
char *StringFunctions::Ltrim(char a[]) {
static char *temp;
int len = strlen(a);
temp = GetTempString();
Common::strcpy_s(temp, sizeof(_tempString[0]), a);
while (temp[0]==' ' || temp[0]=='\t')
memmove(temp, temp+1, len + 1);
return temp;
}
char *StringFunctions::Mid(char a[], int pos, int n) {
static char *temp;
int i;
temp = GetTempString();
pos--;
if (pos+n > (int)strlen(a))
n = strlen(a)-pos;
for (i = 0; i<n; i++)
temp[i] = a[pos+i];
temp[i] = '\0';
return temp;
}
char *StringFunctions::Right(char a[], int l) {
static char *temp;
int i;
temp = GetTempString();
if (l > (int)strlen(a))
l = strlen(a);
for (i = 0; i<l; i++)
temp[i] = a[strlen(a)-l+i];
temp[i] = '\0';
return temp;
}
char *StringFunctions::Rtrim(char a[]) {
static char *temp;
int len;
temp = GetTempString();
Common::strcpy_s(temp, sizeof(_tempString[0]), a);
while (((len = strlen(temp))) && (temp[len-1]==' ' || temp[len-1]=='\t'))
Common::strcpy_s(temp, sizeof(_tempString[0]), Left(temp, len-1));
return temp;
}
char *StringFunctions::hugo_strcpy(char *s, const char *t) {
char *r = s;
while ((*s++ = *t++) != 0) ;
return r;
}
char *StringFunctions::GetTempString() {
static char *r;
r = &_tempString[_tempstringCount][0];
if (++_tempstringCount >= NUM_TEMPSTRINGS)
_tempstringCount = 0;
return r;
}
char *StringFunctions::hugo_strlwr(char *s) {
for (char *sp = s; *sp; ++sp)
*sp = tolower(*sp);
return s;
}
char *StringFunctions::hugo_strupr(char *s) {
for (char *sp = s; *sp; ++sp)
*sp = toupper(*sp);
return s;
}
} // End of namespace Hugo
} // End of namespace Glk

View File

@@ -0,0 +1,68 @@
/* 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_HUGO_STRINGFN
#define GLK_HUGO_STRINGFN
#include "common/algorithm.h"
#include "common/str.h"
namespace Glk {
namespace Hugo {
#define NUM_TEMPSTRINGS 2
/**
* The following string-manipulation functions closely mimic BASIC-language string functionality.
* They do not alter the provided string; instead, they return a pointer to a static (modified) copy.
*/
class StringFunctions {
private:
char _tempString[NUM_TEMPSTRINGS][1025];
int _tempstringCount;
char *GetTempString();
public:
StringFunctions() : _tempstringCount(0) {
Common::fill(&_tempString[0][0], &_tempString[NUM_TEMPSTRINGS - 1][1025], '\0');
}
char *Left(char a[], int l);
char *Ltrim(char a[]);
char *Mid(char a[], int pos, int n);
char *Right(char a[], int l);
char *Rtrim(char a[]);
char *hugo_strcpy(char *s, const char *t);
char *hugo_strlwr(char *s);
char *hugo_strupr(char *s);
};
} // End of namespace Hugo
} // End of namespace Glk
#endif