Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

KYRA: Don't require the Mac version of Legend of Kyrandia to be installed #3552

Merged
merged 12 commits into from Nov 22, 2021
22 changes: 20 additions & 2 deletions common/stuffit.cpp
Expand Up @@ -43,6 +43,7 @@ class StuffItArchive : public Common::Archive {
~StuffItArchive() override;

bool open(const Common::String &filename);
bool open(Common::SeekableReadStream *stream);
void close();
bool isOpen() const { return _stream != nullptr; }

Expand Down Expand Up @@ -90,9 +91,14 @@ static const uint32 s_magicNumbers[] = {
};

bool StuffItArchive::open(const Common::String &filename) {
Common::SeekableReadStream *stream = SearchMan.createReadStreamForMember(filename);
return open(stream);
}

bool StuffItArchive::open(Common::SeekableReadStream *stream) {
close();

_stream = SearchMan.createReadStreamForMember(filename);
_stream = stream;

if (!_stream)
return false;
Expand Down Expand Up @@ -195,7 +201,8 @@ bool StuffItArchive::open(const Common::String &filename) {
}

void StuffItArchive::close() {
delete _stream; _stream = nullptr;
delete _stream;
_stream = nullptr;
_map.clear();
}

Expand Down Expand Up @@ -537,4 +544,15 @@ Common::Archive *createStuffItArchive(const Common::String &fileName) {
return archive;
}

Common::Archive *createStuffItArchive(Common::SeekableReadStream *stream) {
StuffItArchive *archive = new StuffItArchive();

if (!archive->open(stream)) {
delete archive;
return nullptr;
}

return archive;
}

} // End of namespace Common
3 changes: 3 additions & 0 deletions common/stuffit.h
Expand Up @@ -25,6 +25,7 @@
* StuffIt decompressor used in engines:
* - grim
* - groovie
* - kyra
*/

#ifndef COMMON_STUFFIT_H
Expand All @@ -43,6 +44,7 @@ namespace Common {

class Archive;
class String;
class SeekableReadStream;

/**
* This factory method creates an Archive instance corresponding to the content
Expand All @@ -51,6 +53,7 @@ class String;
* May return 0 in case of a failure.
*/
Archive *createStuffItArchive(const String &fileName);
Archive *createStuffItArchive(SeekableReadStream *stream);

/** @} */

Expand Down
13 changes: 13 additions & 0 deletions engines/kyra/detection_tables.h
Expand Up @@ -325,6 +325,19 @@ const KYRAGameDescription adGameDescs[] = {
KYRA1_FLOPPY_FLAGS
},

{
{
"kyra1",
"StuffIt",
AD_ENTRY1s("xn--Install Legend of Kyrandia-jf8p", "1d763e991c787431cac3786afbbdae72", 53899),
Common::EN_ANY,
Common::kPlatformMacintosh,
ADGF_MACRESFORK,
GUIO3(GUIO_NOSPEECH, GUIO_MIDIGM, GUIO_RENDERVGA)
},
KYRA1_FLOPPY_CMP_FLAGS
},

{ // FM-TOWNS version
{
"kyra1",
Expand Down
36 changes: 36 additions & 0 deletions engines/kyra/engine/util.cpp
Expand Up @@ -20,6 +20,8 @@
*
*/

#include "common/macresman.h"
#include "common/punycode.h"
#include "kyra/engine/util.h"

namespace Kyra {
Expand Down Expand Up @@ -115,4 +117,38 @@ Common::String Util::decodeString2(const Common::String &src) {
return tmp;
}

Common::String Util::findMacResourceFile(const char *baseName) {
// The original executable has a TM char as its last character (character
// 0xAA from Mac code page). Depending on the emulator or platform used to
// copy the file it might have been reencoded to something else. So I look
// for multiple versions, also for punycode encoded files and also for the
// case where the user might have just removed the last character by
// renaming the file.

const Common::CodePage tryCodePages[] = {
Common::kMacRoman,
Common::kISO8859_1
};

Common::MacResManager resource;
Common::String tryName(baseName);
Common::String fileName;

for (int i = 0; i < 2; ++i) {
for (int ii = 0; ii < ARRAYSIZE(tryCodePages); ++ii) {
Common::U32String fn(tryName, tryCodePages[ii]);
fileName = fn.encode(Common::kUtf8);
if (resource.exists(fileName))
return fileName;
fileName = Common::punycode_encodefilename(fn);
if (resource.exists(fileName))
return fileName;
}
tryName += "\xAA";
}

fileName.clear();
return fileName;
}

} // End of namespace Kyra
2 changes: 2 additions & 0 deletions engines/kyra/engine/util.h
Expand Up @@ -40,6 +40,8 @@ class Util {
static Common::String convertUTF8ToDOS(Common::String &str);
static Common::String convertISOToUTF8(Common::String &str);
static void convertISOToDOS(char &c);

static Common::String findMacResourceFile(const char *baseName);
};

} // End of namespace Kyra
Expand Down
47 changes: 45 additions & 2 deletions engines/kyra/resource/resource.cpp
Expand Up @@ -20,6 +20,7 @@
*
*/

#include "kyra/engine/util.h"
#include "kyra/resource/resource.h"
#include "kyra/resource/resource_intern.h"

Expand All @@ -28,9 +29,12 @@

namespace Kyra {

Resource::Resource(KyraEngine_v1 *vm) : _archiveCache(), _files(), _archiveFiles(), _protectedFiles(), _loaders(), _vm(vm), _bigEndianPlatForm(vm->gameFlags().platform == Common::kPlatformAmiga || vm->gameFlags().platform == Common::kPlatformSegaCD) {
Resource::Resource(KyraEngine_v1 *vm) : _archiveCache(), _files(), _archiveFiles(), _protectedFiles(), _macResMan(), _loaders(), _vm(vm), _bigEndianPlatForm(vm->gameFlags().platform == Common::kPlatformAmiga || vm->gameFlags().platform == Common::kPlatformSegaCD) {
initializeLoaders();

if (_vm->gameFlags().useInstallerPackage)
_macResMan = new Common::MacResManager();

// Initialize directories for playing from CD or with original
// directory structure
if (_vm->game() == GI_KYRA3)
Expand All @@ -52,6 +56,7 @@ Resource::~Resource() {
for (ArchiveMap::iterator i = _archiveCache.begin(); i != _archiveCache.end(); ++i)
delete i->_value;
_archiveCache.clear();
delete _macResMan;
}

bool Resource::reset() {
Expand All @@ -62,7 +67,27 @@ bool Resource::reset() {
if (!dir.exists() || !dir.isDirectory())
error("invalid game path '%s'", dir.getPath().c_str());

if (_vm->game() == GI_KYRA1 || _vm->game() == GI_EOB1) {
if (_vm->game() == GI_KYRA1 && _vm->gameFlags().platform == Common::kPlatformMacintosh && _vm->gameFlags().useInstallerPackage) {
Common::String kyraInstaller = Util::findMacResourceFile("Install Legend of Kyrandia");

if (kyraInstaller.empty()) {
error("Could not find Legend of Kyrandia installer file");
}

Common::Archive *archive = loadStuffItArchive(kyraInstaller);
if (!archive)
error("Failed to load Legend of Kyrandia installer file");

_files.add("installer", archive, 0, false);

Common::ArchiveMemberList members;
archive->listMatchingMembers(members, "*.PAK");
for (Common::ArchiveMemberList::const_iterator it = members.begin(); it != members.end(); ++it) {
Common::String name = (*it)->getName();
Common::Archive *pak = loadArchive(name, *it);
_files.add(name, pak, 0, false);
}
} else if (_vm->game() == GI_KYRA1 || _vm->game() == GI_EOB1) {
// We only need kyra.dat for the demo.
if (_vm->gameFlags().isDemo && !_vm->gameFlags().isTalkie)
return true;
Expand Down Expand Up @@ -321,6 +346,11 @@ bool Resource::loadFileToBuf(const char *file, void *buf, uint32 maxSize) {
return true;
}

Common::Archive *Resource::getCachedArchive(const Common::String &file) const {
ArchiveMap::iterator a = _archiveCache.find(file);
return a != _archiveCache.end() ? a->_value : 0;
}

Common::SeekableReadStream *Resource::createReadStream(const Common::String &file) {
return _files.createReadStreamForMember(file);
}
Expand Down Expand Up @@ -375,6 +405,19 @@ Common::Archive *Resource::loadInstallerArchive(const Common::String &file, cons
return archive;
}

Common::Archive *Resource::loadStuffItArchive(const Common::String &file) {
ArchiveMap::iterator cachedArchive = _archiveCache.find(file);
if (cachedArchive != _archiveCache.end())
return cachedArchive->_value;

Common::Archive *archive = StuffItLoader::load(this, file, _macResMan);
if (!archive)
return nullptr;

_archiveCache[file] = archive;
return archive;
}

#pragma mark -

void Resource::initializeLoaders() {
Expand Down
7 changes: 7 additions & 0 deletions engines/kyra/resource/resource.h
Expand Up @@ -30,6 +30,7 @@
#include "common/list.h"
#include "common/hash-str.h"
#include "common/hashmap.h"
#include "common/macresman.h"
#include "common/stream.h"
#include "common/ptr.h"
#include "common/archive.h"
Expand Down Expand Up @@ -84,6 +85,9 @@ class Resource {
Common::SeekableReadStreamEndian *createEndianAwareReadStream(const Common::String &file, int endianness = kPlatformEndianness);

bool loadFileToBuf(const char *file, void *buf, uint32 maxSize);

Common::Archive *getCachedArchive(const Common::String &file) const;

protected:
typedef Common::HashMap<Common::String, Common::Archive *, Common::CaseSensitiveString_Hash, Common::CaseSensitiveString_EqualTo> ArchiveMap;
ArchiveMap _archiveCache;
Expand All @@ -92,8 +96,11 @@ class Resource {
Common::SearchSet _archiveFiles;
Common::SearchSet _protectedFiles;

Common::MacResManager *_macResMan;

Common::Archive *loadArchive(const Common::String &name, Common::ArchiveMemberPtr member);
Common::Archive *loadInstallerArchive(const Common::String &file, const Common::String &ext, const uint8 offset);
Common::Archive *loadStuffItArchive(const Common::String &file);

bool loadProtectedFiles(const char *const * list);

Expand Down
16 changes: 15 additions & 1 deletion engines/kyra/resource/resource_intern.cpp
Expand Up @@ -26,6 +26,8 @@
#include "common/endian.h"
#include "common/memstream.h"
#include "common/substream.h"
#include "common/macresman.h"
#include "common/stuffit.h"

namespace Kyra {

Expand Down Expand Up @@ -927,7 +929,7 @@ struct InsArchive {
uint32 totalSize;
};

} // end of anonymouse namespace
} // end of anonymous namespace

class CmpVocDecoder {
public:
Expand Down Expand Up @@ -1198,6 +1200,18 @@ Common::Archive *InstallerLoader::load(Resource *owner, const Common::String &fi
return new CachedArchive(fileList);
}

Common::Archive *StuffItLoader::load(Resource *owner, const Common::String &filename, Common::MacResManager *macResMan) {
if (macResMan->open(filename)) {
Common::SeekableReadStream *stream = macResMan->getDataFork();
if (stream) {
Common::Archive *archive = Common::createStuffItArchive(stream);
return archive;
}
}

error("StuffItLoader::load: Could not load %s", filename.c_str());
}

CmpVocDecoder::CmpVocDecoder() {
_tbl1 = new int32[4000];
_p1 = _tbl1 + 2000;
Expand Down
6 changes: 6 additions & 0 deletions engines/kyra/resource/resource_intern.h
Expand Up @@ -28,6 +28,7 @@
#include "common/hashmap.h"
#include "common/str.h"
#include "common/list.h"
#include "common/macresman.h"
#include "common/stream.h"

namespace Kyra {
Expand Down Expand Up @@ -142,6 +143,11 @@ class InstallerLoader {
static Common::Archive *load(Resource *owner, const Common::String &filename, const Common::String &extension, const uint8 offset);
};

class StuffItLoader {
public:
static Common::Archive *load(Resource *owner, const Common::String &filename, Common::MacResManager *macResMan);
};

class EndianAwareStreamWrapper : public Common::SeekableReadStreamEndian {
public:
EndianAwareStreamWrapper(Common::SeekableReadStream *stream, bool bigEndian, bool disposeAfterUse = true) : Common::SeekableReadStreamEndian(bigEndian), Common::ReadStreamEndian(bigEndian), _stream(stream), _dispose(disposeAfterUse) {}
Expand Down