SciVersion GameFeatures::detectMessageFunctionType() { if (_messageFunctionType != SCI_VERSION_NONE) return _messageFunctionType; if (getSciVersion() > SCI_VERSION_1_1) { _messageFunctionType = SCI_VERSION_1_1; return _messageFunctionType; } else if (getSciVersion() < SCI_VERSION_1_1) { _messageFunctionType = SCI_VERSION_1_LATE; return _messageFunctionType; } Common::List<ResourceId> resources = g_sci->getResMan()->listResources(kResourceTypeMessage, -1); if (resources.empty()) { // No messages found, so this doesn't really matter anyway... _messageFunctionType = SCI_VERSION_1_1; return _messageFunctionType; } Resource *res = g_sci->getResMan()->findResource(*resources.begin(), false); assert(res); // Only v2 Message resources use the kGetMessage kernel function. // v3-v5 use the kMessage kernel function. if (READ_SCI11ENDIAN_UINT32(res->data) / 1000 == 2) _messageFunctionType = SCI_VERSION_1_LATE; else _messageFunctionType = SCI_VERSION_1_1; debugC(1, kDebugLevelVM, "Detected message function type: %s", getSciVersionDesc(_messageFunctionType)); return _messageFunctionType; }
SciVersion GameFeatures::detectDoSoundType() { if (_doSoundType == SCI_VERSION_NONE) { if (getSciVersion() == SCI_VERSION_0_EARLY) { // Almost all of the SCI0EARLY games use different sound resources than // SCI0LATE. Although the last SCI0EARLY game (lsl2) uses SCI0LATE resources _doSoundType = g_sci->getResMan()->detectEarlySound() ? SCI_VERSION_0_EARLY : SCI_VERSION_0_LATE; #ifdef ENABLE_SCI32 } else if (getSciVersion() >= SCI_VERSION_2_1_EARLY) { _doSoundType = SCI_VERSION_2_1_EARLY; #endif } else if (SELECTOR(nodePtr) == -1) { // No nodePtr selector, so this game is definitely using newer // SCI0 sound code (i.e. SCI_VERSION_0_LATE) _doSoundType = SCI_VERSION_0_LATE; } else if (getSciVersion() >= SCI_VERSION_1_LATE) { // All SCI1 late games use the newer doSound semantics _doSoundType = SCI_VERSION_1_LATE; } else { if (!autoDetectSoundType()) { warning("DoSound detection failed, taking an educated guess"); if (getSciVersion() >= SCI_VERSION_1_MIDDLE) _doSoundType = SCI_VERSION_1_LATE; else if (getSciVersion() > SCI_VERSION_01) _doSoundType = SCI_VERSION_1_EARLY; } } debugC(1, kDebugLevelSound, "Detected DoSound type: %s", getSciVersionDesc(_doSoundType)); } return _doSoundType; }
reg_t kFileIOClose(EngineState *s, int argc, reg_t *argv) { debugC(kDebugLevelFile, "kFileIO(close): %d", argv[0].toUint16()); if (argv[0] == SIGNAL_REG) return s->r_acc; uint16 handle = argv[0].toUint16(); #ifdef ENABLE_SCI32 if (handle == VIRTUALFILE_HANDLE) { s->_virtualIndexFile->close(); return SIGNAL_REG; } #endif FileHandle *f = getFileFromHandle(s, handle); if (f) { f->close(); if (getSciVersion() <= SCI_VERSION_0_LATE) return s->r_acc; // SCI0 semantics: no value returned return SIGNAL_REG; } if (getSciVersion() <= SCI_VERSION_0_LATE) return s->r_acc; // SCI0 semantics: no value returned return NULL_REG; }
reg_t kFileIOWriteString(EngineState *s, int argc, reg_t *argv) { int handle = argv[0].toUint16(); Common::String str = s->_segMan->getString(argv[1]); debugC(kDebugLevelFile, "kFileIO(writeString): %d", handle); #ifdef ENABLE_SCI32 if (handle == VIRTUALFILE_HANDLE) { s->_virtualIndexFile->write(str.c_str(), str.size()); return NULL_REG; } #endif FileHandle *f = getFileFromHandle(s, handle); if (f) { f->_out->write(str.c_str(), str.size()); if (getSciVersion() <= SCI_VERSION_0_LATE) return s->r_acc; // SCI0 semantics: no value returned return NULL_REG; } if (getSciVersion() <= SCI_VERSION_0_LATE) return s->r_acc; // SCI0 semantics: no value returned return make_reg(0, 6); // DOS - invalid handle }
void SciEngine::updateScummVMAudioOptions() { // Update ScummVM's speech/subtitles settings for SCI1.1 CD games, // depending on the in-game settings if ((isCD() && getSciVersion() == SCI_VERSION_1_1) || getSciVersion() >= SCI_VERSION_2) { uint16 ingameSetting = _gamestate->variables[VAR_GLOBAL][kGlobalVarMessageType].getOffset(); switch (ingameSetting) { case 1: // subtitles ConfMan.setBool("subtitles", true); ConfMan.setBool("speech_mute", true); break; case 2: // speech ConfMan.setBool("subtitles", false); ConfMan.setBool("speech_mute", false); break; case 3: // speech + subtitles ConfMan.setBool("subtitles", true); ConfMan.setBool("speech_mute", false); break; default: break; } } }
// TODO: script_adjust_opcode_formats should probably be part of the // constructor (?) of a VirtualMachine or a ScriptManager class. void script_adjust_opcode_formats() { g_sci->_opcode_formats = new opcode_format[128][4]; memcpy(g_sci->_opcode_formats, g_base_opcode_formats, 128*4*sizeof(opcode_format)); if (g_sci->_features->detectLofsType() != SCI_VERSION_0_EARLY) { g_sci->_opcode_formats[op_lofsa][0] = Script_Offset; g_sci->_opcode_formats[op_lofss][0] = Script_Offset; } #ifdef ENABLE_SCI32 // In SCI32, some arguments are now words instead of bytes if (getSciVersion() >= SCI_VERSION_2) { g_sci->_opcode_formats[op_calle][2] = Script_Word; g_sci->_opcode_formats[op_callk][1] = Script_Word; g_sci->_opcode_formats[op_super][1] = Script_Word; g_sci->_opcode_formats[op_send][0] = Script_Word; g_sci->_opcode_formats[op_self][0] = Script_Word; g_sci->_opcode_formats[op_call][1] = Script_Word; g_sci->_opcode_formats[op_callb][1] = Script_Word; } if (getSciVersion() >= SCI_VERSION_3) { // TODO: There are also opcodes in // here to get the superclass, and possibly the species too. g_sci->_opcode_formats[0x4d/2][0] = Script_None; g_sci->_opcode_formats[0x4e/2][0] = Script_None; } #endif }
void GfxAnimate::kernelAnimate(reg_t listReference, bool cycle, int argc, reg_t *argv) { byte old_picNotValid = _screen->_picNotValid; if (getSciVersion() >= SCI_VERSION_1_1) _palette->palVaryUpdate(); if (listReference.isNull()) { disposeLastCast(); if (_screen->_picNotValid) animateShowPic(); return; } List *list = _s->_segMan->lookupList(listReference); if (!list) error("kAnimate called with non-list as parameter"); if (cycle) { if (!invoke(list, argc, argv)) return; // Look up the list again, as it may have been modified list = _s->_segMan->lookupList(listReference); } Port *oldPort = _ports->setPort((Port *)_ports->_picWind); disposeLastCast(); makeSortedList(list); fill(old_picNotValid); if (old_picNotValid) { // beginUpdate()/endUpdate() were introduced SCI1. // Calling those for SCI0 will work most of the time but breaks minor // stuff like percentage bar of qfg1ega at the character skill screen. if (getSciVersion() >= SCI_VERSION_1_EGA_ONLY) _ports->beginUpdate(_ports->_picWind); update(); if (getSciVersion() >= SCI_VERSION_1_EGA_ONLY) _ports->endUpdate(_ports->_picWind); } drawCels(); if (_screen->_picNotValid) animateShowPic(); updateScreen(old_picNotValid); restoreAndDelete(argc, argv); // We update the screen here as well, some scenes like EQ1 credits run w/o calling kGetEvent thus we wouldn't update // screen at all g_sci->getEventManager()->updateScreen(); _ports->setPort(oldPort); // Now trigger speed throttler throttleSpeed(); }
void Script::init(int script_nr, ResourceManager *resMan) { Resource *script = resMan->findResource(ResourceId(kResourceTypeScript, script_nr), 0); if (!script) error("Script %d not found", script_nr); _localsOffset = 0; _localsBlock = NULL; _localsCount = 0; _markedAsDeleted = false; _nr = script_nr; _buf = 0; _heapStart = 0; _scriptSize = script->size; _bufSize = script->size; _heapSize = 0; _lockers = 1; if (getSciVersion() == SCI_VERSION_0_EARLY) { _bufSize += READ_LE_UINT16(script->data) * 2; } else if (getSciVersion() >= SCI_VERSION_1_1 && getSciVersion() <= SCI_VERSION_2_1) { // In SCI1.1 - SCI2.1, the heap was in a separate space from the script. We append // it to the end of the script, and adjust addressing accordingly. // However, since we address the heap with a 16-bit pointer, the // combined size of the stack and the heap must be 64KB. So far this has // worked for SCI11, SCI2 and SCI21 games. SCI3 games use a different // script format, and theoretically they can exceed the 64KB boundary // using relocation. Resource *heap = resMan->findResource(ResourceId(kResourceTypeHeap, script_nr), 0); _bufSize += heap->size; _heapSize = heap->size; // Ensure that the start of the heap resource can be word-aligned. if (script->size & 2) { _bufSize++; _scriptSize++; } // As mentioned above, the script and the heap together should not exceed 64KB if (script->size + heap->size > 65535) error("Script and heap sizes combined exceed 64K. This means a fundamental " "design bug was made regarding SCI1.1 and newer games.\n" "Please report this error to the ScummVM team"); } else if (getSciVersion() == SCI_VERSION_3) { // Check for scripts over 64KB. These won't work with the current 16-bit address // scheme. We need an overlaying mechanism, or a mechanism to split script parts // in different segments to handle these. For now, simply stop when such a script // is found. // TODO: Remove this once such a mechanism is in place if (script->size > 65535) error("TODO: SCI script %d is over 64KB - it's %d bytes long. This can't " "be handled at the moment, thus stopping", script_nr, script->size); } }
void Script::load(ResourceManager *resMan) { Resource *script = resMan->findResource(ResourceId(kResourceTypeScript, _nr), 0); assert(script != 0); uint extraLocalsWorkaround = 0; if (g_sci->getGameId() == GID_FANMADE && _nr == 1 && script->size == 11140) { // WORKAROUND: Script 1 in Ocean Battle doesn't have enough locals to // fit the string showing how many shots are left (a nasty script bug, // corrupting heap memory). We add 10 more locals so that it has enough // space to use as the target for its kFormat operation. Fixes bug // #3059871. extraLocalsWorkaround = 10; } _bufSize += extraLocalsWorkaround * 2; _buf = (byte *)malloc(_bufSize); assert(_buf); assert(_bufSize >= script->size); memcpy(_buf, script->data, script->size); // Check scripts for matching signatures and patch those, if found matchSignatureAndPatch(_nr, _buf, script->size); if (getSciVersion() >= SCI_VERSION_1_1 && getSciVersion() <= SCI_VERSION_2_1) { Resource *heap = resMan->findResource(ResourceId(kResourceTypeHeap, _nr), 0); assert(heap != 0); _heapStart = _buf + _scriptSize; assert(_bufSize - _scriptSize <= heap->size); memcpy(_heapStart, heap->data, heap->size); } _exportTable = 0; _numExports = 0; _synonyms = 0; _numSynonyms = 0; if (getSciVersion() <= SCI_VERSION_1_LATE) { _exportTable = (const uint16 *)findBlockSCI0(SCI_OBJ_EXPORTS); if (_exportTable) { _numExports = READ_SCI11ENDIAN_UINT16(_exportTable + 1); _exportTable += 3; // skip header plus 2 bytes (_exportTable is a uint16 pointer) } _synonyms = findBlockSCI0(SCI_OBJ_SYNONYMS); if (_synonyms) { _numSynonyms = READ_SCI11ENDIAN_UINT16(_synonyms + 2) / 4; _synonyms += 4; // skip header } const byte* localsBlock = findBlockSCI0(SCI_OBJ_LOCALVARS); if (localsBlock) { _localsOffset = localsBlock - _buf + 4; _localsCount = (READ_LE_UINT16(_buf + _localsOffset - 2) - 4) >> 1; // half block size } } else if (getSciVersion() >= SCI_VERSION_1_1 && getSciVersion() <= SCI_VERSION_2_1) {
SciVersion GameFeatures::detectLofsType() { if (_lofsType == SCI_VERSION_NONE) { // This detection only works (and is only needed) for SCI 1 if (getSciVersion() <= SCI_VERSION_01) { _lofsType = SCI_VERSION_0_EARLY; return _lofsType; } if (getSciVersion() >= SCI_VERSION_1_1 && getSciVersion() <= SCI_VERSION_2_1) { // SCI1.1 type, i.e. we compensate for the fact that the heap is attached // to the end of the script _lofsType = SCI_VERSION_1_1; return _lofsType; } if (getSciVersion() == SCI_VERSION_3) { // SCI3 type, same as pre-SCI1.1, really, as there is no separate heap // resource _lofsType = SCI_VERSION_3; return _lofsType; } // Find a function of the "Game" object (which is the game super class) which invokes lofsa/lofss reg_t gameSuperClass = g_sci->getGameSuperClassAddress(); bool found = false; if (!gameSuperClass.isNull()) { Common::String gameSuperClassName = _segMan->getObjectName(gameSuperClass); const Object *gameSuperObject = _segMan->getObject(gameSuperClass); if (gameSuperObject) { for (uint m = 0; m < gameSuperObject->getMethodCount(); m++) { found = autoDetectLofsType(gameSuperClassName, m); if (found) break; } } else { warning("detectLofsType(): Could not get superclass object"); } } else { warning("detectLofsType(): Could not find superclass of game object"); } if (!found) { warning("detectLofsType(): failed, taking an educated guess"); if (getSciVersion() >= SCI_VERSION_1_MIDDLE) _lofsType = SCI_VERSION_1_MIDDLE; else _lofsType = SCI_VERSION_0_EARLY; } debugC(1, kDebugLevelVM, "Detected Lofs type: %s", getSciVersionDesc(_lofsType)); } return _lofsType; }
void GfxControls16::kernelDrawButton(Common::Rect rect, reg_t obj, const char *text, int16 fontId, int16 style, bool hilite) { int16 sci0EarlyPen = 0, sci0EarlyBack = 0; if (!hilite) { if (getSciVersion() == SCI_VERSION_0_EARLY) { // SCI0early actually used hardcoded green/black buttons instead of using the port colors sci0EarlyPen = _ports->_curPort->penClr; sci0EarlyBack = _ports->_curPort->backClr; _ports->penColor(0); _ports->backColor(2); } rect.grow(1); _paint16->eraseRect(rect); _paint16->frameRect(rect); rect.grow(-2); _ports->textGreyedOutput(!(style & SCI_CONTROLS_STYLE_ENABLED)); //20140521 //_text16->Box(text, false, rect, SCI_TEXT16_ALIGNMENT_CENTER, fontId); std::map<std::string, std::string>::iterator iter; if(g_sci->_ScriptData) { _ShouterInfo* pInfo = g_sci->_ScriptData->GetShouterInfo(); iter = pInfo->SentenceList.find(text); if(iter == pInfo->SentenceList.end()) _text16->Box(text, false, rect, SCI_TEXT16_ALIGNMENT_CENTER, fontId, true); else _text16->Box(iter->second.c_str(), false, rect, SCI_TEXT16_ALIGNMENT_CENTER, fontId, true); } else { _text16->Box(text, false, rect, SCI_TEXT16_ALIGNMENT_CENTER, fontId); } //End _ports->textGreyedOutput(false); rect.grow(1); if (style & SCI_CONTROLS_STYLE_SELECTED) _paint16->frameRect(rect); if (!getPicNotValid()) { rect.grow(1); _paint16->bitsShow(rect); } if (getSciVersion() == SCI_VERSION_0_EARLY) { _ports->penColor(sci0EarlyPen); _ports->backColor(sci0EarlyBack); } } else { // SCI0early used xor to invert button rectangles resulting in pink/white buttons if (getSciVersion() == SCI_VERSION_0_EARLY) _paint16->invertRectViaXOR(rect); else _paint16->invertRect(rect); _paint16->bitsShow(rect); } }
void Script::initializeObjects(SegManager *segMan, SegmentId segmentId) { if (getSciVersion() <= SCI_VERSION_1_LATE) initializeObjectsSci0(segMan, segmentId); else if (getSciVersion() >= SCI_VERSION_1_1 && getSciVersion() <= SCI_VERSION_2_1_LATE) initializeObjectsSci11(segMan, segmentId); #ifdef ENABLE_SCI32 else if (getSciVersion() == SCI_VERSION_3) initializeObjectsSci3(segMan, segmentId); #endif }
/** * Causes an immediate plane transition with an optional transition * effect */ reg_t kSetShowStyle(EngineState *s, int argc, reg_t *argv) { ShowStyleType type = (ShowStyleType)argv[0].toUint16(); reg_t planeObj = argv[1]; int16 seconds = argv[2].toSint16(); // NOTE: This value seems to indicate whether the transition is an // “exit” transition (0) or an “enter” transition (-1) for fade // transitions. For other types of transitions, it indicates a palette // index value to use when filling the screen. int16 back = argv[3].toSint16(); int16 priority = argv[4].toSint16(); int16 animate = argv[5].toSint16(); // TODO: Rename to frameOutNow? int16 refFrame = argv[6].toSint16(); int16 blackScreen; reg_t pFadeArray; int16 divisions; // SCI 2–2.1early if (getSciVersion() < SCI_VERSION_2_1_MIDDLE) { blackScreen = 0; pFadeArray = NULL_REG; divisions = argc > 7 ? argv[7].toSint16() : -1; } // SCI 2.1mid–2.1late else if (getSciVersion() < SCI_VERSION_3) { blackScreen = 0; pFadeArray = argc > 7 ? argv[7] : NULL_REG; divisions = argc > 8 ? argv[8].toSint16() : -1; } // SCI 3 else { blackScreen = argv[7].toSint16(); pFadeArray = argc > 8 ? argv[8] : NULL_REG; divisions = argc > 9 ? argv[9].toSint16() : -1; } // TODO: Reuse later for SCI2 and SCI3 implementation and then discard // warning("kSetShowStyle: effect %d, plane: %04x:%04x (%s), sec: %d, " // "dir: %d, prio: %d, animate: %d, ref frame: %d, black screen: %d, " // "pFadeArray: %04x:%04x (%s), divisions: %d", // type, PRINT_REG(planeObj), s->_segMan->getObjectName(planeObj), seconds, // back, priority, animate, refFrame, blackScreen, // PRINT_REG(pFadeArray), s->_segMan->getObjectName(pFadeArray), divisions); // NOTE: The order of planeObj and showStyle are reversed // because this is how SCI3 called the corresponding method // on the KernelMgr g_sci->_gfxTransitions32->kernelSetShowStyle(argc, planeObj, type, seconds, back, priority, animate, refFrame, pFadeArray, divisions, blackScreen); return s->r_acc; }
void GfxAnimate::init() { _lastCastData.clear(); _ignoreFastCast = false; // fastCast object is not found in any SCI games prior SCI1 if (getSciVersion() <= SCI_VERSION_01) _ignoreFastCast = true; // Also if fastCast object exists at gamestartup, we can assume that the interpreter doesnt do kAnimate aborts // (found in Larry 1) if (getSciVersion() > SCI_VERSION_0_EARLY) { if (!_s->_segMan->findObjectByName("fastCast").isNull()) _ignoreFastCast = true; } }
Common::StringArray Kernel::checkStaticSelectorNames() { Common::StringArray names; const int offset = (getSciVersion() < SCI_VERSION_1_1) ? 3 : 0; #ifdef ENABLE_SCI32 const int count = (getSciVersion() <= SCI_VERSION_1_1) ? ARRAYSIZE(sci0Selectors) + offset : ARRAYSIZE(sci2Selectors); #else const int count = ARRAYSIZE(sci0Selectors) + offset; #endif int countSci1 = ARRAYSIZE(sci1Selectors); int countSci11 = ARRAYSIZE(sci11Selectors); // Resize the list of selector names and fill in the SCI 0 names. names.resize(count); if (getSciVersion() <= SCI_VERSION_1_LATE) { // Fill selectors 0 - 2 for SCI0 - SCI1 late names[0] = "species"; names[1] = "superClass"; names[2] = "-info-"; } if (getSciVersion() <= SCI_VERSION_1_1) { // SCI0 - SCI11 for (int i = offset; i < count; i++) names[i] = sci0Selectors[i - offset]; if (getSciVersion() > SCI_VERSION_01) { // Several new selectors were added in SCI 1 and later. names.resize(count + countSci1); for (int i = count; i < count + countSci1; i++) names[i] = sci1Selectors[i - count]; } if (getSciVersion() >= SCI_VERSION_1_1) { // Several new selectors were added in SCI 1.1 names.resize(count + countSci1 + countSci11); for (int i = count + countSci1; i < count + countSci1 + countSci11; i++) names[i] = sci11Selectors[i - count - countSci1]; } #ifdef ENABLE_SCI32 } else { // SCI2+ for (int i = 0; i < count; i++) names[i] = sci2Selectors[i]; #endif } findSpecificSelectors(names); for (const SelectorRemap *selectorRemap = sciSelectorRemap; selectorRemap->slot; ++selectorRemap) { if (getSciVersion() >= selectorRemap->minVersion && getSciVersion() <= selectorRemap->maxVersion) { const uint32 slot = selectorRemap->slot; if (slot >= names.size()) names.resize(slot + 1); names[slot] = selectorRemap->name; } } return names; }
void GfxAnimate::init() { _lastCastData.clear(); _fastCastEnabled = false; if (getSciVersion() == SCI_VERSION_1_1) { // Seems to have been available for all SCI1.1 games _fastCastEnabled = true; } else if (getSciVersion() >= SCI_VERSION_1_EARLY) { // fastCast only exists for some games between SCI1 early and SCI1 late // Try to detect it by code signature // It's extremely important, that we only enable it for games that actually need it if (detectFastCast()) { _fastCastEnabled = true; } } }
// Note: don't do too many steps in here, otherwise cpu will crap out because of // the load void GfxTransitions::fadeOut() { byte oldPalette[3 * 256], workPalette[3 * 256]; int16 stepNr, colorNr; // Sierra did not fade in/out color 255 for sci1.1, but they used it in // several pictures (e.g. qfg3 demo/intro), so the fading looked weird int16 tillColorNr = getSciVersion() >= SCI_VERSION_1_1 ? 255 : 254; g_system->getPaletteManager()->grabPalette(oldPalette, 0, 256); for (stepNr = 100; stepNr >= 0; stepNr -= 10) { for (colorNr = 1; colorNr <= tillColorNr; colorNr++) { if (_palette->colorIsFromMacClut(colorNr)) { workPalette[colorNr * 3 + 0] = oldPalette[colorNr * 3]; workPalette[colorNr * 3 + 1] = oldPalette[colorNr * 3 + 1]; workPalette[colorNr * 3 + 2] = oldPalette[colorNr * 3 + 2]; } else { workPalette[colorNr * 3 + 0] = oldPalette[colorNr * 3] * stepNr / 100; workPalette[colorNr * 3 + 1] = oldPalette[colorNr * 3 + 1] * stepNr / 100; workPalette[colorNr * 3 + 2] = oldPalette[colorNr * 3 + 2] * stepNr / 100; } } g_system->getPaletteManager()->setPalette(workPalette + 3, 1, tillColorNr); g_sci->getEngineState()->wait(2); } }
void SoundCommandParser::initSoundResource(MusicEntry *newSound) { if (newSound->resourceId && _resMan->testResource(ResourceId(kResourceTypeSound, newSound->resourceId))) newSound->soundRes = new SoundResource(newSound->resourceId, _resMan, _soundVersion); else newSound->soundRes = 0; // In SCI1.1 games, sound effects are started from here. If we can find // a relevant audio resource, play it, otherwise switch to synthesized // effects. If the resource exists, play it using map 65535 (sound // effects map) bool checkAudioResource = getSciVersion() >= SCI_VERSION_1_1; // Hoyle 4 has garbled audio resources in place of the sound resources. // The demo of GK1 has no alternate sound effects. if ((g_sci->getGameId() == GID_HOYLE4) || (g_sci->getGameId() == GID_GK1 && g_sci->isDemo())) checkAudioResource = false; if (checkAudioResource && _resMan->testResource(ResourceId(kResourceTypeAudio, newSound->resourceId))) { // Found a relevant audio resource, create an audio stream if there is // no associated sound resource, or if both resources exist and the // user wants the digital version. if (_bMultiMidi || !newSound->soundRes) { int sampleLen; newSound->pStreamAud = _audio->getAudioStream(newSound->resourceId, 65535, &sampleLen); newSound->soundType = Audio::Mixer::kSpeechSoundType; } } if (!newSound->pStreamAud && newSound->soundRes) _music->soundInitSnd(newSound); }
void SciEngine::syncIngameAudioOptions() { // Sync the in-game speech/subtitles settings for SCI1.1 CD games if (isCD() && getSciVersion() == SCI_VERSION_1_1) { bool subtitlesOn = ConfMan.getBool("subtitles"); bool speechOn = !ConfMan.getBool("speech_mute"); if (subtitlesOn && !speechOn) { _gamestate->variables[VAR_GLOBAL][90] = make_reg(0, 1); // subtitles } else if (!subtitlesOn && speechOn) { _gamestate->variables[VAR_GLOBAL][90] = make_reg(0, 2); // speech } else if (subtitlesOn && speechOn) { // Is it a game that supports simultaneous speech and subtitles? switch (_gameId) { case GID_SQ4: case GID_FREDDYPHARKAS: case GID_ECOQUEST: case GID_LSL6: case GID_LAURABOW2: case GID_KQ6: _gamestate->variables[VAR_GLOBAL][90] = make_reg(0, 3); // speech + subtitles break; default: // Game does not support speech and subtitles, set it to speech _gamestate->variables[VAR_GLOBAL][90] = make_reg(0, 2); // speech } } } }
void GuestAdditions::instantiateScriptHook(Script &script, const bool ignoreDelayedRestore) const { if (getSciVersion() < SCI_VERSION_2) { return; } // If there is a delayed restore, we still want to patch the script so // that the automatic return of the game ID works, but we do not want to // patch the scripts that get restored if (ConfMan.getBool("originalsaveload") && (ignoreDelayedRestore || _state->_delayedRestoreGameId == -1)) { return; } if (g_sci->getGameId() == GID_TORIN && script.getScriptNumber() == 64866) { patchGameSaveRestoreTorin(script); } else if (script.getScriptNumber() == 64990) { // 64990 is the system script containing SRDialog. This script is used // by the main Game object, but it is not loaded immediately, so we wait // for it to be loaded before patching it. Attempting to preload this // script early for patching will cause the order of entries in the // segment table to change (versus save games that are not patched), // breaking persistent objects (like the control panel in SQ6) which // require reg_ts created during game startup to always be the same patchGameSaveRestoreSCI32(script); } }
SciSpan<const byte> Script::findBlockSCI0(ScriptObjectTypes type, bool findLastBlock) const { SciSpan<const byte> foundBlock; bool oldScriptHeader = (getSciVersion() == SCI_VERSION_0_EARLY); SciSpan<const byte> buf = *_buf; if (oldScriptHeader) buf += 2; for (;;) { const int blockType = buf.getUint16LEAt(0); if (blockType == 0) break; // the size in the block header includes the size of the header itself const int blockSize = buf.getUint16LEAt(2); assert(blockSize > 0); if (blockType == type) { foundBlock = buf.subspan(0, blockSize, Common::String::format("%s, %s block", _buf->name().c_str(), sciObjectTypeNames[type])); if (!findLastBlock) { break; } } buf += blockSize; } return foundBlock; }
void GfxCompare::kernelBaseSetter(reg_t object) { if (lookupSelector(_segMan, object, SELECTOR(brLeft), NULL, NULL) == kSelectorVariable) { int16 x = readSelectorValue(_segMan, object, SELECTOR(x)); int16 y = readSelectorValue(_segMan, object, SELECTOR(y)); int16 z = (SELECTOR(z) > -1) ? readSelectorValue(_segMan, object, SELECTOR(z)) : 0; int16 yStep = readSelectorValue(_segMan, object, SELECTOR(yStep)); GuiResourceId viewId = readSelectorValue(_segMan, object, SELECTOR(view)); int16 loopNo = readSelectorValue(_segMan, object, SELECTOR(loop)); int16 celNo = readSelectorValue(_segMan, object, SELECTOR(cel)); uint16 scaleSignal = 0; if (getSciVersion() >= SCI_VERSION_1_1) scaleSignal = readSelectorValue(_segMan, object, SELECTOR(scaleSignal)); Common::Rect celRect; GfxView *tmpView = _cache->getView(viewId); if (!tmpView->isScaleable()) scaleSignal = 0; if (scaleSignal & kScaleSignalDoScaling) { celRect = getNSRect(object); } else { tmpView->getCelRect(loopNo, celNo, x, y, z, celRect); } celRect.bottom = y + 1; celRect.top = celRect.bottom - yStep; writeSelectorValue(_segMan, object, SELECTOR(brLeft), celRect.left); writeSelectorValue(_segMan, object, SELECTOR(brRight), celRect.right); writeSelectorValue(_segMan, object, SELECTOR(brTop), celRect.top); writeSelectorValue(_segMan, object, SELECTOR(brBottom), celRect.bottom); } }
static void patchGameSaveRestoreCodeSci2(SegManager *segMan, reg_t methodAddress, byte id, bool doRestore) { Script *script = segMan->getScript(methodAddress.getSegment()); byte *patchPtr = const_cast<byte *>(script->getBuf(methodAddress.getOffset())); int kcallOffset; if (getSciVersion() < SCI_VERSION_2_1_MIDDLE) { if (doRestore) { memcpy(patchPtr, patchGameRestoreSci2, sizeof(patchGameRestoreSci2)); kcallOffset = 9; } else { memcpy(patchPtr, patchGameSaveSci2, sizeof(patchGameSaveSci2)); kcallOffset = 10; } } else { if (doRestore) { memcpy(patchPtr, patchGameRestoreSci21, sizeof(patchGameRestoreSci21)); kcallOffset = 10; } else { memcpy(patchPtr, patchGameSaveSci21, sizeof(patchGameSaveSci21)); kcallOffset = 11; } } patchPtr[kcallOffset] = id; if (g_sci->isBE()) { SWAP(patchPtr[kcallOffset + 1], patchPtr[kcallOffset + 2]); } }
void SciEngine::loadMacExecutable() { if (getPlatform() != Common::kPlatformMacintosh || getSciVersion() < SCI_VERSION_1_EARLY || getSciVersion() > SCI_VERSION_1_1) return; Common::String filename; switch (getGameId()) { case GID_KQ6: filename = "King's Quest VI"; break; case GID_FREDDYPHARKAS: filename = "Freddy Pharkas"; break; default: break; } if (filename.empty()) return; if (!_macExecutable.open(filename) || !_macExecutable.hasResFork()) { // KQ6/Freddy require the executable to load their icon bar palettes if (hasMacIconBar()) error("Could not load Mac resource fork '%s'", filename.c_str()); // TODO: Show some sort of warning dialog saying they can't get any // high-res Mac fonts, when we get to that point ;) } }
void SciEngine::syncIngameAudioOptions() { // Now, sync the in-game speech/subtitles settings for SCI1.1 CD games if (isCD() && getSciVersion() == SCI_VERSION_1_1) { bool subtitlesOn = ConfMan.getBool("subtitles"); bool speechOn = !ConfMan.getBool("speech_mute"); if (subtitlesOn && !speechOn) { _gamestate->variables[VAR_GLOBAL][90] = make_reg(0, 1); // subtitles } else if (!subtitlesOn && speechOn) { _gamestate->variables[VAR_GLOBAL][90] = make_reg(0, 2); // speech } else if (subtitlesOn && speechOn) { // Is it a game that supports simultaneous speech and subtitles? if (getGameId() == GID_SQ4 || getGameId() == GID_FREDDYPHARKAS // TODO: The following need script patches for simultaneous speech and subtitles //|| getGameId() == GID_KQ6 //|| getGameId() == GID_LAURABOW2 ) { _gamestate->variables[VAR_GLOBAL][90] = make_reg(0, 3); // speech + subtitles } else { // Game does not support speech and subtitles, set it to speech _gamestate->variables[VAR_GLOBAL][90] = make_reg(0, 2); // speech } } } }
reg_t kSetSynonyms(EngineState *s, int argc, reg_t *argv) { SegManager *segMan = s->_segMan; reg_t object = argv[0]; List *list; Node *node; int script; int numSynonyms = 0; Vocabulary *voc = g_sci->getVocabulary(); // Only SCI0-SCI1 EGA games had a parser. In newer versions, this is a stub if (getSciVersion() > SCI_VERSION_1_EGA_ONLY) return s->r_acc; voc->clearSynonyms(); list = s->_segMan->lookupList(readSelector(segMan, object, SELECTOR(elements))); node = s->_segMan->lookupNode(list->first); while (node) { reg_t objpos = node->value; int seg; script = readSelectorValue(segMan, objpos, SELECTOR(number)); seg = s->_segMan->getScriptSegment(script); if (seg > 0) numSynonyms = s->_segMan->getScript(seg)->getSynonymsNr(); if (numSynonyms) { const byte *synonyms = s->_segMan->getScript(seg)->getSynonyms(); if (synonyms) { debugC(kDebugLevelParser, "Setting %d synonyms for script.%d", numSynonyms, script); if (numSynonyms > 16384) { error("Segtable corruption: script.%03d has %d synonyms", script, numSynonyms); /* We used to reset the corrupted value here. I really don't think it's appropriate. * Lars */ } else for (int i = 0; i < numSynonyms; i++) { synonym_t tmp; tmp.replaceant = READ_LE_UINT16(synonyms + i * 4); tmp.replacement = READ_LE_UINT16(synonyms + i * 4 + 2); voc->addSynonym(tmp); } } else warning("Synonyms of script.%03d were requested, but script is not available", script); } node = s->_segMan->lookupNode(node->succ); } debugC(kDebugLevelParser, "A total of %d synonyms are active now.", numSynonyms); return s->r_acc; }
SciVersion GameFeatures::detectSetCursorType() { if (_setCursorType == SCI_VERSION_NONE) { if (getSciVersion() <= SCI_VERSION_1_MIDDLE) { // SCI1 middle and older games never use cursor views _setCursorType = SCI_VERSION_0_EARLY; } else if (getSciVersion() >= SCI_VERSION_1_1) { // SCI1.1 games always use cursor views _setCursorType = SCI_VERSION_1_1; } else { // SCI1 late game, detect cursor semantics // If the Cursor object doesn't exist, we're using the SCI0 early // kSetCursor semantics. if (_segMan->findObjectByName("Cursor") == NULL_REG) { _setCursorType = SCI_VERSION_0_EARLY; debugC(1, kDebugLevelGraphics, "Detected SetCursor type: %s", getSciVersionDesc(_setCursorType)); return _setCursorType; } // Check for the existence of the handCursor object (first found). // This is based on KQ5. reg_t objAddr = _segMan->findObjectByName("handCursor", 0); // If that doesn't exist, we assume it uses SCI1.1 kSetCursor semantics if (objAddr == NULL_REG) { _setCursorType = SCI_VERSION_1_1; debugC(1, kDebugLevelGraphics, "Detected SetCursor type: %s", getSciVersionDesc(_setCursorType)); return _setCursorType; } // Now we check what the number variable holds in the handCursor // object. uint16 number = readSelectorValue(_segMan, objAddr, SELECTOR(number)); // If the number is 0, it uses views and therefore the SCI1.1 // kSetCursor semantics, otherwise it uses the SCI0 early kSetCursor // semantics. if (number == 0) _setCursorType = SCI_VERSION_1_1; else _setCursorType = SCI_VERSION_0_EARLY; } debugC(1, kDebugLevelGraphics, "Detected SetCursor type: %s", getSciVersionDesc(_setCursorType)); } return _setCursorType; }
reg_t kArrayGetElement(EngineState *s, int argc, reg_t *argv) { if (getSciVersion() == SCI_VERSION_2_1_LATE) { return kStringGetChar(s, argc, argv); } SciArray &array = *s->_segMan->lookupArray(argv[0]); return array.getAsID(argv[1].toUint16()); }
reg_t kArrayFree(EngineState *s, int argc, reg_t *argv) { if (getSciVersion() == SCI_VERSION_2_1_LATE && !s->_segMan->isValidAddr(argv[0], SEG_TYPE_ARRAY)) { return s->r_acc; } s->_segMan->freeArray(argv[0]); return s->r_acc; }
void Script::load(ResourceManager *resMan) { Resource *script = resMan->findResource(ResourceId(kResourceTypeScript, _nr), 0); assert(script != 0); _buf = (byte *)malloc(_bufSize); assert(_buf); assert(_bufSize >= script->size); memcpy(_buf, script->data, script->size); // Check scripts for matching signatures and patch those, if found matchSignatureAndPatch(_nr, _buf, script->size); if (getSciVersion() >= SCI_VERSION_1_1 && getSciVersion() <= SCI_VERSION_2_1) { Resource *heap = resMan->findResource(ResourceId(kResourceTypeHeap, _nr), 0); assert(heap != 0); _heapStart = _buf + _scriptSize; assert(_bufSize - _scriptSize <= heap->size); memcpy(_heapStart, heap->data, heap->size); } _exportTable = 0; _numExports = 0; _synonyms = 0; _numSynonyms = 0; if (getSciVersion() <= SCI_VERSION_1_LATE) { _exportTable = (const uint16 *)findBlockSCI0(SCI_OBJ_EXPORTS); if (_exportTable) { _numExports = READ_SCI11ENDIAN_UINT16(_exportTable + 1); _exportTable += 3; // skip header plus 2 bytes (_exportTable is a uint16 pointer) } _synonyms = findBlockSCI0(SCI_OBJ_SYNONYMS); if (_synonyms) { _numSynonyms = READ_SCI11ENDIAN_UINT16(_synonyms + 2) / 4; _synonyms += 4; // skip header } const byte* localsBlock = findBlockSCI0(SCI_OBJ_LOCALVARS); if (localsBlock) { _localsOffset = localsBlock - _buf + 4; _localsCount = (READ_LE_UINT16(_buf + _localsOffset - 2) - 4) >> 1; // half block size } } else if (getSciVersion() >= SCI_VERSION_1_1 && getSciVersion() <= SCI_VERSION_2_1) {