diff options
-rw-r--r-- | engines/sci/engine/gc.cpp | 10 | ||||
-rw-r--r-- | engines/sci/engine/kernel.h | 9 | ||||
-rw-r--r-- | engines/sci/engine/kernel_tables.h | 43 | ||||
-rw-r--r-- | engines/sci/engine/kgraphics32.cpp | 234 | ||||
-rw-r--r-- | engines/sci/graphics/controls32.cpp | 459 | ||||
-rw-r--r-- | engines/sci/graphics/controls32.h | 371 | ||||
-rw-r--r-- | engines/sci/graphics/screen_item32.cpp | 21 | ||||
-rw-r--r-- | engines/sci/graphics/text32.cpp | 70 | ||||
-rw-r--r-- | engines/sci/graphics/text32.h | 19 | ||||
-rw-r--r-- | engines/sci/sci.cpp | 2 |
10 files changed, 1085 insertions, 153 deletions
diff --git a/engines/sci/engine/gc.cpp b/engines/sci/engine/gc.cpp index 70c8c52bf0..b229490570 100644 --- a/engines/sci/engine/gc.cpp +++ b/engines/sci/engine/gc.cpp @@ -24,6 +24,10 @@ #include "common/array.h" #include "sci/graphics/ports.h" +#ifdef ENABLE_SCI32 +#include "sci/graphics/controls32.h" +#endif + namespace Sci { //#define GC_DEBUG_CODE @@ -150,6 +154,12 @@ AddrSet *findAllActiveReferences(EngineState *s) { } } +#ifdef ENABLE_SCI32 + // Init: ScrollWindows + if (g_sci->_gfxControls32) + wm.pushArray(g_sci->_gfxControls32->listObjectReferences()); +#endif + debugC(kDebugLevelGC, "[GC] -- Finished explicitly loaded scripts, done with root set"); processWorkList(s->_segMan, wm, heap); diff --git a/engines/sci/engine/kernel.h b/engines/sci/engine/kernel.h index 983bd7af11..6604387254 100644 --- a/engines/sci/engine/kernel.h +++ b/engines/sci/engine/kernel.h @@ -467,7 +467,16 @@ reg_t kStringTrnExclude(EngineState *s, int argc, reg_t *argv); reg_t kScrollWindowCreate(EngineState *s, int argc, reg_t *argv); reg_t kScrollWindowAdd(EngineState *s, int argc, reg_t *argv); +reg_t kScrollWindowPageUp(EngineState *s, int argc, reg_t *argv); +reg_t kScrollWindowPageDown(EngineState *s, int argc, reg_t *argv); +reg_t kScrollWindowUpArrow(EngineState *s, int argc, reg_t *argv); +reg_t kScrollWindowDownArrow(EngineState *s, int argc, reg_t *argv); +reg_t kScrollWindowHome(EngineState *s, int argc, reg_t *argv); +reg_t kScrollWindowEnd(EngineState *s, int argc, reg_t *argv); reg_t kScrollWindowWhere(EngineState *s, int argc, reg_t *argv); +reg_t kScrollWindowGo(EngineState *s, int argc, reg_t *argv); +reg_t kScrollWindowModify(EngineState *s, int argc, reg_t *argv); +reg_t kScrollWindowHide(EngineState *s, int argc, reg_t *argv); reg_t kScrollWindowShow(EngineState *s, int argc, reg_t *argv); reg_t kScrollWindowDestroy(EngineState *s, int argc, reg_t *argv); diff --git a/engines/sci/engine/kernel_tables.h b/engines/sci/engine/kernel_tables.h index 7b5deda2bb..82184c27d4 100644 --- a/engines/sci/engine/kernel_tables.h +++ b/engines/sci/engine/kernel_tables.h @@ -468,25 +468,30 @@ static const SciKernelMapSubEntry kString_subops[] = { // version, subId, function-mapping, signature, workarounds static const SciKernelMapSubEntry kScrollWindow_subops[] = { { SIG_SCI32, 0, MAP_CALL(ScrollWindowCreate), "oi", NULL }, - { SIG_SCI32, 1, MAP_CALL(ScrollWindowAdd), "o.ii.(.)", NULL }, - { SIG_SCI32, 2, MAP_DUMMY(ScrollWindowClear), "o", NULL }, - { SIG_SCI32, 3, MAP_DUMMY(ScrollWindowPageUp), "o", NULL }, - { SIG_SCI32, 4, MAP_DUMMY(ScrollWindowPageDown), "o", NULL }, - { SIG_SCI32, 5, MAP_DUMMY(ScrollWindowUpArrow), "o", NULL }, - { SIG_SCI32, 6, MAP_DUMMY(ScrollWindowDownArrow), "o", NULL }, - { SIG_SCI32, 7, MAP_DUMMY(ScrollWindowHome), "o", NULL }, - { SIG_SCI32, 8, MAP_DUMMY(ScrollWindowEnd), "o", NULL }, - { SIG_SCI32, 9, MAP_DUMMY(ScrollWindowResize), "o.", NULL }, - { SIG_SCI32, 10, MAP_CALL(ScrollWindowWhere), "oi", NULL }, - { SIG_SCI32, 11, MAP_DUMMY(ScrollWindowGo), "o..", NULL }, - { SIG_SCI32, 12, MAP_DUMMY(ScrollWindowInsert), "o.....", NULL }, - { SIG_SCI32, 13, MAP_DUMMY(ScrollWindowDelete), "o.", NULL }, - { SIG_SCI32, 14, MAP_DUMMY(ScrollWindowModify), "o.....(.)", NULL }, - { SIG_SCI32, 15, MAP_DUMMY(ScrollWindowHide), "o", NULL }, - { SIG_SCI32, 16, MAP_CALL(ScrollWindowShow), "o", NULL }, - { SIG_SCI32, 17, MAP_CALL(ScrollWindowDestroy), "o", NULL }, - { SIG_SCI32, 18, MAP_DUMMY(ScrollWindowText), "o", NULL }, - { SIG_SCI32, 19, MAP_DUMMY(ScrollWindowReconstruct), "o.", NULL }, + { SIG_SCI32, 1, MAP_CALL(ScrollWindowAdd), "iriii(i)", NULL }, + { SIG_SCI32, 2, MAP_DUMMY(ScrollWindowClear), "i", NULL }, + { SIG_SCI32, 3, MAP_CALL(ScrollWindowPageUp), "i", NULL }, + { SIG_SCI32, 4, MAP_CALL(ScrollWindowPageDown), "i", NULL }, + { SIG_SCI32, 5, MAP_CALL(ScrollWindowUpArrow), "i", NULL }, + { SIG_SCI32, 6, MAP_CALL(ScrollWindowDownArrow), "i", NULL }, + { SIG_SCI32, 7, MAP_CALL(ScrollWindowHome), "i", NULL }, + { SIG_SCI32, 8, MAP_CALL(ScrollWindowEnd), "i", NULL }, + { SIG_SCI32, 9, MAP_DUMMY(ScrollWindowResize), "i.", NULL }, + { SIG_SCI32, 10, MAP_CALL(ScrollWindowWhere), "ii", NULL }, + { SIG_SCI32, 11, MAP_CALL(ScrollWindowGo), "i..", NULL }, + { SIG_SCI32, 12, MAP_DUMMY(ScrollWindowInsert), "i.....", NULL }, + { SIG_SCI32, 13, MAP_DUMMY(ScrollWindowDelete), "i.", NULL }, + { SIG_SCI32, 14, MAP_CALL(ScrollWindowModify), "iiriii(i)", NULL }, + { SIG_SCI32, 15, MAP_CALL(ScrollWindowHide), "i", NULL }, + { SIG_SCI32, 16, MAP_CALL(ScrollWindowShow), "i", NULL }, + { SIG_SCI32, 17, MAP_CALL(ScrollWindowDestroy), "i", NULL }, + // LSL6hires uses kScrollWindowText and kScrollWindowReconstruct to try to save + // and restore the content of the game's subtitle window, but this feature did not + // use the normal save/load functionality of the engine and was actually broken + // (all text formatting was missing on restore). Since there is no real reason to + // save the subtitle scrollback anyway, we just ignore calls to these two functions. + { SIG_SCI32, 18, MAP_EMPTY(ScrollWindowText), "i", NULL }, + { SIG_SCI32, 19, MAP_EMPTY(ScrollWindowReconstruct), "i.", NULL }, SCI_SUBOPENTRY_TERMINATOR }; diff --git a/engines/sci/engine/kgraphics32.cpp b/engines/sci/engine/kgraphics32.cpp index 647f9dfe4c..f8ec96c843 100644 --- a/engines/sci/engine/kgraphics32.cpp +++ b/engines/sci/engine/kgraphics32.cpp @@ -360,140 +360,138 @@ reg_t kScrollWindow(EngineState *s, int argc, reg_t *argv) { } reg_t kScrollWindowCreate(EngineState *s, int argc, reg_t *argv) { - debug("kScrollWindowCreate"); - kStub(s, argc, argv); - return argv[0]; + const reg_t object = argv[0]; + const uint16 maxNumEntries = argv[1].toUint16(); + + SegManager *segMan = s->_segMan; + const int16 borderColor = readSelectorValue(segMan, object, SELECTOR(borderColor)); + const TextAlign alignment = (TextAlign)readSelectorValue(segMan, object, SELECTOR(mode)); + const GuiResourceId fontId = (GuiResourceId)readSelectorValue(segMan, object, SELECTOR(font)); + const int16 backColor = readSelectorValue(segMan, object, SELECTOR(back)); + const int16 foreColor = readSelectorValue(segMan, object, SELECTOR(fore)); + const reg_t plane = readSelector(segMan, object, SELECTOR(plane)); + + Common::Rect rect; + rect.left = readSelectorValue(segMan, object, SELECTOR(nsLeft)); + rect.top = readSelectorValue(segMan, object, SELECTOR(nsTop)); + rect.right = readSelectorValue(segMan, object, SELECTOR(nsRight)) + 1; + rect.bottom = readSelectorValue(segMan, object, SELECTOR(nsBottom)) + 1; + const Common::Point position(rect.left, rect.top); + + return g_sci->_gfxControls32->makeScrollWindow(rect, position, plane, foreColor, backColor, fontId, alignment, borderColor, maxNumEntries); } reg_t kScrollWindowAdd(EngineState *s, int argc, reg_t *argv) { - debug("kScrollWindowAdd"); - return kStubNull(s, argc, argv); + ScrollWindow *scrollWindow = g_sci->_gfxControls32->getScrollWindow(argv[0]); + + const Common::String text = s->_segMan->getString(argv[1]); + const GuiResourceId fontId = argv[2].toSint16(); + const int16 color = argv[3].toSint16(); + const TextAlign alignment = (TextAlign)argv[4].toSint16(); + const bool scrollTo = argc > 5 ? (bool)argv[5].toUint16() : true; + + return scrollWindow->add(text, fontId, color, alignment, scrollTo); } reg_t kScrollWindowWhere(EngineState *s, int argc, reg_t *argv) { - debug("kScrollWindowWhere"); - return kStubNull(s, argc, argv); + ScrollWindow *scrollWindow = g_sci->_gfxControls32->getScrollWindow(argv[0]); + + const uint16 where = (argv[1].toUint16() * scrollWindow->where()).toInt(); + + return make_reg(0, where); +} + +reg_t kScrollWindowGo(EngineState *s, int argc, reg_t *argv) { + ScrollWindow *scrollWindow = g_sci->_gfxControls32->getScrollWindow(argv[0]); + + const Ratio scrollTop(argv[1].toSint16(), argv[2].toSint16()); + scrollWindow->go(scrollTop); + + return s->r_acc; +} + +reg_t kScrollWindowModify(EngineState *s, int argc, reg_t *argv) { + ScrollWindow *scrollWindow = g_sci->_gfxControls32->getScrollWindow(argv[0]); + + const reg_t entryId = argv[1]; + const Common::String newText = s->_segMan->getString(argv[2]); + const GuiResourceId fontId = argv[3].toSint16(); + const int16 color = argv[4].toSint16(); + const TextAlign alignment = (TextAlign)argv[5].toSint16(); + const bool scrollTo = argc > 6 ? (bool)argv[6].toUint16() : true; + + return scrollWindow->modify(entryId, newText, fontId, color, alignment, scrollTo); +} + +reg_t kScrollWindowHide(EngineState *s, int argc, reg_t *argv) { + ScrollWindow *scrollWindow = g_sci->_gfxControls32->getScrollWindow(argv[0]); + + scrollWindow->hide(); + + return s->r_acc; } reg_t kScrollWindowShow(EngineState *s, int argc, reg_t *argv) { - debug("kScrollWindowShow"); - return kStubNull(s, argc, argv); + ScrollWindow *scrollWindow = g_sci->_gfxControls32->getScrollWindow(argv[0]); + + scrollWindow->show(); + + return s->r_acc; } -reg_t kScrollWindowDestroy(EngineState *s, int argc, reg_t *argv) { - debug("kScrollWindowDestroy"); - return kStubNull(s, argc, argv); +reg_t kScrollWindowPageUp(EngineState *s, int argc, reg_t *argv) { + ScrollWindow *scrollWindow = g_sci->_gfxControls32->getScrollWindow(argv[0]); + + scrollWindow->pageUp(); + + return s->r_acc; } -#if 0 -reg_t kScrollWindow(EngineState *s, int argc, reg_t *argv) { - // Used by SQ6 and LSL6 hires for the text area in the bottom of the - // screen. The relevant scripts also exist in Phantasmagoria 1, but they're - // unused. This is always called by scripts 64906 (ScrollerWindow) and - // 64907 (ScrollableWindow). - - reg_t kWindow = argv[1]; - uint16 op = argv[0].toUint16(); - switch (op) { - case 0: // Init - // TODO: Init reads the nsLeft, nsTop, nsRight, nsBottom, - // borderColor, fore, back, mode, font, plane selectors - // from the window in argv[1]. - g_sci->_gfxFrameout->initScrollText(argv[2].toUint16()); // maxItems - g_sci->_gfxFrameout->clearScrollTexts(); - return argv[1]; // kWindow - case 1: // Show message, called by ScrollableWindow::addString - case 14: // Modify message, called by ScrollableWindow::modifyString - // TODO: The parameters in Modify are shifted by one: the first - // argument is the handle of the text to modify. The others - // are as Add. - { - Common::String text = s->_segMan->getString(argv[2]); - uint16 x = 0; - uint16 y = 0; - // TODO: argv[3] is font - // TODO: argv[4] is color - // TODO: argv[5] is alignment (0 = left, 1 = center, 2 = right) - // font,color,alignment may also be -1. (Maybe same as previous?) - // TODO: argv[6] is an optional bool, defaulting to true if not present. - // If true, the old contents are scrolled out of view. - // TODO: Return a handle of the inserted text. (Used for modify/insert) - // This handle looks like it should also be usable by kString. - g_sci->_gfxFrameout->addScrollTextEntry(text, kWindow, x, y, (op == 14)); - } - break; - case 2: // Clear, called by ScrollableWindow::erase - g_sci->_gfxFrameout->clearScrollTexts(); - break; - case 3: // Page up, called by ScrollableWindow::scrollTo - // TODO - kStub(s, argc, argv); - break; - case 4: // Page down, called by ScrollableWindow::scrollTo - // TODO - kStub(s, argc, argv); - break; - case 5: // Up arrow, called by ScrollableWindow::scrollTo - g_sci->_gfxFrameout->prevScrollText(); - break; - case 6: // Down arrow, called by ScrollableWindow::scrollTo - g_sci->_gfxFrameout->nextScrollText(); - break; - case 7: // Home, called by ScrollableWindow::scrollTo - g_sci->_gfxFrameout->firstScrollText(); - break; - case 8: // End, called by ScrollableWindow::scrollTo - g_sci->_gfxFrameout->lastScrollText(); - break; - case 9: // Resize, called by ScrollableWindow::resize and ScrollerWindow::resize - // TODO: This reads the nsLeft, nsTop, nsRight, nsBottom - // selectors from the SCI object passed in argv[2]. - kStub(s, argc, argv); - break; - case 10: // Where, called by ScrollableWindow::where - // TODO: - // Gives the current relative scroll location as a fraction - // with argv[2] as the denominator. (Return value is the numerator.) - // Silenced the warnings because of the high amount of console spam - //kStub(s, argc, argv); - break; - case 11: // Go, called by ScrollableWindow::scrollTo - // TODO: - // Two arguments provide a fraction: argv[2] is num., argv[3] is denom. - // Scrolls to the relative location given by the fraction. - kStub(s, argc, argv); - break; - case 12: // Insert, called by ScrollableWindow::insertString - // 5 extra parameters here: - // handle of insert location (new string takes that position). - // text, font, color, alignment - // TODO - kStub(s, argc, argv); - break; - // case 13 (Delete) is handled below - // case 14 (Modify) is handled above - case 15: // Hide, called by ScrollableWindow::hide - g_sci->_gfxFrameout->toggleScrollText(false); - break; - case 16: // Show, called by ScrollableWindow::show - g_sci->_gfxFrameout->toggleScrollText(true); - break; - case 17: // Destroy, called by ScrollableWindow::dispose - g_sci->_gfxFrameout->clearScrollTexts(); - break; - case 13: // Delete, unused - case 18: // Text, unused - case 19: // Reconstruct, unused - error("kScrollWindow: Unused subop %d invoked", op); - break; - default: - error("kScrollWindow: unknown subop %d", op); - break; - } +reg_t kScrollWindowPageDown(EngineState *s, int argc, reg_t *argv) { + ScrollWindow *scrollWindow = g_sci->_gfxControls32->getScrollWindow(argv[0]); + + scrollWindow->pageDown(); + + return s->r_acc; +} + +reg_t kScrollWindowUpArrow(EngineState *s, int argc, reg_t *argv) { + ScrollWindow *scrollWindow = g_sci->_gfxControls32->getScrollWindow(argv[0]); + + scrollWindow->upArrow(); + + return s->r_acc; +} + +reg_t kScrollWindowDownArrow(EngineState *s, int argc, reg_t *argv) { + ScrollWindow *scrollWindow = g_sci->_gfxControls32->getScrollWindow(argv[0]); + + scrollWindow->downArrow(); + + return s->r_acc; +} + +reg_t kScrollWindowHome(EngineState *s, int argc, reg_t *argv) { + ScrollWindow *scrollWindow = g_sci->_gfxControls32->getScrollWindow(argv[0]); + + scrollWindow->home(); + + return s->r_acc; +} + +reg_t kScrollWindowEnd(EngineState *s, int argc, reg_t *argv) { + ScrollWindow *scrollWindow = g_sci->_gfxControls32->getScrollWindow(argv[0]); + + scrollWindow->end(); + + return s->r_acc; +} + +reg_t kScrollWindowDestroy(EngineState *s, int argc, reg_t *argv) { + g_sci->_gfxControls32->destroyScrollWindow(argv[0]); return s->r_acc; } -#endif reg_t kFont(EngineState *s, int argc, reg_t *argv) { if (!s) diff --git a/engines/sci/graphics/controls32.cpp b/engines/sci/graphics/controls32.cpp index faf1d7d1a2..04014154ae 100644 --- a/engines/sci/graphics/controls32.cpp +++ b/engines/sci/graphics/controls32.cpp @@ -41,7 +41,31 @@ GfxControls32::GfxControls32(SegManager *segMan, GfxCache *cache, GfxText32 *tex _gfxCache(cache), _gfxText32(text), _overwriteMode(false), - _nextCursorFlashTick(0) {} + _nextCursorFlashTick(0), + // SSCI used a memory handle for a ScrollWindow object + // as ID. We use a simple numeric handle instead. + _nextScrollWindowId(10000) {} + +GfxControls32::~GfxControls32() { + ScrollWindowMap::iterator it; + for (it = _scrollWindows.begin(); it != _scrollWindows.end(); ++it) + delete it->_value; +} + +#pragma mark - +#pragma mark Garbage collection + +Common::Array<reg_t> GfxControls32::listObjectReferences() { + Common::Array<reg_t> ret; + ScrollWindowMap::const_iterator it; + for (it = _scrollWindows.begin(); it != _scrollWindows.end(); ++it) + ret.push_back(it->_value->getBitmap()); + + return ret; +} + +#pragma mark - +#pragma mark Text input control reg_t GfxControls32::kernelEditText(const reg_t controlObject) { SegManager *segMan = _segMan; @@ -350,4 +374,437 @@ void GfxControls32::flashCursor(TextEditor &editor) { _nextCursorFlashTick = g_sci->getTickCount() + 30; } } + +#pragma mark - +#pragma mark Scrollable window control + +ScrollWindow::ScrollWindow(SegManager *segMan, const Common::Rect &gameRect, const Common::Point &position, const reg_t plane, const uint8 defaultForeColor, const uint8 defaultBackColor, const GuiResourceId defaultFontId, const TextAlign defaultAlignment, const int16 defaultBorderColor, const uint16 maxNumEntries) : + _gfxText32(segMan, g_sci->_gfxCache), + _maxNumEntries(maxNumEntries), + _firstVisibleChar(0), + _topVisibleLine(0), + _lastVisibleChar(0), + _bottomVisibleLine(0), + _numLines(0), + _numVisibleLines(0), + _plane(plane), + _foreColor(defaultForeColor), + _backColor(defaultBackColor), + _borderColor(defaultBorderColor), + _fontId(defaultFontId), + _alignment(defaultAlignment), + _visible(false), + _position(position), + _screenItem(nullptr), + _nextEntryId(1) { + + _entries.reserve(maxNumEntries); + + _gfxText32.setFont(_fontId); + _pointSize = _gfxText32._font->getHeight(); + + const uint16 scriptWidth = g_sci->_gfxFrameout->getCurrentBuffer().scriptWidth; + const uint16 scriptHeight = g_sci->_gfxFrameout->getCurrentBuffer().scriptHeight; + + Common::Rect bitmapRect(gameRect); + mulinc(bitmapRect, Ratio(_gfxText32._scaledWidth, scriptWidth), Ratio(_gfxText32._scaledHeight, scriptHeight)); + + _textRect.left = 2; + _textRect.top = 2; + _textRect.right = bitmapRect.width() - 2; + _textRect.bottom = bitmapRect.height() - 2; + + uint8 skipColor = 0; + while (skipColor == _foreColor || skipColor == _backColor) { + skipColor++; + } + + assert(bitmapRect.width() > 0 && bitmapRect.height() > 0); + _bitmap = _gfxText32.createFontBitmap(bitmapRect.width(), bitmapRect.height(), _textRect, "", _foreColor, _backColor, skipColor, _fontId, _alignment, _borderColor, false, false); + + debugC(1, kDebugLevelGraphics, "New ScrollWindow: textRect size: %d x %d, bitmap: %04x:%04x", _textRect.width(), _textRect.height(), PRINT_REG(_bitmap)); +} + +ScrollWindow::~ScrollWindow() { + // _gfxText32._bitmap will get GCed once ScrollWindow is gone. + // _screenItem will be deleted by GfxFrameout +} + +Ratio ScrollWindow::where() const { + return Ratio(_topVisibleLine, MAX(_numLines, 1)); +} + +void ScrollWindow::show() { + if (_visible) { + return; + } + + if (_screenItem == nullptr) { + CelInfo32 celInfo; + celInfo.type = kCelTypeMem; + celInfo.bitmap = _bitmap; + + _screenItem = new ScreenItem(_plane, celInfo, _position, ScaleInfo()); + } + + Plane *plane = g_sci->_gfxFrameout->getPlanes().findByObject(_plane); + plane->_screenItemList.add(_screenItem); + + _visible = true; +} + +void ScrollWindow::hide() { + if (!_visible) { + return; + } + + g_sci->_gfxFrameout->deleteScreenItem(_screenItem, _plane); + _screenItem = nullptr; + g_sci->_gfxFrameout->frameOut(true); + + _visible = false; +} + +reg_t ScrollWindow::add(const Common::String &text, const GuiResourceId fontId, const int16 foreColor, const TextAlign alignment, const bool scrollTo) { + if (_entries.size() == _maxNumEntries) { + ScrollWindowEntry removedEntry = _entries.remove_at(0); + _text.erase(0, removedEntry.text.size()); + // `_firstVisibleChar` will be reset shortly if + // `scrollTo` is true, so there is no reason to + // update it + if (!scrollTo) { + _firstVisibleChar -= removedEntry.text.size(); + } + } + + _entries.push_back(ScrollWindowEntry()); + ScrollWindowEntry &entry = _entries.back(); + + // NOTE: In SSCI the line ID was a memory handle for the + // string of this line. We use a numeric ID instead. + entry.id = make_reg(0, _nextEntryId++); + + if (_nextEntryId > _maxNumEntries) { + _nextEntryId = 1; + } + + // NOTE: In SSCI this was updated after _text was + // updated, which meant there was an extra unnecessary + // subtraction operation (subtracting `entry.text` size) + if (scrollTo) { + _firstVisibleChar = _text.size(); + } + + fillEntry(entry, text, fontId, foreColor, alignment); + _text += entry.text; + + computeLineIndices(); + update(true); + + return entry.id; +} + +void ScrollWindow::fillEntry(ScrollWindowEntry &entry, const Common::String &text, const GuiResourceId fontId, const int16 foreColor, const TextAlign alignment) { + entry.alignment = alignment; + entry.foreColor = foreColor; + entry.fontId = fontId; + + Common::String formattedText; + + // NB: There are inconsistencies here. + // If there is a multi-line entry with non-default properties, and it + // is only partially displayed, it may not be displayed right, since the + // property directives are only added to the first line. + // (Verified by trying this in SSCI SQ6 with a custom ScrollWindowAdd call.) + // + // The converse is also a potential issue (but unverified), where lines + // with properties -1 can inherit properties from the previously rendered + // line instead of the defaults. + + // NOTE: SSCI added "|s<lineIndex>|" here, but |s| is + // not a valid control code, so it just always ended up + // getting skipped + if (entry.fontId != -1) { + formattedText += Common::String::format("|f%d|", entry.fontId); + } + if (entry.foreColor != -1) { + formattedText += Common::String::format("|c%d|", entry.foreColor); + } + if (entry.alignment != -1) { + formattedText += Common::String::format("|a%d|", entry.alignment); + } + formattedText += text; + entry.text = formattedText; +} + +reg_t ScrollWindow::modify(const reg_t id, const Common::String &text, const GuiResourceId fontId, const int16 foreColor, const TextAlign alignment, const bool scrollTo) { + + EntriesList::iterator it = _entries.begin(); + uint firstCharLocation = 0; + for ( ; it != _entries.end(); ++it) { + if (it->id == id) { + break; + } + firstCharLocation += it->text.size(); + } + + if (it == _entries.end()) { + return make_reg(0, 0); + } + + ScrollWindowEntry &entry = *it; + + uint oldTextLength = entry.text.size(); + + fillEntry(entry, text, fontId, foreColor, alignment); + _text.replace(firstCharLocation, oldTextLength, entry.text); + + if (scrollTo) { + _firstVisibleChar = firstCharLocation; + } + + computeLineIndices(); + update(true); + + return entry.id; +} + +void ScrollWindow::upArrow() { + if (_topVisibleLine == 0) { + return; + } + + _topVisibleLine--; + _bottomVisibleLine--; + + if (_bottomVisibleLine - _topVisibleLine + 1 < _numVisibleLines) { + _bottomVisibleLine = _numLines - 1; + } + + _firstVisibleChar = _startsOfLines[_topVisibleLine]; + _lastVisibleChar = _startsOfLines[_bottomVisibleLine + 1] - 1; + + _visibleText = Common::String(_text.c_str() + _firstVisibleChar, _text.c_str() + _lastVisibleChar + 1); + + Common::String lineText(_text.c_str() + _startsOfLines[_topVisibleLine], _text.c_str() + _startsOfLines[_topVisibleLine + 1] - 1); + + debugC(3, kDebugLevelGraphics, "ScrollWindow::upArrow: top: %d, bottom: %d, num: %d, numvis: %d, lineText: %s", _topVisibleLine, _bottomVisibleLine, _numLines, _numVisibleLines, lineText.c_str()); + + _gfxText32.scrollLine(lineText, _numVisibleLines, _foreColor, _alignment, _fontId, kScrollUp); + + if (_visible) { + assert(_screenItem); + + _screenItem->update(); + g_sci->_gfxFrameout->frameOut(true); + } +} + +void ScrollWindow::downArrow() { + if (_topVisibleLine + 1 >= _numLines) { + return; + } + + _topVisibleLine++; + _bottomVisibleLine++; + + if (_bottomVisibleLine + 1 >= _numLines) { + _bottomVisibleLine = _numLines - 1; + } + + _firstVisibleChar = _startsOfLines[_topVisibleLine]; + _lastVisibleChar = _startsOfLines[_bottomVisibleLine + 1] - 1; + + _visibleText = Common::String(_text.c_str() + _firstVisibleChar, _text.c_str() + _lastVisibleChar + 1); + + Common::String lineText; + if (_bottomVisibleLine - _topVisibleLine + 1 == _numVisibleLines) { + lineText = Common::String(_text.c_str() + _startsOfLines[_bottomVisibleLine], _text.c_str() + _startsOfLines[_bottomVisibleLine + 1] - 1); + } else { + // scroll in empty string + } + + debugC(3, kDebugLevelGraphics, "ScrollWindow::downArrow: top: %d, bottom: %d, num: %d, numvis: %d, lineText: %s", _topVisibleLine, _bottomVisibleLine, _numLines, _numVisibleLines, lineText.c_str()); + + + _gfxText32.scrollLine(lineText, _numVisibleLines, _foreColor, _alignment, _fontId, kScrollDown); + + if (_visible) { + assert(_screenItem); + + _screenItem->update(); + g_sci->_gfxFrameout->frameOut(true); + } +} + +void ScrollWindow::go(const Ratio location) { + const int line = (location * _numLines).toInt(); + if (line < 0 || line > _numLines) { + error("Index is Out of Range in ScrollWindow"); + } + + _firstVisibleChar = _startsOfLines[line]; + update(true); + + // HACK: + // It usually isn't possible to set _topVisibleLine >= _numLines, and so + // update() doesn't. However, in this case we should set _topVisibleLine + // past the end. This is clearly visible in Phantasmagoria when dragging + // the slider in the About dialog to the very end. The slider ends up lower + // than where it can be moved by scrolling down with the arrows. + if (location.isOne()) { + _topVisibleLine = _numLines; + } +} + +void ScrollWindow::home() { + if (_firstVisibleChar == 0) { + return; + } + + _firstVisibleChar = 0; + update(true); +} + +void ScrollWindow::end() { + if (_bottomVisibleLine + 1 >= _numLines) { + return; + } + + int line = _numLines - _numVisibleLines; + if (line < 0) { + line = 0; + } + _firstVisibleChar = _startsOfLines[line]; + update(true); +} + +void ScrollWindow::pageUp() { + if (_topVisibleLine == 0) { + return; + } + + _topVisibleLine -= _numVisibleLines; + if (_topVisibleLine < 0) { + _topVisibleLine = 0; + } + + _firstVisibleChar = _startsOfLines[_topVisibleLine]; + update(true); +} + +void ScrollWindow::pageDown() { + if (_topVisibleLine + 1 >= _numLines) { + return; + } + + _topVisibleLine += _numVisibleLines; + if (_topVisibleLine + 1 >= _numLines) { + _topVisibleLine = _numLines - 1; + } + + _firstVisibleChar = _startsOfLines[_topVisibleLine]; + update(true); +} + +void ScrollWindow::computeLineIndices() { + _gfxText32.setFont(_fontId); + // NOTE: Unlike SSCI, foreColor and alignment are not + // set since these properties do not affect the width of + // lines + + if (_gfxText32._font->getHeight() != _pointSize) { + error("Illegal font size font = %d pointSize = %d, should be %d.", _fontId, _gfxText32._font->getHeight(), _pointSize); + } + + Common::Rect lineRect(0, 0, _textRect.width(), _pointSize + 3); + + _startsOfLines.clear(); + + // NOTE: The original engine had a 1000-line limit; we + // do not enforce any limit + for (uint charIndex = 0; charIndex < _text.size(); ) { + _startsOfLines.push_back(charIndex); + charIndex += _gfxText32.getTextCount(_text, charIndex, lineRect, false); + } + + _numLines = _startsOfLines.size(); + + _startsOfLines.push_back(_text.size()); + + _lastVisibleChar = _gfxText32.getTextCount(_text, 0, _fontId, _textRect, false) - 1; + + _bottomVisibleLine = 0; + while ( + _bottomVisibleLine < _numLines - 1 && + _startsOfLines[_bottomVisibleLine + 1] < _lastVisibleChar + ) { + ++_bottomVisibleLine; + } + + _numVisibleLines = _bottomVisibleLine + 1; +} + +void ScrollWindow::update(const bool doFrameOut) { + _topVisibleLine = 0; + while ( + _topVisibleLine < _numLines - 1 && + _firstVisibleChar >= _startsOfLines[_topVisibleLine + 1] + ) { + ++_topVisibleLine; + } + + _bottomVisibleLine = _topVisibleLine + _numVisibleLines - 1; + if (_bottomVisibleLine >= _numLines) { + _bottomVisibleLine = _numLines - 1; + } + + _firstVisibleChar = _startsOfLines[_topVisibleLine]; + + if (_bottomVisibleLine >= 0) { + _lastVisibleChar = _startsOfLines[_bottomVisibleLine + 1] - 1; + } else { + _lastVisibleChar = -1; + } + + _visibleText = Common::String(_text.c_str() + _firstVisibleChar, _text.c_str() + _lastVisibleChar + 1); + + _gfxText32.erase(_textRect, false); + _gfxText32.drawTextBox(_visibleText); + + if (_visible) { + assert(_screenItem); + + _screenItem->update(); + if (doFrameOut) { + g_sci->_gfxFrameout->frameOut(true); + } + } +} + +reg_t GfxControls32::makeScrollWindow(const Common::Rect &gameRect, const Common::Point &position, const reg_t planeObj, const uint8 defaultForeColor, const uint8 defaultBackColor, const GuiResourceId defaultFontId, const TextAlign defaultAlignment, const int16 defaultBorderColor, const uint16 maxNumEntries) { + + ScrollWindow *scrollWindow = new ScrollWindow(_segMan, gameRect, position, planeObj, defaultForeColor, defaultBackColor, defaultFontId, defaultAlignment, defaultBorderColor, maxNumEntries); + + const uint16 id = _nextScrollWindowId++; + _scrollWindows[id] = scrollWindow; + return make_reg(0, id); +} + +ScrollWindow *GfxControls32::getScrollWindow(const reg_t id) { + ScrollWindowMap::iterator it; + it = _scrollWindows.find(id.toUint16()); + if (it == _scrollWindows.end()) + error("Invalid ScrollWindow ID"); + + return it->_value; +} + +void GfxControls32::destroyScrollWindow(const reg_t id) { + ScrollWindow *scrollWindow = getScrollWindow(id); + scrollWindow->hide(); + _scrollWindows.erase(id.getOffset()); + delete scrollWindow; +} + } // End of namespace Sci diff --git a/engines/sci/graphics/controls32.h b/engines/sci/graphics/controls32.h index 1bb7679ddd..62ab90de1f 100644 --- a/engines/sci/graphics/controls32.h +++ b/engines/sci/graphics/controls32.h @@ -23,12 +23,15 @@ #ifndef SCI_GRAPHICS_CONTROLS32_H #define SCI_GRAPHICS_CONTROLS32_H +#include "sci/graphics/text32.h" + namespace Sci { class GfxCache; class GfxScreen; class GfxText32; + struct TextEditor { /** * The bitmap where the editor is rendered. @@ -100,24 +103,388 @@ struct TextEditor { }; /** + * A single block of text written to a ScrollWindow. + */ +struct ScrollWindowEntry { + /** + * ID of the line. In SSCI this was actually a memory + * handle for the string of this line. We use a simple + * numeric ID instead. + */ + reg_t id; + + /** + * The alignment to use when rendering this line of + * text. If -1, the default alignment from the + * corresponding ScrollWindow will be used. + */ + TextAlign alignment; + + /** + * The color to use to render this line of text. If -1, + * the default foreground color from the corresponding + * ScrollWindow will be used. + */ + int16 foreColor; + + /** + * The font to use to render this line of text. If -1, + * the default font from the corresponding ScrollWindow + * will be used. + */ + GuiResourceId fontId; + + /** + * The text. + */ + Common::String text; +}; + +class ScreenItem; + +/** + * A scrollable text window. + */ +class ScrollWindow { +public: + ScrollWindow(SegManager *segMan, const Common::Rect &gameRect, const Common::Point &position, const reg_t planeObj, const uint8 defaultForeColor, const uint8 defaultBackColor, const GuiResourceId defaultFontId, const TextAlign defaultAlignment, const int16 defaultBorderColor, const uint16 maxNumEntries); + ~ScrollWindow(); + + /** + * Adds a new text entry to the window. If `fontId`, + * `foreColor`, or `alignment` are `-1`, the + * ScrollWindow's default values will be used. + */ + reg_t add(const Common::String &text, const GuiResourceId fontId, const int16 foreColor, const TextAlign alignment, const bool scrollTo); + + /** + * Modifies an existing text entry with the given ID. If + * `fontId`, `foreColor`, or `alignment` are `-1`, the + * ScrollWindow's default values will be used. + */ + reg_t modify(const reg_t id, const Common::String &text, const GuiResourceId fontId, const int16 foreColor, const TextAlign alignment, const bool scrollTo); + + /** + * Shows the ScrollWindow if it is not already visible. + */ + void show(); + + /** + * Hides the ScrollWindow if it is currently visible. + */ + void hide(); + + /** + * Gets the number of lines that the content of a + * ScrollWindow is scrolled upward, as a ratio of the + * total number of lines of content. + */ + Ratio where() const; + + /** + * Scrolls the window to a specific location. + */ + void go(const Ratio location); + + /** + * Scrolls the window to the top. + */ + void home(); + + /** + * Scrolls the window to the bottom. + */ + void end(); + + /** + * Scrolls the window up one line. + */ + void upArrow(); + + /** + * Scrolls the window down one line. + */ + void downArrow(); + + /** + * Scrolls the window up by one page. + */ + void pageUp(); + + /** + * Scrolls the window down by one page. + */ + void pageDown(); + + /** + * Gets a reference to the in-memory bitmap that + * is used to render the text in the ScrollWindow. + */ + const reg_t getBitmap() const { return _bitmap; } + +private: + typedef Common::Array<ScrollWindowEntry> EntriesList; + + /** + * A convenience function that fills a + * ScrollWindowEntry's properties. + */ + void fillEntry(ScrollWindowEntry &entry, const Common::String &text, const GuiResourceId fontId, const int16 foreColor, const TextAlign alignment); + + /** + * Rescans the entire text of the ScrollWindow when an + * entry is added or modified, calculating the character + * offsets of all line endings, the total number of + * lines of text, the height of the viewport (in lines + * of text), the last character visible in the viewport + * (assuming the viewport is scrolled to the top), and + * the line index of the bottommost visible line + * (assuming the viewport is scrolled to the top). + */ + void computeLineIndices(); + + /** + * Calculates which text is visible within the + * ScrollWindow's viewport and renders the text to the + * internal bitmap. + * + * If `doFrameOut` is true, the screen will be refreshed + * immediately instead of waiting for the next call to + * `kFrameOut`. + */ + void update(const bool doFrameOut); + + /** + * The text renderer. + */ + GfxText32 _gfxText32; + + /** + * The individual text entries added to the + * ScrollWindow. + */ + EntriesList _entries; + + /** + * The maximum number of entries allowed. Once this + * limit is reached, the oldest entry will be removed + * when a new entry is added. + */ + uint _maxNumEntries; + + /** + * A mapping from a line index to the line's character + * offset in `_text`. + */ + Common::Array<int> _startsOfLines; + + /** + * All text added to the window. + */ + Common::String _text; + + /** + * Text that is within the viewport of the ScrollWindow. + */ + Common::String _visibleText; + + /** + * The offset of the first visible character in `_text`. + */ + int _firstVisibleChar; + + /** + * The index of the line that is at the top of the + * viewport. + */ + int _topVisibleLine; + + /** + * The index of the last visible character in `_text`, + * or -1 if there is no text. + */ + int _lastVisibleChar; + + /** + * The index of the line that is at the bottom of the + * viewport, or -1 if there is no text. + */ + int _bottomVisibleLine; + + /** + * The total number of lines in the backbuffer. This + * number may be higher than the total number of entries + * if an entry contains newlines. + */ + int _numLines; + + /** + * The number of lines that are currently visible in the + * text area of the window. + */ + int _numVisibleLines; + + /** + * The plane in which the ScrollWindow should be + * rendered. + */ + reg_t _plane; + + /** + * The default text color. + */ + uint8 _foreColor; + + /** + * The default background color of the text bitmap. + */ + uint8 _backColor; + + /** + * The default border color of the text bitmap. If -1, + * the viewport will have no border. + */ + int16 _borderColor; + + /** + * The default font used for rendering text into the + * ScrollWindow. + */ + GuiResourceId _fontId; + + /** + * The default text alignment used for rendering text + * into the ScrollWindow. + */ + TextAlign _alignment; + + /** + * The visibility of the ScrollWindow. + */ + bool _visible; + + /** + * The dimensions of the text box inside the font + * bitmap, in text-system coordinates. + */ + Common::Rect _textRect; + + /** + * The top-left corner of the ScrollWindow's screen + * item, in game script coordinates, relative to the + * parent plane. + */ + Common::Point _position; + + /** + * The height of the default font in screen pixels. All + * fonts rendered into the ScrollWindow must have this + * same height. + */ + uint8 _pointSize; + + /** + * The bitmap used to render text. + */ + reg_t _bitmap; + + /** + * A monotonically increasing ID used to identify + * text entries added to the ScrollWindow. + */ + uint16 _nextEntryId; + + /** + * The ScrollWindow's screen item. + */ + ScreenItem *_screenItem; +}; + +/** * Controls class, handles drawing of controls in SCI32 (SCI2, SCI2.1, SCI3) games */ class GfxControls32 { public: GfxControls32(SegManager *segMan, GfxCache *cache, GfxText32 *text); - - reg_t kernelEditText(const reg_t controlObject); + ~GfxControls32(); private: SegManager *_segMan; GfxCache *_gfxCache; GfxText32 *_gfxText32; +#pragma mark - +#pragma mark Garbage collection +public: + Common::Array<reg_t> listObjectReferences(); + +#pragma mark - +#pragma mark Text input control +public: + reg_t kernelEditText(const reg_t controlObject); + +private: + /** + * If true, typing will overwrite text that already + * exists at the text cursor's current position. + */ bool _overwriteMode; + + /** + * The tick at which the text cursor should be toggled + * by `flashCursor`. + */ uint32 _nextCursorFlashTick; + + /** + * Draws the text cursor for the given editor. + */ void drawCursor(TextEditor &editor); + + /** + * Erases the text cursor for the given editor. + */ void eraseCursor(TextEditor &editor); + + /** + * Toggles the text cursor for the given editor to be + * either drawn or erased. + */ void flashCursor(TextEditor &editor); + +#pragma mark - +#pragma mark Scrollable window control +public: + /** + * Creates a new scrollable window and returns the ID + * for the new window, which is used by game scripts to + * interact with scrollable windows. + */ + reg_t makeScrollWindow(const Common::Rect &gameRect, const Common::Point &position, const reg_t plane, const uint8 defaultForeColor, const uint8 defaultBackColor, const GuiResourceId defaultFontId, const TextAlign defaultAlignment, const int16 defaultBorderColor, const uint16 maxNumEntries); + + /** + * Gets a registered ScrollWindow instance by ID. + */ + ScrollWindow *getScrollWindow(const reg_t id); + + /** + * Destroys the scroll window with the given ID. + */ + void destroyScrollWindow(const reg_t id); + +private: + typedef Common::HashMap<uint16, ScrollWindow *> ScrollWindowMap; + + /** + * Monotonically increasing ID used to identify + * ScrollWindow instances. + */ + uint16 _nextScrollWindowId; + + /** + * A lookup table for registered ScrollWindow instances. + */ + ScrollWindowMap _scrollWindows; }; } // End of namespace Sci diff --git a/engines/sci/graphics/screen_item32.cpp b/engines/sci/graphics/screen_item32.cpp index a3728d9ce2..c0b3240c7e 100644 --- a/engines/sci/graphics/screen_item32.cpp +++ b/engines/sci/graphics/screen_item32.cpp @@ -516,15 +516,22 @@ void ScreenItem::update(const reg_t object) { } void ScreenItem::update() { - // TODO: error out if we're not contained in our plane's ScreenItemList + Plane *plane = g_sci->_gfxFrameout->getPlanes().findByObject(_plane); + if (plane == nullptr) { + error("ScreenItem::update: Invalid plane %04x:%04x", PRINT_REG(_plane)); + } + + if (plane->_screenItemList.findByObject(_object) == nullptr) { + error("ScreenItem::update: %04x:%04x not in plane %04x:%04x", PRINT_REG(_object), PRINT_REG(_plane)); + } - if (!_created) { - _updated = g_sci->_gfxFrameout->getScreenCount(); - } - _deleted = 0; + if (!_created) { + _updated = g_sci->_gfxFrameout->getScreenCount(); + } + _deleted = 0; - delete _celObj; - _celObj = nullptr; + delete _celObj; + _celObj = nullptr; } // TODO: This code is quite similar to calcRects, so try to deduplicate diff --git a/engines/sci/graphics/text32.cpp b/engines/sci/graphics/text32.cpp index f38a95de5b..d3b5bf5396 100644 --- a/engines/sci/graphics/text32.cpp +++ b/engines/sci/graphics/text32.cpp @@ -237,8 +237,10 @@ void GfxText32::drawTextBox() { int16 textRectWidth = _textRect.width(); _drawPosition.y = _textRect.top; uint charIndex = 0; - if (getLongest(&charIndex, textRectWidth) == 0) { - error("DrawTextBox GetLongest=0"); + if (g_sci->getGameId() != GID_PHANTASMAGORIA) { + if (getLongest(&charIndex, textRectWidth) == 0) { + error("DrawTextBox GetLongest=0"); + } } charIndex = 0; @@ -651,5 +653,69 @@ int16 GfxText32::getTextCount(const Common::String &text, const uint index, cons return getTextCount(text, index, textRect, doScaling); } +void GfxText32::scrollLine(const Common::String &lineText, int numLines, uint8 color, TextAlign align, GuiResourceId fontId, ScrollDirection dir) { + BitmapResource bmr(_bitmap); + byte *pixels = bmr.getPixels(); + + int h = _font->getHeight(); + + if (dir == kScrollUp) { + // Scroll existing text down + for (int i = 0; i < (numLines - 1) * h; ++i) { + int y = _textRect.top + numLines * h - i - 1; + memcpy(pixels + y * _width + _textRect.left, + pixels + (y - h) * _width + _textRect.left, + _textRect.width()); + } + } else { + // Scroll existing text up + for (int i = 0; i < (numLines - 1) * h; ++i) { + int y = _textRect.top + i; + memcpy(pixels + y * _width + _textRect.left, + pixels + (y + h) * _width + _textRect.left, + _textRect.width()); + } + } + + Common::Rect lineRect = _textRect; + + if (dir == kScrollUp) { + lineRect.bottom = lineRect.top + h; + } else { + // It is unclear to me what the purpose of this bottom++ is. + // It does not seem to be the usual inc/exc issue. + lineRect.top += (numLines - 1) * h; + lineRect.bottom++; + } + + erase(lineRect, false); + + _drawPosition.x = _textRect.left; + _drawPosition.y = _textRect.top; + if (dir == kScrollDown) { + _drawPosition.y += (numLines - 1) * h; + } + + _foreColor = color; + _alignment = align; + //int fc = _foreColor; + + setFont(fontId); + + _text = lineText; + int16 textWidth = getTextWidth(0, lineText.size()); + + if (_alignment == kTextAlignCenter) { + _drawPosition.x += (_textRect.width() - textWidth) / 2; + } else if (_alignment == kTextAlignRight) { + _drawPosition.x += _textRect.width() - textWidth; + } + + //_foreColor = fc; + //setFont(fontId); + + drawText(0, lineText.size()); +} + } // End of namespace Sci diff --git a/engines/sci/graphics/text32.h b/engines/sci/graphics/text32.h index c28629871f..88cf149da7 100644 --- a/engines/sci/graphics/text32.h +++ b/engines/sci/graphics/text32.h @@ -30,9 +30,15 @@ namespace Sci { enum TextAlign { - kTextAlignLeft = 0, - kTextAlignCenter = 1, - kTextAlignRight = 2 + kTextAlignDefault = -1, + kTextAlignLeft = 0, + kTextAlignCenter = 1, + kTextAlignRight = 2 +}; + +enum ScrollDirection { + kScrollUp, + kScrollDown }; enum BitmapFlags { @@ -457,6 +463,13 @@ public: * `textRect` using the given font. */ int16 getTextCount(const Common::String &text, const uint index, const GuiResourceId fontId, const Common::Rect &textRect, const bool doScaling); + + /** + * Scroll up/down one line. `numLines` is the number of the lines in the + * textarea, and `textLine` contains the text to draw as the newly + * visible line. Originally FontMgr::DrawOneLine and FontMgr::UpOneLine. + */ + void scrollLine(const Common::String &textLine, int numLines, uint8 color, TextAlign align, GuiResourceId fontId, ScrollDirection dir); }; } // End of namespace Sci diff --git a/engines/sci/sci.cpp b/engines/sci/sci.cpp index b74dc46e87..c0f8f215ad 100644 --- a/engines/sci/sci.cpp +++ b/engines/sci/sci.cpp @@ -49,7 +49,6 @@ #include "sci/graphics/cache.h" #include "sci/graphics/compare.h" #include "sci/graphics/controls16.h" -#include "sci/graphics/controls32.h" #include "sci/graphics/coordadjuster.h" #include "sci/graphics/cursor.h" #include "sci/graphics/maciconbar.h" @@ -65,6 +64,7 @@ #include "sci/graphics/transitions.h" #ifdef ENABLE_SCI32 +#include "sci/graphics/controls32.h" #include "sci/graphics/palette32.h" #include "sci/graphics/text32.h" #include "sci/graphics/frameout.h" |