[Scummvm-git-logs] scummvm master -> 01bb2bc68901dffa7b107705d05ff6636f65e513

waltervn noreply at scummvm.org
Mon Jun 5 13:05:47 UTC 2023


This automated email contains information about 3 new commits which have been
pushed to the 'scummvm' repo located at https://github.com/scummvm/scummvm .

Summary:
1af9e3fbff ADL: Add check for missing item picture
b9adfbaa01 ADL: Limit number of dropped items drawn
01bb2bc689 ADL: Add support for three more hires4 versions


Commit: 1af9e3fbff0026ae4117dbe043777f07d0171a85
    https://github.com/scummvm/scummvm/commit/1af9e3fbff0026ae4117dbe043777f07d0171a85
Author: Walter van Niftrik (walter at scummvm.org)
Date: 2023-06-05T11:19:51+02:00

Commit Message:
ADL: Add check for missing item picture

This prevents a crash or assert when the feathers are dropped in hires4.
The feathers will still be invisible, as they are in the original game.

Changed paths:
    engines/adl/adl_v2.cpp


diff --git a/engines/adl/adl_v2.cpp b/engines/adl/adl_v2.cpp
index e0793cc1c14..a1b5b343816 100644
--- a/engines/adl/adl_v2.cpp
+++ b/engines/adl/adl_v2.cpp
@@ -189,6 +189,12 @@ void AdlEngine_v2::printString(const Common::String &str) {
 
 void AdlEngine_v2::drawItem(Item &item, const Common::Point &pos) {
 	item.isOnScreen = true;
+
+	if (item.picture == 0 || (uint)(item.picture - 1) >= _itemPics.size()) {
+		warning("Item picture %d not found", item.picture);
+		return;
+	}
+
 	StreamPtr stream(_itemPics[item.picture - 1]->createReadStream());
 	stream->readByte(); // Skip clear opcode
 	_graphics->drawPic(*stream, pos);
@@ -196,7 +202,7 @@ void AdlEngine_v2::drawItem(Item &item, const Common::Point &pos) {
 
 void AdlEngine_v2::loadRoom(byte roomNr) {
 	if (Common::find(_brokenRooms.begin(), _brokenRooms.end(), roomNr) != _brokenRooms.end()) {
-		debug("Warning: attempt to load non-existent room %d", roomNr);
+		warning("Attempt to load non-existent room %d", roomNr);
 		_roomData.description.clear();
 		_roomData.pictures.clear();
 		_roomData.commands.clear();


Commit: b9adfbaa011df4ed30ae3652c6c50a6d13cc1c5c
    https://github.com/scummvm/scummvm/commit/b9adfbaa011df4ed30ae3652c6c50a6d13cc1c5c
Author: Walter van Niftrik (walter at scummvm.org)
Date: 2023-06-05T11:19:51+02:00

Commit Message:
ADL: Limit number of dropped items drawn

This prevents a possible out-of-bounds array read.

Changed paths:
    engines/adl/adl_v2.cpp


diff --git a/engines/adl/adl_v2.cpp b/engines/adl/adl_v2.cpp
index a1b5b343816..1509f7b7f65 100644
--- a/engines/adl/adl_v2.cpp
+++ b/engines/adl/adl_v2.cpp
@@ -314,7 +314,7 @@ void AdlEngine_v2::drawItems() {
 		if (item->region == _state.region && item->room == _state.room && !item->isOnScreen) {
 			if (item->state == IDI_ITEM_DROPPED) {
 				// Draw dropped item if in normal view
-				if (getCurRoom().picture == getCurRoom().curPicture)
+				if (getCurRoom().picture == getCurRoom().curPicture && _itemsOnScreen < _itemOffsets.size())
 					drawItem(*item, _itemOffsets[_itemsOnScreen++]);
 			} else {
 				// Draw fixed item if current view is in the pic list


Commit: 01bb2bc68901dffa7b107705d05ff6636f65e513
    https://github.com/scummvm/scummvm/commit/01bb2bc68901dffa7b107705d05ff6636f65e513
Author: Walter van Niftrik (walter at scummvm.org)
Date: 2023-06-05T14:26:21+02:00

Commit Message:
ADL: Add support for three more hires4 versions

Changed paths:
    engines/adl/adl.cpp
    engines/adl/adl.h
    engines/adl/adl_v2.cpp
    engines/adl/adl_v2.h
    engines/adl/detection.cpp
    engines/adl/detection.h
    engines/adl/hires2.cpp
    engines/adl/hires4.cpp


diff --git a/engines/adl/adl.cpp b/engines/adl/adl.cpp
index 701f6004d0b..503bed68310 100644
--- a/engines/adl/adl.cpp
+++ b/engines/adl/adl.cpp
@@ -298,12 +298,46 @@ byte AdlEngine::inputKey(bool showCursor) const {
 	return key;
 }
 
-void AdlEngine::loadWords(Common::ReadStream &stream, WordMap &map, Common::StringArray &pri) const {
+void AdlEngine::waitKey(uint32 ms, Common::KeyCode keycode) const {
+	uint32 start = g_system->getMillis();
+
+	while (!shouldQuit()) {
+		Common::Event event;
+		if (pollEvent(event)) {
+			if (event.type == Common::EVENT_KEYDOWN)
+				if (keycode == Common::KEYCODE_INVALID || keycode == event.kbd.keycode)
+					return;
+		}
+
+		if (ms && g_system->getMillis() - start >= ms)
+			return;
+
+		g_system->delayMillis(16);
+	}
+}
+
+void AdlEngine::loadWords(Common::ReadStream &stream, WordMap &map, Common::StringArray &pri, uint count) const {
 	uint index = 0;
 
 	map.clear();
 	pri.clear();
 
+	// WORKAROUND: Several games contain one or more word lists without a terminator
+	switch (getGameType()) {
+	case GAME_TYPE_HIRES3:
+		if (&map == &_verbs)
+			count = 72;
+		else
+			count = 113;
+		break;
+	case GAME_TYPE_HIRES5:
+		if (_state.region == 15 && &map == &_nouns)
+			count = 81;
+		break;
+	default:
+		break;
+	}
+
 	while (1) {
 		++index;
 
@@ -327,17 +361,8 @@ void AdlEngine::loadWords(Common::ReadStream &stream, WordMap &map, Common::Stri
 		if (synonyms == 0xff)
 			break;
 
-		// WORKAROUND: Missing verb list terminator in hires3
-		if (getGameType() == GAME_TYPE_HIRES3 && index == 72 && synonyms == 0)
-			return;
-
-		// WORKAROUND: Missing noun list terminator in hires3
-		if (getGameType() == GAME_TYPE_HIRES3 && index == 113)
-			return;
-
-		// WORKAROUND: Missing noun list terminator in hires5 region 15
-		if (getGameType() == GAME_TYPE_HIRES5 && _state.region == 15 && index == 81)
-			return;
+		if (index == count)
+			break;
 
 		for (uint i = 0; i < synonyms; ++i) {
 			if (stream.read((char *)buf, IDI_WORD_SIZE) < IDI_WORD_SIZE)
diff --git a/engines/adl/adl.h b/engines/adl/adl.h
index 166b2988ec5..6ec3357fd6d 100644
--- a/engines/adl/adl.h
+++ b/engines/adl/adl.h
@@ -31,6 +31,7 @@
 #include "common/func.h"
 #include "common/ptr.h"
 #include "common/scummsys.h"
+#include "common/keyboard.h"
 
 #include "engines/engine.h"
 
@@ -286,12 +287,13 @@ protected:
 	virtual Common::String getLine();
 	Common::String inputString(byte prompt = 0) const;
 	byte inputKey(bool showCursor = true) const;
+	void waitKey(uint32 ms = 0, Common::KeyCode keycode = Common::KEYCODE_INVALID) const;
 	virtual void getInput(uint &verb, uint &noun);
 	Common::String getWord(const Common::String &line, uint &index) const;
 
 	virtual Common::String formatVerbError(const Common::String &verb) const;
 	virtual Common::String formatNounError(const Common::String &verb, const Common::String &noun) const;
-	void loadWords(Common::ReadStream &stream, WordMap &map, Common::StringArray &pri) const;
+	void loadWords(Common::ReadStream &stream, WordMap &map, Common::StringArray &pri, uint count = 0) const;
 	void readCommands(Common::ReadStream &stream, Commands &commands);
 	void removeCommand(Commands &commands, uint idx);
 	Command &getCommand(Commands &commands, uint idx);
diff --git a/engines/adl/adl_v2.cpp b/engines/adl/adl_v2.cpp
index 1509f7b7f65..380e3bd4b24 100644
--- a/engines/adl/adl_v2.cpp
+++ b/engines/adl/adl_v2.cpp
@@ -43,6 +43,27 @@ AdlEngine_v2::AdlEngine_v2(OSystem *syst, const AdlGameDescription *gd) :
 		_picOnScreen(0),
 		_itemsOnScreen(0) { }
 
+void AdlEngine_v2::mapExeStrings(const Common::StringArray &strings) {
+	if (strings.size() < 11)
+		error("Not enough strings found in executable");
+
+	// Parser messages
+	_strings.verbError = strings[2];
+	_strings.nounError = strings[3];
+	_strings.enterCommand = strings[4];
+
+	// Line feeds
+	_strings.lineFeeds = strings[0];
+
+	// Opcode strings
+	_strings_v2.saveInsert = strings[5];
+	_strings_v2.saveReplace = strings[6];
+	_strings_v2.restoreInsert = strings[7];
+	_strings_v2.restoreReplace = strings[8];
+	_strings.playAgain = strings[9];
+	_strings.pressReturn = strings[10];
+}
+
 void AdlEngine_v2::insertDisk(byte volume) {
 	delete _disk;
 	_disk = new DiskImage();
diff --git a/engines/adl/adl_v2.h b/engines/adl/adl_v2.h
index ed5c54ee39e..412971e8752 100644
--- a/engines/adl/adl_v2.h
+++ b/engines/adl/adl_v2.h
@@ -49,6 +49,7 @@ protected:
 	// Engine
 	bool canSaveGameStateCurrently() override;
 
+	void mapExeStrings(const Common::StringArray &strings);
 	void insertDisk(byte volume);
 	virtual DataBlockPtr readDataBlockPtr(Common::ReadStream &f) const;
 	virtual void adjustDataBlockPtr(byte &track, byte &sector, byte &offset, byte &size) const { }
diff --git a/engines/adl/detection.cpp b/engines/adl/detection.cpp
index 3cd25e5e17f..fab2d7964dd 100644
--- a/engines/adl/detection.cpp
+++ b/engines/adl/detection.cpp
@@ -296,9 +296,41 @@ static const AdlGameDescription gameDiskDescriptions[] = {
 		GAME_TYPE_HIRES3,
 		GAME_VER_NONE
 	},
-	{ // Hi-Res Adventure #4: Ulysses and the Golden Fleece - Apple II - Load 'N' Go
+	{ // Hi-Res Adventure #4: Ulysses and the Golden Fleece - Apple II - Original release
 		{
-			"hires4", "",
+			"hires4", "On-Line Systems [A]",
+			{
+				{ "ulyssesa", 0, "fac225127a35cf2596d41e91647a532c", 143360 },
+				{ "ulyssesb", 1, "793a01392a094d5e2988deab5510e9fc", 143360 },
+				AD_LISTEND
+			},
+			Common::EN_ANY,
+			Common::kPlatformApple2,
+			ADGF_NO_FLAGS,
+			DEFAULT_OPTIONS
+		},
+		GAME_TYPE_HIRES4,
+		GAME_VER_HR4_V1_0
+	},
+	{ // Hi-Res Adventure #4: Ulysses and the Golden Fleece - Apple II - Version 1.1
+		{
+			"hires4", "On-Line Systems [B]",
+			{
+				{ "ulyssesa", 0, "420f515e64612d21446ede8078055f0e", 143360 },
+				{ "ulyssesb", 1, "9fa8552255ae651b252844168b8b6617", 143360 },
+				AD_LISTEND
+			},
+			Common::EN_ANY,
+			Common::kPlatformApple2,
+			ADGF_NO_FLAGS,
+			DEFAULT_OPTIONS
+		},
+		GAME_TYPE_HIRES4,
+		GAME_VER_HR4_V1_1
+	},
+	{ // Hi-Res Adventure #4: Ulysses and the Golden Fleece - Apple II - Green Valley Publishing - Version 0.0
+		{
+			"hires4", "Green Valley [A]",
 			{
 				{ "ulyssesa", 0, "1eaeb2f1a773ce2d1cb9f16b2ef09049", 143360 },
 				{ "ulyssesb", 1, "9fa8552255ae651b252844168b8b6617", 143360 },
@@ -310,7 +342,23 @@ static const AdlGameDescription gameDiskDescriptions[] = {
 			DEFAULT_OPTIONS
 		},
 		GAME_TYPE_HIRES4,
-		GAME_VER_NONE
+		GAME_VER_HR4_LNG
+	},
+	{ // Hi-Res Adventure #4: Ulysses and the Golden Fleece - Apple II - Green Valley Publishing - Version 1.1
+		{
+			"hires4", "Green Valley [B]",
+			{
+				{ "ulyssesa", 0, "35b6dce492c893327796645f481737ca", 143360 },
+  				{ "ulyssesb", 1, "9fa8552255ae651b252844168b8b6617", 143360 },
+				AD_LISTEND
+			},
+			Common::EN_ANY,
+			Common::kPlatformApple2,
+			ADGF_NO_FLAGS,
+			DEFAULT_OPTIONS
+		},
+		GAME_TYPE_HIRES4,
+		GAME_VER_HR4_LNG
 	},
 	{ // Hi-Res Adventure #4: Ulysses and the Golden Fleece - Atari 8-bit - Re-release
 		{
diff --git a/engines/adl/detection.h b/engines/adl/detection.h
index 0592a1f804f..058bf2a294d 100644
--- a/engines/adl/detection.h
+++ b/engines/adl/detection.h
@@ -62,7 +62,10 @@ enum GameVersion {
 	GAME_VER_HR1_COARSE,
 	GAME_VER_HR1_VF1,
 	GAME_VER_HR1_VF2,
-	GAME_VER_HR1_PD
+	GAME_VER_HR1_PD,
+	GAME_VER_HR4_V1_0,
+	GAME_VER_HR4_V1_1,
+	GAME_VER_HR4_LNG
 };
 
 struct AdlGameDescription {
diff --git a/engines/adl/hires2.cpp b/engines/adl/hires2.cpp
index 4b7f7638521..9b11f6db616 100644
--- a/engines/adl/hires2.cpp
+++ b/engines/adl/hires2.cpp
@@ -72,35 +72,17 @@ void HiResBaseEngine::init() {
 	stream.reset(_disk->createReadStream(0x19, 0x0, 0x00, 25, 13));
 	Common::StringArray exeStrings;
 	extractExeStrings(*stream, 0x1566, exeStrings);
-
-	if (exeStrings.size() < 11)
-		error("Failed to load strings from executable");
+	mapExeStrings(exeStrings);
 
 	// Heuristic to test for early versions that differ slightly
 	// Later versions have two additional strings for "INIT DISK"
 	const bool oldEngine = exeStrings.size() < 13;
 
-	// Read parser messages
-	_strings.verbError = exeStrings[2];
-	_strings.nounError = exeStrings[3];
-	_strings.enterCommand = exeStrings[4];
-
 	if (!oldEngine) {
 		stream.reset(_disk->createReadStream(0x19, 0x7, 0xd7));
 		_strings_v2.time = readString(*stream, 0xff);
 	}
 
-	// Read line feeds
-	_strings.lineFeeds = exeStrings[0];
-
-	// Read opcode strings
-	_strings_v2.saveInsert = exeStrings[5];
-	_strings_v2.saveReplace = exeStrings[6];
-	_strings_v2.restoreInsert = exeStrings[7];
-	_strings_v2.restoreReplace = exeStrings[8];
-	_strings.playAgain = exeStrings[9];
-	_strings.pressReturn = exeStrings[10];
-
 	// Load global picture data
 	stream.reset(_disk->createReadStream(0x19, 0xa, 0x80, 0));
 	loadPictures(*stream);
diff --git a/engines/adl/hires4.cpp b/engines/adl/hires4.cpp
index 1e9f906e30b..49a2a2dcf00 100644
--- a/engines/adl/hires4.cpp
+++ b/engines/adl/hires4.cpp
@@ -40,7 +40,7 @@ namespace Adl {
 #define IDI_HR4_NUM_VARS 40
 #define IDI_HR4_NUM_ITEM_DESCS 44
 #define IDI_HR4_NUM_ITEM_PICS 41
-#define IDI_HR4_NUM_ITEM_OFFSETS 40
+#define IDI_HR4_NUM_ITEM_OFFSETS 16
 
 // Messages used outside of scripts
 #define IDI_HR4_MSG_CANT_GO_THERE      110
@@ -49,32 +49,130 @@ namespace Adl {
 #define IDI_HR4_MSG_ITEM_NOT_HERE      115
 #define IDI_HR4_MSG_THANKS_FOR_PLAYING 113
 
-class HiRes4Engine : public AdlEngine_v3 {
+class HiRes4BaseEngine : public AdlEngine_v3 {
 public:
-	HiRes4Engine(OSystem *syst, const AdlGameDescription *gd) :
-			AdlEngine_v3(syst, gd),
-			_boot(nullptr) { _brokenRooms.push_back(121); }
-	~HiRes4Engine() override;
+	HiRes4BaseEngine(OSystem *syst, const AdlGameDescription *gd);
+	~HiRes4BaseEngine() override;
+
+protected:
+	// AdlEngine
+	void init() override;
+	void initGameState() override;
+
+	DiskImage *_boot;
+};
+
+HiRes4BaseEngine::HiRes4BaseEngine(OSystem *syst, const AdlGameDescription *gd) :
+		AdlEngine_v3(syst, gd),
+		_boot(nullptr) {
+
+	_brokenRooms.push_back(121);
+	_messageIds.cantGoThere = IDI_HR4_MSG_CANT_GO_THERE;
+	_messageIds.dontUnderstand = IDI_HR4_MSG_DONT_UNDERSTAND;
+	_messageIds.itemDoesntMove = IDI_HR4_MSG_ITEM_DOESNT_MOVE;
+	_messageIds.itemNotHere = IDI_HR4_MSG_ITEM_NOT_HERE;
+	_messageIds.thanksForPlaying = IDI_HR4_MSG_THANKS_FOR_PLAYING;
+}
+
+HiRes4BaseEngine::~HiRes4BaseEngine() {
+	delete _boot;
+}
+
+void HiRes4BaseEngine::init() {
+	_graphics = new GraphicsMan_v2<Display_A2>(*static_cast<Display_A2 *>(_display));
+
+	_boot = new DiskImage();
+	if (!_boot->open(getDiskImageName(0)))
+		error("Failed to open disk image '%s'", getDiskImageName(0).c_str());
+
+	insertDisk(1);
+}
+
+void HiRes4BaseEngine::initGameState() {
+	_state.vars.resize(IDI_HR4_NUM_VARS);
+}
+
+class HiRes4Engine_v1_0 : public HiRes4BaseEngine {
+public:
+	HiRes4Engine_v1_0(OSystem *syst, const AdlGameDescription *gd) : HiRes4BaseEngine(syst, gd) { }
 
 private:
 	// AdlEngine
 	void runIntro() override;
 	void init() override;
 	void initGameState() override;
+};
 
-	void putSpace(uint x, uint y) const;
-	void drawChar(byte c, Common::SeekableReadStream &shapeTable, Common::Point &pos) const;
-	void drawText(const Common::String &str, Common::SeekableReadStream &shapeTable, const float ht, const float vt) const;
+void HiRes4Engine_v1_0::runIntro() {
+	StreamPtr stream(_boot->createReadStream(0x06, 0x3, 0xb9, 1));
 
-	void runIntroAdvise(Common::SeekableReadStream &menu);
-	void runIntroLogo(Common::SeekableReadStream &ms2);
-	void runIntroTitle(Common::SeekableReadStream &menu, Common::SeekableReadStream &ms2);
-	void runIntroInstructions(Common::SeekableReadStream &instructions);
-	void runIntroLoading(Common::SeekableReadStream &adventure);
+	_display->setMode(Display::kModeText);
 
-	static const uint kClock = 1022727; // Apple II CPU clock rate
+	Common::String str = readString(*stream);
 
-	DiskImage *_boot;
+	if (stream->eos() || stream->err())
+		error("Error reading disk image");
+
+	_display->printString(str);
+
+	waitKey(0, Common::KEYCODE_RETURN);
+}
+
+void HiRes4Engine_v1_0::init() {
+	HiRes4BaseEngine::init();
+
+	StreamPtr stream(_boot->createReadStream(0x9, 0x1, 0x00, 13));
+	Common::StringArray exeStrings;
+	extractExeStrings(*stream, 0x1566, exeStrings);
+	mapExeStrings(exeStrings);
+
+	stream.reset(_boot->createReadStream(0x0e, 0x5, 0x00, 3, 13));
+	loadMessages(*stream, IDI_HR4_NUM_MESSAGES);
+
+	stream.reset(_boot->createReadStream(0x09, 0x0, 0x80, 0, 13));
+	loadPictures(*stream);
+
+	stream.reset(_boot->createReadStream(0x0d, 0xc, 0x05, 0, 13));
+	loadItemPictures(*stream, IDI_HR4_NUM_ITEM_PICS);
+
+	stream.reset(_boot->createReadStream(0x07, 0x0, 0x15, 2, 13));
+	loadItemDescriptions(*stream, IDI_HR4_NUM_ITEM_DESCS);
+
+	stream.reset(_boot->createReadStream(0x0c, 0x9, 0xa5, 5, 13));
+	readCommands(*stream, _roomCommands);
+
+	stream.reset(_boot->createReadStream(0x07, 0xc, 0x00, 3, 13));
+	readCommands(*stream, _globalCommands);
+
+	stream.reset(_boot->createReadStream(0x0a, 0x7, 0x15, 0, 13));
+	loadDroppedItemOffsets(*stream, IDI_HR4_NUM_ITEM_OFFSETS);
+
+	stream.reset(_boot->createReadStream(0x08, 0x3, 0x00, 3, 13));
+	loadWords(*stream, _verbs, _priVerbs, 80); // Missing terminator
+
+	stream.reset(_boot->createReadStream(0x05, 0x7, 0x00, 6, 13));
+	loadWords(*stream, _nouns, _priNouns, 109); // Missing terminator
+}
+
+void HiRes4Engine_v1_0::initGameState() {
+	HiRes4BaseEngine::initGameState();
+
+	StreamPtr stream(_boot->createReadStream(0x04, 0xa, 0x0e, 9, 13));
+	loadRooms(*stream, IDI_HR4_NUM_ROOMS);
+
+	stream.reset(_boot->createReadStream(0x04, 0x5, 0x00, 12, 13));
+	loadItems(*stream);
+}
+
+class HiRes4Engine_v1_1 : public HiRes4BaseEngine {
+public:
+	HiRes4Engine_v1_1(OSystem *syst, const AdlGameDescription *gd) : HiRes4BaseEngine(syst, gd) { }
+
+private:
+	// AdlEngine
+	void runIntro() override;
+	void init() override;
+	void initGameState() override;
 };
 
 // TODO: It might be worth replacing this with a more generic variant that
@@ -123,11 +221,112 @@ static Common::MemoryReadStream *decodeData(Common::SeekableReadStream &stream,
 	return new Common::MemoryReadStream(buf, streamSize, DisposeAfterUse::YES);
 }
 
-HiRes4Engine::~HiRes4Engine() {
-	delete _boot;
+void HiRes4Engine_v1_1::runIntro() {
+	Common::ScopedPtr<Files_AppleDOS> files(new Files_AppleDOS());
+	files->open(getDiskImageName(0));
+
+	StreamPtr menu(files->createReadStream("\b\b\b\b\b\b\bULYSSES\r(C) 1982"));
+	menu->seek(0x2eb);
+
+	for (uint i = 0; i < 4; ++i) {
+		const int16 y[4] = { 0, 2, 4, 16 };
+		Common::String s = menu->readString(0, 39);
+		_display->moveCursorTo(Common::Point(0, y[i]));
+		_display->printString(s);
+	}
+
+	waitKey(3000);
 }
 
-void HiRes4Engine::putSpace(uint x, uint y) const {
+void HiRes4Engine_v1_1::init() {
+	HiRes4BaseEngine::init();
+
+	StreamPtr stream(readSkewedSectors(_boot, 0x05, 0x6, 1));
+	_strings.verbError = readStringAt(*stream, 0x4f);
+	_strings.nounError = readStringAt(*stream, 0x8e);
+	_strings.enterCommand = readStringAt(*stream, 0xbc);
+
+	stream.reset(readSkewedSectors(_boot, 0x05, 0x3, 1));
+	stream->skip(0xd7);
+	_strings_v2.time = readString(*stream, 0xff);
+
+	stream.reset(readSkewedSectors(_boot, 0x05, 0x7, 2));
+	_strings.lineFeeds = readStringAt(*stream, 0xf8);
+
+	stream.reset(readSkewedSectors(_boot, 0x06, 0xf, 3));
+	_strings_v2.saveInsert = readStringAt(*stream, 0x5f);
+	_strings_v2.saveReplace = readStringAt(*stream, 0xe5);
+	_strings_v2.restoreInsert = readStringAt(*stream, 0x132);
+	_strings_v2.restoreReplace = readStringAt(*stream, 0x1c2);
+	_strings.playAgain = readStringAt(*stream, 0x225);
+
+	stream.reset(readSkewedSectors(_boot, 0x0a, 0x0, 5));
+	loadMessages(*stream, IDI_HR4_NUM_MESSAGES);
+
+	stream.reset(readSkewedSectors(_boot, 0x05, 0x2, 1));
+	stream->skip(0x80);
+	loadPictures(*stream);
+
+	stream.reset(readSkewedSectors(_boot, 0x09, 0x2, 1));
+	stream->skip(0x05);
+	loadItemPictures(*stream, IDI_HR4_NUM_ITEM_PICS);
+
+	stream.reset(readSkewedSectors(_boot, 0x04, 0x0, 3));
+	stream->skip(0x15);
+	loadItemDescriptions(*stream, IDI_HR4_NUM_ITEM_DESCS);
+
+	stream.reset(readSkewedSectors(_boot, 0x08, 0x2, 6));
+	stream->skip(0xa5);
+	readCommands(*stream, _roomCommands);
+
+	stream.reset(readSkewedSectors(_boot, 0x04, 0xc, 4));
+	stream.reset(decodeData(*stream, 0x218, 0x318, 0xee));
+	readCommands(*stream, _globalCommands);
+
+	stream.reset(readSkewedSectors(_boot, 0x06, 0x6, 1));
+	stream->skip(0x15);
+	loadDroppedItemOffsets(*stream, IDI_HR4_NUM_ITEM_OFFSETS);
+
+	stream.reset(readSkewedSectors(_boot, 0x05, 0x0, 4));
+	loadWords(*stream, _verbs, _priVerbs);
+
+	stream.reset(readSkewedSectors(_boot, 0x0b, 0xb, 7));
+	loadWords(*stream, _nouns, _priNouns);
+}
+
+void HiRes4Engine_v1_1::initGameState() {
+	HiRes4BaseEngine::initGameState();
+
+	StreamPtr stream(readSkewedSectors(_boot, 0x0b, 0x9, 10));
+	stream->skip(0x0e);
+	loadRooms(*stream, IDI_HR4_NUM_ROOMS);
+
+	stream.reset(readSkewedSectors(_boot, 0x0b, 0x0, 13));
+	stream.reset(decodeData(*stream, 0x43, 0x143, 0x91));
+	loadItems(*stream);
+}
+
+class HiRes4Engine_LNG : public HiRes4Engine_v1_1 {
+public:
+	HiRes4Engine_LNG(OSystem *syst, const AdlGameDescription *gd) : HiRes4Engine_v1_1(syst, gd) { }
+
+private:
+	// AdlEngine
+	void runIntro() override;
+
+	void putSpace(uint x, uint y) const;
+	void drawChar(byte c, Common::SeekableReadStream &shapeTable, Common::Point &pos) const;
+	void drawText(const Common::String &str, Common::SeekableReadStream &shapeTable, const float ht, const float vt) const;
+
+	void runIntroLogo(Common::SeekableReadStream &ms2);
+	void runIntroTitle(Common::SeekableReadStream &menu, Common::SeekableReadStream &ms2);
+	void runIntroInstructions(Common::SeekableReadStream &instructions);
+	void runIntroLoading(Common::SeekableReadStream &adventure);
+
+	static const uint kClock = 1022727; // Apple II CPU clock rate
+};
+
+void HiRes4Engine_LNG::putSpace(uint x, uint y) const {
 	if (shouldQuit())
 		return;
 
@@ -137,7 +336,7 @@ void HiRes4Engine::putSpace(uint x, uint y) const {
 	delay(2);
 }
 
-void HiRes4Engine::drawChar(byte c, Common::SeekableReadStream &shapeTable, Common::Point &pos) const {
+void HiRes4Engine_LNG::drawChar(byte c, Common::SeekableReadStream &shapeTable, Common::Point &pos) const {
 	shapeTable.seek(0);
 	byte entries = shapeTable.readByte();
 
@@ -152,7 +351,7 @@ void HiRes4Engine::drawChar(byte c, Common::SeekableReadStream &shapeTable, Comm
 	_graphics->drawShape(shapeTable, pos);
 }
 
-void HiRes4Engine::drawText(const Common::String &str, Common::SeekableReadStream &shapeTable, const float ht, const float vt) const {
+void HiRes4Engine_LNG::drawText(const Common::String &str, Common::SeekableReadStream &shapeTable, const float ht, const float vt) const {
 	if (shouldQuit())
 		return;
 
@@ -171,86 +370,7 @@ void HiRes4Engine::drawText(const Common::String &str, Common::SeekableReadStrea
 	}
 }
 
-void HiRes4Engine::runIntroAdvise(Common::SeekableReadStream &menu) {
-	Common::StringArray backupText;
-	backupText.push_back(readStringAt(menu, 0x659, '"'));
-	backupText.push_back(readStringAt(menu, 0x682, '"'));
-	backupText.push_back(readStringAt(menu, 0x6a9, '"'));
-	backupText.push_back(readStringAt(menu, 0x6c6, '"'));
-
-	_display->setMode(Display::kModeText);
-
-	for (uint x = 2; x <= 36; ++x)
-		putSpace(x, 2);
-
-	for (uint y = 3; y <= 20; ++y) {
-		putSpace(2, y);
-		putSpace(36, y);
-	}
-
-	for (uint x = 2; x <= 36; ++x)
-		putSpace(x, 20);
-
-	for (uint x = 0; x <= 38; ++x)
-		putSpace(x, 0);
-
-	for (uint y = 1; y <= 21; ++y) {
-		putSpace(0, y);
-		putSpace(38, y);
-	}
-
-	for (uint x = 0; x <= 38; ++x)
-		putSpace(x, 22);
-
-	int y = 7;
-
-	for (uint i = 0; i < backupText.size(); ++i) {
-		uint x = 0;
-
-		do {
-			if (shouldQuit())
-				return;
-
-			++x;
-
-			Common::String left = backupText[i];
-			left.erase(x, Common::String::npos);
-			Common::String right = backupText[i];
-			right.erase(0, right.size() - x);
-
-			_display->moveCursorTo(Common::Point(19 - x, y));
-			_display->printAsciiString(left);
-			_display->moveCursorTo(Common::Point(19, y));
-			_display->printAsciiString(right);
-			_display->renderText();
-			delay(35);
-		} while (x != backupText[i].size() / 2);
-
-		if (i == 2)
-			y = 18;
-		else
-			y += 2;
-	}
-
-	Common::String cursor = readStringAt(menu, 0x781, '"');
-
-	uint cursorIdx = 0;
-	while (!shouldQuit()) {
-		Common::Event event;
-		if (pollEvent(event)) {
-			if (event.type == Common::EVENT_KEYDOWN)
-				break;
-		}
-
-		_display->moveCursorTo(Common::Point(32, 18));
-		_display->printChar(_display->asciiToNative(cursor[cursorIdx]));
-		_display->renderText();
-		g_system->delayMillis(25);
-		cursorIdx = (cursorIdx + 1) % cursor.size();
-	}
-}
-
-void HiRes4Engine::runIntroLogo(Common::SeekableReadStream &ms2) {
+void HiRes4Engine_LNG::runIntroLogo(Common::SeekableReadStream &ms2) {
 	Display_A2 *display = static_cast<Display_A2 *>(_display);
 	const uint width = display->getGfxWidth();
 	const uint height = display->getGfxHeight();
@@ -298,7 +418,7 @@ void HiRes4Engine::runIntroLogo(Common::SeekableReadStream &ms2) {
 	}
 }
 
-void HiRes4Engine::runIntroTitle(Common::SeekableReadStream &menu, Common::SeekableReadStream &ms2) {
+void HiRes4Engine_LNG::runIntroTitle(Common::SeekableReadStream &menu, Common::SeekableReadStream &ms2) {
 	ms2.seek(0x2290);
 	StreamPtr shapeTable(ms2.readStream(0x450));
 	if (ms2.err() || ms2.eos())
@@ -339,7 +459,7 @@ void HiRes4Engine::runIntroTitle(Common::SeekableReadStream &menu, Common::Seeka
 	drawText(titleString, *shapeTable, 12.5f, 14.0f + menuStrings.size() * 1.2f + 2.0f);
 }
 
-void HiRes4Engine::runIntroInstructions(Common::SeekableReadStream &instructions) {
+void HiRes4Engine_LNG::runIntroInstructions(Common::SeekableReadStream &instructions) {
 	Common::String line;
 	Common::String pressKey(readStringAt(instructions, 0xad6, '"'));
 	instructions.seek(0);
@@ -399,7 +519,7 @@ void HiRes4Engine::runIntroInstructions(Common::SeekableReadStream &instructions
 	}
 }
 
-void HiRes4Engine::runIntroLoading(Common::SeekableReadStream &adventure) {
+void HiRes4Engine_LNG::runIntroLoading(Common::SeekableReadStream &adventure) {
 	_display->home();
 	_display->setMode(Display::kModeText);
 
@@ -419,32 +539,53 @@ void HiRes4Engine::runIntroLoading(Common::SeekableReadStream &adventure) {
 		_display->printString(Common::String(text[i], kStringLen));
 	}
 
-	delay(4000);
+	waitKey(3000);
 }
 
-void HiRes4Engine::runIntro() {
+void HiRes4Engine_LNG::runIntro() {
 	Common::ScopedPtr<Files_AppleDOS> files(new Files_AppleDOS());
 	files->open(getDiskImageName(0));
 
 	while (!shouldQuit()) {
 		StreamPtr menu(files->createReadStream("MENU"));
-		runIntroAdvise(*menu);
 
-		if (shouldQuit())
-			return;
+		const bool oldVersion = files->exists("MS2");
 
-		StreamPtr ms2(files->createReadStream("MS2"));
-		runIntroLogo(*ms2);
+		if (oldVersion) {
+			// Version 0.0
+			StreamPtr ms2(files->createReadStream("MS2"));
+			runIntroLogo(*ms2);
 
-		if (shouldQuit())
-			return;
+			if (shouldQuit())
+				return;
 
-		_graphics->setBounds(Common::Rect(280, 192));
-		runIntroTitle(*menu, *ms2);
-		_graphics->setBounds(Common::Rect(280, 160));
+			_graphics->setBounds(Common::Rect(280, 192));
+
+			runIntroTitle(*menu, *ms2);
+			_graphics->setBounds(Common::Rect(280, 160));
+		} else {
+			// Version 1.1
+			// This version also has a publisher logo, but it uses BASIC
+			// graphics routines that have not been implemented, so we skip it.
+
+			// File offset, x and y coordinates for each line of text in the title screen
+			// We skip the "create data disk" menu option
+			const uint text[][3] = { { 0x13, 4, 1 }, { 0x42, 7, 9 }, { 0x66, 7, 11 }, { 0xaa, 7, 17 } };
+
+			for (uint i = 0; i < ARRAYSIZE(text); ++i) {
+				Common::String str = readStringAt(*menu, text[i][0], '"');
+				for (char &c : str) {
+					c = _display->asciiToNative(c);
+					if (i == 0)
+						c &= ~0xc0; // Invert first line
+				}
+				_display->moveCursorTo(Common::Point(text[i][1], text[i][2]));
+				_display->printString(str);
+			}
+		}
 
 		while (1) {
-			char key = inputKey();
+			const char key = inputKey(false);
 
 			if (shouldQuit())
 				return;
@@ -462,86 +603,6 @@ void HiRes4Engine::runIntro() {
 	}
 }
 
-void HiRes4Engine::init() {
-	_graphics = new GraphicsMan_v2<Display_A2>(*static_cast<Display_A2 *>(_display));
-
-	_boot = new DiskImage();
-	if (!_boot->open(getDiskImageName(0)))
-		error("Failed to open disk image '%s'", getDiskImageName(0).c_str());
-
-	insertDisk(1);
-
-	StreamPtr stream(readSkewedSectors(_boot, 0x05, 0x6, 1));
-	_strings.verbError = readStringAt(*stream, 0x4f);
-	_strings.nounError = readStringAt(*stream, 0x8e);
-	_strings.enterCommand = readStringAt(*stream, 0xbc);
-
-	stream.reset(readSkewedSectors(_boot, 0x05, 0x3, 1));
-	stream->skip(0xd7);
-	_strings_v2.time = readString(*stream, 0xff);
-
-	stream.reset(readSkewedSectors(_boot, 0x05, 0x7, 2));
-	_strings.lineFeeds = readStringAt(*stream, 0xf8);
-
-	stream.reset(readSkewedSectors(_boot, 0x06, 0xf, 3));
-	_strings_v2.saveInsert = readStringAt(*stream, 0x5f);
-	_strings_v2.saveReplace = readStringAt(*stream, 0xe5);
-	_strings_v2.restoreInsert = readStringAt(*stream, 0x132);
-	_strings_v2.restoreReplace = readStringAt(*stream, 0x1c2);
-	_strings.playAgain = readStringAt(*stream, 0x225);
-
-	_messageIds.cantGoThere = IDI_HR4_MSG_CANT_GO_THERE;
-	_messageIds.dontUnderstand = IDI_HR4_MSG_DONT_UNDERSTAND;
-	_messageIds.itemDoesntMove = IDI_HR4_MSG_ITEM_DOESNT_MOVE;
-	_messageIds.itemNotHere = IDI_HR4_MSG_ITEM_NOT_HERE;
-	_messageIds.thanksForPlaying = IDI_HR4_MSG_THANKS_FOR_PLAYING;
-
-	stream.reset(readSkewedSectors(_boot, 0x0a, 0x0, 5));
-	loadMessages(*stream, IDI_HR4_NUM_MESSAGES);
-
-	stream.reset(readSkewedSectors(_boot, 0x05, 0x2, 1));
-	stream->skip(0x80);
-	loadPictures(*stream);
-
-	stream.reset(readSkewedSectors(_boot, 0x09, 0x2, 1));
-	stream->skip(0x05);
-	loadItemPictures(*stream, IDI_HR4_NUM_ITEM_PICS);
-
-	stream.reset(readSkewedSectors(_boot, 0x04, 0x0, 3));
-	stream->skip(0x15);
-	loadItemDescriptions(*stream, IDI_HR4_NUM_ITEM_DESCS);
-
-	stream.reset(readSkewedSectors(_boot, 0x08, 0x2, 6));
-	stream->skip(0xa5);
-	readCommands(*stream, _roomCommands);
-
-	stream.reset(readSkewedSectors(_boot, 0x04, 0xc, 4));
-	stream.reset(decodeData(*stream, 0x218, 0x318, 0xee));
-	readCommands(*stream, _globalCommands);
-
-	stream.reset(readSkewedSectors(_boot, 0x06, 0x6, 1));
-	stream->skip(0x15);
-	loadDroppedItemOffsets(*stream, IDI_HR4_NUM_ITEM_OFFSETS);
-
-	stream.reset(readSkewedSectors(_boot, 0x05, 0x0, 4));
-	loadWords(*stream, _verbs, _priVerbs);
-
-	stream.reset(readSkewedSectors(_boot, 0x0b, 0xb, 7));
-	loadWords(*stream, _nouns, _priNouns);
-}
-
-void HiRes4Engine::initGameState() {
-	_state.vars.resize(IDI_HR4_NUM_VARS);
-
-	StreamPtr stream(readSkewedSectors(_boot, 0x0b, 0x9, 10));
-	stream->skip(0x0e);
-	loadRooms(*stream, IDI_HR4_NUM_ROOMS);
-
-	stream.reset(readSkewedSectors(_boot, 0x0b, 0x0, 13));
-	stream.reset(decodeData(*stream, 0x43, 0x143, 0x91));
-	loadItems(*stream);
-}
-
 class HiRes4Engine_Atari : public AdlEngine_v3 {
 public:
 	HiRes4Engine_Atari(OSystem *syst, const AdlGameDescription *gd) :
@@ -743,7 +804,14 @@ void HiRes4Engine_Atari::adjustDataBlockPtr(byte &track, byte &sector, byte &off
 Engine *HiRes4Engine_create(OSystem *syst, const AdlGameDescription *gd) {
 	switch (getPlatform(*gd)) {
 	case Common::kPlatformApple2:
-		return new HiRes4Engine(syst, gd);
+		switch (getGameVersion(*gd)) {
+		case GAME_VER_HR4_LNG:
+			return new HiRes4Engine_LNG(syst, gd);
+		case GAME_VER_HR4_V1_1:
+			return new HiRes4Engine_v1_1(syst, gd);
+		default:
+			return new HiRes4Engine_v1_0(syst, gd);
+		}
 	case Common::kPlatformAtari8Bit:
 		return new HiRes4Engine_Atari(syst, gd);
 	default:




More information about the Scummvm-git-logs mailing list