static void patchGameSaveRestoreCode(SegManager *segMan, reg_t methodAddress, byte id) { Script *script = segMan->getScript(methodAddress.getSegment()); byte *patchPtr = const_cast<byte *>(script->getBuf(methodAddress.getOffset())); memcpy(patchPtr, patchGameRestoreSave, sizeof(patchGameRestoreSave)); patchPtr[8] = id; }
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]); } }
Common::String Kernel::lookupText(reg_t address, int index) { char *seeker; Resource *textres; if (address.getSegment()) return _segMan->getString(address); int textlen; int _index = index; textres = _resMan->findResource(ResourceId(kResourceTypeText, address.getOffset()), 0); if (!textres) { error("text.%03d not found", address.getOffset()); return NULL; /* Will probably segfault */ } textlen = textres->size; seeker = (char *) textres->data; while (index--) while ((textlen--) && (*seeker++)) ; if (textlen) return seeker; error("Index %d out of bounds in text.%03d", _index, address.getOffset()); return NULL; }
void GuestAdditions::syncGK1VolumeFromScummVM(const int16 musicVolume, const int16 dacVolume) const { const reg_t soundsId = _state->variables[VAR_GLOBAL][kGlobalVarSounds]; if (!soundsId.isNull()) { List *sounds = _segMan->lookupList(readSelector(_segMan, soundsId, SELECTOR(elements))); reg_t soundId = sounds->first; while (!soundId.isNull()) { Node *sound = _segMan->lookupNode(soundId); const int16 type = readSelectorValue(_segMan, sound->value, SELECTOR(type)); int16 volume; if (type == kSoundsMusicType) { volume = ConfMan.getBool("mute") ? 0 : musicVolume; writeSelectorValue(_segMan, sound->value, SELECTOR(musicVolume), musicVolume); } else if (type == kSoundsSoundType) { volume = dacVolume; writeSelectorValue(_segMan, sound->value, SELECTOR(soundVolume), dacVolume); } else { error("Unknown sound type %d", type); } // `setVolume` will set the `vol` property on the sound object; // if it did not do this, an invocation of the `setVol` selector // would need to be here (though doing so would result in // recursion, so don't) g_sci->_soundCmd->setVolume(sound->value, volume); soundId = sound->succ; } } }
reg_t reg_t::operator+(const reg_t right) const { if (isPointer() && right.isNumber()) { // Pointer arithmetics. Only some pointer types make sense here SegmentObj *mobj = g_sci->getEngineState()->_segMan->getSegmentObj(segment); if (!mobj) error("[VM]: Attempt to add %d to invalid pointer %04x:%04x", right.offset, PRINT_REG(*this)); switch (mobj->getType()) { case SEG_TYPE_LOCALS: case SEG_TYPE_SCRIPT: case SEG_TYPE_STACK: case SEG_TYPE_DYNMEM: return make_reg(segment, offset + right.toSint16()); default: return lookForWorkaround(right); } } else if (isNumber() && right.isPointer()) { // Adding a pointer to a number, flip the order return right + *this; } else if (isNumber() && right.isNumber()) { // Normal arithmetics return make_reg(0, toSint16() + right.toSint16()); } else { return lookForWorkaround(right); } }
static void patchGameSaveRestoreCodeSci21(SegManager *segMan, reg_t methodAddress, byte id, bool doRestore) { Script *script = segMan->getScript(methodAddress.getSegment()); byte *patchPtr = const_cast<byte *>(script->getBuf(methodAddress.getOffset())); memcpy(patchPtr, patchGameRestoreSaveSci21, sizeof(patchGameRestoreSaveSci21)); if (doRestore) patchPtr[2] = 0x78; // push1 patchPtr[9] = id; }
reg_t reg_t::operator-(const reg_t right) const { if (getSegment() == right.getSegment()) { // We can subtract numbers, or pointers with the same segment, // an operation which will yield a number like in C return make_reg(0, toSint16() - right.toSint16()); } else { return *this + make_reg(right.getSegment(), -right.toSint16()); } }
void GuestAdditions::syncGK2UI() const { const reg_t sliderId = _segMan->findObjectByName("soundSlider"); if (!sliderId.isNull() && _segMan->getObject(sliderId)->isInserted()) { const reg_t oldAcc = _state->r_acc; invokeSelector(sliderId, SELECTOR(initialOff)); writeSelector(_segMan, sliderId, SELECTOR(x), _state->r_acc); _state->r_acc = oldAcc; } }
static void patchGameSaveRestoreCode(SegManager *segMan, reg_t methodAddress, byte id) { Script *script = segMan->getScript(methodAddress.getSegment()); byte *patchPtr = const_cast<byte *>(script->getBuf(methodAddress.getOffset())); if (getSciVersion() <= SCI_VERSION_1_1) memcpy(patchPtr, patchGameRestoreSave, sizeof(patchGameRestoreSave)); else // SCI2+ memcpy(patchPtr, patchGameRestoreSaveSci2, sizeof(patchGameRestoreSaveSci2)); patchPtr[8] = id; }
void GuestAdditions::syncSQ6UI() const { const reg_t bars[] = { _segMan->findObjectByName("musicBar"), _segMan->findObjectByName("soundBar") }; for (int i = 0; i < ARRAYSIZE(bars); ++i) { const reg_t barId = bars[i]; if (!barId.isNull()) { invokeSelector(barId, SELECTOR(show)); } } }
void GuestAdditions::syncShivers1UI(const int16 dacVolume) const { const reg_t sliderId = _segMan->findObjectByName("spVolume"); if (!sliderId.isNull()) { const int16 xPosition = dacVolume * 78 / Audio32::kMaxVolume + 32; writeSelectorValue(_segMan, sliderId, SELECTOR(x), xPosition); if (_segMan->getObject(sliderId)->isInserted()) { g_sci->_gfxFrameout->kernelUpdateScreenItem(sliderId); } } }
void GuestAdditions::syncPQ4UI(const int16 musicVolume) const { const SegmentId segment = _segMan->getScriptSegment(9, SCRIPT_GET_DONT_LOAD); if (segment != 0 && _segMan->getScript(segment)->getLocalsCount() > 2) { const reg_t barId = _segMan->getScript(segment)->getLocalsBegin()[2]; if (!barId.isNull()) { reg_t params[] = { make_reg(0, musicVolume) }; invokeSelector(barId, SELECTOR(setSize), 1, params); } } }
void GuestAdditions::syncLSL6HiresUI(const int16 musicVolume) const { const reg_t musicDialId = _segMan->findObjectByName("volumeDial"); if (!musicDialId.isNull()) { writeSelectorValue(_segMan, musicDialId, SELECTOR(curPos), musicVolume); writeSelectorValue(_segMan, musicDialId, SELECTOR(cel), musicVolume); reg_t params[] = { make_reg(0, musicVolume) }; invokeSelector(musicDialId, SELECTOR(update), 1, params); if (_segMan->getObject(musicDialId)->isInserted()) { g_sci->_gfxFrameout->kernelUpdateScreenItem(musicDialId); } } }
void GuestAdditions::syncAudioVolumeGlobalsToScummVM(const int index, const reg_t value) const { switch (g_sci->getGameId()) { case GID_GK2: if (index == kGlobalVarGK2MusicVolume) { const int16 musicVolume = value.toSint16() * Audio::Mixer::kMaxMixerVolume / Audio32::kMaxVolume; ConfMan.setInt("music_volume", musicVolume); } break; case GID_LSL6HIRES: if (index == kGlobalVarLSL6HiresMusicVolume) { const int16 musicVolume = value.toSint16() * Audio::Mixer::kMaxMixerVolume / kLSL6HiresUIVolumeMax; ConfMan.setInt("music_volume", musicVolume); } break; case GID_PHANTASMAGORIA: if (index == kGlobalVarPhant1MusicVolume) { const int16 musicVolume = value.toSint16() * Audio::Mixer::kMaxMixerVolume / MUSIC_MASTERVOLUME_MAX; ConfMan.setInt("music_volume", musicVolume); } else if (index == kGlobalVarPhant1DACVolume) { const int16 dacVolume = value.toSint16() * Audio::Mixer::kMaxMixerVolume / Audio32::kMaxVolume; ConfMan.setInt("sfx_volume", dacVolume); ConfMan.setInt("speech_volume", dacVolume); } break; case GID_TORIN: if (index == kGlobalVarTorinMusicVolume || index == kGlobalVarTorinSFXVolume || index == kGlobalVarTorinSpeechVolume) { const int16 volume = value.toSint16() * Audio::Mixer::kMaxMixerVolume / 100; switch (index) { case kGlobalVarTorinMusicVolume: ConfMan.setInt("music_volume", volume); break; case kGlobalVarTorinSFXVolume: ConfMan.setInt("sfx_volume", volume); break; case kGlobalVarTorinSpeechVolume: ConfMan.setInt("speech_volume", volume); break; } } break; default: break; } }
SegmentRef Script::dereference(reg_t pointer) { if (pointer.getOffset() > _buf->size()) { error("Script::dereference(): Attempt to dereference invalid pointer %04x:%04x into script %d segment (script size=%u)", PRINT_REG(pointer), _nr, _buf->size()); return SegmentRef(); } SegmentRef ret; ret.isRaw = true; ret.maxSize = _buf->size() - pointer.getOffset(); ret.raw = _buf->getUnsafeDataAt(pointer.getOffset(), ret.maxSize); return ret; }
void GuestAdditions::syncTorinUI(const int16 musicVolume, const int16 sfxVolume, const int16 speechVolume) const { const reg_t sliders[] = { _segMan->findObjectByName("oMusicScroll"), _segMan->findObjectByName("oSFXScroll"), _segMan->findObjectByName("oAudioScroll") }; const int16 values[] = { musicVolume, sfxVolume, speechVolume }; for (int i = 0; i < ARRAYSIZE(sliders); ++i) { const reg_t sliderId = sliders[i]; if (!sliderId.isNull()) { reg_t params[] = { make_reg(0, values[i]) }; invokeSelector(sliderId, SELECTOR(setPos), 1, params); } } }
void GuestAdditions::syncTextSpeedFromScummVM() const { const int16 textSpeed = 8 - (ConfMan.getInt("talkspeed") + 1) * 8 / 255; _state->variables[VAR_GLOBAL][kGlobalVarTextSpeed] = make_reg(0, textSpeed); if (g_sci->getGameId() == GID_GK1) { const reg_t textBarId = _segMan->findObjectByName("textBar"); if (!textBarId.isNull()) { // Resetting the bar position to 0 causes the game to retrieve the // new text speed value and re-render writeSelectorValue(_segMan, textBarId, SELECTOR(position), 0); } } }
void GuestAdditions::syncQFG4UI(const int16 musicVolume) const { const reg_t sliderId = _segMan->findObjectByName("volumeSlider"); if (!sliderId.isNull()) { const int16 yPosition = 84 - musicVolume * 34 / 10; writeSelectorValue(_segMan, sliderId, SELECTOR(y), yPosition); // There does not seem to be any good way to learn whether the // volume slider is visible (and thus eligible for // kUpdateScreenItem) const reg_t planeId = readSelector(_segMan, sliderId, SELECTOR(plane)); if (g_sci->_gfxFrameout->getPlanes().findByObject(planeId) != nullptr) { g_sci->_gfxFrameout->kernelUpdateScreenItem(sliderId); } } }
void GuestAdditions::syncMessageTypeToScummVMUsingDefaultStrategy(const int index, const reg_t value) { if (index == kGlobalVarMessageType) { // ScummVM audio options haven't been applied yet. Use this set call // as a trigger to apply defaults from ScummVM, ignoring the default // value that was just received from the game scripts if (!_messageTypeSynced || _state->variables[VAR_GLOBAL][kGlobalVarQuit] == TRUE_REG) { _messageTypeSynced = true; syncAudioOptionsFromScummVM(); return; } ConfMan.setBool("subtitles", value.toSint16() & kMessageTypeSubtitles); ConfMan.setBool("speech_mute", !(value.toSint16() & kMessageTypeSpeech)); } }
static bool isSaneNodePointer(SegManager *segMan, reg_t addr) { bool havePrev = false; reg_t prev = addr; do { Node *node = segMan->lookupNode(addr, false); if (!node) { if ((g_sci->getGameId() == GID_ICEMAN) && (g_sci->getEngineState()->currentRoomNumber() == 40)) { // ICEMAN: when plotting course, unDrawLast is called by startPlot::changeState // there is no previous entry so we get 0 in here } else if ((g_sci->getGameId() == GID_HOYLE1) && (g_sci->getEngineState()->currentRoomNumber() == 3)) { // HOYLE1: after sorting cards in hearts, in the next round // we get an invalid node - bug #3038433 } else { error("isSaneNodePointer: Node at %04x:%04x wasn't found", PRINT_REG(addr)); } return false; } if (havePrev && node->pred != prev) { error("isSaneNodePointer: Node at %04x:%04x points to invalid predecessor %04x:%04x (should be %04x:%04x)", PRINT_REG(addr), PRINT_REG(node->pred), PRINT_REG(prev)); //node->pred = prev; // fix the problem in the node return false; } prev = addr; addr = node->succ; havePrev = true; } while (!addr.isNull()); return true; }
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 Object::initSuperClass(SegManager *segMan, reg_t addr) { uint16 superClassOffset = getSuperClassSelector().getOffset(); if (superClassOffset == 0xffff) // -1 setSuperClassSelector(NULL_REG); // no superclass else setSuperClassSelector(segMan->getClassAddress(superClassOffset, SCRIPT_GET_LOCK, addr.getSegment())); }
uint16 Kernel::findRegType(reg_t reg) { // No segment? Must be integer if (!reg.getSegment()) return SIG_TYPE_INTEGER | (reg.getOffset() ? 0 : SIG_TYPE_NULL); if (reg.getSegment() == 0xFFFF) return SIG_TYPE_UNINITIALIZED; // Otherwise it's an object SegmentObj *mobj = _segMan->getSegmentObj(reg.getSegment()); if (!mobj) return SIG_TYPE_ERROR; uint16 result = 0; if (!mobj->isValidOffset(reg.getOffset())) result |= SIG_IS_INVALID; switch (mobj->getType()) { case SEG_TYPE_SCRIPT: if (reg.getOffset() <= (*(Script *)mobj).getBufSize() && reg.getOffset() >= (uint)-SCRIPT_OBJECT_MAGIC_OFFSET && (*(Script *)mobj).offsetIsObject(reg.getOffset())) { result |= ((Script *)mobj)->getObject(reg.getOffset()) ? SIG_TYPE_OBJECT : SIG_TYPE_REFERENCE; } else result |= SIG_TYPE_REFERENCE; break; case SEG_TYPE_CLONES: result |= SIG_TYPE_OBJECT; break; case SEG_TYPE_LOCALS: case SEG_TYPE_STACK: case SEG_TYPE_DYNMEM: case SEG_TYPE_HUNK: #ifdef ENABLE_SCI32 case SEG_TYPE_ARRAY: case SEG_TYPE_STRING: #endif result |= SIG_TYPE_REFERENCE; break; case SEG_TYPE_LISTS: result |= SIG_TYPE_LIST; break; case SEG_TYPE_NODES: result |= SIG_TYPE_NODE; break; default: return SIG_TYPE_ERROR; } return result; }
void GuestAdditions::syncGK2VolumeFromScummVM(const int16 musicVolume) const { _state->variables[VAR_GLOBAL][kGlobalVarGK2MusicVolume] = make_reg(0, musicVolume); // Calling `setVol` on all sounds is necessary to propagate the volume // change to existing sounds, and matches how game scripts propagate // volume changes when the in-game music slider is moved const reg_t soundsId = _state->variables[VAR_GLOBAL][kGlobalVarSounds]; if (!soundsId.isNull()) { List *sounds = _segMan->lookupList(readSelector(_segMan, soundsId, SELECTOR(elements))); reg_t soundId = sounds->first; while (!soundId.isNull()) { Node *sound = _segMan->lookupNode(soundId); reg_t params[] = { make_reg(0, musicVolume) }; invokeSelector(sound->value, SELECTOR(setVol), 1, params); soundId = sound->succ; } } }
reg_t reg_t::operator%(const reg_t right) const { if (isNumber() && right.isNumber() && !right.isNull()) { // Support for negative numbers was added in Iceman, and perhaps in // SCI0 0.000.685 and later. Theoretically, this wasn't really used // in SCI0, so the result is probably unpredictable. Such a case // would indicate either a script bug, or a modulo on an unsigned // integer larger than 32767. In any case, such a case should be // investigated, instead of being silently accepted. if (getSciVersion() <= SCI_VERSION_0_LATE && (toSint16() < 0 || right.toSint16() < 0)) warning("Modulo of a negative number has been requested for SCI0. This *could* lead to issues"); int16 value = toSint16(); int16 modulo = ABS(right.toSint16()); int16 result = value % modulo; if (result < 0) result += modulo; return make_reg(0, result); } else return lookForWorkaround(right, "modulo"); }
void MessageState::outputString(reg_t buf, const Common::String &str) { #ifdef ENABLE_SCI32 if (getSciVersion() >= SCI_VERSION_2) { if (_segMan->getSegmentType(buf.getSegment()) == SEG_TYPE_STRING) { SciString *sciString = _segMan->lookupString(buf); sciString->setSize(str.size() + 1); for (uint32 i = 0; i < str.size(); i++) sciString->setValue(i, str.c_str()[i]); sciString->setValue(str.size(), 0); } else if (_segMan->getSegmentType(buf.getSegment()) == SEG_TYPE_ARRAY) { // Happens in the intro of LSL6, we are asked to write the string // into an array SciArray<reg_t> *sciString = _segMan->lookupArray(buf); sciString->setSize(str.size() + 1); for (uint32 i = 0; i < str.size(); i++) sciString->setValue(i, make_reg(0, str.c_str()[i])); sciString->setValue(str.size(), NULL_REG); } } else { #endif SegmentRef buffer_r = _segMan->dereference(buf); if ((unsigned)buffer_r.maxSize >= str.size() + 1) { _segMan->strcpy(buf, str.c_str()); } else { // LSL6 sets an exit text here, but the buffer size allocated // is too small. Don't display a warning in this case, as we // don't use the exit text anyway - bug report #3035533 if (g_sci->getGameId() == GID_LSL6 && str.hasPrefix("\r\n(c) 1993 Sierra On-Line, Inc")) { // LSL6 buggy exit text, don't show warning } else { warning("Message: buffer %04x:%04x invalid or too small to hold the following text of %i bytes: '%s'", PRINT_REG(buf), str.size() + 1, str.c_str()); } // Set buffer to empty string if possible if (buffer_r.maxSize > 0) _segMan->strcpy(buf, ""); } #ifdef ENABLE_SCI32 } #endif }
void GfxPaint16::bitsGetRect(reg_t memoryHandle, Common::Rect *destRect) { byte *memoryPtr = NULL; if (!memoryHandle.isNull()) { memoryPtr = _segMan->getHunkPointer(memoryHandle); if (memoryPtr) { _screen->bitsGetRect(memoryPtr, destRect); } } }
Common::Array<reg_t> Script::listAllOutgoingReferences(reg_t addr) const { Common::Array<reg_t> tmp; if (addr.getOffset() <= _buf->size() && addr.getOffset() >= (uint)-SCRIPT_OBJECT_MAGIC_OFFSET && offsetIsObject(addr.getOffset())) { const Object *obj = getObject(addr.getOffset()); if (obj) { // Note all local variables, if we have a local variable environment if (_localsSegment) tmp.push_back(make_reg(_localsSegment, 0)); for (uint i = 0; i < obj->getVarCount(); i++) tmp.push_back(obj->getVariable(i)); } else { error("Request for outgoing script-object reference at %04x:%04x failed in script %d", PRINT_REG(addr), _nr); } } else { /* warning("Unexpected request for outgoing script-object references at %04x:%04x", PRINT_REG(addr));*/ /* Happens e.g. when we're looking into strings */ } return tmp; }
reg_t kAddBefore(EngineState *s, int argc, reg_t *argv) { List *list = s->_segMan->lookupList(argv[0]); Node *firstNode = s->_segMan->lookupNode(argv[1]); Node *newNode = s->_segMan->lookupNode(argv[2]); #ifdef CHECK_LISTS checkListPointer(s->_segMan, argv[0]); #endif if (!newNode) { error("New 'node' %04x:%04x is not a node", PRINT_REG(argv[2])); return NULL_REG; } if (argc != 3 && argc != 4) { error("kAddBefore: Haven't got 3 or 4 arguments, aborting"); return NULL_REG; } if (argc == 4) newNode->key = argv[3]; if (firstNode) { // We're really appending before const reg_t oldPred = firstNode->pred; newNode->succ = argv[1]; firstNode->pred = argv[2]; newNode->pred = oldPred; if (oldPred.isNull()) // Appended before first node? // Set new node as first list node list->first = argv[2]; else s->_segMan->lookupNode(oldPred)->succ = argv[2]; } else { addToFront(s, argv[0], argv[2]); // Set as initial list node } return s->r_acc; }
void GfxPaint16::bitsRestore(reg_t memoryHandle) { byte *memoryPtr = NULL; if (!memoryHandle.isNull()) { memoryPtr = _segMan->getHunkPointer(memoryHandle); if (memoryPtr) { _screen->bitsRestore(memoryPtr); _segMan->freeHunkEntry(memoryHandle); } } }