[Scummvm-cvs-logs] SF.net SVN: scummvm:[52137] scummvm/trunk

sev at users.sourceforge.net sev at users.sourceforge.net
Tue Aug 17 11:28:21 CEST 2010


Revision: 52137
          http://scummvm.svn.sourceforge.net/scummvm/?rev=52137&view=rev
Author:   sev
Date:     2010-08-17 09:28:20 +0000 (Tue, 17 Aug 2010)

Log Message:
-----------
HUGO: Adding engine to the main tree

Modified Paths:
--------------
    scummvm/trunk/Makefile.common
    scummvm/trunk/base/plugins.cpp
    scummvm/trunk/configure
    scummvm/trunk/engines/engines.mk
    scummvm/trunk/tools/README

Added Paths:
-----------
    scummvm/trunk/dists/engine-data/hugo.dat
    scummvm/trunk/engines/hugo/
    scummvm/trunk/engines/hugo/detection.cpp
    scummvm/trunk/engines/hugo/display.cpp
    scummvm/trunk/engines/hugo/display.h
    scummvm/trunk/engines/hugo/engine.cpp
    scummvm/trunk/engines/hugo/engine.h
    scummvm/trunk/engines/hugo/file.cpp
    scummvm/trunk/engines/hugo/file.h
    scummvm/trunk/engines/hugo/game.h
    scummvm/trunk/engines/hugo/global.h
    scummvm/trunk/engines/hugo/hugo.cpp
    scummvm/trunk/engines/hugo/hugo.h
    scummvm/trunk/engines/hugo/intro.cpp
    scummvm/trunk/engines/hugo/intro.h
    scummvm/trunk/engines/hugo/inventory.cpp
    scummvm/trunk/engines/hugo/inventory.h
    scummvm/trunk/engines/hugo/module.mk
    scummvm/trunk/engines/hugo/mouse.cpp
    scummvm/trunk/engines/hugo/mouse.h
    scummvm/trunk/engines/hugo/parser.cpp
    scummvm/trunk/engines/hugo/parser.h
    scummvm/trunk/engines/hugo/route.cpp
    scummvm/trunk/engines/hugo/route.h
    scummvm/trunk/engines/hugo/schedule.cpp
    scummvm/trunk/engines/hugo/schedule.h
    scummvm/trunk/engines/hugo/sound.cpp
    scummvm/trunk/engines/hugo/sound.h
    scummvm/trunk/engines/hugo/util.cpp
    scummvm/trunk/engines/hugo/util.h
    scummvm/trunk/tools/create_hugo/
    scummvm/trunk/tools/create_hugo/create_hugo.cpp
    scummvm/trunk/tools/create_hugo/create_hugo.h
    scummvm/trunk/tools/create_hugo/enums.h
    scummvm/trunk/tools/create_hugo/module.mk
    scummvm/trunk/tools/create_hugo/staticdata.h
    scummvm/trunk/tools/create_hugo/staticdisplay.h
    scummvm/trunk/tools/create_hugo/staticengine.h
    scummvm/trunk/tools/create_hugo/staticintro.h
    scummvm/trunk/tools/create_hugo/staticmouse.h
    scummvm/trunk/tools/create_hugo/staticparser.h
    scummvm/trunk/tools/create_hugo/staticschedule.h
    scummvm/trunk/tools/create_hugo/staticutil.h

Modified: scummvm/trunk/Makefile.common
===================================================================
--- scummvm/trunk/Makefile.common	2010-08-17 08:33:50 UTC (rev 52136)
+++ scummvm/trunk/Makefile.common	2010-08-17 09:28:20 UTC (rev 52137)
@@ -233,6 +233,9 @@
 ifdef ENABLE_DRASCULA
 DIST_FILES_ENGINEDATA+=drascula.dat
 endif
+ifdef ENABLE_HUGO
+DIST_FILES_ENGINEDATA+=hugo.dat
+endif
 ifdef ENABLE_KYRA
 DIST_FILES_ENGINEDATA+=kyra.dat
 endif

Modified: scummvm/trunk/base/plugins.cpp
===================================================================
--- scummvm/trunk/base/plugins.cpp	2010-08-17 08:33:50 UTC (rev 52136)
+++ scummvm/trunk/base/plugins.cpp	2010-08-17 09:28:20 UTC (rev 52137)
@@ -115,6 +115,9 @@
 		#if PLUGIN_ENABLED_STATIC(GROOVIE)
 		LINK_PLUGIN(GROOVIE)
 		#endif
+		#if PLUGIN_ENABLED_STATIC(HUGO)
+		LINK_PLUGIN(HUGO)
+		#endif
 		#if PLUGIN_ENABLED_STATIC(KYRA)
 		LINK_PLUGIN(KYRA)
 		#endif

Modified: scummvm/trunk/configure
===================================================================
--- scummvm/trunk/configure	2010-08-17 08:33:50 UTC (rev 52136)
+++ scummvm/trunk/configure	2010-08-17 09:28:20 UTC (rev 52137)
@@ -88,6 +88,7 @@
 add_engine gob "Gobli*ns" yes
 add_engine groovie "Groovie" yes "groovie2"
 add_engine groovie2 "Groovie 2 games" no
+add_engine hugo "Hugo Trilogy" no
 add_engine kyra "Legend of Kyrandia" yes "lol"
 add_engine lol "Lands of Lore" no
 add_engine lure "Lure of the Temptress" yes

Added: scummvm/trunk/dists/engine-data/hugo.dat
===================================================================
(Binary files differ)


Property changes on: scummvm/trunk/dists/engine-data/hugo.dat
___________________________________________________________________
Added: svn:executable
   + *
Added: svn:mime-type
   + application/octet-stream

Modified: scummvm/trunk/engines/engines.mk
===================================================================
--- scummvm/trunk/engines/engines.mk	2010-08-17 08:33:50 UTC (rev 52136)
+++ scummvm/trunk/engines/engines.mk	2010-08-17 09:28:20 UTC (rev 52137)
@@ -60,6 +60,11 @@
 endif
 endif
 
+ifdef ENABLE_HUGO
+DEFINES += -DENABLE_HUGO=$(ENABLE_HUGO)
+MODULES += engines/hugo
+endif
+
 ifdef ENABLE_KYRA
 DEFINES += -DENABLE_KYRA=$(ENABLE_KYRA)
 MODULES += engines/kyra

Added: scummvm/trunk/engines/hugo/detection.cpp
===================================================================
--- scummvm/trunk/engines/hugo/detection.cpp	                        (rev 0)
+++ scummvm/trunk/engines/hugo/detection.cpp	2010-08-17 09:28:20 UTC (rev 52137)
@@ -0,0 +1,234 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "engines/advancedDetector.h"
+
+#include "hugo/hugo.h"
+#include "hugo/intro.h"
+
+namespace Hugo {
+
+struct HugoGameDescription {
+	ADGameDescription desc;
+	GameType gameType;
+};
+
+uint32 HugoEngine::getFeatures() const {
+	return _gameDescription->desc.flags;
+}
+
+static const PlainGameDescriptor hugoGames[] = {
+	// Games
+	{"hugo1", "Hugo 1: Hugo's House of Horrors"},
+	{"hugo2", "Hugo 2: Hugo's Mystery Adventure"},
+	{"hugo3", "Hugo 3: Hugo's Amazon Adventure"},
+
+	{0, 0}
+};
+
+static const HugoGameDescription gameDescriptions[] = {
+
+	// Hugo1 DOS
+	{
+		{
+			"hugo1", 0,
+			AD_ENTRY1s("house.art", "c9403b2fe539185c9fd569b6cc4ff5ca", 14811),
+			Common::EN_ANY,
+			Common::kPlatformPC,
+			ADGF_NO_FLAGS,
+			Common::GUIO_NONE
+		},
+		kGameTypeHugo1
+	},
+	// Hugo1 Windows
+	{
+		{
+			"hugo1", 0,
+			AD_ENTRY1s("objects.dat", "3ba0f108f7690a05a34c56a02fbe644a", 126488),
+			Common::EN_ANY,
+			Common::kPlatformWindows,
+			GF_PACKED,
+			Common::GUIO_NONE
+		},
+		kGameTypeHugo1
+	},
+	// Hugo2 DOS
+	{
+		{
+			"hugo2", 0,
+			AD_ENTRY1s("objects.dat", "88a718cc0ff2b3b25d49aaaa69d6d52c", 155240),
+			Common::EN_ANY,
+			Common::kPlatformPC,
+			GF_PACKED,
+			Common::GUIO_NONE
+		},
+		kGameTypeHugo2
+	},
+	// Hugo2 Windows
+	{
+		{
+			"hugo2", 0,
+			AD_ENTRY1s("objects.dat", "5df4ffc851e66a544c0e95e4e084a806", 158480),
+			Common::EN_ANY,
+			Common::kPlatformWindows,
+			GF_PACKED,
+			Common::GUIO_NONE
+		},
+		kGameTypeHugo2
+	},
+	// Hugo3 DOS
+	{
+		{
+			"hugo3", 0,
+			AD_ENTRY1s("objects.dat", "bb1b061538a445f2eb99b682c0f506cc", 136419),
+			Common::EN_ANY,
+			Common::kPlatformPC,
+			GF_PACKED,
+			Common::GUIO_NONE
+		},
+		kGameTypeHugo3
+	},
+	// Hugo3 Windows
+	{
+		{
+			"hugo3", 0,
+			AD_ENTRY1s("objects.dat", "c9a8af7aa14cc907434eecee3ddd06d3", 136638),
+			Common::EN_ANY,
+			Common::kPlatformWindows,
+			GF_PACKED,
+			Common::GUIO_NONE
+		},
+		kGameTypeHugo3
+	},
+
+	{AD_TABLE_END_MARKER, kGameTypeNone}
+};
+
+static const ADParams detectionParams = {
+	// Pointer to ADGameDescription or its superset structure
+	(const byte *)gameDescriptions,
+	// Size of that superset structure
+	sizeof(HugoGameDescription),
+	// Number of bytes to compute MD5 sum for
+	5000,
+	// List of all engine targets
+	hugoGames,
+	// Structure for autoupgrading obsolete targets
+	0,
+	// Name of single gameid (optional)
+	0,
+	// List of files for file-based fallback detection (optional)
+	0,
+	// Flags
+	0,
+	// Additional GUI options (for every game}
+	Common::GUIO_NONE,
+	// Maximum directory depth
+	1,
+	// List of directory globs
+	0
+};
+
+class HugoMetaEngine : public AdvancedMetaEngine {
+public:
+	HugoMetaEngine() : AdvancedMetaEngine(detectionParams) {}
+
+	const char *getName() const {
+		return "Hugo Engine";
+	}
+
+	const char *getOriginalCopyright() const {
+		return "Hugo Engine (C) 1989-1997 David P. Gray";
+	}
+
+	bool createInstance(OSystem *syst, Engine **engine, const ADGameDescription *gd) const;
+
+	bool hasFeature(MetaEngineFeature f) const;
+};
+
+bool HugoMetaEngine::createInstance(OSystem *syst, Engine **engine, const ADGameDescription *gd) const {
+	if (gd) {
+		*engine = new HugoEngine(syst, (const HugoGameDescription *)gd);
+		((HugoEngine *)*engine)->initGame((const HugoGameDescription *)gd);
+		((HugoEngine *)*engine)->initGamePart((const HugoGameDescription *)gd);
+	}
+	return gd != 0;
+}
+
+bool HugoMetaEngine::hasFeature(MetaEngineFeature f) const {
+	return false;
+}
+
+} // End of namespace Hugo
+
+#if PLUGIN_ENABLED_DYNAMIC(HUGO)
+REGISTER_PLUGIN_DYNAMIC(HUGO, PLUGIN_TYPE_ENGINE, Hugo::HugoMetaEngine);
+#else
+REGISTER_PLUGIN_STATIC(HUGO, PLUGIN_TYPE_ENGINE, Hugo::HugoMetaEngine);
+#endif
+
+namespace Hugo {
+
+void HugoEngine::initGame(const HugoGameDescription *gd) {
+	_gameType = gd->gameType;
+	_platform = gd->desc.platform;
+	_packedFl = (getFeatures() & GF_PACKED);
+}
+
+void HugoEngine::initGamePart(const HugoGameDescription *gd) {
+	char tmpStr[8];
+	_gameVariant = _gameType - 1 + (_platform == Common::kPlatformWindows ? 0 : 3);
+
+//Generate filenames
+	if (gd->desc.platform == Common::kPlatformWindows)
+		sprintf(tmpStr, "%s%c", gd->desc.gameid, 'w');
+	else
+		sprintf(tmpStr, "%s%c", gd->desc.gameid, 'd');
+
+	sprintf(_initFilename, "%s-00.SAV", tmpStr);
+	sprintf(_saveFilename, "%s-%s.SAV", tmpStr, "%d");
+
+	switch (_gameVariant) {
+	case 0:
+		_introHandler = new intro_1w(*this);
+		break;
+	case 1:
+		_introHandler = new intro_2w(*this);
+		break;
+	case 2:
+		_introHandler = new intro_3w(*this);
+		break;
+	case 3:
+		_introHandler = new intro_1d(*this);
+		break;
+	case 4:
+		_introHandler = new intro_2d(*this);
+		break;
+	case 5:
+		_introHandler = new intro_3d(*this);
+		break;
+	}
+}
+} // End of namespace Gob


Property changes on: scummvm/trunk/engines/hugo/detection.cpp
___________________________________________________________________
Added: svn:executable
   + *
Added: svn:mime-type
   + text/plain
Added: svn:keywords
   + Date Rev Author URL Id
Added: svn:eol-style
   + native

Added: scummvm/trunk/engines/hugo/display.cpp
===================================================================
--- scummvm/trunk/engines/hugo/display.cpp	                        (rev 0)
+++ scummvm/trunk/engines/hugo/display.cpp	2010-08-17 09:28:20 UTC (rev 52137)
@@ -0,0 +1,509 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+/*
+ * This code is based on original Hugo Trilogy source code
+ *
+ * Copyright (c) 1989-1995 David P. Gray
+ *
+ */
+
+// Display.c - DIB related code for HUGOWIN
+
+#include "common/system.h"
+
+#include "hugo/game.h"
+#include "hugo/hugo.h"
+#include "hugo/display.h"
+#include "hugo/file.h"
+#include "hugo/util.h"
+
+namespace Hugo {
+
+#define CENTER          -1                  // Used to center text in x
+#define NUM_COLORS  16              // Num colors to save in palette
+#define DMAX            16              // Size of add/restore rect lists
+#define BMAX                (DMAX * 2)  // Size of dirty rect blit list
+
+#define INX(X, B) (X >= B->x && X <= B->x + B->dx)
+#define INY(Y, B) (Y >= B->y && Y <= B->y + B->dy)
+#define OVERLAP(A, B) ((INX(A->x, B) || INX(A->x + A->dx, B) || INX(B->x, A) || INX(B->x + B->dx, A)) && (INY(A->y, B) || INY(A->y + A->dy, B) || INY(B->y, A) || INY(B->y + B->dy, A)))
+
+struct rect_t {                                 // Rectangle used in Display list
+	int16 x;                                        // Position in dib
+	int16 y;                                        // Position in dib
+	int16 dx;                                   // width
+	int16 dy;                                   // height
+};
+
+Screen::Screen(HugoEngine &vm) : _vm(vm) {
+
+}
+
+void Screen::createPal() {
+	debugC(1, kDebugDisplay, "createPal");
+
+	g_system->setPalette(_vm._palette, 0, NUM_COLORS);
+}
+
+// Translate from our 16-color palette to Windows logical palette index
+uint32 Screen::GetPalIndex(byte color) {
+	debugC(1, kDebugDisplay, "getPalIndex(%d)", color);
+
+	warning("STUB: GetPalIndex()");
+	return 0;
+	//return(PALETTEINDEX(ctab[color]));
+}
+
+// Create DIB headers and init palette
+void Screen::initDisplay() {
+	debugC(1, kDebugDisplay, "initDisplay");
+	// Create logical palette
+	createPal();
+}
+
+// Move an image from source to destination
+void Screen::moveImage(image_pt srcImage, uint16 x1, uint16 y1, uint16 dx, uint16 dy, uint16 width1, image_pt dstImage, uint16 x2, uint16 y2, uint16 width2) {
+	int16 wrap_src = width1 - dx;                   // Wrap to next src row
+	int16 wrap_dst = width2 - dx;                   // Wrap to next dst row
+	int16 x;
+
+	debugC(3, kDebugDisplay, "moveImage(srcImage, %d, %d, %d, %d, %d, dstImage, %d, %d, %d)", x1, y1, dx, dy, width1, x2, y2, width2);
+
+	srcImage += y1 * width1 + x1;                   // Offset into src image
+	dstImage += y2 * width2 + x2;                   // offset into dst image
+
+	while (dy--) {                                  // For each row
+		for (x = dx; x--;)                          // For each column
+			*dstImage++ = *srcImage++;
+		srcImage += wrap_src;                       // Wrap to next line
+		dstImage += wrap_dst;
+	}
+}
+
+void Screen::displayBackground() {
+	debugC(1, kDebugDisplay, "displayBackground");
+
+	g_system->copyRectToScreen(_frontBuffer, 320, 0, 0, 320, 200);
+}
+
+// Blit the supplied rectangle from _frontBuffer to the screen
+void Screen::displayRect(int16 x, int16 y, int16 dx, int16 dy) {
+
+	/* TODO: Suppress this commented block if it's confirmed to be useless
+	    // Find destination rectangle from current scaling
+	    int16 sx =  (int16)((int32)config.cx * x  / XPIX);
+	    int16 sy =  (int16)((int32)config.cy * (y - DIBOFF_Y) / VIEW_DY);
+	    int16 dsx = (int16)((int32)config.cx * dx / XPIX);
+	    int16 dsy = (int16)((int32)config.cy * dy / VIEW_DY);
+	*/
+	debugC(3, kDebugDisplay, "displayRect(%d, %d, %d, %d)", x, y, dx, dy);
+
+	g_system->copyRectToScreen(&_frontBuffer[x + y * 320], 320, x, y, dx, dy);
+}
+
+void Screen::remapPal(uint16 oldIndex, uint16 newIndex) {
+// Change a color by remapping supplied palette index with new index
+	debugC(1, kDebugDisplay, "Remap_pal(%d, %d)", oldIndex, newIndex);
+
+	warning("STUB: Remap_pal()");
+	//bminfo.bmiColors[oldIndex] = ctab[newIndex];
+}
+
+void Screen::savePal(Common::WriteStream *f) {
+	debugC(1, kDebugDisplay, "savePal");
+
+	warning("STUB: savePal()");
+	//fwrite(bminfo.bmiColors, sizeof(bminfo.bmiColors), 1, f);
+}
+
+void Screen::restorePal(Common::SeekableReadStream *f) {
+	debugC(1, kDebugDisplay, "restorePal");
+
+	warning("STUB: restorePal()");
+	//fread(bminfo.bmiColors, sizeof(bminfo.bmiColors), 1, f);
+}
+
+
+// Set the new background color
+void Screen::setBackgroundColor(long color) {
+	debugC(1, kDebugDisplay, "setBackgroundColor(%ld)", color);
+
+	// How???  Translate existing pixels in dib before objects rendered?
+}
+
+// Write the supplied character in the supplied color to x,y pixel coords
+void Screen::writeChar(int16 x, int16 y, char c, byte color) {
+	debugC(1, kDebugDisplay, "writeChar(%d, %d, %c, %d)", x, y, c, color);
+
+	warning("STUB: writeChar()");
+	// x = (int16)((long) x * config.cx / XPIX);
+	// y = (int16)((long) y * config.cy / YPIX);
+	// SetTextColor(hDC, GetPalIndex(color));
+	// TextOut(hDC, x, y, &c, 1);
+}
+
+// Clear prompt line for next command
+void Screen::clearPromptLine() {
+	debugC(1, kDebugDisplay, "clearPromptLine");
+}
+
+
+// Return the overlay state (Foreground/Background) of the currently
+// processed object by looking down the current column for an overlay
+// base bit set (in which case the object is foreground).
+overlayState_t Screen::findOvl(seq_t *seq_p, image_pt dst_p, uint16 y) {
+	debugC(4, kDebugDisplay, "findOvl");
+
+	for (; y < seq_p->lines; y++) {              // Each line in object
+		image_pt ovb_p = _vm.getBaseBoundaryOverlay() + ((uint16)(dst_p - _frontBuffer) >> 3);  // Ptr into overlay bits
+		if (*ovb_p & (0x80 >> ((uint16)(dst_p - _frontBuffer) & 7))) // Overlay bit is set
+			return FG;                              // Found a bit - must be foreground
+		dst_p += XPIX;
+	}
+
+	return BG;                                      // No bits set, must be background
+}
+
+// Merge an object frame into _frontBuffer at sx, sy and update rectangle list.
+// If fore TRUE, force object above any overlay
+void Screen::displayFrame(int sx, int sy, seq_t *seq, bool foreFl) {
+	overlayState_t overlayState = UNDEF;            // Overlay state of object
+	image_pt image;                                 // Ptr to object image data
+	image_pt subFrontBuffer;                        // Ptr to offset in _frontBuffer
+	image_pt overlay;                               // Ptr to overlay data
+	int16    frontBufferwrap;                       // Wrap dst_p to next line
+	int16    imageWrap;                             // Wrap src_p to next line
+	uint16   x, y;                                  // Index into object data
+
+	debugC(3, kDebugDisplay, "displayFrame(%d, %d, seq, %d)", sx, sy, (foreFl) ? 1 : 0);
+
+	image = seq->imagePtr;                          // Source ptr
+	subFrontBuffer = &_frontBuffer[sy * XPIX + sx]; // Destination ptr
+	overlay = &_vm.getFirstOverlay()[(sy * XPIX + sx) >> 3];    // Overlay ptr
+	frontBufferwrap = XPIX - seq->x2 - 1;           // Wraps dest_p after each line
+	imageWrap = seq->bytesPerLine8 - seq->x2 - 1;
+
+	for (y = 0; y < seq->lines; y++) {              // Each line in object
+		for (x = 0; x <= seq->x2; x++) {
+			if (*image) {                           // Non-transparent
+				overlay = _vm.getFirstOverlay() + ((uint16)(subFrontBuffer - _frontBuffer) >> 3);       // Ptr into overlay bits
+				if (*overlay & (0x80 >> ((uint16)(subFrontBuffer - _frontBuffer) & 7))) {   // Overlay bit is set
+					if (overlayState == UNDEF)      // Overlay defined yet?
+						overlayState = findOvl(seq, subFrontBuffer, y);// No, find it.
+					if (foreFl || overlayState == FG)   // Object foreground
+						*subFrontBuffer = *image;   // Copy pixel
+				} else                              // No overlay
+					*subFrontBuffer = *image;       // Copy pixel
+			}
+			image++;
+			subFrontBuffer++;
+		}
+		image += imageWrap;
+		subFrontBuffer += frontBufferwrap;
+	}
+
+	// Add this rectangle to the display list
+	displayList(D_ADD, sx, sy, seq->x2 + 1, seq->lines);
+}
+
+// Merge rectangles A,B leaving result in B
+void Screen::merge(rect_t *rectA, rect_t *rectB) {
+	debugC(6, kDebugDisplay, "merge");
+
+	int16 xa = rectA->x + rectA->dx;                // Find x2,y2 for each rectangle
+	int16 xb = rectB->x + rectB->dx;
+	int16 ya = rectA->y + rectA->dy;
+	int16 yb = rectB->y + rectB->dy;
+
+	rectB->x = MIN(rectA->x, rectB->x);             // Minimum x,y
+	rectB->y = MIN(rectA->y, rectB->y);
+	rectB->dx = MAX(xa, xb) - rectB->x;             // Maximum dx,dy
+	rectB->dy = MAX(ya, yb) - rectB->y;
+}
+
+// Coalesce the rectangles in the restore/add list into one unified
+// blist.  len is the sizes of alist or rlist.  blen is current length
+// of blist.  bmax is the max size of the blist.  Note that blist can
+// have holes, in which case dx = 0.  Returns used length of blist.
+int16 Screen::mergeLists(rect_t *list, rect_t *blist, int16 len, int16 blen, int16 bmax) {
+	int16   coalesce[BMAX];                         // List of overlapping rects
+
+	debugC(4, kDebugDisplay, "mergeLists");
+
+	// Process the list
+	for (int16 a = 0; a < len; a++, list++) {
+		// Compile list of overlapping rectangles in blit list
+		int16 c = 0;
+		rect_t *bp = blist;
+		for (int16 b = 0; b < blen; b++, bp++)
+			if (bp->dx)                             // blist entry used
+				if (OVERLAP(list, bp))
+					coalesce[c++] = b;
+
+		// Any overlapping blit rects?
+		if (c == 0)                                 // None, add a new entry
+			blist[blen++] = *list;
+		else {                                      // At least one overlapping
+			// Merge add-list entry with first blist entry
+			bp = &blist[coalesce[0]];
+			merge(list, bp);
+
+			// Merge any more blist entries
+			while (--c) {
+				rect_t *cp = &blist[coalesce[c]];
+				merge(cp, bp);
+				cp->dx = 0;                         // Delete entry
+			}
+		}
+	}
+	return blen;
+}
+
+// Process the display list
+// Trailing args are int16 x,y,dx,dy for the D_ADD operation
+void Screen::displayList(dupdate_t update, ...) {
+	static int16  addIndex, restoreIndex;           // Index into add/restore lists
+	static rect_t restoreList[DMAX];                // The restore list
+	static rect_t addList[DMAX];                    // The add list
+	static rect_t blistList[BMAX];                  // The blit list
+	int16         blitLength = 0;                   // Length of blit list
+	rect_t       *p;                                // Ptr to dlist entry
+	va_list       marker;                           // Args used for D_ADD operation
+
+	debugC(6, kDebugDisplay, "displayList");
+
+	switch (update) {
+	case D_INIT:                                    // Init lists, restore whole screen
+		addIndex = restoreIndex = 0;
+		memcpy(_frontBuffer, _backBuffer, sizeof(_frontBuffer));
+		break;
+	case D_ADD:                                     // Add a rectangle to list
+		if (addIndex >= DMAX) {
+			Utils::Warn(false, "Display list exceeded");
+			return;
+		}
+		va_start(marker, update);                   // Initialize variable arguments
+		p = &addList[addIndex];
+		p->x  = va_arg(marker, int);                // x
+		p->y  = va_arg(marker, int);                // y
+		p->dx = va_arg(marker, int);                // dx
+		p->dy = va_arg(marker, int);                // dy
+		va_end(marker);                             // Reset variable arguments
+		addIndex++;
+		break;
+	case D_DISPLAY:                                 // Display whole list
+		// Don't blit if newscreen just loaded because _frontBuffer will
+		// get blitted via InvalidateRect() at end of this cycle
+		// and blitting here causes objects to appear too soon.
+		if (_vm.getGameStatus().newScreenFl) {
+			_vm.getGameStatus().newScreenFl = false;
+			break;
+		}
+
+		// Coalesce restore-list, add-list into combined blit-list
+		blitLength = mergeLists(restoreList, blistList, restoreIndex, blitLength, BMAX);
+		blitLength = mergeLists(addList, blistList, addIndex,  blitLength, BMAX);
+
+		// Blit the combined blit-list
+		for (restoreIndex = 0, p = blistList; restoreIndex < blitLength; restoreIndex++, p++)
+			if (p->dx)                              // Marks a used entry
+				displayRect(p->x, p->y, p->dx, p->dy);
+		break;
+	case D_RESTORE:                                 // Restore each rectangle
+		for (restoreIndex = 0, p = addList; restoreIndex < addIndex; restoreIndex++, p++) {
+			// Restoring from _backBuffer to _frontBuffer
+			restoreList[restoreIndex] = *p;         // Copy add-list to restore-list
+			moveImage(_backBuffer, p->x, p->y, p->dx, p->dy, XPIX, _frontBuffer, p->x, p->y, XPIX);
+		}
+		addIndex = 0;                               // Reset add-list
+		break;
+	}
+}
+
+void Screen::writeChr(int sx, int sy, byte color, char *local_fontdata) {
+	/*
+	    Write supplied character (font data) at sx,sy in supplied color
+	    Font data as follows:
+
+	    *(fontdata+1) = Font Height (pixels)
+	    *(fontdata+1) = Font Width (pixels)
+	    *(fontdata+x) = Font Bitmap (monochrome)
+	*/
+
+	debugC(2, kDebugDisplay, "writeChr(%d, %d, %d, %d)", sx, sy, color, local_fontdata[0]);
+
+	byte height = local_fontdata[0];
+	byte width = 8; //local_fontdata[1];
+
+	//warning("STUB: writechr(sx %u, sy %u, color %u, height %u, width %u)", sx, sy, color, height, width);
+
+	// This can probably be optimized quite a bit...
+	for (int y = 0; y < height; ++y)
+		for (int x = 0; x < width; ++x) {
+			int pixel = y * width + x;
+			int bitpos = pixel % 8;
+			int offset = pixel / 8;
+			byte bitTest = (1 << bitpos);
+			if ((local_fontdata[2 + offset] & bitTest) == bitTest)
+				_frontBuffer[(sy + y) * 320 + sx + x] = color;
+			//printf("offset: %u, bitpos %u\n", offset, bitpos);
+		}
+}
+
+// Returns height of characters in current font
+int16 Screen::fontHeight() {
+	debugC(2, kDebugDisplay, "fontHeight");
+
+	static int16 height[NUM_FONTS] = {5, 7, 8};
+	return(height[_fnt - FIRST_FONT]);
+}
+
+/* TODO: Suppress block if it's confirmed to be useless */
+// static int16 Char_len (char c) {
+// /* Returns length of single character in pixels */
+//	return (*(_font[_fnt][c] + 1) + 1);
+// }
+
+
+// Returns length of supplied string in pixels
+int16 Screen::stringLength(char *s) {
+	int16 sum;
+	byte **fontArr = _font[_fnt];
+
+	debugC(2, kDebugDisplay, "stringLength(%s)", s);
+
+	for (sum = 0; *s; s++)
+		sum += *(fontArr[*s] + 1) + 1;
+
+	return(sum);
+}
+
+// Return x which would center supplied string
+int16 Screen::center(char *s) {
+	debugC(1, kDebugDisplay, "center(%s)", s);
+
+	return ((int16)((XPIX - stringLength(s)) >> 1));
+}
+
+// Write string at sx,sy in supplied color in current font
+// If sx == CENTER, center it
+void Screen::writeStr(int16 sx, int16 sy, char *s, byte color) {
+	byte **font = _font[_fnt];
+
+	debugC(2, kDebugDisplay, "writeStr(%d, %d, %s, %d)", sx, sy, s, color);
+
+	if (sx == CENTER)
+		sx = center(s);
+
+	for (; *s; s++) {
+		writeChr(sx, sy, color, (char *)font[*s]);
+		sx += *(font[*s] + 1) + 1;
+	}
+}
+
+// Shadowed version of writestr
+void Screen::shadowStr(int16 sx, int16 sy, char *s, byte color) {
+	debugC(1, kDebugDisplay, "shadowStr(%d, %d, %s, %d)", sx, sy, s, color);
+
+	if (sx == CENTER)
+		sx = center(s);
+
+	writeStr(sx + 1, sy + 1, s, _TBLACK);
+	writeStr(sx, sy, s, color);
+}
+
+// Load font file, construct font ptrs and reverse data bytes
+void Screen::loadFont(int16 fontId) {
+	byte  height, width;
+	static bool fontLoadedFl[NUM_FONTS] = {0, 0, 0};
+
+	debugC(2, kDebugDisplay, "loadFont(%d)", fontId);
+
+	_fnt = fontId - FIRST_FONT;                     // Set current font number
+
+	if (fontLoadedFl[_fnt])                             // If already loaded, return
+		return;
+
+	fontLoadedFl[_fnt] = true;
+	_vm.file().readUIFItem(fontId, _fontdata[_fnt]);
+
+	// Compile font ptrs.  Note: First ptr points to height,width of font
+	_font[_fnt][0] = _fontdata[_fnt];               // Store height,width of fonts
+
+	int16 offset = 2;                                       // Start at fontdata[2] ([0],[1] used for height,width)
+
+	// Setup the font array (127 characters)
+	for (int i = 1; i < 128; i++) {
+		_font[_fnt][i] = _fontdata[_fnt] + offset;
+		height = *(_fontdata[_fnt] + offset);
+		width  = *(_fontdata[_fnt] + offset + 1);
+
+		int16 size = height * ((width + 7) >> 3);
+		for (int j = 0; j < size; j++)
+			Utils::reverseByte(&_fontdata[_fnt][offset + 2 + j]);
+
+		offset += 2 + size;
+	}
+
+	// for (i = 0; i < 128; ++i) {
+	//      if( (char)i != 'f' && (char)i != '\\'){
+	//          continue;
+	//      }
+	//      int myHeight = _font[_fnt][i][0];
+	//      int myWidth = _font[_fnt][i][1];
+	//      printf("\n\nFor the letter %c, (%u, %u):\n", i, myWidth, myHeight);
+	//      for (int y = 0; y < myHeight; ++y) {
+	//          for (int x = 0; x < 8; ++x) {
+	//              int pixel = y * (8) + x;
+	//              int bitpos = pixel % 8;
+	//              int offset = pixel / 8;
+	//              byte bitTest = (1 << bitpos);
+	//              if ((_font[_fnt][i][2 + offset] & bitTest) == bitTest)
+	//                  printf("1");
+	//              else
+	//                  printf("0");
+	//          }
+	//          printf("\n");
+	//      }
+	//  }
+}
+
+void Screen::userHelp() {
+// Introduce user to the game
+// DOS versions Only
+	Utils::Box(BOX_ANY , "F1  - Press F1 again\n"
+	           "      for instructions\n"
+	           "F2  - Sound on/off\n"
+	           "F3  - Recall last line\n"
+	           "F4  - Save game\n"
+	           "F5  - Restore game\n"
+	           "F6  - Inventory\n"
+	           "F8  - Turbo button\n"
+	           "F9  - Boss button\n\n"
+	           "ESC - Return to game");
+}
+
+} // end of namespace Hugo


Property changes on: scummvm/trunk/engines/hugo/display.cpp
___________________________________________________________________
Added: svn:executable
   + *
Added: svn:mime-type
   + text/plain
Added: svn:keywords
   + Date Rev Author URL Id
Added: svn:eol-style
   + native

Added: scummvm/trunk/engines/hugo/display.h
===================================================================
--- scummvm/trunk/engines/hugo/display.h	                        (rev 0)
+++ scummvm/trunk/engines/hugo/display.h	2010-08-17 09:28:20 UTC (rev 52137)
@@ -0,0 +1,109 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+/*
+ * This code is based on original Hugo Trilogy source code
+ *
+ * Copyright (c) 1989-1995 David P. Gray
+ *
+ */
+
+#ifndef HUGO_DISPLAY_H
+#define HUGO_DISPLAY_H
+namespace Hugo {
+
+enum overlayState_t {UNDEF, FG, BG};                        // Overlay state
+struct rect_t;
+
+class Screen {
+public:
+	Screen(HugoEngine &vm);
+
+	int16    fontHeight();
+	int16    stringLength(char *s);
+
+	void     displayBackground();
+	void     displayFrame(int sx, int sy, seq_t *seq, bool foreFl);
+	void     displayList(dupdate_t update, ...);
+	void     displayRect(int16 x, int16 y, int16 dx, int16 dy);
+	void     initDisplay();
+	void     loadFont(int16 fontId);
+	void     moveImage(image_pt srcImage, uint16 x1, uint16 y1, uint16 dx, uint16 dy, uint16 width1, image_pt dstImage, uint16 x2, uint16 y2, uint16 width2);
+	void     remapPal(uint16 oldIndex, uint16 newIndex);
+	void     restorePal(Common::SeekableReadStream *f);
+	void     savePal(Common::WriteStream *f);
+	void     setBackgroundColor(long color);
+	void     shadowStr(int16 sx, int16 sy, char *s, byte color);
+	void     userHelp();
+	void     writeChar(int16 x, int16 y, char c, byte color);
+	void     writeStr(int16 sx, int16 sy, char *s, byte color);
+
+	icondib_t &getIconBuffer() {
+		return _iconBuffer;
+	}
+	viewdib_t &getBackBuffer() {
+		return _backBuffer;
+	}
+	viewdib_t &getBackBufferBackup() {
+		return _backBufferBackup;
+	}
+	viewdib_t &getFrontBuffer() {
+		return _frontBuffer;
+	}
+	viewdib_t &getGUIBuffer() {
+		return _GUIBuffer;
+	}
+
+private:
+	HugoEngine &_vm;
+
+	// Fonts used in dib (non-GDI)
+	byte _fnt;                                      // Current font number
+	byte _fontdata[NUM_FONTS][FONTSIZE];            // Font data
+	byte *_font[NUM_FONTS][FONT_LEN];               // Ptrs to each char
+
+	viewdib_t _frontBuffer;
+	viewdib_t _backBuffer;
+	viewdib_t _GUIBuffer;                           // User interface images
+	viewdib_t _backBufferBackup;                    // Backup _backBuffer during inventory
+	icondib_t _iconBuffer;                          // Inventory icon DIB
+
+	void createPal();
+	overlayState_t findOvl(seq_t *seq_p, image_pt dst_p, uint16 y);
+	void merge(rect_t *rectA, rect_t *rectB);
+	int16 mergeLists(rect_t *list, rect_t *blist, int16 len, int16 blen, int16 bmax);
+	void writeChr(int sx, int sy, byte color, char *local_fontdata);
+	int16 center(char *s);
+
+// Also used in rout.cpp when DEBUG_ROUTE is defined
+	unsigned int GetPalIndex(byte color);
+
+// Useless ?
+	void     clearPromptLine();
+
+};
+
+} // end of namespace Hugo
+#endif //HUGO_DISPLAY_H


Property changes on: scummvm/trunk/engines/hugo/display.h
___________________________________________________________________
Added: svn:executable
   + *
Added: svn:mime-type
   + text/plain
Added: svn:keywords
   + Date Rev Author URL Id
Added: svn:eol-style
   + native

Added: scummvm/trunk/engines/hugo/engine.cpp
===================================================================
--- scummvm/trunk/engines/hugo/engine.cpp	                        (rev 0)
+++ scummvm/trunk/engines/hugo/engine.cpp	2010-08-17 09:28:20 UTC (rev 52137)
@@ -0,0 +1,993 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+/*
+ * This code is based on original Hugo 1-3 Trilogy source code
+ *
+ * Copyright (c) 1989-1995 David P. Gray
+ *
+ */
+
+#include "common/system.h"
+#include "common/random.h"
+#include "common/EventRecorder.h"
+
+#include "hugo/game.h"
+#include "hugo/hugo.h"
+#include "hugo/engine.h"
+#include "hugo/global.h"
+#include "hugo/file.h"
+#include "hugo/schedule.h"
+#include "hugo/display.h"
+#include "hugo/parser.h"
+#include "hugo/route.h"
+#include "hugo/util.h"
+#include "hugo/sound.h"
+
+namespace Hugo {
+
+#define EDGE           10                       // Closest object can get to edge of screen
+#define EDGE2          (EDGE * 2)           // Push object further back on edge collision
+#define SHIFT          8                            // Place hero this far inside bounding box
+#define MAX_OBJECTS    128              // Used in Update_images()
+#define BOUND(X, Y)   ((_boundary[Y * XBYTES + X / 8] & (0x80 >> X % 8)) != 0)  // Boundary bit set
+
+config_t    _config;                                // User's config
+maze_t      _maze = {false, 0, 0, 0, 0, 0, 0, 0, 0}; // Default to not in maze
+hugo_boot_t _boot;                                  // Boot info structure file
+char        _textBoxBuffer[MAX_BOX];                // Buffer for text box
+command_t   _line = "";                             // Line of user text input
+
+
+// Sets the playlist to be the default tune selection
+void HugoEngine::initPlaylist(bool playlist[MAX_TUNES]) {
+	debugC(1, kDebugEngine, "initPlaylist");
+
+	for (int16 i = 0; i < MAX_TUNES; i++)
+		playlist[i] = false;
+	for (int16 i = 0; _defltTunes[i] != -1; i++)
+		playlist[_defltTunes[i]] = true;
+}
+
+// Initialize the dynamic game status
+void HugoEngine::initStatus() {
+	debugC(1, kDebugEngine, "initStatus");
+	_status.initSaveFl    = false;                  // Don't force initial save
+	_status.storyModeFl   = false;                  // Not in story mode
+	_status.gameOverFl    = false;                  // Hero not knobbled yet
+	_status.recordFl      = false;                  // Not record mode
+	_status.playbackFl    = false;                  // Not playback mode
+	_status.demoFl        = false;                  // Not demo mode
+	_status.textBoxFl     = false;                  // Not processing a text box
+//	Strangerke - Not used ?
+//	_status.mmtime        = false;                   // Multimedia timer support
+	_status.lookFl        = false;                  // Toolbar "look" button
+	_status.recallFl      = false;                  // Toolbar "recall" button
+	_status.leftButtonFl  = false;                  // Left mouse button pressed
+	_status.rightButtonFl = false;                  // Right mouse button pressed
+	_status.newScreenFl   = false;                  // Screen not just loaded
+	_status.jumpExitFl    = false;                  // Can't jump to a screen exit
+	_status.godModeFl     = false;                  // No special cheats allowed
+	_status.helpFl        = false;                  // Not calling WinHelp()
+	_status.path[0]       = 0;                      // Path to write files
+	_status.saveSlot      = 0;                      // Slot to save/restore game
+	_status.screenWidth   = 0;                      // Desktop screen width
+
+	// Initialize every start of new game
+	_status.tick            = 0;                    // Tick count
+	_status.saveTick        = 0;                    // Time of last save
+	_status.viewState       = V_IDLE;               // View state
+	_status.inventoryState  = I_OFF;                // Inventory icon bar state
+	_status.inventoryHeight = 0;                    // Inventory icon bar pos
+	_status.inventoryObjId  = -1;                   // Inventory object selected (none)
+	_status.routeIndex      = -1;                   // Hero not following a route
+	_status.go_for          = GO_SPACE;             // Hero walking to space
+	_status.go_id           = -1;                   // Hero not walking to anything
+}
+
+// Initialize default config values.  Must be done before Initialize().
+// Reset needed to save config.cx,cy which get splatted during OnFileNew()
+void HugoEngine::initConfig(inst_t action) {
+	static int16 cx, cy;                    // Save window size, pos
+	int16        i;
+
+	debugC(1, kDebugEngine, "initConfig(%d)", action);
+
+	switch (action) {
+	case INSTALL:
+		_config.musicFl = true;                     // Music state initially on
+		_config.soundFl = true;                     // Sound state initially on
+		_config.turboFl = false;                    // Turbo state initially off
+		_config.backgroundMusicFl = false;          // No music when inactive
+		_config.cx = VIEW_DX * 2;                   // Window view size
+		_config.cy = VIEW_DY * 2;
+
+//		_config.wx = 0;
+//		_config.wy = 0;
+
+		_config.musicVolume = 85;                   // Music volume %
+		_config.soundVolume = 100;                  // Sound volume %
+		initPlaylist(_config.playlist);             // Initialize default tune playlist
+
+		HugoEngine::get().file().readBootFile();    // Read startup structure
+		HugoEngine::get().file().readConfig();      // Read user's saved config
+
+		cx = _config.cx;                            // Save these around OnFileNew()
+		cy = _config.cy;
+//		wx = _config.wx;
+//		wy = _config.wy;
+		break;
+	case RESET:
+		_config.cx = cx;                            // Restore cx, cy
+		_config.cy = cy;
+//		_config.wx = wx;
+//		_config.wy = wy;
+
+		// Find first tune and play it
+		for (i = 0; i < MAX_TUNES; i++)
+			if (_config.playlist[i]) {
+				sound().playMusic(i);
+				break;
+			}
+
+		HugoEngine::get().file().initSavedGame();   // Initialize saved game
+		break;
+	case RESTORE:
+		warning("Unhandled action RESTORE");
+		break;
+	}
+}
+void HugoEngine::initialize() {
+	debugC(1, kDebugEngine, "initialize");
+
+	sound().initSound(INSTALL);
+	HugoEngine::get().scheduler().initEventQueue(); // Init scheduler stuff
+	screen().initDisplay();                         // Create Dibs and palette
+	HugoEngine::get().file().openDatabaseFiles();   // Open database files
+	calcMaxScore();                                 // Initialise maxscore
+
+	_rnd = new Common::RandomSource();
+	g_eventRec.registerRandomSource(*_rnd, "hugo");
+
+	_rnd->setSeed(42);                              // Kick random number generator
+
+	switch (getGameType()) {
+	case kGameTypeHugo1:
+		_episode = "\"HUGO'S HOUSE OF HORRORS\"";
+		_picDir = "";
+		break;
+	case kGameTypeHugo2:
+		_episode = "\"Hugo's Mystery Adventure\"";
+		_picDir = "hugo2/";
+		break;
+	case kGameTypeHugo3:
+		_episode = "\"Hugo's Amazon Adventure\"";
+		_picDir = "hugo3/";
+		break;
+	default:
+		error("Unknown game");
+	}
+}
+
+// Restore all resources before termination
+void HugoEngine::shutdown() {
+	debugC(1, kDebugEngine, "shutdown");
+
+	sound().initSound(RESTORE);
+
+	HugoEngine::get().file().closeDatabaseFiles();
+	if (_status.recordFl || _status.playbackFl)
+		HugoEngine::get().file().closePlaybackFile();
+	freeObjects();
+}
+
+void HugoEngine::readObjectImages() {
+	debugC(1, kDebugEngine, "readObjectImages");
+
+	for (int i = 0; i < _numObj; i++)
+		HugoEngine::get().file().readImage(i, &_objects[i]);
+}
+
+// Read the uif image file (inventory icons)
+void HugoEngine::readUIFImages() {
+	debugC(1, kDebugEngine, "readUIFImages");
+
+	HugoEngine::get().file().readUIFItem(UIF_IMAGES, screen().getGUIBuffer());              // Read all uif images
+}
+
+// Read scenery, overlay files for given screen number
+void HugoEngine::readScreenFiles(int screenNum) {
+	debugC(1, kDebugEngine, "readScreenFiles(%d)", screenNum);
+
+	HugoEngine::get().file().readBackground(screenNum);                     // Scenery file
+	memcpy(screen().getBackBuffer(), screen().getFrontBuffer(), sizeof(screen().getFrontBuffer()));// Make a copy
+	HugoEngine::get().file().readOverlay(screenNum, _boundary, BOUNDARY);       // Boundary file
+	HugoEngine::get().file().readOverlay(screenNum, _overlay, OVERLAY);         // Overlay file
+	HugoEngine::get().file().readOverlay(screenNum, _ovlBase, OVLBASE);         // Overlay base file
+}
+
+// Update all object positions.  Process object 'local' events
+// including boundary events and collisions
+void HugoEngine::moveObjects() {
+	object_t *obj;
+	seq_t    *currImage;
+	int       x1, x2, y1, y2;                       // object coordinates
+	int       dx, dy;                               // Allowable motion wrt boundary
+	char      radius;                               // Radius for chase (8 bit signed)
+
+	debugC(4, kDebugEngine, "moveObjects");
+
+	// If route mode enabled, do special route processing
+	if (_status.routeIndex >= 0)
+		route().processRoute();
+
+	// Perform any adjustments to velocity based on special path types
+	// and store all (visible) object baselines into the boundary file.
+	// Don't store foreground or background objects
+	for (int i = 0; i < _numObj; i++) {
+		obj = &_objects[i];                         // Get pointer to object
+		currImage = obj->currImagePtr;              // Get ptr to current image
+		if (obj->screenIndex == *_screen_p) {
+			switch (obj->pathType) {
+			case CHASE:
+			case CHASE2:
+				radius = obj->radius;               // Default to object's radius
+				if (radius < 0)                     // If radius infinity, use closer value
+					radius = DX;
+
+				dx = _hero->x + _hero->currImagePtr->x1 - obj->x - currImage->x1;
+				dy = _hero->y + _hero->currImagePtr->y2 - obj->y - currImage->y2 - 1;
+				if (abs(dx) <= radius)
+					obj->vx = 0;
+				else
+					obj->vx = dx > 0 ? MIN(dx, obj->vxPath) : MAX(dx, -obj->vxPath);
+				if (abs(dy) <= radius)
+					obj->vy = 0;
+				else
+					obj->vy = dy > 0 ? MIN(dy, obj->vyPath) : MAX(dy, -obj->vyPath);
+
+				// Set first image in sequence (if multi-seq object)
+				switch (obj->seqNumb) {
+				case 4:
+					if (!obj->vx) {                 // Got 4 directions
+						if (obj->vx != obj->oldvx)  // vx just stopped
+							if (dy >= 0)
+								obj->currImagePtr = obj->seqList[DOWN].seqPtr;
+							else
+								obj->currImagePtr = obj->seqList[_UP].seqPtr;
+					} else if (obj->vx != obj->oldvx)
+						if (dx > 0)
+							obj->currImagePtr = obj->seqList[RIGHT].seqPtr;
+						else
+							obj->currImagePtr = obj->seqList[LEFT].seqPtr;
+					break;
+				case 3:
+				case 2:
+					if (obj->vx != obj->oldvx)      // vx just stopped
+						if (dx > 0)                 // Left & right only
+							obj->currImagePtr = obj->seqList[RIGHT].seqPtr;
+						else
+							obj->currImagePtr = obj->seqList[LEFT].seqPtr;
+					break;
+				}
+
+				if (obj->vx || obj->vy)
+					obj->cycling = CYCLE_FORWARD;
+				else {
+					obj->cycling = NOT_CYCLING;
+					boundaryCollision(obj);         // Must have got hero!
+				}
+				obj->oldvx = obj->vx;
+				obj->oldvy = obj->vy;
+				currImage = obj->currImagePtr;      // Get (new) ptr to current image
+				break;
+			case WANDER2:
+			case WANDER:
+				if (!_rnd->getRandomNumber(3 * NORMAL_TPS)) {       // Kick on random interval
+					obj->vx = _rnd->getRandomNumber(obj->vxPath << 1) - obj->vxPath;
+					obj->vy = _rnd->getRandomNumber(obj->vyPath << 1) - obj->vyPath;
+
+					// Set first image in sequence (if multi-seq object)
+					if (obj->seqNumb > 1) {
+						if (!obj->vx && (obj->seqNumb >= 4)) {
+							if (obj->vx != obj->oldvx)  // vx just stopped
+								if (obj->vy > 0)
+									obj->currImagePtr = obj->seqList[DOWN].seqPtr;
+								else
+									obj->currImagePtr = obj->seqList[_UP].seqPtr;
+						} else if (obj->vx != obj->oldvx)
+							if (obj->vx > 0)
+								obj->currImagePtr = obj->seqList[RIGHT].seqPtr;
+							else
+								obj->currImagePtr = obj->seqList[LEFT].seqPtr;
+					}
+					obj->oldvx = obj->vx;
+					obj->oldvy = obj->vy;
+					currImage = obj->currImagePtr;  // Get (new) ptr to current image
+				}
+				if (obj->vx || obj->vy)
+					obj->cycling = CYCLE_FORWARD;
+				break;
+			default:
+				; // Really, nothing
+			}
+			// Store boundaries
+			if ((obj->cycling > ALMOST_INVISIBLE) && (obj->priority == FLOATING))
+				storeBoundary(obj->x + currImage->x1, obj->x + currImage->x2, obj->y + currImage->y2);
+		}
+	}
+
+	// Move objects, allowing for boundaries
+	for (int i = 0; i < _numObj; i++) {
+		obj = &_objects[i];                         // Get pointer to object
+		if ((obj->screenIndex == *_screen_p) && (obj->vx || obj->vy)) {
+			// Only process if it's moving
+
+			// Do object movement.  Delta_x,y return allowed movement in x,y
+			//  to move as close to a boundary as possible without crossing it.
+			currImage = obj->currImagePtr;          // Get ptr to current image
+			x1 = obj->x + currImage->x1;            // Left edge of object
+			x2 = obj->x + currImage->x2;            // Right edge
+			y1 = obj->y + currImage->y1;            // Top edge
+			y2 = obj->y + currImage->y2;            // Bottom edge
+
+			if ((obj->cycling > ALMOST_INVISIBLE) && (obj->priority == FLOATING))
+				clearBoundary(x1, x2, y2);          // Clear our own boundary
+			dx = deltaX(x1, x2, obj->vx, y2);
+			if (dx != obj->vx) {
+				// An object boundary collision!
+				boundaryCollision(obj);
+				obj->vx = 0;
+			}
+
+			dy = deltaY(x1, x2, obj->vy, y2);
+
+			if (dy != obj->vy) {
+				// An object boundary collision!
+				boundaryCollision(obj);
+				obj->vy = 0;
+			}
+
+			if ((obj->cycling > ALMOST_INVISIBLE) && (obj->priority == FLOATING))
+				storeBoundary(x1, x2, y2);          // Re-store our own boundary
+
+			obj->x += dx;                           // Update object position
+			obj->y += dy;
+
+			// Don't let object go outside screen
+			if (x1 < EDGE)
+				obj->x = EDGE2;
+			if (x2 > (XPIX - EDGE))
+				obj->x = XPIX - EDGE2 - (x2 - x1);
+			if (y1 < EDGE)
+				obj->y = EDGE2;
+			if (y2 > (YPIX - EDGE))
+				obj->y = YPIX - EDGE2 - (y2 - y1);
+
+			if ((obj->vx == 0) && (obj->vy == 0) && (obj->pathType != WANDER2) && (obj->pathType != CHASE2))
+				obj->cycling = NOT_CYCLING;
+		}
+	}
+
+	// Clear all object baselines from the boundary file.
+	for (int i = 0; i < _numObj; i++) {
+		obj = &_objects[i];                         // Get pointer to object
+		currImage = obj->currImagePtr;              // Get ptr to current image
+		if ((obj->screenIndex == *_screen_p) && (obj->cycling > ALMOST_INVISIBLE) && (obj->priority == FLOATING))
+			clearBoundary(obj->oldx + currImage->x1, obj->oldx + currImage->x2, obj->oldy + currImage->y2);
+	}
+
+	// If maze mode is enabled, do special maze processing
+	if (_maze.enabledFl)
+		processMaze();
+}
+
+// Return maximum allowed movement (from zero to vx) such that object does
+// not cross a boundary (either background or another object)
+int HugoEngine::deltaX(int x1, int x2, int vx, int y) {
+// Explanation of algorithm:  The boundaries are drawn as contiguous
+// lines 1 pixel wide.  Since DX,DY are not necessarily 1, we must
+// detect boundary crossing.  If vx positive, examine each pixel from
+// x1 old to x2 new, else x2 old to x1 new, both at the y2 line.
+// If vx zero, no need to check.  If vy non-zero then examine each
+// pixel on the line segment x1 to x2 from y old to y new.
+// Fix from Hugo I v1.5:
+// Note the diff is munged in the return statement to cater for a special
+// cases arising from differences in image widths from one sequence to
+// another.  The problem occurs reversing direction at a wall where the
+// new image intersects before the object can move away.  This is cured
+// by comparing the intersection with half the object width pos. If the
+// intersection is in the other half wrt the intended direction, use the
+// desired vx, else use the computed delta.  i.e. believe the desired vx
+	int b;
+
+	debugC(3, kDebugEngine, "deltaX(%d, %d, %d, %d)", x1, x2, vx, y);
+
+	if (vx == 0)
+		return(0);                                  // Object stationary
+
+	y *= XBYTES;                                    // Offset into boundary file
+	if (vx > 0) {
+		// Moving to right
+		for (int i = x1 >> 3; i <= (x2 + vx) >> 3; i++) // Search by byte
+			if ((b = Utils::firstBit((byte)(_boundary[y + i] | _objBound[y + i]))) < 8) {   // b is index or 8
+				// Compute x of boundary and test if intersection
+				b += i << 3;
+				if ((b >= x1) && (b <= x2 + vx))
+					return((b < x1 + ((x2 - x1) >> 1)) ? vx : b - x2 - 1); // return dx
+			}
+	} else {
+		// Moving to left
+		for (int i = x2 >> 3; i >= (x1 + vx) >> 3; i--)// Search by byte
+			if ((b = Utils::lastBit((byte)(_boundary[y + i] | _objBound[y + i]))) < 8) {    // b is index or 8
+				// Compute x of boundary and test if intersection
+				b += i << 3;
+				if ((b >= x1 + vx) && (b <= x2))
+					return((b > x1 + ((x2 - x1) >> 1)) ? vx : b - x1 + 1); // return dx
+			}
+	}
+	return(vx);
+}
+
+// Similar to Delta_x, but for movement in y direction.  Special case of
+// bytes at end of line segment; must only count boundary bits falling on
+// line segment.
+int HugoEngine::deltaY(int x1, int x2, int vy, int y) {
+	int inc, i, j, b;
+
+	debugC(3, kDebugEngine, "deltaY(%d, %d, %d, %d)", x1, x2, vy, y);
+
+	if (vy == 0)
+		return(0);                                  // Object stationary
+
+	inc = (vy > 0 ? 1 : -1);
+	for (j = y + inc; j != (y + vy + inc); j += inc) //Search by byte
+		for (i = x1 >> 3; i <= x2 >> 3; i++)
+			if (b = _boundary[j * XBYTES + i] | _objBound[j * XBYTES + i]) {    // Any bit set
+				// Make sure boundary bits fall on line segment
+				if (i == (x2 >> 3))                 // Adjust right end
+					b &= 0xff << ((i << 3) + 7 - x2);
+				else if (i == (x1 >> 3))            // Adjust left end
+					b &= 0xff >> (x1 - (i << 3));
+				if (b)
+					return(j - y - inc);
+			}
+	return(vy);
+}
+
+// Store a horizontal line segment in the object boundary file
+void HugoEngine::storeBoundary(int x1, int x2, int y) {
+	byte *b;                                        // ptr to boundary byte
+
+	debugC(5, kDebugEngine, "storeBoundary(%d, %d, %d)", x1, x2, y);
+
+	for (int i = x1 >> 3; i <= x2 >> 3; i++) {      // For each byte in line
+		b = &_objBound[y * XBYTES + i];             // get boundary byte
+		if (i == x2 >> 3)                           // Adjust right end
+			*b |= 0xff << ((i << 3) + 7 - x2);
+		else if (i == x1 >> 3)                      // Adjust left end
+			*b |= 0xff >> (x1 - (i << 3));
+		else
+			*b = 0xff;
+	}
+}
+
+// Clear a horizontal line segment in the object boundary file
+void HugoEngine::clearBoundary(int x1, int x2, int y) {
+	int  i;
+	byte *b;                                        // ptr to boundary byte
+
+	debugC(5, kDebugEngine, "clearBoundary(%d, %d, %d)", x1, x2, y);
+
+	for (i = x1 >> 3; i <= x2 >> 3; i++) {          // For each byte in line
+		b = &_objBound[y * XBYTES + i];             // get boundary byte
+		if (i == x2 >> 3)                           // Adjust right end
+			*b &= ~(0xff << ((i << 3) + 7 - x2));
+		else if (i == x1 >> 3)                      // Adjust left end
+			*b &= ~(0xff >> (x1 - (i << 3)));
+		else
+			*b = 0;
+	}
+}
+
+// Maze mode is enabled.  Check to see whether hero has crossed the maze
+// bounding box, if so, go to the next room */
+void HugoEngine::processMaze() {
+	seq_t      *currImage;
+	int         x1, x2, y1, y2;                     // hero coordinates
+
+	debugC(1, kDebugEngine, "processMaze");
+
+	//actlist     alnewscr  = {&aheroxy,&astophero,&aherostop,&anewscr,NULL};
+	//actlist_pt  alist     = &alnewscr[0];
+
+	currImage = _hero->currImagePtr;                // Get ptr to current image
+	x1 = _hero->x + currImage->x1;                  // Left edge of object
+	x2 = _hero->x + currImage->x2;                  // Right edge
+	y1 = _hero->y + currImage->y1;                  // Top edge
+	y2 = _hero->y + currImage->y2;                  // Bottom edge
+
+	if (x1 < _maze.x1) {
+		// Exit west
+//		anewscr.screen = *_screen_p - 1;
+		_actListArr[_alNewscrIndex][3].a8.screenIndex = *_screen_p - 1;
+//		aheroxy.x = _maze.x2 - SHIFT - (x2 - x1);
+		_actListArr[_alNewscrIndex][0].a2.x = _maze.x2 - SHIFT - (x2 - x1);
+//		aheroxy.y = _hero_p->y;
+		_actListArr[_alNewscrIndex][0].a2.y = _hero->y;
+		_status.routeIndex = -1;
+		HugoEngine::get().scheduler().insertActionList(_alNewscrIndex);
+	} else if (x2 > _maze.x2) {
+		// Exit east
+//			anewscr.screen = *_screen_p + 1;
+		_actListArr[_alNewscrIndex][3].a8.screenIndex = *_screen_p + 1;
+//			aheroxy.x = _maze.x1 + SHIFT;
+		_actListArr[_alNewscrIndex][0].a2.x = _maze.x1 + SHIFT;
+//			aheroxy.y = _hero_p->y;
+		_actListArr[_alNewscrIndex][0].a2.y = _hero->y;
+		_status.routeIndex = -1;
+		HugoEngine::get().scheduler().insertActionList(_alNewscrIndex);
+	} else if (y1 < _maze.y1 - SHIFT) {
+		// Exit north
+//				anewscr.screen = *_screen_p - _maze.size;
+		_actListArr[_alNewscrIndex][3].a8.screenIndex = *_screen_p - _maze.size;
+//				aheroxy.x = _maze.x3;             // special offset for perspective
+		_actListArr[_alNewscrIndex][0].a2.x = _maze.x3;
+//				aheroxy.y = _maze.y2 - SHIFT - (y2 - y1);
+		_actListArr[_alNewscrIndex][0].a2.y = _maze.y2 - SHIFT - (y2 - y1);
+		_status.routeIndex = -1;
+		HugoEngine::get().scheduler().insertActionList(_alNewscrIndex);
+	} else if (y2 > _maze.y2 - SHIFT / 2) {
+		// Exit south
+//					anewscr.screen = *_screen_p + _maze.size;
+		_actListArr[_alNewscrIndex][3].a8.screenIndex = *_screen_p + _maze.size;
+//					aheroxy.x = _maze.x4;            // special offset for perspective
+		_actListArr[_alNewscrIndex][0].a2.x = _maze.x4;
+//					aheroxy.y = _maze.y1 + SHIFT;
+		_actListArr[_alNewscrIndex][0].a2.y = _maze.y1 + SHIFT;
+		_status.routeIndex = -1;
+		HugoEngine::get().scheduler().insertActionList(_alNewscrIndex);
+	}
+}
+
+// Compare function for the quicksort.  The sort is to order the objects in
+// increasing vertical position, using y+y2 as the baseline
+// Returns -1 if ay2 < by2 else 1 if ay2 > by2 else 0
+int y2comp(const void *a, const void *b) {
+	int       ay2, by2;
+
+	debugC(6, kDebugEngine, "y2comp");
+
+	const object_t *p1 = &HugoEngine::get()._objects[*(const byte *)a];
+	const object_t *p2 = &HugoEngine::get()._objects[*(const byte *)b];
+
+	if (p1 == p2)
+		// Why does qsort try the same indexes?
+		return (0);
+
+	if (p1->priority == BACKGROUND)
+		return (-1);
+
+	if (p2->priority == BACKGROUND)
+		return (1);
+
+	if (p1->priority == FOREGROUND)
+		return (1);
+
+	if (p2->priority == FOREGROUND)
+		return (-1);
+
+	ay2 = p1->y + p1->currImagePtr->y2;
+	by2 = p2->y + p2->currImagePtr->y2;
+
+	return(ay2 - by2);
+}
+
+// Draw all objects on screen as follows:
+// 1. Sort 'FLOATING' objects in order of y2 (base of object)
+// 2. Display new object frames/positions in dib
+// Finally, cycle any animating objects to next frame
+void HugoEngine::updateImages() {
+	int       i, j, num_objs;
+	object_t *obj;                                  // Pointer to object
+	seq_t    *seqPtr;                               // Save curr_seq_p
+	byte      objindex[MAX_OBJECTS];                // Array of indeces to objects
+
+	debugC(5, kDebugEngine, "updateImages");
+
+	// Initialise the index array to visible objects in current screen
+	for (i = 0, num_objs = 0; i < _numObj; i++) {
+		obj = &_objects[i];
+		if ((obj->screenIndex == *_screen_p) && (obj->cycling >= ALMOST_INVISIBLE))
+			objindex[num_objs++] = i;
+	}
+
+	// Sort the objects into increasing y+y2 (painter's algorithm)
+	qsort(objindex, num_objs, sizeof(objindex[0]), y2comp);
+
+	// Add each visible object to display list
+	for (i = 0; i < num_objs; i++) {
+		obj = &_objects[objindex[i]];
+		// Count down inter-frame timer
+		if (obj->frameTimer)
+			obj->frameTimer--;
+
+		if (obj->cycling > ALMOST_INVISIBLE)        // Only if visible
+			switch (obj->cycling) {
+			case NOT_CYCLING:
+				screen().displayFrame(obj->x, obj->y, obj->currImagePtr, obj->priority == OVEROVL);
+				break;
+			case CYCLE_FORWARD:
+				if (obj->frameTimer)                // Not time to see next frame yet
+					screen().displayFrame(obj->x, obj->y, obj->currImagePtr, obj->priority == OVEROVL);
+				else
+					screen().displayFrame(obj->x, obj->y, obj->currImagePtr->nextSeqPtr, obj->priority == OVEROVL);
+				break;
+			case CYCLE_BACKWARD:
+				seqPtr = obj->currImagePtr;
+				if (!obj->frameTimer)               // Show next frame
+					while (seqPtr->nextSeqPtr != obj->currImagePtr)
+						seqPtr = seqPtr->nextSeqPtr;
+				screen().displayFrame(obj->x, obj->y, seqPtr, obj->priority == OVEROVL);
+				break;
+			default:
+				break;
+			}
+	}
+
+	// Cycle any animating objects
+	for (i = 0; i < num_objs; i++) {
+		obj = &_objects[objindex[i]];
+		if (obj->cycling != INVISIBLE) {
+			// Only if it's visible
+			if (obj->cycling == ALMOST_INVISIBLE)
+				obj->cycling = INVISIBLE;
+
+			// Now Rotate to next picture in sequence
+			switch (obj->cycling) {
+			case NOT_CYCLING:
+				break;
+			case CYCLE_FORWARD:
+				if (!obj->frameTimer) {
+					// Time to step to next frame
+					obj->currImagePtr = obj->currImagePtr->nextSeqPtr;
+					// Find out if this is last frame of sequence
+					// If so, reset frame_timer and decrement n_cycle
+					if (obj->frameInterval || obj->cycleNumb) {
+						obj->frameTimer = obj->frameInterval;
+						for (j = 0; j < obj->seqNumb; j++)
+							if (obj->currImagePtr->nextSeqPtr == obj->seqList[j].seqPtr)
+								if (obj->cycleNumb)     // Decr cycleNumb if Non-continous
+									if (!--obj->cycleNumb)
+										obj->cycling = NOT_CYCLING;
+					}
+				}
+				break;
+			case CYCLE_BACKWARD:
+				if (!obj->frameTimer) {
+					// Time to step to prev frame
+					seqPtr = obj->currImagePtr;
+					while (obj->currImagePtr->nextSeqPtr != seqPtr)
+						obj->currImagePtr = obj->currImagePtr->nextSeqPtr;
+					// Find out if this is first frame of sequence
+					// If so, reset frame_timer and decrement n_cycle
+					if (obj->frameInterval || obj->cycleNumb) {
+						obj->frameTimer = obj->frameInterval;
+						for (j = 0; j < obj->seqNumb; j++)
+							if (obj->currImagePtr == obj->seqList[j].seqPtr)
+								if (obj->cycleNumb)     // Decr cycleNumb if Non-continous
+									if (!--obj->cycleNumb)
+										obj->cycling = NOT_CYCLING;
+					}
+				}
+				break;
+			default:
+				break;
+			}
+			obj->oldx = obj->x;
+			obj->oldy = obj->y;
+		}
+	}
+}
+
+// Return object index of the topmost object under the cursor, or -1 if none
+// Objects are filtered if not "useful"
+int16 HugoEngine::findObject(uint16 x, uint16 y) {
+	object_t *obj;
+	seq_t    *curImage;
+	int16     objIndex = -1;                        // Index of found object
+	uint16    y2Max = 0;                            // Greatest y2
+	int       i;
+
+	debugC(3, kDebugEngine, "findObject(%d, %d)", x, y);
+
+	// Check objects on screen
+	for (i = 0, obj = _objects; i < _numObj; i++, obj++)    {
+		// Object must be in current screen and "useful"
+		if (obj->screenIndex == *_screen_p && (obj->genericCmd || obj->objValue || obj->cmdIndex)) {
+			curImage = obj->currImagePtr;
+			// Object must have a visible image...
+			if (curImage != NULL && obj->cycling != INVISIBLE) {
+				// If cursor inside object
+				if (x >= (uint16)obj->x && x <= obj->x + curImage->x2 && y >= (uint16)obj->y && y <= obj->y + curImage->y2)
+					// If object is closest so far
+					if (obj->y + curImage->y2 > y2Max) {
+						y2Max = obj->y + curImage->y2;
+						objIndex = i;               // Found an object!
+					}
+			} else
+				// ...or a dummy object that has a hotspot rectangle
+				if (curImage == NULL && obj->vxPath != 0 && !obj->carriedFl) {
+					// If cursor inside special rectangle
+					if ((int16)x >= obj->oldx && (int16)x < obj->oldx + obj->vxPath && (int16)y >= obj->oldy && (int16)y < obj->oldy + obj->vyPath)
+						// If object is closest so far
+						if (obj->oldy + obj->vyPath - 1 > (int16)y2Max) {
+							y2Max = obj->oldy + obj->vyPath - 1;
+							objIndex = i;           // Found an object!
+						}
+				}
+		}
+	}
+	return objIndex;
+}
+
+// Find a clear space around supplied object that hero can walk to
+bool HugoEngine::findObjectSpace(object_t *obj, int16 *destx, int16 *desty) {
+//	bool   found = false;                            // TRUE if we found a clear space
+	bool   foundFl;
+	seq_t *curImage = obj->currImagePtr;
+	int16  x;
+	int16  y  = obj->y + curImage->y2 - 1;
+
+	debugC(1, kDebugEngine, "findObjectSpace(obj, %d, %d)", *destx, *desty);
+
+//	if (!found)                                      // Try left rear corner
+	for (foundFl = true, *destx = x = obj->x + curImage->x1; x < *destx + HERO_MAX_WIDTH; x++)
+		if (BOUND(x, y))
+			foundFl = false;
+
+	if (!foundFl)                                       // Try right rear corner
+		for (foundFl = true, *destx = x = obj->x + curImage->x2 - HERO_MAX_WIDTH + 1; x <= obj->x + (int16)curImage->x2; x++)
+			if (BOUND(x, y))
+				foundFl = false;
+
+	if (!foundFl)                                       // Try left front corner
+		for (foundFl = true, y += 2, *destx = x = obj->x + curImage->x1; x < *destx + HERO_MAX_WIDTH; x++)
+			if (BOUND(x, y))
+				foundFl = false;
+
+	if (!foundFl)                                       // Try right rear corner
+		for (foundFl = true, *destx = x = obj->x + curImage->x2 - HERO_MAX_WIDTH + 1; x <= obj->x + (int16)curImage->x2; x++)
+			if (BOUND(x, y))
+				foundFl = false;
+
+	*desty = y;
+	return(foundFl);
+}
+
+// Search background command list for this screen for supplied object.
+// Return first associated verb (not "look") or NULL if none found.
+char *HugoEngine::useBG(char *name) {
+	int i;
+	objectList_t p = _backgroundObjects[*_screen_p];
+
+	debugC(1, kDebugEngine, "useBG(%s)", name);
+
+	for (i = 0; *_arrayVerbs[p[i].verbIndex]; i++)
+		if ((name == _arrayNouns[p[i].nounIndex][0] &&
+		        p[i].verbIndex != _look) &&
+		        ((p[i].roomState == DONT_CARE) || (p[i].roomState == _screenStates[*_screen_p])))
+			return (_arrayVerbs[p[i].verbIndex][0]);
+
+	return (NULL);
+}
+
+// If status.objid = -1, pick up objid, else use status.objid on objid,
+// if objid can't be picked up, use it directly
+void HugoEngine::useObject(int16 objId) {
+	object_t *obj = &_objects[objId];               // Ptr to object
+	uses_t *use;                                    // Ptr to use entry
+	target_t *target;                               // Ptr to target entry
+	bool foundFl;                                   // TRUE if found target entry
+	char *verb;                                     // Background verb to use directly
+
+	debugC(1, kDebugEngine, "useObject(%d)", objId);
+
+	if (_status.inventoryObjId == -1) {
+		// Get or use objid directly
+		if ((obj->genericCmd & TAKE) || obj->objValue)  // Get collectible item
+			sprintf(_line, "%s %s", _arrayVerbs[_take][0], _arrayNouns[obj->nounIndex][0]);
+		else if (obj->cmdIndex != 0)            // Use non-collectible item if able
+			sprintf(_line, "%s %s", _arrayVerbs[_cmdList[obj->cmdIndex][1].verbIndex][0], _arrayNouns[obj->nounIndex][0]);
+		else if ((verb = useBG(_arrayNouns[obj->nounIndex][0])) != NULL)
+			sprintf(_line, "%s %s", verb, _arrayNouns[obj->nounIndex][0]);
+		else
+			return;                         // Can't use object directly
+	} else {
+		// Use status.objid on objid
+		// Default to first cmd verb
+		sprintf(_line, "%s %s %s", _arrayVerbs[_cmdList[_objects[_status.inventoryObjId].cmdIndex][1].verbIndex][0], _arrayNouns[_objects[_status.inventoryObjId].nounIndex][0], _arrayNouns[obj->nounIndex][0]);
+
+		// Check valid use of objects and override verb if necessary
+		for (use = _uses; use->objId != _numObj; use++)
+			if (_status.inventoryObjId == use->objId) {
+				// Look for secondary object, if found use matching verb
+				for (foundFl = false, target = use->targets; _arrayNouns[target->nounIndex] != NULL; target++)
+					if (_arrayNouns[target->nounIndex][0] == _arrayNouns[obj->nounIndex][0]) {
+						foundFl = true;
+						sprintf(_line, "%s %s %s", _arrayVerbs[target->verbIndex][0], _arrayNouns[_objects[_status.inventoryObjId].nounIndex][0], _arrayNouns[obj->nounIndex][0]);
+					}
+
+				// No valid use of objects found, print failure string
+				if (!foundFl) {
+					// Deselect dragged icon if inventory not active
+					if (_status.inventoryState != I_ACTIVE)
+						_status.inventoryObjId  = -1;
+					Utils::Box(BOX_ANY, HugoEngine::get()._textData[use->dataIndex]);
+					return;
+				}
+			}
+	}
+
+	if (_status.inventoryState == I_ACTIVE)         // If inventory active, remove it
+		_status.inventoryState = I_UP;
+	_status.inventoryObjId  = -1;                   // Deselect any dragged icon
+	parser().lineHandler();                         // and process command
+}
+
+// Issue "Look at <object>" command
+// Note special case of swapped hero image
+void HugoEngine::lookObject(object_t *obj) {
+	debugC(1, kDebugEngine, "lookObject");
+
+	if (obj == _hero) {
+		// Hero swapped - look at other
+		obj = &_objects[_heroImage];
+	}
+	parser().command("%s %s", _arrayVerbs[_look][0], _arrayNouns[obj->nounIndex][0]);
+}
+
+// Free all object images
+void HugoEngine::freeObjects() {
+	object_t *obj;
+	seq_t *seq;
+
+	debugC(1, kDebugEngine, "freeObjects");
+
+	// Nothing to do if not allocated yet
+	if (_hero->seqList[0].seqPtr == NULL)
+		return;
+
+	// Free all sequence lists and image data
+	for (int i = 0; i < _numObj; i++) {
+		obj = &_objects[i];
+		for (int j = 0; j < obj->seqNumb; j++) {        // for each sequence
+			seq = obj->seqList[j].seqPtr;           // Free image
+			if (seq == NULL)                        // Failure during database load
+				break;
+			do {
+				free(seq->imagePtr);
+				seq = seq->nextSeqPtr;
+			} while (seq != obj->seqList[j].seqPtr);
+			free(seq);                              // Free sequence record
+		}
+	}
+}
+
+// Add action lists for this screen to event queue
+void HugoEngine::screenActions(int screenNum) {
+	uint16 *screenAct = _screenActs[screenNum];
+
+	debugC(1, kDebugEngine, "screenActions(%d)", screenNum);
+
+	if (screenAct) {
+		for (int i = 0; screenAct[i]; i++)
+			HugoEngine::get().scheduler().insertActionList(screenAct[i]);
+	}
+}
+
+// Set the new screen number into the hero object and any carried objects
+void HugoEngine::setNewScreen(int screenNum) {
+	debugC(1, kDebugEngine, "setNewScreen(%d)", screenNum);
+
+	*_screen_p = screenNum;                             // HERO object
+	for (int i = HERO + 1; i < _numObj; i++)            // Any others
+		if (_objects[i].carriedFl)                      // being carried
+			_objects[i].screenIndex = screenNum;
+}
+
+// An object has collided with a boundary.  See if any actions are required
+void HugoEngine::boundaryCollision(object_t *obj) {
+	int         x, y, dx, dy;
+	char        radius;                             // 8 bits signed
+	hotspot_t   *hotspot;
+
+	debugC(1, kDebugEngine, "boundaryCollision");
+
+	if (obj == _hero) {
+		// Hotspots only relevant to HERO
+		if (obj->vx > 0)
+			x = obj->x + obj->currImagePtr->x2;
+		else
+			x = obj->x + obj->currImagePtr->x1;
+		y = obj->y + obj->currImagePtr->y2;
+
+		for (int i = 0; _hotspots[i].screenIndex >= 0; i++) {
+			hotspot = &_hotspots[i];
+			if (hotspot->screenIndex == obj->screenIndex)
+				if ((x >= hotspot->x1) && (x <= hotspot->x2) && (y >= hotspot->y1) && (y <= hotspot->y2)) {
+					HugoEngine::get().scheduler().insertActionList(hotspot->actIndex);
+					break;
+				}
+		}
+	} else {
+		// Check whether an object collided with HERO
+		dx = _hero->x + _hero->currImagePtr->x1 - obj->x - obj->currImagePtr->x1;
+		dy = _hero->y + _hero->currImagePtr->y2 - obj->y - obj->currImagePtr->y2;
+		// If object's radius is infinity, use a closer value
+		radius = obj->radius;
+		if (radius < 0)
+			radius = DX * 2;
+		if ((abs(dx) <= radius) && (abs(dy) <= radius))
+			HugoEngine::get().scheduler().insertActionList(obj->actIndex);
+	}
+}
+
+// Initialize screen components and display results
+void HugoEngine::initNewScreenDisplay() {
+	debugC(1, kDebugEngine, "initNewScreenDisplay");
+
+	screen().displayList(D_INIT);
+	screen().setBackgroundColor(_TBLACK);
+	screen().displayBackground();
+
+	// Stop premature object display in Display_list(D_DISPLAY)
+	_status.newScreenFl = true;
+}
+
+// Add up all the object values and all the bonus points
+void HugoEngine::calcMaxScore() {
+	int i;
+
+	debugC(1, kDebugEngine, "calcMaxScore");
+
+	for (i = 0; i < _numObj; i++)
+		_maxscore += _objects[i].objValue;
+
+	for (i = 0; i < _numBonuses; i++)
+		_maxscore += _points[i].score;
+}
+
+// Exit game, advertise trilogy, show copyright
+void HugoEngine::endGame() {
+	debugC(1, kDebugEngine, "endGame");
+
+	if (!_boot.registered)
+		Utils::Box(BOX_ANY, HugoEngine::get()._textEngine[kEsAdvertise]);
+	Utils::Box(BOX_ANY, "%s\n%s", _episode, COPYRIGHT);
+	_status.viewState = V_EXIT;
+}
+
+} // end of namespace Hugo


Property changes on: scummvm/trunk/engines/hugo/engine.cpp
___________________________________________________________________
Added: svn:executable
   + *
Added: svn:mime-type
   + text/plain
Added: svn:keywords
   + Date Rev Author URL Id
Added: svn:eol-style
   + native

Added: scummvm/trunk/engines/hugo/engine.h
===================================================================
--- scummvm/trunk/engines/hugo/engine.h	                        (rev 0)
+++ scummvm/trunk/engines/hugo/engine.h	2010-08-17 09:28:20 UTC (rev 52137)
@@ -0,0 +1,43 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+/*
+ * This code is based on original Hugo 1-3 Trilogy source code
+ *
+ * Copyright (c) 1989-1995 David P. Gray
+ *
+ */
+
+#ifndef HUGO_ENGINE_H
+#define HUGO_ENGINE_H
+namespace Hugo {
+
+enum seqTextEngine {
+	// Strings used by the engine
+	kEsAdvertise = 0
+};
+
+} // end of namespace Hugo
+#endif // HUGO_ENGINE_H


Property changes on: scummvm/trunk/engines/hugo/engine.h
___________________________________________________________________
Added: svn:executable
   + *
Added: svn:mime-type
   + text/plain
Added: svn:keywords
   + Date Rev Author URL Id
Added: svn:eol-style
   + native

Added: scummvm/trunk/engines/hugo/file.cpp
===================================================================
--- scummvm/trunk/engines/hugo/file.cpp	                        (rev 0)
+++ scummvm/trunk/engines/hugo/file.cpp	2010-08-17 09:28:20 UTC (rev 52137)
@@ -0,0 +1,924 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+/*
+ * This code is based on original Hugo Trilogy source code
+ *
+ * Copyright (c) 1989-1995 David P. Gray
+ *
+ */
+
+#include "common/system.h"
+#include "common/file.h"
+#include "common/savefile.h"
+
+#include "hugo/game.h"
+#include "hugo/hugo.h"
+#include "hugo/file.h"
+#include "hugo/global.h"
+#include "hugo/schedule.h"
+#include "hugo/display.h"
+#include "hugo/util.h"
+
+namespace Hugo {
+FileManager::FileManager(HugoEngine &vm) : _vm(vm) {
+
+}
+
+byte *FileManager::convertPCC(byte *p, uint16 y, uint16 bpl, image_pt dataPtr) {
+// Convert 4 planes (RGBI) data to 8-bit DIB format
+// Return original plane data ptr
+	uint16 r, g, b, i;                              // Byte index within each plane
+	char   bit;                                     // Bit index within a byte
+
+	debugC(2, kDebugFile, "convertPCC(byte *p, %d, %d, image_pt data_p)", y, bpl);
+
+	dataPtr += y * bpl * 8;                         // Point to correct DIB line
+	for (r = 0, g = bpl, b = g + bpl, i = b + bpl; r < bpl; r++, g++, b++, i++) // Each byte in all planes
+		for (bit = 7; bit >= 0; bit--)                                          // Each bit in byte
+			*dataPtr++ = (((p[r] >> bit & 1) << 0) |
+			              ((p[g] >> bit & 1) << 1) |
+			              ((p[b] >> bit & 1) << 2) |
+			              ((p[i] >> bit & 1) << 3));
+	return p;
+}
+
+seq_t *FileManager::readPCX(Common::File &f, seq_t *seqPtr, byte *imagePtr, bool firstFl, const char *name) {
+// Read a pcx file of length len.  Use supplied seq_p and image_p or
+// allocate space if NULL.  Name used for errors.  Returns address of seq_p
+// Set first TRUE to initialize b_index (i.e. not reading a sequential image in file).
+
+	struct {                                            // Structure of PCX file header
+		byte   mfctr, vers, enc, bpx;
+		uint16  x1, y1, x2, y2;                         // bounding box
+		uint16  xres, yres;
+		byte   palette[48];                             // EGA color palette
+		byte   vmode, planes;
+		uint16 bytesPerLine;                            // Bytes per line
+		byte   fill2[60];
+	} PCC_header;                                       // Header of a PCC file
+
+	byte  c, d;                                     // code and data bytes from PCX file
+	byte  pline[XPIX];                              // Hold 4 planes of data
+	byte  *p = pline;                               // Ptr to above
+	byte  i;                                        // PCX repeat count
+	uint16 bytesPerLine4;                           // BPL in 4-bit format
+	uint16 size;                                    // Size of image
+	uint16 y = 0;                                   // Current line index
+
+	debugC(1, kDebugFile, "readPCX(..., %s)", name);
+
+	// Read in the PCC header and check consistency
+	PCC_header.mfctr = f.readByte();
+	PCC_header.vers = f.readByte();
+	PCC_header.enc = f.readByte();
+	PCC_header.bpx = f.readByte();
+	PCC_header.x1 = f.readUint16LE();
+	PCC_header.y1 = f.readUint16LE();
+	PCC_header.x2 = f.readUint16LE();
+	PCC_header.y2 = f.readUint16LE();
+	PCC_header.xres = f.readUint16LE();
+	PCC_header.yres = f.readUint16LE();
+	f.read(PCC_header.palette, sizeof(PCC_header.palette));
+	PCC_header.vmode = f.readByte();
+	PCC_header.planes = f.readByte();
+	PCC_header.bytesPerLine = f.readUint16LE();
+	f.read(PCC_header.fill2, sizeof(PCC_header.fill2));
+
+	if (PCC_header.mfctr != 10)
+		Utils::Error(PCCH_ERR, name);
+
+	// Allocate memory for seq_t if NULL
+	if (seqPtr == NULL)
+		if ((seqPtr = (seq_t *)malloc(sizeof(seq_t))) == NULL)
+			Utils::Error(HEAP_ERR, name);
+
+	// Find size of image data in 8-bit DIB format
+	// Note save of x2 - marks end of valid data before garbage
+	bytesPerLine4 = PCC_header.bytesPerLine * 4;    // 4-bit bpl
+	seqPtr->bytesPerLine8 = bytesPerLine4 * 2;      // 8-bit bpl
+	seqPtr->lines = PCC_header.y2 - PCC_header.y1 + 1;
+	seqPtr->x2 = PCC_header.x2 - PCC_header.x1 + 1;
+	size = seqPtr->lines * seqPtr->bytesPerLine8;
+
+	// Allocate memory for image data if NULL
+	if (imagePtr == NULL)
+		if ((imagePtr = (byte *)malloc((size_t) size)) == NULL)
+			Utils::Error(HEAP_ERR, name);
+	seqPtr->imagePtr = imagePtr;
+
+	// Process the image data, converting to 8-bit DIB format
+	while (y < seqPtr->lines) {
+		c = f.readByte();
+		if ((c & REP_MASK) == REP_MASK) {
+			d = f.readByte();                       // Read data byte
+			for (i = 0; i < (c & LEN_MASK); i++) {
+				*p++ = d;
+				if ((uint16)(p - pline) == bytesPerLine4)
+					p = convertPCC(pline, y++, PCC_header.bytesPerLine, imagePtr);
+			}
+		} else {
+			*p++ = c;
+			if ((uint16)(p - pline) == bytesPerLine4)
+				p = convertPCC(pline, y++, PCC_header.bytesPerLine, imagePtr);
+		}
+	}
+	return seqPtr;
+}
+
+void FileManager::readImage(int objNum, object_t *objPtr) {
+// Read object file of PCC images into object supplied
+	byte       x, y, j, k;
+	uint16     x2;                                  // Limit on x in image data
+	seq_t     *seqPtr;                              // Ptr to sequence structure
+	image_pt   dibPtr;                              // Ptr to DIB data
+	objBlock_t objBlock;                            // Info on file within database
+	bool       firstFl = true;                      // Initializes pcx read function
+
+	debugC(1, kDebugFile, "readImage(%d, object_t *objPtr)", objNum);
+
+	if (!objPtr->seqNumb)                           // This object has no images
+		return;
+
+	if (_vm.isPacked()) {
+		_objectsArchive.seek((uint32)objNum * sizeof(objBlock_t), SEEK_SET);
+
+		objBlock.objOffset = _objectsArchive.readUint32LE();
+		objBlock.objLength = _objectsArchive.readUint32LE();
+
+		_objectsArchive.seek(objBlock.objOffset, SEEK_SET);
+	} else {
+		char *buf = (char *) malloc(2048 + 1);      // Buffer for file access
+		strcat(strcat(strcpy(buf, _vm._picDir), _vm._arrayNouns[objPtr->nounIndex][0]), OBJEXT);
+		if (!_objectsArchive.open(buf)) {
+			warning("File %s not found, trying again with %s%s", buf, _vm._arrayNouns[objPtr->nounIndex][0], OBJEXT);
+			strcat(strcpy(buf, _vm._arrayNouns[objPtr->nounIndex][0]), OBJEXT);
+			if (!_objectsArchive.open(buf))
+				Utils::Error(FILE_ERR, buf);
+		}
+	}
+
+	// Now read the images into an images list
+	for (j = 0; j < objPtr->seqNumb; j++) {         // for each sequence
+		for (k = 0; k < objPtr->seqList[j].imageNbr; k++) { // each image
+			if (k == 0) {                           // First image
+				// Read this image - allocate both seq and image memory
+				seqPtr = readPCX(_objectsArchive, NULL, NULL, firstFl, _vm._arrayNouns[objPtr->nounIndex][0]);
+				objPtr->seqList[j].seqPtr = seqPtr;
+				firstFl = false;
+			} else {                                // Subsequent image
+				// Read this image - allocate both seq and image memory
+				seqPtr->nextSeqPtr = readPCX(_objectsArchive, NULL, NULL, firstFl, _vm._arrayNouns[objPtr->nounIndex][0]);
+				seqPtr = seqPtr->nextSeqPtr;
+			}
+
+			// Compute the bounding box - x1, x2, y1, y2
+			// Note use of x2 - marks end of valid data in row
+			x2 = seqPtr->x2;
+			seqPtr->x1 = seqPtr->x2;
+			seqPtr->x2 = 0;
+			seqPtr->y1 = seqPtr->lines;
+			seqPtr->y2 = 0;
+			dibPtr = seqPtr->imagePtr;
+			for (y = 0; y < seqPtr->lines; y++, dibPtr += seqPtr->bytesPerLine8 - x2)
+				for (x = 0; x < x2; x++)
+					if (*dibPtr++) {                    // Some data found
+						if (x < seqPtr->x1)
+							seqPtr->x1 = x;
+						if (x > seqPtr->x2)
+							seqPtr->x2 = x;
+						if (y < seqPtr->y1)
+							seqPtr->y1 = y;
+						if (y > seqPtr->y2)
+							seqPtr->y2 = y;
+					}
+		}
+		seqPtr->nextSeqPtr = objPtr->seqList[j].seqPtr; // loop linked list to head
+	}
+
+	// Set the current image sequence to first or last
+	switch (objPtr->cycling) {
+	case INVISIBLE:                                 // (May become visible later)
+	case ALMOST_INVISIBLE:
+	case NOT_CYCLING:
+	case CYCLE_FORWARD:
+		objPtr->currImagePtr = objPtr->seqList[0].seqPtr;
+		break;
+	case CYCLE_BACKWARD:
+		objPtr->currImagePtr = seqPtr;
+		break;
+	}
+
+	if (!_vm.isPacked())
+		_objectsArchive.close();
+}
+
+void FileManager::readBackground(int screenIndex) {
+// Read a PCX image into dib_a
+	seq_t        seq;                               // Image sequence structure for Read_pcx
+	sceneBlock_t sceneBlock;                        // Read a database header entry
+
+	debugC(1, kDebugFile, "readBackground(%d)", screenIndex);
+
+	if (_vm.isPacked()) {
+		_sceneryArchive.seek((uint32) screenIndex * sizeof(sceneBlock_t), SEEK_SET);
+
+		sceneBlock.scene_off = _sceneryArchive.readUint32LE();
+		sceneBlock.scene_len = _sceneryArchive.readUint32LE();
+		sceneBlock.b_off = _sceneryArchive.readUint32LE();
+		sceneBlock.b_len = _sceneryArchive.readUint32LE();
+		sceneBlock.o_off = _sceneryArchive.readUint32LE();
+		sceneBlock.o_len = _sceneryArchive.readUint32LE();
+		sceneBlock.ob_off = _sceneryArchive.readUint32LE();
+		sceneBlock.ob_len = _sceneryArchive.readUint32LE();
+
+		_sceneryArchive.seek(sceneBlock.scene_off, SEEK_SET);
+	} else {
+		char *buf = (char *) malloc(2048 + 1);      // Buffer for file access
+		strcat(strcat(strcpy(buf, _vm._picDir), _vm._screenNames[screenIndex]), BKGEXT);
+		if (!_sceneryArchive.open(buf)) {
+			warning("File %s not found, trying again with %s.ART", buf, _vm._screenNames[screenIndex]);
+			strcat(strcpy(buf, _vm._screenNames[screenIndex]), ".ART");
+			if (!_sceneryArchive.open(buf))
+				Utils::Error(FILE_ERR, buf);
+		}
+	}
+
+	// Read the image into dummy seq and static dib_a
+	readPCX(_sceneryArchive, &seq, _vm.screen().getFrontBuffer(), true, _vm._screenNames[screenIndex]);
+
+	if (!_vm.isPacked())
+		_sceneryArchive.close();
+}
+
+sound_pt FileManager::getSound(int16 sound, uint16 *size) {
+// Read sound (or music) file data.  Call with SILENCE to free-up
+// any allocated memory.  Also returns size of data
+
+	static sound_hdr_t s_hdr[MAX_SOUNDS];           // Sound lookup table
+	sound_pt           soundPtr;                    // Ptr to sound data
+	Common::File       fp;                          // Handle to SOUND_FILE
+//	bool               music = sound < NUM_TUNES;    // TRUE if music, else sound file
+
+	debugC(1, kDebugFile, "getSound(%d, %d)", sound, *size);
+
+	// No more to do if SILENCE (called for cleanup purposes)
+	if (sound == _vm._soundSilence)
+		return(NULL);
+
+	// Open sounds file
+	if (!fp.open(SOUND_FILE)) {
+//		Error(FILE_ERR, SOUND_FILE);
+		warning("Hugo Error: File not found %s", SOUND_FILE);
+		return(NULL);
+	}
+
+	// If this is the first call, read the lookup table
+	static bool has_read_header = false;
+	if (!has_read_header) {
+		if (fp.read(s_hdr, sizeof(s_hdr)) != sizeof(s_hdr))
+			Utils::Error(FILE_ERR, SOUND_FILE);
+		has_read_header = true;
+	}
+
+	*size = s_hdr[sound].size;
+	if (*size == 0)
+		Utils::Error(SOUND_ERR, SOUND_FILE);
+
+	// Allocate memory for sound or music, if possible
+	if ((soundPtr = (byte *)malloc(s_hdr[sound].size)) == 0) {
+		Utils::Warn(false, "Low on memory");
+		return(NULL);
+	}
+
+	// Seek to data and read it
+	fp.seek(s_hdr[sound].offset, SEEK_SET);
+	if (fp.read(soundPtr, s_hdr[sound].size) != s_hdr[sound].size)
+		Utils::Error(FILE_ERR, SOUND_FILE);
+
+	fp.close();
+
+	return soundPtr;
+}
+
+bool FileManager::fileExists(char *filename) {
+// Return whether file exists or not
+	Common::File f;
+	if (f.open(filename)) {
+		f.close();
+		return true;
+	}
+	return false;
+}
+
+void FileManager::readOverlay(int screenNum, image_pt image, ovl_t overlayType) {
+// Open and read in an overlay file, close file
+	uint32       i;
+	int16        j, k;
+	char         data;                              // Must be 8 bits signed
+	image_pt     tmpImage = image;                  // temp ptr to overlay file
+	sceneBlock_t sceneBlock;                        // Database header entry
+
+	debugC(1, kDebugFile, "readOverlay(%d, ...)", screenNum);
+
+	if (_vm.isPacked()) {
+		_sceneryArchive.seek((uint32)screenNum * sizeof(sceneBlock_t), SEEK_SET);
+
+		sceneBlock.scene_off = _sceneryArchive.readUint32LE();
+		sceneBlock.scene_len = _sceneryArchive.readUint32LE();
+		sceneBlock.b_off = _sceneryArchive.readUint32LE();
+		sceneBlock.b_len = _sceneryArchive.readUint32LE();
+		sceneBlock.o_off = _sceneryArchive.readUint32LE();
+		sceneBlock.o_len = _sceneryArchive.readUint32LE();
+		sceneBlock.ob_off = _sceneryArchive.readUint32LE();
+		sceneBlock.ob_len = _sceneryArchive.readUint32LE();
+
+		switch (overlayType) {
+		case BOUNDARY:
+			_sceneryArchive.seek(sceneBlock.b_off, SEEK_SET);
+			i = sceneBlock.b_len;
+			break;
+		case OVERLAY:
+			_sceneryArchive.seek(sceneBlock.o_off, SEEK_SET);
+			i = sceneBlock.o_len;
+			break;
+		case OVLBASE:
+			_sceneryArchive.seek(sceneBlock.ob_off, SEEK_SET);
+			i = sceneBlock.ob_len;
+			break;
+		default:
+			Utils::Error(FILE_ERR, "Bad ovl_type");
+			break;
+		}
+		if (i == 0) {
+			for (i = 0; i < OVL_SIZE; i++)
+				image[i] = 0;
+			return;
+		}
+	} else {
+		const char  *ovl_ext[] = {".b", ".o", ".ob"};
+		char *buf = (char *) malloc(2048 + 1);      // Buffer for file access
+
+		strcat(strcpy(buf, _vm._screenNames[screenNum]), ovl_ext[overlayType]);
+
+		if (!fileExists(buf)) {
+			for (i = 0; i < OVL_SIZE; i++)
+				image[i] = 0;
+			return;
+		}
+
+		if (!_sceneryArchive.open(buf))
+			Utils::Error(FILE_ERR, buf);
+
+//		if (eof(f_scenery)) {
+//			_lclose(f_scenery);
+//			return;
+//		}
+	}
+
+	switch (_vm._gameVariant) {
+	case 0:                                         // Hugo 1 DOS and WIN don't pack data
+	case 3:
+		_sceneryArchive.read(tmpImage, OVL_SIZE);
+		break;
+	default:
+		// Read in the overlay file using MAC Packbits.  (We're not proud!)
+		k = 0;                                      // byte count
+		do {
+			data = _sceneryArchive.readByte();      // Read a code byte
+			if ((byte)data == 0x80)             // Noop
+				k = k;
+			else if (data >= 0) {                   // Copy next data+1 literally
+				for (i = 0; i <= (byte)data; i++, k++)
+					*tmpImage++ = _sceneryArchive.readByte();
+			} else {                            // Repeat next byte -data+1 times
+				j = _sceneryArchive.readByte();
+
+				for (i = 0; i < (byte)(-data + 1); i++, k++)
+					*tmpImage++ = j;
+			}
+		} while (k < OVL_SIZE);
+		break;
+	}
+
+	if (!_vm.isPacked())
+		_sceneryArchive.close();
+}
+
+void FileManager::saveSeq(object_t *obj) {
+// Save sequence number and image number in given object
+	byte   j, k;
+	seq_t *q;
+	bool   found;
+
+	debugC(1, kDebugFile, "saveSeq");
+
+	for (j = 0, found = false; !found && (j < obj->seqNumb); j++) {
+		q = obj->seqList[j].seqPtr;
+		for (k = 0; !found && (k < obj->seqList[j].imageNbr); k++) {
+			if (obj->currImagePtr == q) {
+				found = true;
+				obj->curSeqNum = j;
+				obj->curImageNum = k;
+			} else
+				q = q->nextSeqPtr;
+		}
+	}
+}
+
+void FileManager::restoreSeq(object_t *obj) {
+// Set up cur_seq_p from stored sequence and image number in object
+	int    j;
+	seq_t *q;
+
+	debugC(1, kDebugFile, "restoreSeq");
+
+	q = obj->seqList[obj->curSeqNum].seqPtr;
+	for (j = 0; j < obj->curImageNum; j++)
+		q = q->nextSeqPtr;
+	obj->currImagePtr = q;
+}
+
+void FileManager::saveGame(int16 slot, const char *descrip) {
+// Save game to supplied slot (-1 is INITFILE)
+	int     i;
+	char    path[256];                                  // Full path of saved game
+
+	debugC(1, kDebugFile, "saveGame(%d, %s)", slot, descrip);
+
+	// Get full path of saved game file - note test for INITFILE
+	if (slot == -1)
+		sprintf(path, "%s", _vm._initFilename);
+	else
+		sprintf(path, _vm._saveFilename, slot);
+
+	Common::WriteStream *out = 0;
+	if (!(out = _vm.getSaveFileManager()->openForSaving(path))) {
+		warning("Can't create file '%s', game not saved", path);
+		return;
+	}
+
+	// Write version.  We can't restore from obsolete versions
+	out->write(&kSavegameVersion, sizeof(kSavegameVersion));
+
+	// Save description of saved game
+	out->write(descrip, DESCRIPLEN);
+
+	// Save objects
+	for (i = 0; i < _vm._numObj; i++) {
+		// Save where curr_seq_p is pointing to
+		saveSeq(&_vm._objects[i]);
+		out->write(&_vm._objects[i], sizeof(object_t));
+	}
+
+	const status_t &gameStatus = _vm.getGameStatus();
+
+	// Save whether hero image is swapped
+	out->write(&_vm._heroImage, sizeof(_vm._heroImage));
+
+	// Save score
+	int score = _vm.getScore();
+	out->write(&score, sizeof(score));
+
+	// Save story mode
+	out->write(&gameStatus.storyModeFl, sizeof(gameStatus.storyModeFl));
+
+	// Save jumpexit mode
+	out->write(&gameStatus.jumpExitFl, sizeof(gameStatus.jumpExitFl));
+
+	// Save gameover status
+	out->write(&gameStatus.gameOverFl, sizeof(gameStatus.gameOverFl));
+
+	// Save screen states
+	out->write(_vm._screenStates, sizeof(*_vm._screenStates) * _vm._numScreens);
+
+	// Save points table
+	out->write(_vm._points, sizeof(point_t) * _vm._numBonuses);
+
+	// Now save current time and all current events in event queue
+	_vm.scheduler().saveEvents(out);
+
+	// Save palette table
+	_vm.screen().savePal(out);
+
+	// Save maze status
+	out->write(&_maze, sizeof(maze_t));
+
+	out->finalize();
+
+	delete out;
+}
+
+void FileManager::restoreGame(int16 slot) {
+// Restore game from supplied slot number (-1 is INITFILE)
+	int       i;
+	char      path[256];                            // Full path of saved game
+	object_t *p;
+	seqList_t seqList[MAX_SEQUENCES];
+//	cmdList  *cmds;                                  // Save command list pointer
+	uint16    cmdIndex;                             // Save command list pointer
+//	char      ver[sizeof(VER)];                      // Compare versions
+
+	debugC(1, kDebugFile, "restoreGame(%d)", slot);
+
+	// Initialize new-game status
+	_vm.initStatus();
+
+	// Get full path of saved game file - note test for INITFILE
+	if (slot == -1)
+		sprintf(path, "%s", _vm._initFilename);
+	else
+		sprintf(path, _vm._saveFilename, slot);
+
+	Common::SeekableReadStream *in = 0;
+	if (!(in = _vm.getSaveFileManager()->openForLoading(path)))
+		return;
+
+	// Check version, can't restore from different versions
+	int saveVersion;
+	in->read(&saveVersion, sizeof(saveVersion));
+	if (saveVersion != kSavegameVersion) {
+		Utils::Error(GEN_ERR, "Savegame of incompatible version");
+		return;
+	}
+
+	// Skip over description
+	in->seek(DESCRIPLEN, SEEK_CUR);
+
+	// If hero image is currently swapped, swap it back before restore
+	if (_vm._heroImage != HERO)
+		_vm.scheduler().swapImages(HERO, _vm._heroImage);
+
+	// Restore objects, retain current seqList which points to dynamic mem
+	// Also, retain cmnd_t pointers
+	for (i = 0; i < _vm._numObj; i++) {
+		p = &_vm._objects[i];
+		memcpy(seqList, p->seqList, sizeof(seqList_t));
+		cmdIndex = p->cmdIndex;
+		in->read(p, sizeof(object_t));
+		p->cmdIndex = cmdIndex;
+		memcpy(p->seqList, seqList, sizeof(seqList_t));
+	}
+
+	in->read(&_vm._heroImage, sizeof(_vm._heroImage));
+
+	// If hero swapped in saved game, swap it
+	if ((i = _vm._heroImage) != HERO)
+		_vm.scheduler().swapImages(HERO, _vm._heroImage);
+	_vm._heroImage = i;
+
+	status_t &gameStatus = _vm.getGameStatus();
+
+	int score;
+	in->read(&score, sizeof(score));
+	_vm.setScore(score);
+
+	in->read(&gameStatus.storyModeFl, sizeof(gameStatus.storyModeFl));
+	in->read(&gameStatus.jumpExitFl, sizeof(gameStatus.jumpExitFl));
+	in->read(&gameStatus.gameOverFl, sizeof(gameStatus.gameOverFl));
+	in->read(_vm._screenStates, sizeof(*_vm._screenStates) * _vm._numScreens);
+
+	// Restore points table
+	in->read(_vm._points, sizeof(point_t) * _vm._numBonuses);
+
+	// Restore ptrs to currently loaded objects
+	for (i = 0; i < _vm._numObj; i++)
+		restoreSeq(&_vm._objects[i]);
+
+	// Now restore time of the save and the event queue
+	_vm.scheduler().restoreEvents(in);
+
+	// Restore palette and change it if necessary
+	_vm.screen().restorePal(in);
+
+	// Restore maze status
+	in->read(&_maze, sizeof(maze_t));
+
+	delete in;
+}
+
+void FileManager::initSavedGame() {
+// Initialize the size of a saved game (from the fixed initial game).
+// If status.initsave is TRUE, or the initial saved game is not found,
+// force a save to create one.  Normally the game will be shipped with
+// the initial game file but useful to force a write during development
+// when the size is changeable.
+// The net result is a valid INITFILE, with status.savesize initialized.
+	Common::File f;                                 // Handle of saved game file
+	char path[256];                                 // Full path of INITFILE
+
+	debugC(1, kDebugFile, "initSavedGame");
+
+	// Get full path of INITFILE
+	sprintf(path, "%s", _vm._initFilename);
+
+
+	// Force save of initial game
+	if (_vm.getGameStatus().initSaveFl)
+		saveGame(-1, "");
+
+	// If initial game doesn't exist, create it
+	Common::SeekableReadStream *in = 0;
+	if (!(in = _vm.getSaveFileManager()->openForLoading(path))) {
+		saveGame(-1, "");
+		if (!(in = _vm.getSaveFileManager()->openForLoading(path))) {
+			Utils::Error(WRITE_ERR, path);
+			return;
+		}
+	}
+
+	// Must have an open saved game now
+	_vm.getGameStatus().saveSize = in->size();
+	delete in;
+
+	// Check sanity - maybe disk full or path set to read-only drive?
+	if (_vm.getGameStatus().saveSize == -1)
+		Utils::Error(WRITE_ERR, path);
+}
+
+// Record and playback handling stuff:
+typedef struct {
+//	int    key;                                     // Character
+	uint32 time;                                    // Time at which character was pressed
+} pbdata_t;
+static pbdata_t pbdata;
+FILE            *fpb;
+
+void FileManager::openPlaybackFile(bool playbackFl, bool recordFl) {
+	debugC(1, kDebugFile, "openPlaybackFile(%d, %d)", (playbackFl) ? 1 : 0, (recordFl) ? 1 : 0);
+
+	if (playbackFl) {
+		if (!(fpb = fopen(PBFILE, "r+b")))
+			Utils::Error(FILE_ERR, PBFILE);
+	} else if (recordFl)
+		fpb = fopen(PBFILE, "wb");
+	pbdata.time = 0;                                // Say no key available
+}
+
+void FileManager::closePlaybackFile() {
+	fclose(fpb);
+}
+
+void FileManager::openDatabaseFiles() {
+//TODO : HUGO 1 DOS uses _stringtData instead of a strings.dat
+//This should be tested adequately and should be handled by an error and not by a warning.
+	debugC(1, kDebugFile, "openDatabaseFiles");
+
+	if (!_stringArchive.open(STRING_FILE))
+//		Error(FILE_ERR, STRING_FILE);
+		warning("Hugo Error: File not found %s", STRING_FILE);
+	if (_vm.isPacked()) {
+		if (!_sceneryArchive.open(SCENERY_FILE))
+			Utils::Error(FILE_ERR, SCENERY_FILE);
+		if (!_objectsArchive.open(OBJECTS_FILE))
+			Utils::Error(FILE_ERR, OBJECTS_FILE);
+	}
+}
+
+void FileManager::closeDatabaseFiles() {
+// TODO: stringArchive shouldn't be closed in Hugo 1 DOS
+	debugC(1, kDebugFile, "closeDatabaseFiles");
+
+	_stringArchive.close();
+	if (_vm.isPacked()) {
+		_sceneryArchive.close();
+		_objectsArchive.close();
+	}
+}
+
+char *FileManager::fetchString(int index) {
+//TODO : HUGO 1 DOS uses _stringtData instead of a strings.dat
+// Fetch string from file, decode and return ptr to string in memory
+	uint32 off1, off2;
+
+	debugC(1, kDebugFile, "fetchString(%d)", index);
+
+	// Get offset to string[index] (and next for length calculation)
+	_stringArchive.seek((uint32)index * sizeof(uint32), SEEK_SET);
+	if (_stringArchive.read((char *)&off1, sizeof(uint32)) == 0)
+		Utils::Error(FILE_ERR, "String offset");
+	if (_stringArchive.read((char *)&off2, sizeof(uint32)) == 0)
+		Utils::Error(FILE_ERR, "String offset");
+
+	// Check size of string
+	if ((off2 - off1) >= MAX_BOX)
+		Utils::Error(FILE_ERR, "Fetched string too long!");
+
+	// Position to string and read it into gen purpose _textBoxBuffer
+	_stringArchive.seek(off1, SEEK_SET);
+	if (_stringArchive.read(_textBoxBuffer, (uint16)(off2 - off1)) == 0)
+		Utils::Error(FILE_ERR, "Fetch_string");
+
+	// Null terminate, decode and return it
+	_textBoxBuffer[off2-off1] = '\0';
+	_vm.scheduler().decodeString(_textBoxBuffer);
+	return _textBoxBuffer;
+}
+
+void FileManager::printBootText() {
+// Read the encrypted text from the boot file and print it
+	Common::File ofp;
+	int  i;
+	char *buf;
+
+	debugC(1, kDebugFile, "printBootText");
+
+	if (!ofp.open(BOOTFILE))
+		Utils::Error(FILE_ERR, BOOTFILE);
+
+	// Allocate space for the text and print it
+	buf = (char *)malloc(_boot.exit_len + 1);
+	if (buf) {
+		// Skip over the boot structure (already read) and read exit text
+		ofp.seek((long)sizeof(_boot), SEEK_SET);
+		if (ofp.read(buf, _boot.exit_len) != (size_t)_boot.exit_len)
+			Utils::Error(FILE_ERR, BOOTFILE);
+
+		// Decrypt the exit text, using CRYPT substring
+		for (i = 0; i < _boot.exit_len; i++)
+			buf[i] ^= CRYPT[i % strlen(CRYPT)];
+
+		buf[i] = '\0';
+		//Box(BOX_OK, buf_p);
+		//MessageBox(hwnd, buf_p, "License", MB_ICONINFORMATION);
+		warning("printBootText(): License: %s", buf);
+	}
+
+	free(buf);
+	ofp.close();
+}
+
+void FileManager::readBootFile() {
+// Reads boot file for program environment.  Fatal error if not there or
+// file checksum is bad.  De-crypts structure while checking checksum
+	byte checksum;
+	byte *p;
+	Common::File ofp;
+	uint32 i;
+
+	debugC(1, kDebugFile, "readBootFile");
+
+	if (!ofp.open(BOOTFILE))
+		Utils::Error(FILE_ERR, BOOTFILE);
+
+	if (ofp.size() < (int32)sizeof(_boot))
+		Utils::Error(FILE_ERR, BOOTFILE);
+
+	_boot.checksum = ofp.readByte();
+	_boot.registered = ofp.readByte();
+	ofp.read(_boot.pbswitch, sizeof(_boot.pbswitch));
+	ofp.read(_boot.distrib, sizeof(_boot.distrib));
+	_boot.exit_len = ofp.readUint16LE();
+
+	p = (byte *)&_boot;
+	for (i = 0, checksum = 0; i < sizeof(_boot); i++) {
+		checksum ^= p[i];
+		p[i] ^= CRYPT[i % strlen(CRYPT)];
+	}
+	ofp.close();
+
+	if (checksum)
+		Utils::Error(GEN_ERR, "Program startup file invalid");
+}
+
+void FileManager::readConfig() {
+// Read the user's config if it exists
+	Common::File f;
+	fpath_t  path;
+	config_t tmpConfig = _config;
+
+	debugC(1, kDebugFile, "readConfig");
+
+	sprintf(path, "%s%s", _vm.getGameStatus().path, CONFIGFILE);
+	if (f.open(path)) {
+		// If config format changed, ignore it and use defaults
+		if (f.read(&_config, sizeof(_config)) != sizeof(_config))
+			_config = tmpConfig;
+
+		f.close();
+	}
+}
+
+void FileManager::writeConfig() {
+// Write the user's config
+	FILE   *f;
+	fpath_t path;
+
+	debugC(1, kDebugFile, "writeConfig");
+
+	// Write user's config
+	// No error checking in case CD-ROM with no alternate path specified
+	sprintf(path, "%s%s", _vm.getGameStatus().path, CONFIGFILE);
+	if ((f = fopen(path, "w+")) != NULL)
+		fwrite(&_config, sizeof(_config), 1, f);
+
+	fclose(f);
+}
+
+uif_hdr_t *FileManager::getUIFHeader(uif_t id) {
+// Returns address of uif_hdr[id], reading it in if first call
+	static uif_hdr_t UIFHeader[MAX_UIFS];           // Lookup for uif fonts/images
+	static bool firstFl = true;
+	Common::File ip;                                // Image data file
+
+	debugC(1, kDebugFile, "getUIFHeader(%d)", id);
+
+	// Initialize offset lookup if not read yet
+	if (firstFl) {
+		firstFl = false;
+		// Open unbuffered to do far read
+		if (!ip.open(UIF_FILE))
+			Utils::Error(FILE_ERR, UIF_FILE);
+
+		if (ip.size() < (int32)sizeof(UIFHeader))
+			Utils::Error(FILE_ERR, UIF_FILE);
+
+		for (int i = 0; i < MAX_UIFS; ++i) {
+			UIFHeader[i].size = ip.readUint16LE();
+			UIFHeader[i].offset = ip.readUint32LE();
+		}
+
+		ip.close();
+	}
+	return &UIFHeader[id];
+}
+
+void FileManager::readUIFItem(int16 id, byte *buf) {
+// Read uif item into supplied buffer.
+	Common::File ip;                                // UIF_FILE handle
+	uif_hdr_t *UIFHeaderPtr;                        // Lookup table of items
+	seq_t seq;                                      // Dummy seq_t for image data
+
+	debugC(1, kDebugFile, "readUIFItem(%d, ...)", id);
+
+	// Open uif file to read data
+	if (!ip.open(UIF_FILE))
+		Utils::Error(FILE_ERR, UIF_FILE);
+
+	// Seek to data
+	UIFHeaderPtr = getUIFHeader((uif_t)id);
+	ip.seek(UIFHeaderPtr->offset, SEEK_SET);
+
+	// We support pcx images and straight data
+	switch (id) {
+	case UIF_IMAGES:                                // Read uif images file
+		readPCX(ip, &seq, buf, true, UIF_FILE);
+		break;
+	default:                                        // Read file data into supplied array
+		if (ip.read(buf, UIFHeaderPtr->size) != UIFHeaderPtr->size)
+			Utils::Error(FILE_ERR, UIF_FILE);
+		break;
+	}
+
+	ip.close();
+}
+
+void FileManager::instructions() {
+// Simple instructions given when F1 pressed twice in a row
+// Only in DOS versions
+#define HELPFILE "help.dat"
+#define EOP '#' /* Marks end of a page in help file */
+
+	Common::File f;
+	char line[1024], *wrkLine;
+	char readBuf[2];
+
+	wrkLine = line;
+	if (!f.open(UIF_FILE))
+		Utils::Error(FILE_ERR, HELPFILE);
+
+	while (f.read(readBuf, 1)) {
+		wrkLine[0] = readBuf[0];
+		do {
+			f.read(wrkLine, 1);
+		} while (*wrkLine++ != EOP);
+		wrkLine[-2] = '\0';      /* Remove EOP and previous CR */
+		Utils::Box(BOX_ANY, line);
+		f.read(wrkLine, 1);    /* Remove CR after EOP */
+	}
+	f.close();
+}
+
+} // end of namespace Hugo


Property changes on: scummvm/trunk/engines/hugo/file.cpp
___________________________________________________________________
Added: svn:executable
   + *
Added: svn:mime-type
   + text/plain
Added: svn:keywords
   + Date Rev Author URL Id
Added: svn:eol-style
   + native

Added: scummvm/trunk/engines/hugo/file.h
===================================================================
--- scummvm/trunk/engines/hugo/file.h	                        (rev 0)
+++ scummvm/trunk/engines/hugo/file.h	2010-08-17 09:28:20 UTC (rev 52137)
@@ -0,0 +1,86 @@
+/* ScummVM - Graphic Adventure Engine
+ *

@@ Diff output truncated at 100000 characters. @@

This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.




More information about the Scummvm-git-logs mailing list