ADL: Add support for three more hires4 versions
This commit is contained in:
parent
b9adfbaa01
commit
01bb2bc689
8 changed files with 384 additions and 234 deletions
|
@ -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)
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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 { }
|
||||
|
|
|
@ -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
|
||||
{
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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) :
|
||||
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); }
|
||||
~HiRes4Engine() override;
|
||||
_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);
|
||||
}
|
||||
|
||||
void HiRes4Engine::putSpace(uint x, uint y) const {
|
||||
waitKey(3000);
|
||||
}
|
||||
|
||||
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,20 +539,20 @@ 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");
|
||||
|
||||
if (oldVersion) {
|
||||
// Version 0.0
|
||||
StreamPtr ms2(files->createReadStream("MS2"));
|
||||
runIntroLogo(*ms2);
|
||||
|
||||
|
@ -440,11 +560,32 @@ void HiRes4Engine::runIntro() {
|
|||
return;
|
||||
|
||||
_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:
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue