void SoundCommandParser::processPlaySound(reg_t obj) { MusicEntry *musicSlot = _music->getSlot(obj); if (!musicSlot) { warning("kDoSound(play): Slot not found (%04x:%04x), initializing it manually", PRINT_REG(obj)); // The sound hasn't been initialized for some reason, so initialize it // here. Happens in KQ6, room 460, when giving the creature (child) to // the bookworm. Fixes bugs #3413301 and #3421098. processInitSound(obj); musicSlot = _music->getSlot(obj); if (!musicSlot) error("Failed to initialize uninitialized sound slot"); } int resourceId = getSoundResourceId(obj); if (musicSlot->resourceId != resourceId) { // another sound loaded into struct processDisposeSound(obj); processInitSound(obj); // Find slot again :) musicSlot = _music->getSlot(obj); } writeSelector(_segMan, obj, SELECTOR(handle), obj); if (_soundVersion >= SCI_VERSION_1_EARLY) { writeSelector(_segMan, obj, SELECTOR(nodePtr), obj); writeSelectorValue(_segMan, obj, SELECTOR(min), 0); writeSelectorValue(_segMan, obj, SELECTOR(sec), 0); writeSelectorValue(_segMan, obj, SELECTOR(frame), 0); writeSelectorValue(_segMan, obj, SELECTOR(signal), 0); } else { writeSelectorValue(_segMan, obj, SELECTOR(state), kSoundPlaying); } musicSlot->loop = readSelectorValue(_segMan, obj, SELECTOR(loop)); musicSlot->priority = readSelectorValue(_segMan, obj, SELECTOR(priority)); // Reset hold when starting a new song. kDoSoundSetHold is always called after // kDoSoundPlay to set it properly, if needed. Fixes bug #3413589. musicSlot->hold = -1; if (_soundVersion >= SCI_VERSION_1_EARLY) musicSlot->volume = readSelectorValue(_segMan, obj, SELECTOR(vol)); debugC(kDebugLevelSound, "kDoSound(play): %04x:%04x number %d, loop %d, prio %d, vol %d", PRINT_REG(obj), resourceId, musicSlot->loop, musicSlot->priority, musicSlot->volume); _music->soundPlay(musicSlot); // Reset any left-over signals musicSlot->signal = 0; musicSlot->fadeStep = 0; }
reg_t kListEachElementDo(EngineState *s, int argc, reg_t *argv) { List *list = s->_segMan->lookupList(argv[0]); Node *curNode = s->_segMan->lookupNode(list->first); reg_t curObject; Selector slc = argv[1].toUint16(); ObjVarRef address; while (curNode) { // We get the next node here as the current node might be gone after the invoke reg_t nextNode = curNode->succ; curObject = curNode->value; // First, check if the target selector is a variable if (lookupSelector(s->_segMan, curObject, slc, &address, NULL) == kSelectorVariable) { // This can only happen with 3 params (list, target selector, variable) if (argc != 3) { error("kListEachElementDo: Attempted to modify a variable selector with %d params", argc); } else { writeSelector(s->_segMan, curObject, slc, argv[2]); } } else { invokeSelector(s, curObject, slc, argc, argv, argc - 2, argv + 2); } curNode = s->_segMan->lookupNode(nextNode); } return s->r_acc; }
void SoundCommandParser::processInitSound(reg_t obj) { int resourceId = getSoundResourceId(obj); // Check if a track with the same sound object is already playing MusicEntry *oldSound = _music->getSlot(obj); if (oldSound) processDisposeSound(obj); MusicEntry *newSound = new MusicEntry(); newSound->resourceId = resourceId; newSound->soundObj = obj; newSound->loop = readSelectorValue(_segMan, obj, SELECTOR(loop)); newSound->priority = readSelectorValue(_segMan, obj, SELECTOR(pri)) & 0xFF; if (_soundVersion >= SCI_VERSION_1_EARLY) newSound->volume = CLIP<int>(readSelectorValue(_segMan, obj, SELECTOR(vol)), 0, MUSIC_VOLUME_MAX); newSound->reverb = -1; // initialize to SCI invalid, it'll be set correctly in soundInitSnd() below debugC(kDebugLevelSound, "kDoSound(init): %04x:%04x number %d, loop %d, prio %d, vol %d", PRINT_REG(obj), resourceId, newSound->loop, newSound->priority, newSound->volume); initSoundResource(newSound); _music->pushBackSlot(newSound); if (newSound->soundRes || newSound->pStreamAud) { // Notify the engine if (_soundVersion <= SCI_VERSION_0_LATE) writeSelectorValue(_segMan, obj, SELECTOR(state), kSoundInitialized); else writeSelector(_segMan, obj, SELECTOR(nodePtr), obj); } }
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; } }
void CssPrettyWriter::writeRulesetStart(const TokenList &selector) { indent(); writeSelector(selector); writeStr(" {", 2); newline(); indent_size++; }
reg_t kRobot(EngineState *s, int argc, reg_t *argv) { int16 subop = argv[0].toUint16(); switch (subop) { case 0: { // init int id = argv[1].toUint16(); reg_t obj = argv[2]; int16 flag = argv[3].toSint16(); int16 x = argv[4].toUint16(); int16 y = argv[5].toUint16(); warning("kRobot(init), id %d, obj %04x:%04x, flag %d, x=%d, y=%d", id, PRINT_REG(obj), flag, x, y); g_sci->_robotDecoder->load(id); g_sci->_robotDecoder->start(); g_sci->_robotDecoder->setPos(x, y); } break; case 1: // LSL6 hires (startup) // TODO return NULL_REG; // an integer is expected case 4: { // start - we don't really have a use for this one //int id = argv[1].toUint16(); //warning("kRobot(start), id %d", id); } break; case 7: // unknown, called e.g. by Phantasmagoria warning("kRobot(%d)", subop); break; case 8: // sync //if (true) { // debug: automatically skip all robot videos if (g_sci->_robotDecoder->endOfVideo()) { g_sci->_robotDecoder->close(); // Signal the engine scripts that the video is done writeSelector(s->_segMan, argv[1], SELECTOR(signal), SIGNAL_REG); } else { writeSelector(s->_segMan, argv[1], SELECTOR(signal), NULL_REG); } break; default: warning("kRobot(%d)", subop); break; } return s->r_acc; }
reg_t kSort(EngineState *s, int argc, reg_t *argv) { SegManager *segMan = s->_segMan; reg_t source = argv[0]; reg_t dest = argv[1]; reg_t order_func = argv[2]; int input_size = (int16)readSelectorValue(segMan, source, SELECTOR(size)); reg_t input_data = readSelector(segMan, source, SELECTOR(elements)); reg_t output_data = readSelector(segMan, dest, SELECTOR(elements)); List *list; Node *node; if (!input_size) return s->r_acc; if (output_data.isNull()) { list = s->_segMan->allocateList(&output_data); list->first = list->last = NULL_REG; writeSelector(segMan, dest, SELECTOR(elements), output_data); } writeSelectorValue(segMan, dest, SELECTOR(size), input_size); list = s->_segMan->lookupList(input_data); node = s->_segMan->lookupNode(list->first); sort_temp_t *temp_array = (sort_temp_t *)malloc(sizeof(sort_temp_t) * input_size); int i = 0; while (node) { reg_t params[1] = { node->value }; invokeSelector(s, order_func, SELECTOR(doit), argc, argv, 1, params); temp_array[i].key = node->key; temp_array[i].value = node->value; temp_array[i].order = s->r_acc; i++; node = s->_segMan->lookupNode(node->succ); } qsort(temp_array, input_size, sizeof(sort_temp_t), sort_temp_cmp); for (i = 0;i < input_size;i++) { reg_t lNode = s->_segMan->newNode(temp_array[i].value, temp_array[i].key); addToEnd(s, output_data, lNode); } free(temp_array); return s->r_acc; }
reg_t kListEachElementDo(EngineState *s, int argc, reg_t *argv) { const reg_t listReg = argv[0]; List *list = s->_segMan->lookupList(listReg); Node *curNode = s->_segMan->lookupNode(list->first); Selector slc = argv[1].toUint16(); ObjVarRef address; ++list->numRecursions; if (list->numRecursions >= ARRAYSIZE(list->nextNodes)) { error("Too much recursion in kListEachElementDo"); } while (curNode) { // We get the next node here as the current node might be deleted by the // invoke. In the case that the next node is also deleted, kDeleteKey // needs to be able to adjust the location of the next node, which is // why it is stored on the list instead of on the stack list->nextNodes[list->numRecursions] = curNode->succ; reg_t curObject = curNode->value; // First, check if the target selector is a variable if (lookupSelector(s->_segMan, curObject, slc, &address, NULL) == kSelectorVariable) { // This can only happen with 3 params (list, target selector, variable) if (argc != 3) { error("kListEachElementDo: Attempted to modify a variable selector with %d params", argc); } else { writeSelector(s->_segMan, curObject, slc, argv[2]); } } else { invokeSelector(s, curObject, slc, argc, argv, argc - 2, argv + 2); // Check if the call above leads to a game restore, in which case // the segment manager will be reset, and the original list will // be invalidated if (s->abortScriptProcessing == kAbortLoadGame) return s->r_acc; } curNode = s->_segMan->lookupNode(list->nextNodes[list->numRecursions]); } if (s->_segMan->isValidAddr(listReg, SEG_TYPE_LISTS)) { --list->numRecursions; } return s->r_acc; }
void SoundCommandParser::processDisposeSound(reg_t obj) { MusicEntry *musicSlot = _music->getSlot(obj); if (!musicSlot) { warning("kDoSound(dispose): Slot not found (%04x:%04x)", PRINT_REG(obj)); return; } processStopSound(obj, false); _music->soundKill(musicSlot); writeSelectorValue(_segMan, obj, SELECTOR(handle), 0); if (_soundVersion >= SCI_VERSION_1_EARLY) writeSelector(_segMan, obj, SELECTOR(nodePtr), NULL_REG); else writeSelectorValue(_segMan, obj, SELECTOR(state), kSoundStopped); }
reg_t GuestAdditions::promptSaveRestoreTorin(EngineState *s, int argc, reg_t *argv) const { const bool isSave = (argc > 0 && (bool)argv[0].toSint16()); int saveNo; if (isSave) { GUI::SaveLoadChooser dialog(_("Save game:"), _("Save"), true); saveNo = dialog.runModalWithCurrentTarget(); if (saveNo != -1) { reg_t descriptionId = s->variables[VAR_LOCAL][1]; reg_t dataId; SciArray &description = *_segMan->allocateArray(kArrayTypeString, 0, &dataId); Common::String descriptionString = dialog.getResultString(); if (descriptionString.empty()) descriptionString = dialog.createDefaultSaveDescription(saveNo - 1); description.fromString(descriptionString); writeSelector(_segMan, descriptionId, SELECTOR(data), dataId); } } else { if (s->_delayedRestoreGameId != -1) { saveNo = s->_delayedRestoreGameId; } else { GUI::SaveLoadChooser dialog(_("Restore game:"), _("Restore"), false); saveNo = dialog.runModalWithCurrentTarget(); } } if (saveNo > 0) { // The autosave slot in ScummVM takes up slot 0, but in SCI the first // non-autosave save game number needs to be 0, so reduce the save // number here to match what would come from the normal SCI save/restore // dialog. There is additional special code for handling the autosave // game inside of kRestoreGame32. --saveNo; } if (saveNo != -1) { assert(s->variablesMax[VAR_LOCAL] > 2); s->variables[VAR_LOCAL][2] = make_reg(0, saveNo); s->variables[VAR_LOCAL][3] = make_reg(0, isSave ? 1 : 0); } return make_reg(0, saveNo != -1); }
void GfxAnimate::drawCels() { reg_t bitsHandle; AnimateList::iterator it; const AnimateList::iterator end = _list.end(); _lastCastData.clear(); for (it = _list.begin(); it != end; ++it) { if (!(it->signal & (kSignalNoUpdate | kSignalHidden | kSignalAlwaysUpdate))) { // Save background bitsHandle = _paint16->bitsSave(it->celRect, GFX_SCREEN_MASK_ALL); writeSelector(_s->_segMan, it->object, SELECTOR(underBits), bitsHandle); // draw corresponding cel _paint16->drawCel(it->viewId, it->loopNo, it->celNo, it->celRect, it->priority, it->paletteNo, it->scaleX, it->scaleY); it->showBitsFlag = true; if (it->signal & kSignalRemoveView) it->signal &= ~kSignalRemoveView; // Remember that entry in lastCast _lastCastData.push_back(*it); } } }
reg_t GfxText32::createTextBitmap(reg_t textObject, uint16 maxWidth, uint16 maxHeight, reg_t prevHunk) { reg_t stringObject = readSelector(_segMan, textObject, SELECTOR(text)); // The object in the text selector of the item can be either a raw string // or a Str object. In the latter case, we need to access the object's data // selector to get the raw string. if (_segMan->isHeapObject(stringObject)) stringObject = readSelector(_segMan, stringObject, SELECTOR(data)); Common::String text = _segMan->getString(stringObject); // HACK: The character offsets of the up and down arrow buttons are off by one // in GK1, for some unknown reason. Fix them here. if (text.size() == 1 && (text[0] == 29 || text[0] == 30)) { text.setChar(text[0] + 1, 0); } GuiResourceId fontId = readSelectorValue(_segMan, textObject, SELECTOR(font)); GfxFont *font = _cache->getFont(fontId); bool dimmed = readSelectorValue(_segMan, textObject, SELECTOR(dimmed)); int16 alignment = readSelectorValue(_segMan, textObject, SELECTOR(mode)); uint16 foreColor = readSelectorValue(_segMan, textObject, SELECTOR(fore)); uint16 backColor = readSelectorValue(_segMan, textObject, SELECTOR(back)); Common::Rect nsRect = g_sci->_gfxCompare->getNSRect(textObject); uint16 width = nsRect.width() + 1; uint16 height = nsRect.height() + 1; // Limit rectangle dimensions, if requested if (maxWidth > 0) width = maxWidth; if (maxHeight > 0) height = maxHeight; // Upscale the coordinates/width if the fonts are already upscaled if (_screen->fontIsUpscaled()) { width = width * _screen->getDisplayWidth() / _screen->getWidth(); height = height * _screen->getDisplayHeight() / _screen->getHeight(); } int entrySize = width * height + BITMAP_HEADER_SIZE; reg_t memoryId = NULL_REG; if (prevHunk.isNull()) { memoryId = _segMan->allocateHunkEntry("TextBitmap()", entrySize); writeSelector(_segMan, textObject, SELECTOR(bitmap), memoryId); } else { memoryId = prevHunk; } byte *memoryPtr = _segMan->getHunkPointer(memoryId); if (prevHunk.isNull()) memset(memoryPtr, 0, BITMAP_HEADER_SIZE); byte *bitmap = memoryPtr + BITMAP_HEADER_SIZE; memset(bitmap, backColor, width * height); // Save totalWidth, totalHeight WRITE_LE_UINT16(memoryPtr, width); WRITE_LE_UINT16(memoryPtr + 2, height); int16 charCount = 0; uint16 curX = 0, curY = 0; const char *txt = text.c_str(); int16 textWidth, textHeight, totalHeight = 0, offsetX = 0, offsetY = 0; uint16 start = 0; // Calculate total text height while (*txt) { charCount = GetLongest(txt, width, font); if (charCount == 0) break; Width(txt, 0, (int16)strlen(txt), fontId, textWidth, textHeight, true); totalHeight += textHeight; txt += charCount; while (*txt == ' ') txt++; // skip over breaking spaces } txt = text.c_str(); // Draw text in buffer while (*txt) { charCount = GetLongest(txt, width, font); if (charCount == 0) break; Width(txt, start, charCount, fontId, textWidth, textHeight, true); switch (alignment) { case SCI_TEXT32_ALIGNMENT_RIGHT: offsetX = width - textWidth; break; case SCI_TEXT32_ALIGNMENT_CENTER: // Center text both horizontally and vertically offsetX = (width - textWidth) / 2; offsetY = (height - totalHeight) / 2; break; case SCI_TEXT32_ALIGNMENT_LEFT: offsetX = 0; break; default: warning("Invalid alignment %d used in TextBox()", alignment); } for (int i = 0; i < charCount; i++) { unsigned char curChar = txt[i]; font->drawToBuffer(curChar, curY + offsetY, curX + offsetX, foreColor, dimmed, bitmap, width, height); curX += font->getCharWidth(curChar); } curX = 0; curY += font->getHeight(); txt += charCount; while (*txt == ' ') txt++; // skip over breaking spaces } return memoryId; }
reg_t GfxMenu::kernelSelect(reg_t eventObject, bool pauseSound) { int16 eventType = readSelectorValue(_segMan, eventObject, SELECTOR(type)); int16 keyPress, keyModifier; GuiMenuItemList::iterator itemIterator = _itemList.begin(); GuiMenuItemList::iterator itemEnd = _itemList.end(); GuiMenuItemEntry *itemEntry = NULL; bool forceClaimed = false; switch (eventType) { case SCI_EVENT_KEYBOARD: keyPress = readSelectorValue(_segMan, eventObject, SELECTOR(message)); keyModifier = readSelectorValue(_segMan, eventObject, SELECTOR(modifiers)); // If tab got pressed, handle it here as if it was Ctrl-I - at least // sci0 also did it that way if (keyPress == SCI_KEY_TAB) { keyModifier = SCI_KEYMOD_CTRL; keyPress = 'i'; } switch (keyPress) { case 0: break; case SCI_KEY_ESC: interactiveStart(pauseSound); itemEntry = interactiveWithKeyboard(); interactiveEnd(pauseSound); forceClaimed = true; break; default: while (itemIterator != itemEnd) { itemEntry = *itemIterator; if (itemEntry->keyPress == keyPress && itemEntry->keyModifier == keyModifier && itemEntry->enabled) break; itemIterator++; } if (itemIterator == itemEnd) itemEntry = NULL; } break; case SCI_EVENT_SAID: while (itemIterator != itemEnd) { itemEntry = *itemIterator; if (!itemEntry->saidVmPtr.isNull()) { byte *saidSpec = _segMan->derefBulkPtr(itemEntry->saidVmPtr, 0); if (!saidSpec) { warning("Could not dereference saidSpec"); continue; } if (said(saidSpec, 0) != SAID_NO_MATCH) break; } itemIterator++; } if (itemIterator == itemEnd) itemEntry = NULL; break; case SCI_EVENT_MOUSE_PRESS: { Common::Point mousePosition; mousePosition.x = readSelectorValue(_segMan, eventObject, SELECTOR(x)); mousePosition.y = readSelectorValue(_segMan, eventObject, SELECTOR(y)); if (mousePosition.y < 10) { interactiveStart(pauseSound); itemEntry = interactiveWithMouse(); interactiveEnd(pauseSound); forceClaimed = true; } } break; } if (!_menuSaveHandle.isNull()) { _paint16->bitsRestore(_menuSaveHandle); // Display line inbetween menubar and actual menu Common::Rect menuLine = _menuRect; menuLine.bottom = menuLine.top + 1; _paint16->bitsShow(menuLine); _paint16->kernelGraphRedrawBox(_menuRect); _menuSaveHandle = NULL_REG; } if (!_barSaveHandle.isNull()) { _paint16->bitsRestore(_barSaveHandle); _paint16->bitsShow(_ports->_menuRect); _barSaveHandle = NULL_REG; } if (_oldPort) { _ports->setPort(_oldPort); _oldPort = NULL; } if ((itemEntry) || (forceClaimed)) writeSelector(_segMan, eventObject, SELECTOR(claimed), make_reg(0, 1)); if (itemEntry) return make_reg(0, (itemEntry->menuId << 8) | (itemEntry->id)); return NULL_REG; }
void GfxAnimate::update() { reg_t bitsHandle; Common::Rect rect; AnimateList::iterator it; const AnimateList::iterator end = _list.end(); // Remove all no-update cels, if requested for (it = _list.legacy_reverse_begin(); it != end; --it) { if (it->signal & kSignalNoUpdate) { if (!(it->signal & kSignalRemoveView)) { bitsHandle = readSelector(_s->_segMan, it->object, SELECTOR(underBits)); if (_screen->_picNotValid != 1) { _paint16->bitsRestore(bitsHandle); it->showBitsFlag = true; } else { _paint16->bitsFree(bitsHandle); } writeSelectorValue(_s->_segMan, it->object, SELECTOR(underBits), 0); } it->signal &= ~kSignalForceUpdate; if (it->signal & kSignalViewUpdated) it->signal &= ~(kSignalViewUpdated | kSignalNoUpdate); } else if (it->signal & kSignalStopUpdate) { it->signal &= ~kSignalStopUpdate; it->signal |= kSignalNoUpdate; } } // Draw always-update cels for (it = _list.begin(); it != end; ++it) { if (it->signal & kSignalAlwaysUpdate) { // draw corresponding cel _paint16->drawCel(it->viewId, it->loopNo, it->celNo, it->celRect, it->priority, it->paletteNo, it->scaleX, it->scaleY); it->showBitsFlag = true; it->signal &= ~(kSignalStopUpdate | kSignalViewUpdated | kSignalNoUpdate | kSignalForceUpdate); if (!(it->signal & kSignalIgnoreActor)) { rect = it->celRect; rect.top = CLIP<int16>(_ports->kernelPriorityToCoordinate(it->priority) - 1, rect.top, rect.bottom - 1); _paint16->fillRect(rect, GFX_SCREEN_MASK_CONTROL, 0, 0, 15); } } } // Saving background for all NoUpdate-cels for (it = _list.begin(); it != end; ++it) { if (it->signal & kSignalNoUpdate) { if (it->signal & kSignalHidden) { it->signal |= kSignalRemoveView; } else { it->signal &= ~kSignalRemoveView; if (it->signal & kSignalIgnoreActor) bitsHandle = _paint16->bitsSave(it->celRect, GFX_SCREEN_MASK_VISUAL|GFX_SCREEN_MASK_PRIORITY); else bitsHandle = _paint16->bitsSave(it->celRect, GFX_SCREEN_MASK_ALL); writeSelector(_s->_segMan, it->object, SELECTOR(underBits), bitsHandle); } } } // Draw NoUpdate cels for (it = _list.begin(); it != end; ++it) { if (it->signal & kSignalNoUpdate && !(it->signal & kSignalHidden)) { // draw corresponding cel _paint16->drawCel(it->viewId, it->loopNo, it->celNo, it->celRect, it->priority, it->paletteNo, it->scaleX, it->scaleY); it->showBitsFlag = true; if (!(it->signal & kSignalIgnoreActor)) { rect = it->celRect; rect.top = CLIP<int16>(_ports->kernelPriorityToCoordinate(it->priority) - 1, rect.top, rect.bottom - 1); _paint16->fillRect(rect, GFX_SCREEN_MASK_CONTROL, 0, 0, 15); } } } }