void ScreenItem::update(const reg_t object) { SegManager *segMan = g_sci->getEngineState()->_segMan; const GuiResourceId view = readSelectorValue(segMan, object, SELECTOR(view)); const int16 loopNo = readSelectorValue(segMan, object, SELECTOR(loop)); const int16 celNo = readSelectorValue(segMan, object, SELECTOR(cel)); const bool updateCel = ( _celInfo.resourceId != view || _celInfo.loopNo != loopNo || _celInfo.celNo != celNo ); const bool updateBitmap = !readSelector(segMan, object, SELECTOR(bitmap)).isNull(); setFromObject(segMan, object, updateCel, updateBitmap); if (!_created) { _updated = g_sci->_gfxFrameout->getScreenCount(); } _deleted = 0; }
reg_t kListFirstTrue(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; s->r_acc = NULL_REG; // reset the accumulator while (curNode) { 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) { // If it's a variable selector, check its value. // Example: script 64893 in Torin, MenuHandler::isHilited checks // all children for variable selector 0x03ba (bHilited). if (!readSelector(s->_segMan, curObject, slc).isNull()) return curObject; } else { invokeSelector(s, curObject, slc, argc, argv, argc - 2, argv + 2); // Check if the result is true if (!s->r_acc.isNull()) return curObject; } curNode = s->_segMan->lookupNode(nextNode); } // No selector returned true return NULL_REG; }
void GuestAdditions::syncTorinVolumeFromScummVM(const int16 musicVolume, const int16 sfxVolume, const int16 speechVolume) const { _state->variables[VAR_GLOBAL][kGlobalVarTorinMusicVolume] = make_reg(0, musicVolume); _state->variables[VAR_GLOBAL][kGlobalVarTorinSFXVolume] = make_reg(0, sfxVolume); _state->variables[VAR_GLOBAL][kGlobalVarTorinSpeechVolume] = make_reg(0, speechVolume); // Calling `reSyncVol` 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 volume sliders are moved const reg_t soundsId = _state->variables[VAR_GLOBAL][kGlobalVarSounds]; if (!soundsId.isNull()) { const Selector selector = SELECTOR(reSyncVol); List *sounds = _segMan->lookupList(readSelector(_segMan, soundsId, SELECTOR(elements))); reg_t soundId = sounds->first; while (!soundId.isNull()) { Node *sound = _segMan->lookupNode(soundId); const reg_t &soundObj = sound->value; if (_segMan->isHeapObject(soundObj) && lookupSelector(_segMan, soundObj, selector, nullptr, nullptr) != kSelectorNone) { invokeSelector(sound->value, SELECTOR(reSyncVol)); } soundId = sound->succ; } } }
void GfxText32::drawTextBitmap(uint16 x, uint16 y, Common::Rect planeRect, reg_t textObject) { reg_t hunkId = readSelector(_segMan, textObject, SELECTOR(bitmap)); // Sanity check: Check if the hunk is set. If not, either the game scripts // didn't set it, or an old saved game has been loaded, where it wasn't set. if (hunkId.isNull()) return; byte *memoryPtr = _segMan->getHunkPointer(hunkId); if (!memoryPtr) error("Attempt to draw an invalid text bitmap"); byte *surface = memoryPtr + BITMAP_HEADER_SIZE; int curByte = 0; uint16 skipColor = readSelectorValue(_segMan, textObject, SELECTOR(skip)); uint16 textX = planeRect.left + x; uint16 textY = planeRect.top + y; // Get totalWidth, totalHeight uint16 width = READ_LE_UINT16(memoryPtr); uint16 height = READ_LE_UINT16(memoryPtr + 2); // Upscale the coordinates/width if the fonts are already upscaled if (_screen->fontIsUpscaled()) { textX = textX * _screen->getDisplayWidth() / _screen->getWidth(); textY = textY * _screen->getDisplayHeight() / _screen->getHeight(); } for (int curY = 0; curY < height; curY++) { for (int curX = 0; curX < width; curX++) { byte pixel = surface[curByte++]; if (pixel != skipColor) _screen->putFontPixel(textY, curX + textX, curY, pixel); } } }
void GfxControls32::kernelTexteditChange(reg_t controlObject) { SciEvent curEvent; uint16 maxChars = 40; //readSelectorValue(_segMan, controlObject, SELECTOR(max)); // TODO reg_t textReference = readSelector(_segMan, controlObject, SELECTOR(text)); GfxFont *font = _cache->getFont(readSelectorValue(_segMan, controlObject, SELECTOR(font))); Common::String text; uint16 textSize; bool textChanged = false; bool textAddChar = false; Common::Rect rect; if (textReference.isNull()) error("kEditControl called on object that doesn't have a text reference"); text = _segMan->getString(textReference); // TODO: Finish this warning("kEditText ('%s')", text.c_str()); return; uint16 cursorPos = 0; //uint16 oldCursorPos = cursorPos; bool captureEvents = true; EventManager* eventMan = g_sci->getEventManager(); while (captureEvents) { curEvent = g_sci->getEventManager()->getSciEvent(SCI_EVENT_KEYBOARD | SCI_EVENT_PEEK); if (curEvent.type == SCI_EVENT_NONE) { eventMan->getSciEvent(SCI_EVENT_KEYBOARD); // consume the event } else { textSize = text.size(); switch (curEvent.type) { case SCI_EVENT_MOUSE_PRESS: // TODO: Implement mouse support for cursor change break; case SCI_EVENT_KEYBOARD: switch (curEvent.character) { case SCI_KEY_BACKSPACE: if (cursorPos > 0) { cursorPos--; text.deleteChar(cursorPos); textChanged = true; } eventMan->getSciEvent(SCI_EVENT_KEYBOARD); // consume the event break; case SCI_KEY_DELETE: if (cursorPos < textSize) { text.deleteChar(cursorPos); textChanged = true; } eventMan->getSciEvent(SCI_EVENT_KEYBOARD); // consume the event break; case SCI_KEY_HOME: // HOME cursorPos = 0; textChanged = true; eventMan->getSciEvent(SCI_EVENT_KEYBOARD); // consume the event break; case SCI_KEY_END: // END cursorPos = textSize; textChanged = true; eventMan->getSciEvent(SCI_EVENT_KEYBOARD); // consume the event break; case SCI_KEY_LEFT: // LEFT if (cursorPos > 0) { cursorPos--; textChanged = true; } eventMan->getSciEvent(SCI_EVENT_KEYBOARD); // consume the event break; case SCI_KEY_RIGHT: // RIGHT if (cursorPos + 1 <= textSize) { cursorPos++; textChanged = true; } eventMan->getSciEvent(SCI_EVENT_KEYBOARD); // consume the event break; case 3: // returned in SCI1 late and newer when Control - C is pressed if (curEvent.modifiers & SCI_KEYMOD_CTRL) { // Control-C erases the whole line cursorPos = 0; text.clear(); textChanged = true; } eventMan->getSciEvent(SCI_EVENT_KEYBOARD); // consume the event break; case SCI_KEY_UP: case SCI_KEY_DOWN: case SCI_KEY_ENTER: case SCI_KEY_ESC: case SCI_KEY_TAB: case SCI_KEY_SHIFT_TAB: captureEvents = false; break; default: if ((curEvent.modifiers & SCI_KEYMOD_CTRL) && curEvent.character == 'c') { // Control-C in earlier SCI games (SCI0 - SCI1 middle) // Control-C erases the whole line cursorPos = 0; text.clear(); textChanged = true; } else if (curEvent.character > 31 && curEvent.character < 256 && textSize < maxChars) { // insert pressed character textAddChar = true; textChanged = true; } eventMan->getSciEvent(SCI_EVENT_KEYBOARD); // consume the event break; } break; } } if (textChanged) { rect = g_sci->_gfxCompare->getNSRect(controlObject); if (textAddChar) { const char *textPtr = text.c_str(); // We check if we are really able to add the new char uint16 textWidth = 0; while (*textPtr) textWidth += font->getCharWidth((byte)*textPtr++); textWidth += font->getCharWidth(curEvent.character); // Does it fit? if (textWidth >= rect.width()) { return; } text.insertChar(curEvent.character, cursorPos++); // Note: the following checkAltInput call might make the text // too wide to fit, but SSCI fails to check that too. } reg_t hunkId = readSelector(_segMan, controlObject, SELECTOR(bitmap)); Common::Rect nsRect = g_sci->_gfxCompare->getNSRect(controlObject); //texteditCursorErase(); // TODO: Cursor // Write back string _segMan->strcpy(textReference, text.c_str()); // Modify the buffer and show it warning("kernelTexteditChange"); #if 0 _text->createTextBitmap(controlObject, 0, 0, hunkId); _text->drawTextBitmap(0, 0, nsRect, controlObject); //texteditCursorDraw(rect, text.c_str(), cursorPos); // TODO: Cursor g_system->updateScreen(); #endif } else { // TODO: Cursor /* if (g_system->getMillis() >= _texteditBlinkTime) { _paint16->invertRect(_texteditCursorRect); _paint16->bitsShow(_texteditCursorRect); _texteditCursorVisible = !_texteditCursorVisible; texteditSetBlinkTime(); } */ } textAddChar = false; textChanged = false; g_sci->sleep(10); } // while }
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 GfxControls32::kernelEditText(const reg_t controlObject) { SegManager *segMan = _segMan; TextEditor editor; reg_t textObject = readSelector(_segMan, controlObject, SELECTOR(text)); editor.text = _segMan->getString(textObject); editor.foreColor = readSelectorValue(_segMan, controlObject, SELECTOR(fore)); editor.backColor = readSelectorValue(_segMan, controlObject, SELECTOR(back)); editor.skipColor = readSelectorValue(_segMan, controlObject, SELECTOR(skip)); editor.fontId = readSelectorValue(_segMan, controlObject, SELECTOR(font)); editor.maxLength = readSelectorValue(_segMan, controlObject, SELECTOR(width)); editor.bitmap = readSelector(_segMan, controlObject, SELECTOR(bitmap)); editor.cursorCharPosition = 0; editor.cursorIsDrawn = false; editor.borderColor = readSelectorValue(_segMan, controlObject, SELECTOR(borderColor)); reg_t titleObject = readSelector(_segMan, controlObject, SELECTOR(title)); int16 titleHeight = 0; GuiResourceId titleFontId = readSelectorValue(_segMan, controlObject, SELECTOR(titleFont)); if (!titleObject.isNull()) { GfxFont *titleFont = _gfxCache->getFont(titleFontId); titleHeight += _gfxText32->scaleUpHeight(titleFont->getHeight()) + 1; if (editor.borderColor != -1) { titleHeight += 2; } } int16 width = 0; int16 height = titleHeight; GfxFont *editorFont = _gfxCache->getFont(editor.fontId); height += _gfxText32->scaleUpHeight(editorFont->getHeight()) + 1; _gfxText32->setFont(editor.fontId); int16 emSize = _gfxText32->getCharWidth('M', true); width += editor.maxLength * emSize + 1; if (editor.borderColor != -1) { width += 4; height += 2; } Common::Rect editorPlaneRect(width, height); editorPlaneRect.translate(readSelectorValue(_segMan, controlObject, SELECTOR(x)), readSelectorValue(_segMan, controlObject, SELECTOR(y))); reg_t planeObj = readSelector(_segMan, controlObject, SELECTOR(plane)); Plane *sourcePlane = g_sci->_gfxFrameout->getVisiblePlanes().findByObject(planeObj); if (sourcePlane == nullptr) { sourcePlane = g_sci->_gfxFrameout->getPlanes().findByObject(planeObj); if (sourcePlane == nullptr) { error("Could not find plane %04x:%04x", PRINT_REG(planeObj)); } } editorPlaneRect.translate(sourcePlane->_gameRect.left, sourcePlane->_gameRect.top); editor.textRect = Common::Rect(2, titleHeight + 2, width - 1, height - 1); editor.width = width; if (editor.bitmap.isNull()) { TextAlign alignment = (TextAlign)readSelectorValue(_segMan, controlObject, SELECTOR(mode)); if (titleObject.isNull()) { bool dimmed = readSelectorValue(_segMan, controlObject, SELECTOR(dimmed)); editor.bitmap = _gfxText32->createFontBitmap(width, height, editor.textRect, editor.text, editor.foreColor, editor.backColor, editor.skipColor, editor.fontId, alignment, editor.borderColor, dimmed, true, false); } else { error("Titled bitmaps are not known to be used by any game. Please submit a bug report with details about the game you were playing and what you were doing that triggered this error. Thanks!"); } } drawCursor(editor); Plane *plane = new Plane(editorPlaneRect, kPlanePicTransparent); plane->changePic(); g_sci->_gfxFrameout->addPlane(*plane); CelInfo32 celInfo; celInfo.type = kCelTypeMem; celInfo.bitmap = editor.bitmap; ScreenItem *screenItem = new ScreenItem(plane->_object, celInfo, Common::Point(), ScaleInfo()); plane->_screenItemList.add(screenItem); // frameOut must be called after the screen item is // created, and before it is updated at the end of the // event loop, otherwise it has both created and updated // flags set which crashes the engine (it runs updates // before creations) g_sci->_gfxFrameout->frameOut(true); EventManager *eventManager = g_sci->getEventManager(); bool clearTextOnInput = true; bool textChanged = false; for (;;) { // We peek here because the last event needs to be allowed to // dispatch a second time to the normal event handling system. // In the actual engine, the event is always consumed and then // the last event just gets posted back to the event manager for // reprocessing, but instead, we only remove the event from the // queue *after* we have determined it is not a defocusing event const SciEvent event = eventManager->getSciEvent(SCI_EVENT_ANY | SCI_EVENT_PEEK); bool focused = true; // Original engine did not have a QUIT event but we have to handle it if (event.type == SCI_EVENT_QUIT) { focused = false; break; } else if (event.type == SCI_EVENT_MOUSE_PRESS && !editorPlaneRect.contains(event.mousePosSci)) { focused = false; } else if (event.type == SCI_EVENT_KEYBOARD) { switch (event.character) { case SCI_KEY_ESC: case SCI_KEY_UP: case SCI_KEY_DOWN: case SCI_KEY_TAB: case SCI_KEY_SHIFT_TAB: case SCI_KEY_ENTER: focused = false; break; } } if (!focused) { break; } // Consume the event now that we know it is not one of the // defocusing events above if (event.type != SCI_EVENT_NONE) eventManager->getSciEvent(SCI_EVENT_ANY); // NOTE: In the original engine, the font and bitmap were // reset here on each iteration through the loop, but it // doesn't seem like this should be necessary since // control is not yielded back to the VM until input is // received, which means there is nothing that could modify // the GfxText32's state with a different font in the // meantime bool shouldDeleteChar = false; bool shouldRedrawText = false; uint16 lastCursorPosition = editor.cursorCharPosition; if (event.type == SCI_EVENT_KEYBOARD) { switch (event.character) { case SCI_KEY_LEFT: clearTextOnInput = false; if (editor.cursorCharPosition > 0) { --editor.cursorCharPosition; } break; case SCI_KEY_RIGHT: clearTextOnInput = false; if (editor.cursorCharPosition < editor.text.size()) { ++editor.cursorCharPosition; } break; case SCI_KEY_HOME: clearTextOnInput = false; editor.cursorCharPosition = 0; break; case SCI_KEY_END: clearTextOnInput = false; editor.cursorCharPosition = editor.text.size(); break; case SCI_KEY_INSERT: clearTextOnInput = false; // Redrawing also changes the cursor rect to // reflect the new insertion mode shouldRedrawText = true; _overwriteMode = !_overwriteMode; break; case SCI_KEY_DELETE: clearTextOnInput = false; if (editor.cursorCharPosition < editor.text.size()) { shouldDeleteChar = true; } break; case SCI_KEY_BACKSPACE: clearTextOnInput = false; shouldDeleteChar = true; if (editor.cursorCharPosition > 0) { --editor.cursorCharPosition; } break; case SCI_KEY_ETX: editor.text.clear(); editor.cursorCharPosition = 0; shouldRedrawText = true; break; default: { if (event.character >= 20 && event.character < 257) { if (clearTextOnInput) { clearTextOnInput = false; editor.text.clear(); } if ( (_overwriteMode && editor.cursorCharPosition < editor.maxLength) || (editor.text.size() < editor.maxLength && _gfxText32->getCharWidth(event.character, true) + _gfxText32->getStringWidth(editor.text) < editor.textRect.width()) ) { if (_overwriteMode && editor.cursorCharPosition < editor.text.size()) { editor.text.setChar(event.character, editor.cursorCharPosition); } else { editor.text.insertChar(event.character, editor.cursorCharPosition); } ++editor.cursorCharPosition; shouldRedrawText = true; } } } } } if (shouldDeleteChar) { shouldRedrawText = true; if (editor.cursorCharPosition < editor.text.size()) { editor.text.deleteChar(editor.cursorCharPosition); } } if (shouldRedrawText) { eraseCursor(editor); _gfxText32->erase(editor.textRect, true); _gfxText32->drawTextBox(editor.text); drawCursor(editor); textChanged = true; screenItem->_updated = g_sci->_gfxFrameout->getScreenCount(); } else if (editor.cursorCharPosition != lastCursorPosition) { eraseCursor(editor); drawCursor(editor); screenItem->_updated = g_sci->_gfxFrameout->getScreenCount(); } else { flashCursor(editor); screenItem->_updated = g_sci->_gfxFrameout->getScreenCount(); } g_sci->_gfxFrameout->frameOut(true); g_sci->getSciDebugger()->onFrame(); g_sci->_gfxFrameout->throttle(); } g_sci->_gfxFrameout->deletePlane(*plane); if (readSelectorValue(segMan, controlObject, SELECTOR(frameOut))) { g_sci->_gfxFrameout->frameOut(true); } _segMan->freeBitmap(editor.bitmap); if (textChanged) { editor.text.trim(); SciArray &string = *_segMan->lookupArray(textObject); string.fromString(editor.text); } return make_reg(0, textChanged); }
void GfxFrameout::kernelUpdatePlane(reg_t object) { for (PlaneList::iterator it = _planes.begin(); it != _planes.end(); ++it) { if (it->object == object) { // Read some information it->priority = readSelectorValue(_segMan, object, SELECTOR(priority)); GuiResourceId lastPictureId = it->pictureId; it->pictureId = readSelectorValue(_segMan, object, SELECTOR(picture)); if (lastPictureId != it->pictureId) { // picture got changed, load new picture deletePlanePictures(object); // Draw the plane's picture if it's not a translucent/plane colored frame if ((it->pictureId != kPlanePlainColored) && (it->pictureId != kPlaneTranslucent)) { // SQ6 gives us a bad picture number for the control menu if (_resMan->testResource(ResourceId(kResourceTypePic, it->pictureId))) addPlanePicture(object, it->pictureId, 0); } } it->planeRect.top = readSelectorValue(_segMan, object, SELECTOR(top)); it->planeRect.left = readSelectorValue(_segMan, object, SELECTOR(left)); it->planeRect.bottom = readSelectorValue(_segMan, object, SELECTOR(bottom)); it->planeRect.right = readSelectorValue(_segMan, object, SELECTOR(right)); _coordAdjuster->fromScriptToDisplay(it->planeRect.top, it->planeRect.left); _coordAdjuster->fromScriptToDisplay(it->planeRect.bottom, it->planeRect.right); // We get negative left in kq7 in scrolling rooms if (it->planeRect.left < 0) { it->planeOffsetX = -it->planeRect.left; it->planeRect.left = 0; } else { it->planeOffsetX = 0; } if (it->planeRect.top < 0) { it->planeOffsetY = -it->planeRect.top; it->planeRect.top = 0; } else { it->planeOffsetY = 0; } // We get bad plane-bottom in sq6 if (it->planeRect.right > _screen->getWidth()) it->planeRect.right = _screen->getWidth(); if (it->planeRect.bottom > _screen->getHeight()) it->planeRect.bottom = _screen->getHeight(); it->planeClipRect = Common::Rect(it->planeRect.width(), it->planeRect.height()); it->upscaledPlaneRect = it->planeRect; it->upscaledPlaneClipRect = it->planeClipRect; if (_screen->getUpscaledHires()) { _screen->adjustToUpscaledCoordinates(it->upscaledPlaneRect.top, it->upscaledPlaneRect.left); _screen->adjustToUpscaledCoordinates(it->upscaledPlaneRect.bottom, it->upscaledPlaneRect.right); _screen->adjustToUpscaledCoordinates(it->upscaledPlaneClipRect.top, it->upscaledPlaneClipRect.left); _screen->adjustToUpscaledCoordinates(it->upscaledPlaneClipRect.bottom, it->upscaledPlaneClipRect.right); } it->planePictureMirrored = readSelectorValue(_segMan, object, SELECTOR(mirrored)); it->planeBack = readSelectorValue(_segMan, object, SELECTOR(back)); sortPlanes(); // Update the items in the plane for (FrameoutList::iterator listIterator = _screenItems.begin(); listIterator != _screenItems.end(); listIterator++) { reg_t itemPlane = readSelector(_segMan, (*listIterator)->object, SELECTOR(plane)); if (object == itemPlane) { kernelUpdateScreenItem((*listIterator)->object); } } return; } } error("kUpdatePlane called on plane that wasn't added before"); }
void GfxControls32::kernelTexteditChange(reg_t controlObject) { SciEvent curEvent; uint16 maxChars = readSelectorValue(_segMan, controlObject, SELECTOR(max)); reg_t textReference = readSelector(_segMan, controlObject, SELECTOR(text)); GfxFont *font = _cache->getFont(readSelectorValue(_segMan, controlObject, SELECTOR(font))); Common::String text; uint16 textSize; bool textChanged = false; bool textAddChar = false; Common::Rect rect; if (textReference.isNull()) error("kEditControl called on object that doesnt have a text reference"); text = _segMan->getString(textReference); // TODO: Finish this, add a loop etc warning("kEditText ('%s')", text.c_str()); return; uint16 cursorPos = 0; //uint16 oldCursorPos = cursorPos; curEvent = g_sci->getEventManager()->getSciEvent(SCI_EVENT_KEYBOARD); if (curEvent.type != SCI_EVENT_NONE) { textSize = text.size(); switch (curEvent.type) { case SCI_EVENT_MOUSE_PRESS: // TODO: Implement mouse support for cursor change break; case SCI_EVENT_KEYBOARD: switch (curEvent.data) { case SCI_KEY_BACKSPACE: if (cursorPos > 0) { cursorPos--; text.deleteChar(cursorPos); textChanged = true; } break; case SCI_KEY_DELETE: if (cursorPos < textSize) { text.deleteChar(cursorPos); textChanged = true; } break; case SCI_KEY_HOME: // HOME cursorPos = 0; textChanged = true; break; case SCI_KEY_END: // END cursorPos = textSize; textChanged = true; break; case SCI_KEY_LEFT: // LEFT if (cursorPos > 0) { cursorPos--; textChanged = true; } break; case SCI_KEY_RIGHT: // RIGHT if (cursorPos + 1 <= textSize) { cursorPos++; textChanged = true; } break; case 3: // returned in SCI1 late and newer when Control - C is pressed if (curEvent.modifiers & SCI_KEYMOD_CTRL) { // Control-C erases the whole line cursorPos = 0; text.clear(); textChanged = true; } break; default: if ((curEvent.modifiers & SCI_KEYMOD_CTRL) && curEvent.data == 99) { // Control-C in earlier SCI games (SCI0 - SCI1 middle) // Control-C erases the whole line cursorPos = 0; text.clear(); textChanged = true; } else if (curEvent.data > 31 && curEvent.data < 256 && textSize < maxChars) { // insert pressed character textAddChar = true; textChanged = true; } break; } break; } } if (textChanged) { rect = g_sci->_gfxCompare->getNSRect(controlObject); if (textAddChar) { const char *textPtr = text.c_str(); // We check if we are really able to add the new char uint16 textWidth = 0; while (*textPtr) textWidth += font->getCharWidth((byte)*textPtr++); textWidth += font->getCharWidth(curEvent.data); // Does it fit? if (textWidth >= rect.width()) { return; } text.insertChar(curEvent.data, cursorPos++); // Note: the following checkAltInput call might make the text // too wide to fit, but SSCI fails to check that too. } // TODO: Cursor /* texteditCursorErase(); _paint16->eraseRect(rect); _text16->Box(text.c_str(), false, rect, SCI_TEXT16_ALIGNMENT_LEFT, -1); _paint16->bitsShow(rect); texteditCursorDraw(rect, text.c_str(), cursorPos); */ // Write back string _segMan->strcpy(textReference, text.c_str()); } else { // TODO: Cursor /* if (g_system->getMillis() >= _texteditBlinkTime) { _paint16->invertRect(_texteditCursorRect); _paint16->bitsShow(_texteditCursorRect); _texteditCursorVisible = !_texteditCursorVisible; texteditSetBlinkTime(); } */ } }
reg_t kArray(EngineState *s, int argc, reg_t *argv) { uint16 op = argv[0].toUint16(); // Use kString when accessing strings // This is possible, as strings inherit from arrays // and in this case (type 3) arrays are of type char *. // kString is almost exactly the same as kArray, so // this call is possible // TODO: we need to either merge SCI2 strings and // arrays together, and in the future merge them with // the SCI1 strings and arrays in the segment manager if (op == 0) { // New, check if the target type is 3 (string) if (argv[2].toUint16() == 3) return kString(s, argc, argv); } else { if (s->_segMan->getSegmentType(argv[1].segment) == SEG_TYPE_STRING || s->_segMan->getSegmentType(argv[1].segment) == SEG_TYPE_SCRIPT) { return kString(s, argc, argv); } #if 0 if (op == 6) { if (s->_segMan->getSegmentType(argv[3].segment) == SEG_TYPE_STRING || s->_segMan->getSegmentType(argv[3].segment) == SEG_TYPE_SCRIPT) { return kString(s, argc, argv); } } #endif } switch (op) { case 0: { // New reg_t arrayHandle; SciArray<reg_t> *array = s->_segMan->allocateArray(&arrayHandle); array->setType(argv[2].toUint16()); array->setSize(argv[1].toUint16()); return arrayHandle; } case 1: { // Size SciArray<reg_t> *array = s->_segMan->lookupArray(argv[1]); return make_reg(0, array->getSize()); } case 2: { // At (return value at an index) SciArray<reg_t> *array = s->_segMan->lookupArray(argv[1]); return array->getValue(argv[2].toUint16()); } case 3: { // Atput (put value at an index) SciArray<reg_t> *array = s->_segMan->lookupArray(argv[1]); uint32 index = argv[2].toUint16(); uint32 count = argc - 3; if (index + count > 65535) break; if (array->getSize() < index + count) array->setSize(index + count); for (uint16 i = 0; i < count; i++) array->setValue(i + index, argv[i + 3]); return argv[1]; // We also have to return the handle } case 4: // Free // Freeing of arrays is handled by the garbage collector return s->r_acc; case 5: { // Fill SciArray<reg_t> *array = s->_segMan->lookupArray(argv[1]); uint16 index = argv[2].toUint16(); // A count of -1 means fill the rest of the array uint16 count = argv[3].toSint16() == -1 ? array->getSize() - index : argv[3].toUint16(); uint16 arraySize = array->getSize(); if (arraySize < index + count) array->setSize(index + count); for (uint16 i = 0; i < count; i++) array->setValue(i + index, argv[4]); return argv[1]; } case 6: { // Cpy if (argv[1].isNull() || argv[3].isNull()) { if (getSciVersion() == SCI_VERSION_3) { // FIXME: Happens in SCI3, probably because of a missing kernel function. warning("kArray(Cpy): Request to copy from or to a null pointer"); return NULL_REG; } else { // SCI2-2.1: error out error("kArray(Cpy): Request to copy from or to a null pointer"); } } reg_t arrayHandle = argv[1]; SciArray<reg_t> *array1 = s->_segMan->lookupArray(argv[1]); //SciArray<reg_t> *array1 = !argv[1].isNull() ? s->_segMan->lookupArray(argv[1]) : s->_segMan->allocateArray(&arrayHandle); SciArray<reg_t> *array2 = s->_segMan->lookupArray(argv[3]); uint32 index1 = argv[2].toUint16(); uint32 index2 = argv[4].toUint16(); // The original engine ignores bad copies too if (index2 > array2->getSize()) break; // A count of -1 means fill the rest of the array uint32 count = argv[5].toSint16() == -1 ? array2->getSize() - index2 : argv[5].toUint16(); if (array1->getSize() < index1 + count) array1->setSize(index1 + count); for (uint16 i = 0; i < count; i++) array1->setValue(i + index1, array2->getValue(i + index2)); return arrayHandle; } case 7: // Cmp // Not implemented in SSCI warning("kArray(Cmp) called"); return s->r_acc; case 8: { // Dup if (argv[1].isNull()) { warning("kArray(Dup): Request to duplicate a null pointer"); #if 0 // Allocate an array anyway reg_t arrayHandle; SciArray<reg_t> *dupArray = s->_segMan->allocateArray(&arrayHandle); dupArray->setType(3); dupArray->setSize(0); return arrayHandle; #endif return NULL_REG; } SegmentType sourceType = s->_segMan->getSegmentObj(argv[1].segment)->getType(); if (sourceType == SEG_TYPE_SCRIPT) { // A technique used in later SCI2.1 and SCI3 games: the contents of a script // are loaded in an array (well, actually a string). Script *scr = s->_segMan->getScript(argv[1].segment); reg_t stringHandle; SciString *dupString = s->_segMan->allocateString(&stringHandle); dupString->setSize(scr->getBufSize()); dupString->fromString(Common::String((const char *)scr->getBuf())); return stringHandle; } else if (sourceType != SEG_TYPE_ARRAY && sourceType != SEG_TYPE_SCRIPT) { error("kArray(Dup): Request to duplicate a segment which isn't an array or a script"); } reg_t arrayHandle; SciArray<reg_t> *dupArray = s->_segMan->allocateArray(&arrayHandle); // This must occur after allocateArray, as inserting a new object // in the heap object list might invalidate this pointer. Also refer // to the same issue in kClone() SciArray<reg_t> *array = s->_segMan->lookupArray(argv[1]); dupArray->setType(array->getType()); dupArray->setSize(array->getSize()); for (uint32 i = 0; i < array->getSize(); i++) dupArray->setValue(i, array->getValue(i)); return arrayHandle; } case 9: // Getdata if (!s->_segMan->isHeapObject(argv[1])) return argv[1]; return readSelector(s->_segMan, argv[1], SELECTOR(data)); default: error("Unknown kArray subop %d", op); } return NULL_REG; }
void GfxControls16::kernelTexteditChange(reg_t controlObject, reg_t eventObject) { uint16 cursorPos = readSelectorValue(_segMan, controlObject, SELECTOR(cursor)); uint16 maxChars = readSelectorValue(_segMan, controlObject, SELECTOR(max)); reg_t textReference = readSelector(_segMan, controlObject, SELECTOR(text)); Common::String text; uint16 textSize, eventType, eventKey = 0, modifiers = 0; bool textChanged = false; bool textAddChar = false; Common::Rect rect; if (textReference.isNull()) error("kEditControl called on object that doesnt have a text reference"); text = _segMan->getString(textReference); uint16 oldCursorPos = cursorPos; if (!eventObject.isNull()) { textSize = text.size(); eventType = readSelectorValue(_segMan, eventObject, SELECTOR(type)); switch (eventType) { case SCI_EVENT_MOUSE_PRESS: // TODO: Implement mouse support for cursor change break; case SCI_EVENT_KEYBOARD: eventKey = readSelectorValue(_segMan, eventObject, SELECTOR(message)); modifiers = readSelectorValue(_segMan, eventObject, SELECTOR(modifiers)); switch (eventKey) { case SCI_KEY_BACKSPACE: if (cursorPos > 0) { cursorPos--; text.deleteChar(cursorPos); textChanged = true; } break; case SCI_KEY_DELETE: if (cursorPos < textSize) { text.deleteChar(cursorPos); textChanged = true; } break; case SCI_KEY_HOME: // HOME cursorPos = 0; textChanged = true; break; case SCI_KEY_END: // END cursorPos = textSize; textChanged = true; break; case SCI_KEY_LEFT: // LEFT if (cursorPos > 0) { cursorPos--; textChanged = true; } break; case SCI_KEY_RIGHT: // RIGHT if (cursorPos + 1 <= textSize) { cursorPos++; textChanged = true; } break; case 3: // returned in SCI1 late and newer when Control - C is pressed if (modifiers & SCI_KEYMOD_CTRL) { // Control-C erases the whole line cursorPos = 0; text.clear(); textChanged = true; } break; default: if ((modifiers & SCI_KEYMOD_CTRL) && eventKey == 99) { // Control-C in earlier SCI games (SCI0 - SCI1 middle) // Control-C erases the whole line cursorPos = 0; text.clear(); textChanged = true; } else if (eventKey > 31 && eventKey < 256 && textSize < maxChars) { // insert pressed character textAddChar = true; textChanged = true; } break; } break; } } if (g_sci->getVocabulary() && !textChanged && oldCursorPos != cursorPos) { assert(!textAddChar); textChanged = g_sci->getVocabulary()->checkAltInput(text, cursorPos); } if (textChanged) { GuiResourceId oldFontId = _text16->GetFontId(); GuiResourceId fontId = readSelectorValue(_segMan, controlObject, SELECTOR(font)); rect = g_sci->_gfxCompare->getNSRect(controlObject); _text16->SetFont(fontId); if (textAddChar) { const char *textPtr = text.c_str(); // We check if we are really able to add the new char uint16 textWidth = 0; while (*textPtr) textWidth += _text16->_font->getCharWidth((byte)*textPtr++); textWidth += _text16->_font->getCharWidth(eventKey); // Does it fit? if (textWidth >= rect.width()) { _text16->SetFont(oldFontId); return; } text.insertChar(eventKey, cursorPos++); // Note: the following checkAltInput call might make the text // too wide to fit, but SSCI fails to check that too. } if (g_sci->getVocabulary()) g_sci->getVocabulary()->checkAltInput(text, cursorPos); texteditCursorErase(); _paint16->eraseRect(rect); _text16->Box(text.c_str(), false, rect, SCI_TEXT16_ALIGNMENT_LEFT, -1); _paint16->bitsShow(rect); texteditCursorDraw(rect, text.c_str(), cursorPos); _text16->SetFont(oldFontId); // Write back string _segMan->strcpy(textReference, text.c_str()); } else { if (g_system->getMillis() >= _texteditBlinkTime) { _paint16->invertRect(_texteditCursorRect); _paint16->bitsShow(_texteditCursorRect); _texteditCursorVisible = !_texteditCursorVisible; texteditSetBlinkTime(); } } writeSelectorValue(_segMan, controlObject, SELECTOR(cursor), cursorPos); }
/** * Handles the sciAudio calls in fanmade games. * sciAudio is an external .NET library for playing MP3 files in fanmade games. * It runs in the background, and obtains sound commands from the * currently running game via text files (called "conductor files"). * For further info, check: http://sciprogramming.com/community/index.php?topic=634.0 */ void AudioPlayer::handleFanmadeSciAudio(reg_t sciAudioObject, SegManager *segMan) { // TODO: This is a bare bones implementation. Only the play/playx and stop commands // are handled for now - the other commands haven't been observed in any fanmade game // yet. All the volume related and fading functionality is currently missing. Kernel *kernel = g_sci->getKernel(); reg_t commandReg = readSelector(segMan, sciAudioObject, kernel->findSelector("command")); Common::String command = segMan->getString(commandReg); if (command == "play" || command == "playx") { reg_t fileNameReg = readSelector(segMan, sciAudioObject, kernel->findSelector("fileName")); Common::String fileName = segMan->getString(fileNameReg); reg_t loopCountReg = readSelector(segMan, sciAudioObject, kernel->findSelector("loopCount")); Common::String loopCountStr = segMan->getString(loopCountReg); int16 loopCount = atoi(loopCountStr.c_str()); // Adjust loopCount for ScummVM's LoopingAudioStream semantics if (loopCount == -1) { loopCount = 0; // loop endlessly } else if (loopCount >= 0) { // sciAudio loopCount == 0 -> play 1 time -> ScummVM's loopCount should be 1 // sciAudio loopCount == 1 -> play 2 times -> ScummVM's loopCount should be 2 loopCount++; } else { loopCount = 1; // play once in case the value makes no sense } // Determine sound type Audio::Mixer::SoundType soundType = Audio::Mixer::kSFXSoundType; if (fileName.hasPrefix("music")) soundType = Audio::Mixer::kMusicSoundType; else if (fileName.hasPrefix("speech")) soundType = Audio::Mixer::kSpeechSoundType; // Determine compression uint32 audioCompressionType = 0; if ((fileName.hasSuffix(".mp3")) || (fileName.hasSuffix(".sciAudio")) || (fileName.hasSuffix(".sciaudio"))) { audioCompressionType = MKTAG('M','P','3',' '); } else if (fileName.hasSuffix(".wav")) { audioCompressionType = MKTAG('W','A','V',' '); } else if (fileName.hasSuffix(".aiff")) { audioCompressionType = MKTAG('A','I','F','F'); } else { error("sciAudio: unsupported file type"); } Common::File *sciAudioFile = new Common::File(); // Replace backwards slashes for (uint i = 0; i < fileName.size(); i++) { if (fileName[i] == '\\') fileName.setChar('/', i); } sciAudioFile->open("sciAudio/" + fileName); Audio::RewindableAudioStream *audioStream = nullptr; switch (audioCompressionType) { case MKTAG('M','P','3',' '): #ifdef USE_MAD audioStream = Audio::makeMP3Stream(sciAudioFile, DisposeAfterUse::YES); #endif break; case MKTAG('W','A','V',' '): audioStream = Audio::makeWAVStream(sciAudioFile, DisposeAfterUse::YES); break; case MKTAG('A','I','F','F'): audioStream = Audio::makeAIFFStream(sciAudioFile, DisposeAfterUse::YES); break; default: break; } if (!audioStream) { error("sciAudio: requested compression not compiled into ScummVM"); } // We only support one audio handle _mixer->playStream(soundType, &_audioHandle, Audio::makeLoopingAudioStream((Audio::RewindableAudioStream *)audioStream, loopCount)); } else if (command == "stop") { _mixer->stopHandle(_audioHandle); } else { warning("Unhandled sciAudio command: %s", command.c_str()); } }
reg_t kArray(EngineState *s, int argc, reg_t *argv) { uint16 op = argv[0].toUint16(); // Use kString when accessing strings // This is possible, as strings inherit from arrays // and in this case (type 3) arrays are of type char *. // kString is almost exactly the same as kArray, so // this call is possible // TODO: we need to either merge SCI2 strings and // arrays together, and in the future merge them with // the SCI1 strings and arrays in the segment manager if (op == 0) { // New, check if the target type is 3 (string) if (argv[2].toUint16() == 3) return kString(s, argc, argv); } else { if (s->_segMan->getSegmentType(argv[1].getSegment()) == SEG_TYPE_STRING || s->_segMan->getSegmentType(argv[1].getSegment()) == SEG_TYPE_SCRIPT) { return kString(s, argc, argv); } #if 0 if (op == 6) { if (s->_segMan->getSegmentType(argv[3].getSegment()) == SEG_TYPE_STRING || s->_segMan->getSegmentType(argv[3].getSegment()) == SEG_TYPE_SCRIPT) { return kString(s, argc, argv); } } #endif } switch (op) { case 0: { // New reg_t arrayHandle; SciArray<reg_t> *array = s->_segMan->allocateArray(&arrayHandle); array->setType(argv[2].toUint16()); array->setSize(argv[1].toUint16()); return arrayHandle; } case 1: { // Size SciArray<reg_t> *array = s->_segMan->lookupArray(argv[1]); return make_reg(0, array->getSize()); } case 2: { // At (return value at an index) SciArray<reg_t> *array = s->_segMan->lookupArray(argv[1]); if (g_sci->getGameId() == GID_PHANTASMAGORIA2) { // HACK: Phantasmagoria 2 keeps trying to access past the end of an // array when it starts. I'm assuming it's trying to see where the // array ends, or tries to resize it. Adjust the array size // accordingly, and return NULL for now. if (array->getSize() == argv[2].toUint16()) { array->setSize(argv[2].toUint16()); return NULL_REG; } } return array->getValue(argv[2].toUint16()); } case 3: { // Atput (put value at an index) SciArray<reg_t> *array = s->_segMan->lookupArray(argv[1]); uint32 index = argv[2].toUint16(); uint32 count = argc - 3; if (index + count > 65535) break; if (array->getSize() < index + count) array->setSize(index + count); for (uint16 i = 0; i < count; i++) array->setValue(i + index, argv[i + 3]); return argv[1]; // We also have to return the handle } case 4: // Free // Freeing of arrays is handled by the garbage collector return s->r_acc; case 5: { // Fill SciArray<reg_t> *array = s->_segMan->lookupArray(argv[1]); uint16 index = argv[2].toUint16(); // A count of -1 means fill the rest of the array uint16 count = argv[3].toSint16() == -1 ? array->getSize() - index : argv[3].toUint16(); uint16 arraySize = array->getSize(); if (arraySize < index + count) array->setSize(index + count); for (uint16 i = 0; i < count; i++) array->setValue(i + index, argv[4]); return argv[1]; } case 6: { // Cpy if (argv[1].isNull() || argv[3].isNull()) { if (getSciVersion() == SCI_VERSION_3) { // FIXME: Happens in SCI3, probably because of a missing kernel function. warning("kArray(Cpy): Request to copy from or to a null pointer"); return NULL_REG; } else { // SCI2-2.1: error out error("kArray(Cpy): Request to copy from or to a null pointer"); } } reg_t arrayHandle = argv[1]; SciArray<reg_t> *array1 = s->_segMan->lookupArray(argv[1]); SciArray<reg_t> *array2 = s->_segMan->lookupArray(argv[3]); uint32 index1 = argv[2].toUint16(); uint32 index2 = argv[4].toUint16(); // The original engine ignores bad copies too if (index2 > array2->getSize()) break; // A count of -1 means fill the rest of the array uint32 count = argv[5].toSint16() == -1 ? array2->getSize() - index2 : argv[5].toUint16(); if (array1->getSize() < index1 + count) array1->setSize(index1 + count); for (uint16 i = 0; i < count; i++) array1->setValue(i + index1, array2->getValue(i + index2)); return arrayHandle; } case 7: // Cmp // Not implemented in SSCI warning("kArray(Cmp) called"); return s->r_acc; case 8: { // Dup if (argv[1].isNull()) { warning("kArray(Dup): Request to duplicate a null pointer"); #if 0 // Allocate an array anyway reg_t arrayHandle; SciArray<reg_t> *dupArray = s->_segMan->allocateArray(&arrayHandle); dupArray->setType(3); dupArray->setSize(0); return arrayHandle; #endif return NULL_REG; } SegmentObj *sobj = s->_segMan->getSegmentObj(argv[1].getSegment()); if (!sobj || sobj->getType() != SEG_TYPE_ARRAY) error("kArray(Dup): Request to duplicate a segment which isn't an array"); reg_t arrayHandle; SciArray<reg_t> *dupArray = s->_segMan->allocateArray(&arrayHandle); // This must occur after allocateArray, as inserting a new object // in the heap object list might invalidate this pointer. Also refer // to the same issue in kClone() SciArray<reg_t> *array = s->_segMan->lookupArray(argv[1]); dupArray->setType(array->getType()); dupArray->setSize(array->getSize()); for (uint32 i = 0; i < array->getSize(); i++) dupArray->setValue(i, array->getValue(i)); return arrayHandle; } case 9: // Getdata if (!s->_segMan->isHeapObject(argv[1])) return argv[1]; return readSelector(s->_segMan, argv[1], SELECTOR(data)); default: error("Unknown kArray subop %d", op); } return NULL_REG; }
void GfxFrameout::kernelFrameout() { if (g_sci->_robotDecoder->isVideoLoaded()) { bool skipVideo = false; RobotDecoder *videoDecoder = g_sci->_robotDecoder; uint16 x = videoDecoder->getPos().x; uint16 y = videoDecoder->getPos().y; if (videoDecoder->hasDirtyPalette()) videoDecoder->setSystemPalette(); while (!g_engine->shouldQuit() && !videoDecoder->endOfVideo() && !skipVideo) { if (videoDecoder->needsUpdate()) { const Graphics::Surface *frame = videoDecoder->decodeNextFrame(); if (frame) { g_system->copyRectToScreen((byte *)frame->pixels, frame->pitch, x, y, frame->w, frame->h); if (videoDecoder->hasDirtyPalette()) videoDecoder->setSystemPalette(); g_system->updateScreen(); } } Common::Event event; while (g_system->getEventManager()->pollEvent(event)) { if ((event.type == Common::EVENT_KEYDOWN && event.kbd.keycode == Common::KEYCODE_ESCAPE) || event.type == Common::EVENT_LBUTTONUP) skipVideo = true; } g_system->delayMillis(10); } return; } _palette->palVaryUpdate(); for (PlaneList::iterator it = _planes.begin(); it != _planes.end(); it++) { reg_t planeObject = it->object; uint16 planeLastPriority = it->lastPriority; // Update priority here, sq6 sets it w/o UpdatePlane uint16 planePriority = it->priority = readSelectorValue(_segMan, planeObject, SELECTOR(priority)); it->lastPriority = planePriority; if (planePriority == 0xffff) { // Plane currently not meant to be shown // If plane was shown before, delete plane rect if (planePriority != planeLastPriority) _paint32->fillRect(it->planeRect, 0); continue; } if (it->planeBack) _paint32->fillRect(it->planeRect, it->planeBack); GuiResourceId planeMainPictureId = it->pictureId; _coordAdjuster->pictureSetDisplayArea(it->planeRect); _palette->drewPicture(planeMainPictureId); FrameoutList itemList; // Copy screen items of the current frame to the list of items to be drawn for (FrameoutList::iterator listIterator = _screenItems.begin(); listIterator != _screenItems.end(); listIterator++) { reg_t itemPlane = readSelector(_segMan, (*listIterator)->object, SELECTOR(plane)); if (planeObject == itemPlane) { kernelUpdateScreenItem((*listIterator)->object); // TODO: Why is this necessary? itemList.push_back(*listIterator); } } for (PlanePictureList::iterator pictureIt = _planePictures.begin(); pictureIt != _planePictures.end(); pictureIt++) { if (pictureIt->object == planeObject) { GfxPicture *planePicture = pictureIt->picture; // Allocate memory for picture cels pictureIt->pictureCels = new FrameoutEntry[planePicture->getSci32celCount()]; // Add following cels to the itemlist FrameoutEntry *picEntry = pictureIt->pictureCels; int planePictureCels = planePicture->getSci32celCount(); for (int pictureCelNr = 0; pictureCelNr < planePictureCels; pictureCelNr++) { picEntry->celNo = pictureCelNr; picEntry->object = NULL_REG; picEntry->picture = planePicture; picEntry->y = planePicture->getSci32celY(pictureCelNr); picEntry->x = planePicture->getSci32celX(pictureCelNr); picEntry->picStartX = pictureIt->startX; picEntry->priority = planePicture->getSci32celPriority(pictureCelNr); itemList.push_back(picEntry); picEntry++; } } } // Now sort our itemlist Common::sort(itemList.begin(), itemList.end(), sortHelper); // warning("Plane %s", _segMan->getObjectName(planeObject)); for (FrameoutList::iterator listIterator = itemList.begin(); listIterator != itemList.end(); listIterator++) { FrameoutEntry *itemEntry = *listIterator; if (itemEntry->object.isNull()) { // Picture cel data itemEntry->y = ((itemEntry->y * _screen->getHeight()) / scriptsRunningHeight); itemEntry->x = ((itemEntry->x * _screen->getWidth()) / scriptsRunningWidth); itemEntry->picStartX = ((itemEntry->picStartX * _screen->getWidth()) / scriptsRunningWidth); // Out of view int16 pictureCelStartX = itemEntry->picStartX + itemEntry->x; int16 pictureCelEndX = pictureCelStartX + itemEntry->picture->getSci32celWidth(itemEntry->celNo); int16 planeStartX = it->planeOffsetX; int16 planeEndX = planeStartX + it->planeRect.width(); if (pictureCelEndX < planeStartX) continue; if (pictureCelStartX > planeEndX) continue; int16 pictureOffsetX = it->planeOffsetX; int16 pictureX = itemEntry->x; if ((it->planeOffsetX) || (itemEntry->picStartX)) { if (it->planeOffsetX <= itemEntry->picStartX) { pictureX += itemEntry->picStartX - it->planeOffsetX; pictureOffsetX = 0; } else { pictureOffsetX = it->planeOffsetX - itemEntry->picStartX; } } itemEntry->picture->drawSci32Vga(itemEntry->celNo, pictureX, itemEntry->y, pictureOffsetX, it->planePictureMirrored); // warning("picture cel %d %d", itemEntry->celNo, itemEntry->priority); } else if (itemEntry->viewId != 0xFFFF) { GfxView *view = _cache->getView(itemEntry->viewId); // warning("view %s %04x:%04x", _segMan->getObjectName(itemEntry->object), PRINT_REG(itemEntry->object)); if (view->isSci2Hires()) { int16 dummyX = 0; _screen->adjustToUpscaledCoordinates(itemEntry->y, itemEntry->x); _screen->adjustToUpscaledCoordinates(itemEntry->z, dummyX); } else if (getSciVersion() == SCI_VERSION_2_1) { itemEntry->y = (itemEntry->y * _screen->getHeight()) / scriptsRunningHeight; itemEntry->x = (itemEntry->x * _screen->getWidth()) / scriptsRunningWidth; itemEntry->z = (itemEntry->z * _screen->getHeight()) / scriptsRunningHeight; } // Adjust according to current scroll position itemEntry->x -= it->planeOffsetX; uint16 useInsetRect = readSelectorValue(_segMan, itemEntry->object, SELECTOR(useInsetRect)); if (useInsetRect) { itemEntry->celRect.top = readSelectorValue(_segMan, itemEntry->object, SELECTOR(inTop)); itemEntry->celRect.left = readSelectorValue(_segMan, itemEntry->object, SELECTOR(inLeft)); itemEntry->celRect.bottom = readSelectorValue(_segMan, itemEntry->object, SELECTOR(inBottom)) + 1; itemEntry->celRect.right = readSelectorValue(_segMan, itemEntry->object, SELECTOR(inRight)) + 1; if (view->isSci2Hires()) { _screen->adjustToUpscaledCoordinates(itemEntry->celRect.top, itemEntry->celRect.left); _screen->adjustToUpscaledCoordinates(itemEntry->celRect.bottom, itemEntry->celRect.right); } itemEntry->celRect.translate(itemEntry->x, itemEntry->y); // TODO: maybe we should clip the cels rect with this, i'm not sure // the only currently known usage is game menu of gk1 } else { if ((itemEntry->scaleX == 128) && (itemEntry->scaleY == 128)) view->getCelRect(itemEntry->loopNo, itemEntry->celNo, itemEntry->x, itemEntry->y, itemEntry->z, itemEntry->celRect); else view->getCelScaledRect(itemEntry->loopNo, itemEntry->celNo, itemEntry->x, itemEntry->y, itemEntry->z, itemEntry->scaleX, itemEntry->scaleY, itemEntry->celRect); Common::Rect nsRect = itemEntry->celRect; // Translate back to actual coordinate within scrollable plane nsRect.translate(it->planeOffsetX, 0); if (view->isSci2Hires()) { _screen->adjustBackUpscaledCoordinates(nsRect.top, nsRect.left); _screen->adjustBackUpscaledCoordinates(nsRect.bottom, nsRect.right); } else if (getSciVersion() == SCI_VERSION_2_1) { nsRect.top = (nsRect.top * scriptsRunningHeight) / _screen->getHeight(); nsRect.left = (nsRect.left * scriptsRunningWidth) / _screen->getWidth(); nsRect.bottom = (nsRect.bottom * scriptsRunningHeight) / _screen->getHeight(); nsRect.right = (nsRect.right * scriptsRunningWidth) / _screen->getWidth(); } writeSelectorValue(_segMan, itemEntry->object, SELECTOR(nsLeft), nsRect.left); writeSelectorValue(_segMan, itemEntry->object, SELECTOR(nsTop), nsRect.top); writeSelectorValue(_segMan, itemEntry->object, SELECTOR(nsRight), nsRect.right); writeSelectorValue(_segMan, itemEntry->object, SELECTOR(nsBottom), nsRect.bottom); } int16 screenHeight = _screen->getHeight(); int16 screenWidth = _screen->getWidth(); if (view->isSci2Hires()) { screenHeight = _screen->getDisplayHeight(); screenWidth = _screen->getDisplayWidth(); } if (itemEntry->celRect.bottom < 0 || itemEntry->celRect.top >= screenHeight) continue; if (itemEntry->celRect.right < 0 || itemEntry->celRect.left >= screenWidth) continue; Common::Rect clipRect, translatedClipRect; clipRect = itemEntry->celRect; if (view->isSci2Hires()) { clipRect.clip(it->upscaledPlaneClipRect); translatedClipRect = clipRect; translatedClipRect.translate(it->upscaledPlaneRect.left, it->upscaledPlaneRect.top); } else { clipRect.clip(it->planeClipRect); translatedClipRect = clipRect; translatedClipRect.translate(it->planeRect.left, it->planeRect.top); } if (!clipRect.isEmpty()) { if ((itemEntry->scaleX == 128) && (itemEntry->scaleY == 128)) view->draw(itemEntry->celRect, clipRect, translatedClipRect, itemEntry->loopNo, itemEntry->celNo, 255, 0, view->isSci2Hires()); else view->drawScaled(itemEntry->celRect, clipRect, translatedClipRect, itemEntry->loopNo, itemEntry->celNo, 255, itemEntry->scaleX, itemEntry->scaleY); } } else { // Most likely a text entry // This draws text the "SCI0-SCI11" way. In SCI2, text is prerendered in kCreateTextBitmap // TODO: rewrite this the "SCI2" way (i.e. implement the text buffer to draw inside kCreateTextBitmap) if (lookupSelector(_segMan, itemEntry->object, SELECTOR(text), NULL, NULL) == kSelectorVariable) { reg_t stringObject = readSelector(_segMan, itemEntry->object, 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); GfxFont *font = _cache->getFont(readSelectorValue(_segMan, itemEntry->object, SELECTOR(font))); bool dimmed = readSelectorValue(_segMan, itemEntry->object, SELECTOR(dimmed)); uint16 foreColor = readSelectorValue(_segMan, itemEntry->object, SELECTOR(fore)); itemEntry->y = ((itemEntry->y * _screen->getHeight()) / scriptsRunningHeight); itemEntry->x = ((itemEntry->x * _screen->getWidth()) / scriptsRunningWidth); uint16 startX = itemEntry->x + it->planeRect.left; uint16 curY = itemEntry->y + it->planeRect.top; const char *txt = text.c_str(); // HACK. The plane sometimes doesn't contain the correct width. This // hack breaks the dialog options when speaking with Grace, but it's // the best we got up to now. This happens because of the unimplemented // kTextWidth function in SCI32. // TODO: Remove this once kTextWidth has been implemented. uint16 w = it->planeRect.width() >= 20 ? it->planeRect.width() : _screen->getWidth() - 10; int16 charCount; // Upscale the coordinates/width if the fonts are already upscaled if (_screen->fontIsUpscaled()) { startX = startX * _screen->getDisplayWidth() / _screen->getWidth(); curY = curY * _screen->getDisplayHeight() / _screen->getHeight(); w = w * _screen->getDisplayWidth() / _screen->getWidth(); } while (*txt) { charCount = GetLongest(txt, w, font); if (charCount == 0) break; uint16 curX = startX; for (int i = 0; i < charCount; i++) { unsigned char curChar = txt[i]; font->draw(curChar, curY, curX, foreColor, dimmed); curX += font->getCharWidth(curChar); } curY += font->getHeight(); txt += charCount; while (*txt == ' ') txt++; // skip over breaking spaces } } } } for (PlanePictureList::iterator pictureIt = _planePictures.begin(); pictureIt != _planePictures.end(); pictureIt++) { if (pictureIt->object == planeObject) { delete[] pictureIt->pictureCels; pictureIt->pictureCels = 0; } } } _screen->copyToScreen(); g_sci->getEngineState()->_throttleTrigger = true; }
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); } } } }
void ScreenItem::setFromObject(SegManager *segMan, const reg_t object, const bool updateCel, const bool updateBitmap) { _position.x = readSelectorValue(segMan, object, SELECTOR(x)); _position.y = readSelectorValue(segMan, object, SELECTOR(y)); _scale.x = readSelectorValue(segMan, object, SELECTOR(scaleX)); _scale.y = readSelectorValue(segMan, object, SELECTOR(scaleY)); _scale.max = readSelectorValue(segMan, object, SELECTOR(maxScale)); _scale.signal = (ScaleSignals32)(readSelectorValue(segMan, object, SELECTOR(scaleSignal)) & 3); if (updateCel) { _celInfo.resourceId = (GuiResourceId)readSelectorValue(segMan, object, SELECTOR(view)); _celInfo.loopNo = readSelectorValue(segMan, object, SELECTOR(loop)); _celInfo.celNo = readSelectorValue(segMan, object, SELECTOR(cel)); if (_celInfo.resourceId <= kPlanePic) { // TODO: Enhance GfxView or ResourceManager to allow // metadata for resources to be retrieved once, from a // single location Resource *view = g_sci->getResMan()->findResource(ResourceId(kResourceTypeView, _celInfo.resourceId), false); if (!view) { error("Failed to load resource %d", _celInfo.resourceId); } // NOTE: +2 because the header size field itself is excluded from // the header size in the data const uint16 headerSize = READ_SCI11ENDIAN_UINT16(view->data) + 2; const uint8 loopCount = view->data[2]; const uint8 loopSize = view->data[12]; if (_celInfo.loopNo >= loopCount) { const int maxLoopNo = loopCount - 1; _celInfo.loopNo = maxLoopNo; writeSelectorValue(segMan, object, SELECTOR(loop), maxLoopNo); } byte *loopData = view->data + headerSize + (_celInfo.loopNo * loopSize); const int8 seekEntry = loopData[0]; if (seekEntry != -1) { loopData = view->data + headerSize + (seekEntry * loopSize); } const uint8 celCount = loopData[2]; if (_celInfo.celNo >= celCount) { const int maxCelNo = celCount - 1; _celInfo.celNo = maxCelNo; writeSelectorValue(segMan, object, SELECTOR(cel), maxCelNo); } } } if (updateBitmap) { const reg_t bitmap = readSelector(segMan, object, SELECTOR(bitmap)); if (!bitmap.isNull()) { _celInfo.bitmap = bitmap; _celInfo.type = kCelTypeMem; } else { _celInfo.bitmap = NULL_REG; _celInfo.type = kCelTypeView; } } if (updateCel || updateBitmap) { delete _celObj; _celObj = nullptr; } if (readSelectorValue(segMan, object, SELECTOR(fixPriority))) { _fixedPriority = true; _priority = readSelectorValue(segMan, object, SELECTOR(priority)); } else { _fixedPriority = false; writeSelectorValue(segMan, object, SELECTOR(priority), _position.y); } _z = readSelectorValue(segMan, object, SELECTOR(z)); _position.y -= _z; if (readSelectorValue(segMan, object, SELECTOR(useInsetRect))) { _useInsetRect = true; _insetRect.left = readSelectorValue(segMan, object, SELECTOR(inLeft)); _insetRect.top = readSelectorValue(segMan, object, SELECTOR(inTop)); _insetRect.right = readSelectorValue(segMan, object, SELECTOR(inRight)) + 1; _insetRect.bottom = readSelectorValue(segMan, object, SELECTOR(inBottom)) + 1; } else { _useInsetRect = false; } segMan->getObject(object)->clearInfoSelectorFlag(kInfoFlagViewVisible); }