[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 §or, 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 §or, 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