SCI: Implement fallback detection for SCI3

This commit is contained in:
Colin Snover 2017-02-08 11:52:36 -06:00
parent 42406cb11a
commit 866419fa71
4 changed files with 61 additions and 20 deletions

View File

@ -379,6 +379,15 @@ Common::String convertSierraGameId(Common::String sierraId, uint32 *gameFlags, R
return "qfg3";
}
if (sierraId == "l7")
return "lsl7";
if (sierraId == "p2")
return "phantasmagoria2";
if (sierraId == "lite")
return "lighthouse";
return sierraId;
}
@ -628,7 +637,7 @@ const ADGameDescription *SciMetaEngine::fallbackDetect(const FileMap &allFiles,
s_fallbackDesc.platform = Common::kPlatformAmiga;
// Determine the game id
Common::String sierraGameId = resMan.findSierraGameId();
Common::String sierraGameId = resMan.findSierraGameId(s_fallbackDesc.platform == Common::kPlatformMacintosh);
// If we don't have a game id, the game is not SCI
if (sierraGameId.empty())

View File

@ -30,6 +30,7 @@
#include "common/memstream.h"
#endif
#include "sci/parser/vocabulary.h"
#include "sci/resource.h"
#include "sci/resource_intern.h"
#include "sci/util.h"
@ -2690,15 +2691,19 @@ static SciSpan<const byte>::const_iterator findSci0ExportsBlock(const SciSpan<co
// This code duplicates Script::relocateOffsetSci3, but we can't use
// that here since we can't instantiate scripts at this point.
static int relocateOffsetSci3(const SciSpan<const byte> &buf, uint32 offset) {
static int relocateOffsetSci3(const SciSpan<const byte> &buf, uint32 offset, const bool isBE) {
int relocStart = buf.getUint32LEAt(8);
int relocCount = buf.getUint16LEAt(18);
SciSpan<const byte>::const_iterator seeker = buf.cbegin() + relocStart;
for (int i = 0; i < relocCount; ++i) {
if (seeker.getUint32SE() == offset) {
// TODO: Find out what UINT16 at (seeker + 8) means
return buf.getUint16SEAt(offset) + (seeker + 4).getUint32SE();
const uint32 candidateOffset = isBE ? seeker.getUint32BE() : seeker.getUint32LE();
if (candidateOffset == offset) {
if (isBE) {
return buf.getUint16BEAt(offset) + (seeker + 4).getUint32BE();
} else {
return buf.getUint16LEAt(offset) + (seeker + 4).getUint32LE();
}
}
seeker += 10;
}
@ -2706,7 +2711,7 @@ static int relocateOffsetSci3(const SciSpan<const byte> &buf, uint32 offset) {
return -1;
}
reg_t ResourceManager::findGameObject(bool addSci11ScriptOffset) {
reg_t ResourceManager::findGameObject(const bool addSci11ScriptOffset, const bool isBE) {
Resource *script = findResource(ResourceId(kResourceTypeScript, 0), false);
if (!script)
@ -2746,36 +2751,63 @@ reg_t ResourceManager::findGameObject(bool addSci11ScriptOffset) {
return make_reg(1, offset);
} else {
return make_reg(1, relocateOffsetSci3(*script, 22));
return make_reg(1, relocateOffsetSci3(*script, 22, isBE));
}
}
Common::String ResourceManager::findSierraGameId() {
Common::String ResourceManager::findSierraGameId(const bool isBE) {
// In SCI0-SCI1, the heap is embedded in the script. In SCI1.1 - SCI2.1,
// it's in a separate heap resource
Resource *heap = 0;
int nameSelector = 3;
Resource *heap = nullptr;
int nameSelector = -1;
if (getSciVersion() < SCI_VERSION_1_1) {
heap = findResource(ResourceId(kResourceTypeScript, 0), false);
nameSelector = 3;
} else if (getSciVersion() >= SCI_VERSION_1_1 && getSciVersion() <= SCI_VERSION_2_1_LATE) {
heap = findResource(ResourceId(kResourceTypeHeap, 0), false);
nameSelector += 5;
nameSelector = 8;
} else if (getSciVersion() == SCI_VERSION_3) {
warning("TODO: findSierraGameId(): SCI3 equivalent");
heap = findResource(ResourceId(kResourceTypeScript, 0), false);
Resource *vocab = findResource(ResourceId(kResourceTypeVocab, VOCAB_RESOURCE_SELECTORS), false);
const uint16 numSelectors = isBE ? vocab->getUint16BEAt(0) : vocab->getUint16LEAt(0);
for (uint16 i = 0; i < numSelectors; ++i) {
uint16 selectorOffset;
uint16 selectorSize;
if (isBE) {
selectorOffset = vocab->getUint16BEAt((i + 1) * sizeof(uint16));
selectorSize = vocab->getUint16BEAt(selectorOffset);
} else {
selectorOffset = vocab->getUint16LEAt((i + 1) * sizeof(uint16));
selectorSize = vocab->getUint16LEAt(selectorOffset);
}
Common::String selectorName = Common::String((const char *)vocab->getUnsafeDataAt(selectorOffset + 2, selectorSize), selectorSize);
if (selectorName == "name") {
nameSelector = i;
break;
}
}
}
if (!heap)
if (!heap || nameSelector == -1)
return "";
int16 gameObjectOffset = findGameObject(false).getOffset();
int16 gameObjectOffset = findGameObject(false, isBE).getOffset();
if (!gameObjectOffset)
return "";
// Seek to the name selector of the first export
SciSpan<const byte>::const_iterator offsetPtr = heap->cbegin() + gameObjectOffset + nameSelector * 2;
uint16 offset = !isSci11Mac() ? offsetPtr.getUint16LE() : offsetPtr.getUint16BE();
int32 offset;
if (getSciVersion() == SCI_VERSION_3) {
offset = relocateOffsetSci3(*heap, gameObjectOffset + /* base selector offset */ 0x110 + nameSelector * sizeof(uint16), isBE);
} else {
// Seek to the name selector of the first export
SciSpan<const byte>::const_iterator offsetPtr = heap->cbegin() + gameObjectOffset + nameSelector * sizeof(uint16);
offset = !isSci11Mac() ? offsetPtr.getUint16LE() : offsetPtr.getUint16BE();
}
return heap->getStringAt(offset);
}

View File

@ -436,7 +436,7 @@ public:
/**
* Finds the internal Sierra ID of the current game from script 0.
*/
Common::String findSierraGameId();
Common::String findSierraGameId(const bool isBE);
/**
* Finds the location of the game object from script 0.
@ -444,7 +444,7 @@ public:
* games. Needs to be false when the heap is accessed directly inside
* findSierraGameId().
*/
reg_t findGameObject(bool addSci11ScriptOffset = true);
reg_t findGameObject(const bool addSci11ScriptOffset, const bool isBE);
/**
* Converts a map resource type to our type

View File

@ -242,7 +242,7 @@ Common::Error SciEngine::run() {
// Add the after market GM patches for the specified game, if they exist
_resMan->addNewGMPatch(_gameId);
_gameObjectAddress = _resMan->findGameObject();
_gameObjectAddress = _resMan->findGameObject(true, isBE());
_scriptPatcher = new ScriptPatcher();
SegManager *segMan = new SegManager(_resMan, _scriptPatcher);