/* ScummVM - Graphic Adventure Engine * * ScummVM is the legal property of its developers, whose names * are too numerous to list here. Please refer to the COPYRIGHT * file distributed with this source distribution. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * */ #include "common/system.h" #include "graphics/palette.h" #include "graphics/surface.h" #include "xeen/screen.h" #include "xeen/resources.h" #include "xeen/xeen.h" namespace Xeen { Window::Window() : _vm(nullptr), _enabled(false), _a(0), _border(0), _xLo(0), _xHi(0), _ycL(0), _ycH(0) { } Window::Window(XeenEngine *vm, const Common::Rect &bounds, int a, int border, int xLo, int ycL, int xHi, int ycH): _vm(vm), _enabled(false), _a(a), _border(border), _xLo(xLo), _ycL(ycL), _xHi(xHi), _ycH(ycH) { setBounds(bounds); create(_vm->_screen, Common::Rect(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT)); } void Window::setBounds(const Common::Rect &r) { _bounds = r; _innerBounds = r; _innerBounds.grow(-_border); } void Window::open() { if (!_enabled) { _enabled = true; _vm->_screen->_windowStack.push_back(this); open2(); } if (_vm->_mode == MODE_9) { warning("TODO: copyFileToMemory"); } } void Window::open2() { addDirtyRect(_bounds); frame(); fill(); } void Window::frame() { Screen &screen = *_vm->_screen; int xCount = (_bounds.width() - 9) / FONT_WIDTH; int yCount = (_bounds.height() - 9) / FONT_HEIGHT; // Write the top line screen._writePos = Common::Point(_bounds.left, _bounds.top); screen.writeSymbol(0); if (xCount > 0) { int symbolId = 1; for (int i = 0; i < xCount; ++i) { screen.writeSymbol(symbolId); if (++symbolId == 5) symbolId = 1; } } screen._writePos.x = _bounds.right - FONT_WIDTH; screen.writeSymbol(5); // Write the vertical edges if (yCount > 0) { int symbolId = 6; for (int i = 0; i < yCount; ++i) { screen._writePos.y += 8; screen._writePos.x = _bounds.left; screen.writeSymbol(symbolId); screen._writePos.x = _bounds.right - FONT_WIDTH; screen.writeSymbol(symbolId + 4); if (++symbolId == 10) symbolId = 6; } } // Write the bottom line screen._writePos = Common::Point(_bounds.left, _bounds.bottom - FONT_HEIGHT); screen.writeSymbol(14); if (xCount > 0) { int symbolId = 15; for (int i = 0; i < xCount; ++i) { screen.writeSymbol(symbolId); if (++symbolId == 19) symbolId = 15; } } screen._writePos.x = _bounds.right - FONT_WIDTH; screen.writeSymbol(19); } void Window::close() { if (_enabled) { // Update any remaining pending changes to the screen and free // the window's internal surface storage update(); // Remove the window from the stack and flag it as now disabled for (uint i = 0; i < _vm->_screen->_windowStack.size(); ++i) { if (_vm->_screen->_windowStack[i] == this) _vm->_screen->_windowStack.remove_at(i); } _enabled = false; } if (_vm->_mode == MODE_9) { warning("TODO: copyFileToMemory"); } } /** * Update the window */ void Window::update() { // Since all window drawing is done on the screen surface anyway, // there's nothing that needs to be updated here } /** * Adds an area that requires redrawing on the next frame update */ void Window::addDirtyRect(const Common::Rect &r) { _vm->_screen->addDirtyRect(r); } /** * Fill the content area of a window with the current background color */ void Window::fill() { fillRect(_innerBounds, _vm->_screen->_bgColor); } void Window::writeString(const Common::String &s) { _vm->_screen->writeString(s, _innerBounds); } void Window::drawList(DrawStruct *items, int count) { Screen &screen = *_vm->_screen; for (int i = 0; i < count; ++i, ++items) { if (items->_frame == -1 || items->_scale == -1) continue; Common::Rect bounds = _innerBounds; bounds.translate(items->_x, items->_y); // TODO: There are two sprite calls in this method. Figure out why items->_sprites->draw(screen, items->_frame, Common::Point(items->_x, items->_y)); } } /*------------------------------------------------------------------------*/ /** * Constructor */ Screen::Screen(XeenEngine *vm) : _vm(vm) { _fadeIn = false; create(SCREEN_WIDTH, SCREEN_HEIGHT); Common::fill(&_tempPalette[0], &_tempPalette[PALETTE_SIZE], 0); Common::fill(&_mainPalette[0], &_mainPalette[PALETTE_SIZE], 0); // Load font data for the screen File f("fnt"); byte *data = new byte[f.size()]; f.read(data, f.size()); _fontData = data; } Screen::~Screen() { delete[] _fontData; } void Screen::setupWindows() { Window windows[40] = { Window(_vm, Common::Rect(0, 0, 320, 200), 0, 0, 0, 0, 320, 200), Window(_vm, Common::Rect(237, 9, 317, 74), 0, 0, 237, 12, 307, 68), Window(_vm, Common::Rect(225, 1, 319, 73), 1, 8, 225, 1, 319, 73), Window(_vm, Common::Rect(0, 0, 230, 149), 0, 0, 9, 8, 216, 140), Window(_vm, Common::Rect(235, 148, 309, 189), 2, 8, 0, 0, 0, 0), Window(_vm, Common::Rect(70, 20, 250, 183), 3, 8, 80, 38, 240, 166), Window(_vm, Common::Rect(52, 149, 268, 197), 4, 8, 0, 0, 0, 0), Window(_vm, Common::Rect(108, 0, 200, 200), 5, 0, 0, 0, 0, 0), Window(_vm, Common::Rect(232, 9, 312, 74), 0, 0, 0, 0, 0, 0), Window(_vm, Common::Rect(103, 156, 217, 186), 6, 8, 0, 0, 0, 0), Window(_vm, Common::Rect(226, 0, 319, 146), 7, 8, 0, 0, 0, 0), Window(_vm, Common::Rect(8, 8, 224, 140), 8, 8, 8, 8, 224, 200), Window(_vm, Common::Rect(0, 143, 320, 199), 9, 8, 0, 0, 0, 0), Window(_vm, Common::Rect(50, 103, 266, 139), 10, 8, 0, 0, 0, 0), Window(_vm, Common::Rect(0, 7, 320, 138), 11, 8, 0, 0, 0, 0), Window(_vm, Common::Rect(50, 71, 182, 129), 12, 8, 0, 0, 0, 0), Window(_vm, Common::Rect(228, 106, 319, 146), 13, 8, 0, 0, 0, 0), Window(_vm, Common::Rect(20, 142, 290, 199), 14, 8, 0, 0, 0, 0), Window(_vm, Common::Rect(0, 20, 320, 180), 15, 8, 0, 0, 0, 0), Window(_vm, Common::Rect(231, 48, 317, 141), 16, 8, 0, 0, 0, 0), Window(_vm, Common::Rect(72, 37, 248, 163), 17, 8, 0, 0, 0, 0), Window(_vm, Common::Rect(99, 59, 237, 141), 18, 8, 99, 59, 237, 0), Window(_vm, Common::Rect(65, 23, 250, 163), 19, 8, 75, 36, 245, 141), Window(_vm, Common::Rect(80, 28, 256, 148), 20, 8, 80, 28, 256, 172), Window(_vm, Common::Rect(0, 0, 320, 146), 21, 8, 0, 0, 320, 148), Window(_vm, Common::Rect(27, 6, 207, 142), 22, 8, 0, 0, 0, 146), Window(_vm, Common::Rect(15, 15, 161, 91), 23, 8, 0, 0, 0, 0), Window(_vm, Common::Rect(90, 45, 220, 157), 24, 8, 0, 0, 0, 0), Window(_vm, Common::Rect(0, 0, 320, 200), 25, 8, 0, 0, 0, 0), Window(_vm, Common::Rect(0, 101, 320, 146), 26, 8, 0, 101, 320, 0), Window(_vm, Common::Rect(0, 0, 320, 108), 27, 8, 0, 0, 0, 45), Window(_vm, Common::Rect(50, 112, 266, 148), 28, 8, 0, 0, 0, 0), Window(_vm, Common::Rect(12, 11, 164, 94), 0, 0, 0, 0, 52, 0), Window(_vm, Common::Rect(8, 147, 224, 192), 0, 8, 0, 0, 0, 94), Window(_vm, Common::Rect(232, 74, 312, 138), 29, 8, 0, 0, 0, 0), Window(_vm, Common::Rect(226, 26, 319, 146), 30, 8, 0, 0, 0, 0), Window(_vm, Common::Rect(225, 74, 319, 154), 31, 8, 0, 0, 0, 0), Window(_vm, Common::Rect(27, 6, 195, 142), 0, 8, 0, 0, 0, 0), Window(_vm, Common::Rect(225, 140, 319, 199), 0, 8, 0, 0, 0, 0) }; _windows = Common::Array(windows, 40); } void Screen::closeWindows() { for (int i = (int)_windowStack.size() - 1; i >= 0; --i) _windowStack[i]->close(); assert(_windowStack.size() == 0); } void Screen::update() { // Merge the dirty rects mergeDirtyRects(); // Loop through copying dirty areas to the physical screen Common::List::iterator i; for (i = _dirtyRects.begin(); i != _dirtyRects.end(); ++i) { const Common::Rect &r = *i; const byte *srcP = (const byte *)getBasePtr(r.left, r.top); g_system->copyRectToScreen(srcP, this->pitch, r.left, r.top, r.width(), r.height()); } // Signal the physical screen to update g_system->updateScreen(); _dirtyRects.clear(); } void Screen::addDirtyRect(const Common::Rect &r) { assert(r.isValidRect() && r.width() > 0 && r.height() > 0 && r.right <= SCREEN_WIDTH && r.bottom <= SCREEN_HEIGHT); _dirtyRects.push_back(r); } void Screen::mergeDirtyRects() { Common::List::iterator rOuter, rInner; // Ensure dirty rect list has at least two entries rOuter = _dirtyRects.begin(); for (int i = 0; i < 2; ++i, ++rOuter) { if (rOuter == _dirtyRects.end()) return; } // Process the dirty rect list to find any rects to merge for (rOuter = _dirtyRects.begin(); rOuter != _dirtyRects.end(); ++rOuter) { rInner = rOuter; while (++rInner != _dirtyRects.end()) { if ((*rOuter).intersects(*rInner)) { // these two rectangles overlap or // are next to each other - merge them unionRectangle(*rOuter, *rOuter, *rInner); // remove the inner rect from the list _dirtyRects.erase(rInner); // move back to beginning of list rInner = rOuter; } } } } bool Screen::unionRectangle(Common::Rect &destRect, const Common::Rect &src1, const Common::Rect &src2) { destRect = src1; destRect.extend(src2); return !destRect.isEmpty(); } /** * Load a palette resource into the temporary palette */ void Screen::loadPalette(const Common::String &name) { File f(name); for (int i = 0; i < PALETTE_SIZE; ++i) _tempPalette[i] = f.readByte() << 2; } /** * Load a background resource into memory */ void Screen::loadBackground(const Common::String &name) { File f(name); assert(f.size() == (SCREEN_WIDTH * SCREEN_HEIGHT)); f.read((byte *)getPixels(), SCREEN_WIDTH * SCREEN_HEIGHT); addDirtyRect(Common::Rect(0, 0, this->w, this->h)); } /** * Copy a loaded background into a display page */ void Screen::loadPage(int pageNum) { assert(pageNum == 0 || pageNum == 1); if (_pages[0].empty()) { _pages[0].create(SCREEN_WIDTH, SCREEN_HEIGHT); _pages[1].create(SCREEN_WIDTH, SCREEN_HEIGHT); } blitTo(_pages[pageNum]); } void Screen::freePages() { _pages[0].free(); _pages[1].free(); } /** * Merge the two pages along a horizontal split point */ void Screen::horizMerge(int xp) { if (_pages[0].empty()) return; for (int y = 0; y < SCREEN_HEIGHT; ++y) { byte *destP = (byte *)getBasePtr(0, y); const byte *srcP = (const byte *)_pages[0].getBasePtr(0, y); Common::copy(srcP, srcP + SCREEN_WIDTH - xp, destP); if (xp != 0) { srcP = (const byte *)_pages[1].getBasePtr(0, y); Common::copy(srcP + SCREEN_WIDTH - xp, srcP + SCREEN_WIDTH, destP + SCREEN_WIDTH - xp); } } } /** * Merge the two pages along a vertical split point */ void Screen::vertMerge(int yp) { if (_pages[0].empty()) return; for (int y = 0; y < SCREEN_HEIGHT - yp; ++y) { const byte *srcP = (const byte *)_pages[0].getBasePtr(0, y); byte *destP = (byte *)getBasePtr(0, y); Common::copy(srcP, srcP + SCREEN_WIDTH, destP); } for (int y = SCREEN_HEIGHT - yp; y < SCREEN_HEIGHT; ++y) { const byte *srcP = (const byte *)_pages[1].getBasePtr(0, y); byte *destP = (byte *)getBasePtr(0, y); Common::copy(srcP, srcP + SCREEN_WIDTH, destP); } } void Screen::draw(void *data) { // TODO: Figure out data structure that can be passed to method assert(!data); drawScreen(); } /** * Mark the entire screen for drawing */ void Screen::drawScreen() { addDirtyRect(Common::Rect(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT)); } void Screen::fadeIn(int step) { _fadeIn = true; fadeInner(step); } void Screen::fadeOut(int step) { _fadeIn = false; fadeInner(step); } void Screen::fadeInner(int step) { for (int idx = 128; idx >= 0 && !_vm->shouldQuit(); idx -= step) { int val = MAX(idx, 0); bool flag = !_fadeIn; if (!flag) { val = -(val - 128); flag = step != 0x81; } if (!flag) { step = 0x80; } else { // Create a scaled palette from the temporary one for (int i = 0; i < PALETTE_SIZE; ++i) { _mainPalette[i] = (_tempPalette[i] * val * 2) >> 8; } updatePalette(); } _vm->_events->pollEventsAndWait(); } } void Screen::updatePalette() { updatePalette(_mainPalette, 0, 16); } void Screen::updatePalette(const byte *pal, int start, int count16) { g_system->getPaletteManager()->setPalette(pal, start, count16 * 16); } void Screen::saveBackground(int slot) { assert(slot > 0 && slot < 10); _savedScreens[slot - 1].copyFrom(*this); } void Screen::restoreBackground(int slot) { assert(slot > 0 && slot < 10); _savedScreens[slot - 1].blitTo(*this); } } // End of namespace Xeen