/* 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 3 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, see . * */ #include "lastexpress/graphics.h" #include "lastexpress/lastexpress.h" namespace LastExpress { GraphicsManager::GraphicsManager(LastExpressEngine *engine) { _engine = engine; const Graphics::PixelFormat format(2, 5, 6, 5, 0, 11, 5, 0, 0); _screenSurface.create(640, 480, format); _mainSurfaceIsInit = true; memset(_subtitlesBackBuffer, 0, sizeof(_subtitlesBackBuffer)); memset(_mouseBackBuffer, 0, sizeof(_mouseBackBuffer)); _renderBox1.width = 640; _renderBox1.height = 480; _renderBox2.width = 640; _renderBox2.height = 480; } GraphicsManager::~GraphicsManager() { _screenSurface.free(); } bool GraphicsManager::acquireSurface() { // This function is technically useless, but I'm keeping it // as a marker for when the engine wants to acquire the gfx context // for when it has to draw something. // // It has proven to be useful during debugging, // maybe it'll be useful in the future as well... return true; } void GraphicsManager::unlockSurface() { // As before, this function is technically useless, but I'm keeping it // as a marker for when the engine wants to release the gfx context // for when it finishes drawing something. // // It has proven to be useful during debugging, // maybe it'll be useful in the future as well... } void GraphicsManager::burstAll() { bool subtitlesDrawn = false; bool mouseDrawn = false; if ((_engine->getSubtitleManager()->_flags & 1) != 0) { _engine->getSubtitleManager()->vSubOn(); subtitlesDrawn = true; } if (canDrawMouse()) { drawMouse(); mouseDrawn = true; } g_system->copyRectToScreen(_screenSurface.getPixels(), 640 * 2, 0, 0, 640, 480); g_system->updateScreen(); if (mouseDrawn) restoreMouse(); if (subtitlesDrawn) _engine->getSubtitleManager()->vSubOff(); } void GraphicsManager::stepBG(int sceneIndex) { _disableCharacterDrawing = false; if (_stepBGRecursionFlag) { _renderBox1.x = 80; _renderBox1.y = 0; _renderBox1.width = 480; _renderBox1.height = 480; goStepBG(sceneIndex); _disableCharacterDrawing = true; } else { _stepBGRecursionFlag = true; goStepBG(sceneIndex); _stepBGRecursionFlag = false; } } void GraphicsManager::goStepBG(int sceneIndex) { TBM *chosenTbm; char bgName[12]; _engine->getLogicManager()->doPreFunction(&sceneIndex); Common::strcpy_s(bgName, _engine->getLogicManager()->_trainData[sceneIndex].sceneFilename); int loadResult = _engine->getArchiveManager()->loadBG(bgName); if (loadResult == -1) { return; } else if (loadResult == 0) { chosenTbm = &_renderBox1; } else { // loadResult == 1 chosenTbm = &_renderBox2; } _engine->getLogicManager()->_activeNode = sceneIndex; if (_engine->getLogicManager()->_closeUp) { getCharacter(kCharacterCath).characterPosition = _engine->getLogicManager()->_trainData[_engine->getLogicManager()->_nodeReturn].nodePosition; } else { getCharacter(kCharacterCath).characterPosition = _engine->getLogicManager()->_trainData[_engine->getLogicManager()->_activeNode].nodePosition; } _engine->getSoundManager()->_scanAnySoundLoopingSection = true; if (_engine->_navigationEngineIsRunning) { _engine->getLogicManager()->sendAll(kCharacterCath, 17, 0); _engine->getMessageManager()->flush(); if (_disableCharacterDrawing) return; _engine->getOtisManager()->adjustOtisTrueTime(); _engine->getOtisManager()->refreshSequences(); _engine->getOtisManager()->updateAll(); } _engine->getSpriteManager()->drawCycleSimple(_frontBuffer); stepDissolve(chosenTbm); _engine->getLogicManager()->doPostFunction(); } void GraphicsManager::stepDissolve(TBM *tbm) { for (int i = 0; i < _dissolveSteps; ++i) { int32 pos = sizeof(PixMap) * (tbm->x + 640 * tbm->y); int32 curFrameCount = _engine->getSoundFrameCounter(); _engine->getSoundManager()->soundThread(); dissolve(pos + sizeof(PixMap) * (i & 1), tbm->width, tbm->height, _frontBuffer); burstBox(tbm->x, tbm->y, tbm->width, tbm->height); int32 frameTimeDiff = _engine->getSoundFrameCounter() - curFrameCount; int32 timeFactor = 20; if (timeFactor / _dissolveSteps <= frameTimeDiff) { if (_dissolveSteps != 1 && timeFactor / (_dissolveSteps + 1) <= frameTimeDiff) _dissolveSteps--; } else { if (_dissolveSteps != 4 && timeFactor / (_dissolveSteps + 1) > frameTimeDiff) _dissolveSteps++; while (_engine->getSoundFrameCounter() - curFrameCount < timeFactor / _dissolveSteps) { // Not present in the original, since the input callbacks are on a different // thread than the game engine. We need to do this on our end though. _engine->handleEvents(); } } // Not present in the original, like above. _engine->handleEvents(); } if (acquireSurface()) { copy(_frontBuffer, (PixMap *)_screenSurface.getPixels(), tbm->x, tbm->y, tbm->width, tbm->height); unlockSurface(); } burstBox(tbm->x, tbm->y, tbm->width, tbm->height); } void GraphicsManager::clear(Graphics::Surface &surface, int32 x, int32 y, int32 width, int32 height) { surface.fillRect(Common::Rect(x, y, x + width, y + height), 0); } void GraphicsManager::clear(PixMap *pixels, int32 x, int32 y, int32 width, int32 height) { // Shortcut for the whole screen... if (x == 0 && y == 0 && width == 640 && height == 480) { memset(pixels, 0, 640 * 480 * sizeof(PixMap)); return; } // Clear line by line... PixMap *pixBuf = &pixels[640 * y + x]; for (int32 row = 0; row < height; row++) { memset(pixBuf, 0, width * sizeof(PixMap)); pixBuf += 640; } } void GraphicsManager::copy(PixMap *src, PixMap *dst, int32 x, int32 y, int32 width, int32 height) { // Shortcut for the whole screen... if (x == 0 && y == 0 && width == 640 && height == 480) { memcpy(dst, src, 640 * 480 * sizeof(PixMap)); return; } // Copy line by line... int32 offset = x + 640 * y; PixMap *srcPtr = &src[offset]; PixMap *dstPtr = &dst[offset]; for (int32 row = 0; row < height; row++) { memcpy(dstPtr, srcPtr, width * sizeof(PixMap)); srcPtr += 640; dstPtr += 640; } } #define DISSOLVE_MASK _brightnessData[1] void GraphicsManager::goDissolve(int32 location, int32 width, int32 height, PixMap *buffer) { int32 widthCount = width; int32 heightCount = height; PixMap *bufPtr = (PixMap *)((byte *)buffer + location); PixMap *surfPtr = (PixMap *)((byte *)_screenSurface.getPixels() + location); while (heightCount > 0) { while (widthCount > 0) { *surfPtr &= DISSOLVE_MASK; *surfPtr >>= 1; *surfPtr += (*bufPtr & DISSOLVE_MASK) >> 1; surfPtr += 2; bufPtr += 2; widthCount -= 2; } widthCount = width; surfPtr = (PixMap *)(((uintptr)(&surfPtr[640 - width])) ^ 2); bufPtr = (PixMap *)(((uintptr)(&bufPtr[640 - width])) ^ 2); heightCount--; } } #undef DISSOLVE_MASK void GraphicsManager::bitBltSprite255(Sprite *sprite, PixMap *pixels) { PixMap *destPtr; uint16 *palette; byte *compressedData; PixMap *destEndPtr; uint16 cmd; uint16 color; int count; PixMap *sourcePtr; uint16 offset; destPtr = pixels; palette = sprite->colorPalette; compressedData = sprite->compData; destEndPtr = pixels + (640 * 480); while (destPtr < destEndPtr) { cmd = READ_LE_UINT16((uint16 *)compressedData); // Direct color lookup if ((cmd & 0xFF) < 0x80) { *destPtr = palette[(cmd & 0xFF)]; destPtr++; compressedData++; continue; } // Handle compression commands if ((cmd & 0xFF) >= 0xF0) { // Skip pixels command offset = ((cmd & 0xFF) << 8) | ((cmd >> 8) & 0xFF); offset &= 0xFFF; destPtr += offset; compressedData += 2; } else if ((cmd & 0xFF) >= 0xE0) { // Run-length encoding (repeat color) color = palette[(cmd >> 8) & 0xFF]; count = (cmd & 0x0F) + 1; while (count--) { *destPtr = color; destPtr++; } compressedData += 2; } else { // Copy pixels from previous data uint16 cmdHiByte = (cmd >> 8) & 0xFF; uint16 cmdLoByte = cmd & 0xFF; offset = cmdHiByte | ((cmdLoByte & 0x7) << 8); sourcePtr = &destPtr[offset - 0x1000 / sizeof(PixMap)]; count = ((cmdLoByte & 0x78) >> 3) + 3; while (count--) { *destPtr = *sourcePtr; destPtr++; sourcePtr++; } compressedData += 2; } } } void GraphicsManager::bitBltSprite128(Sprite *sprite, PixMap *pixels) { uint16 *palette; PixMap *destPtr; byte *compressedData; PixMap *destEndPtr; int skipValue; byte cmd; PixMap color; int count; int offset; palette = sprite->colorPalette; destPtr = pixels + sprite->rect.width / sizeof(PixMap); compressedData = sprite->compData; destEndPtr = pixels + sprite->rect.height / sizeof(PixMap); skipValue = 639 - (sprite->rect.right - sprite->rect.left); while (destPtr < destEndPtr) { cmd = *compressedData++; // Direct color lookup if (!(cmd & 0x80)) { *destPtr = palette[cmd]; destPtr++; continue; } // Handle compression commands if (cmd & 0x40) { // Pattern command if (cmd & 0x20) { // Extract pattern length count = cmd & 0x1F; offset = skipValue + count; // Write pattern: color, skip, color color = palette[1]; *destPtr = color; destPtr += offset + 1; *destPtr = color; destPtr++; } else { // Complex pattern command count = cmd & 0x1F; if ((count & 0x10)) { count = (((count & 0x0F) << 8) | *compressedData); compressedData++; if ((count & 0x800) != 0) { // Skip pixels count &= 0x7FF; destPtr += count; } else { // Write pattern: color, skip, color color = palette[1]; *destPtr = color; destPtr += count + 1; *destPtr = color; destPtr++; } } else { // Write pattern: color, skip, color color = palette[1]; *destPtr = color; destPtr += count + 1; *destPtr = color; destPtr++; } } } else { // Run-length encoding (repeat color) count = cmd & 0x3F; cmd = *compressedData++; color = palette[cmd]; while (count--) { *destPtr = color; destPtr++; } } } } void GraphicsManager::bitBltSprite32(Sprite *sprite, PixMap *pixels) { uint16 *colorPalette; PixMap *destEndPtr; PixMap *destPtr; byte *compData; byte cmd; uint16 value; byte count; PixMap color; PixMap *tempDestPtr; colorPalette = sprite->colorPalette; destEndPtr = pixels + sprite->rect.height / sizeof(PixMap); destPtr = pixels + sprite->rect.width / sizeof(PixMap); compData = sprite->compData; while (destPtr < destEndPtr) { cmd = *compData++; // Handle run-length encoding if ((cmd & 0x1F) != 0) { count = cmd >> 5; if (count == 0) { count = *compData++; } color = colorPalette[cmd & 0x1F]; while (count--) { *destPtr++ = color; } } else { // Handle other compression commands value = cmd << 3; byte secondByte = *compData++; value = (value & 0xFF00) | secondByte; if ((value & 0x400) != 0) { // Skip pixels command value &= 0x3FF; destPtr += value; } else { // Pattern command: color, skip, color tempDestPtr = destPtr; color = colorPalette[1]; *destPtr = color; tempDestPtr = &destPtr[value + 1]; *tempDestPtr = color; destPtr = tempDestPtr + 1; } } } } void GraphicsManager::bitBltSprite16(Sprite *sprite, PixMap *pixels) { uint16 *colorPalette; PixMap *destPtr; byte *compData; PixMap *destEndPtr; int skipValue; byte cmd; PixMap value; PixMap color; PixMap *tempDestPtr; byte count; colorPalette = sprite->colorPalette; destPtr = pixels + sprite->rect.width / sizeof(PixMap); compData = sprite->compData; destEndPtr = pixels + sprite->rect.height / sizeof(PixMap); skipValue = 639 - (sprite->rect.right - sprite->rect.left); while (destPtr < destEndPtr) { cmd = *compData++; if (!(cmd & 0x80)) { // Handle run-length encoding count = cmd >> 4; if (count == 0) { count = *compData++; } color = colorPalette[cmd & 0xF]; while (count > 0) { *destPtr++ = color; count--; } } else { // Handle other compression commands if (cmd & 0x40) { // Pattern command value = cmd & 0x3F; // Pattern: color, skip by a certain amount, color color = colorPalette[1]; *destPtr = color; tempDestPtr = &destPtr[skipValue + value + 1]; *tempDestPtr = color; destPtr = tempDestPtr + 1; } else { value = cmd & 0x3F; if ((value & 0x20) && ((value = ((value & 0x1F) << 8) | *compData++, (value & 0x1000)))) { // Skip pixels command value &= 0xFFF; destPtr += value; } else { // Pattern: color, skip, color color = colorPalette[1]; *destPtr = color; tempDestPtr = &destPtr[value + 1]; *tempDestPtr = color; destPtr = tempDestPtr + 1; } } } } } void GraphicsManager::bitBltSprite8(Sprite *sprite, PixMap *pixels) { uint16 *colorPalette; PixMap *destPtr; byte *compData; PixMap *destEndPtr; int skipValue; byte cmd; PixMap value; PixMap color; PixMap *tempDestPtr; byte count; colorPalette = sprite->colorPalette; destPtr = pixels + sprite->rect.width / sizeof(PixMap); compData = sprite->compData; destEndPtr = pixels + sprite->rect.height / sizeof(PixMap); skipValue = 639 - (sprite->rect.right - sprite->rect.left); while (destPtr < destEndPtr) { cmd = *compData++; // Handle run-length encoding if (!(cmd & 0x80)) { count = cmd >> 3; if (count == 0) { count = *compData++; } color = colorPalette[cmd & 0x7]; while (count > 0) { *destPtr++ = color; count--; } } else { // Handle pattern commands if (cmd & 0x40) { value = cmd & 0x3F; // Pattern: color, skip by a certain amount, color color = colorPalette[1]; *destPtr = color; tempDestPtr = &destPtr[skipValue + value + 1]; *tempDestPtr = color; destPtr = tempDestPtr + 1; } else { value = cmd & 0x3F; if ((value & 0x20) && ((value = ((value & 0x1F) << 8) | *compData++, (value & 0x1000)))) { // Skip pixels command value &= 0xFFF; destPtr += value; } else { // Pattern: color, skip, color color = colorPalette[1]; *destPtr = color; tempDestPtr = &destPtr[value + 1]; *tempDestPtr = color; destPtr = tempDestPtr + 1; } } } } } void GraphicsManager::bitBltWane128(Sprite *sprite, PixMap *pixels) { uint16 *palette; PixMap *destPtr; byte *compressedData; PixMap *destEndPtr; int skipValue; byte cmd; PixMap color; int count; int page; palette = sprite->colorPalette; destPtr = pixels + sprite->rect.width / sizeof(PixMap); compressedData = sprite->compData; destEndPtr = pixels + sprite->rect.height / sizeof(PixMap); skipValue = 639 - (sprite->rect.right - sprite->rect.left); // Calculate initial page for checkerboard pattern page = ((destPtr - pixels) / (0x500 / sizeof(PixMap))) & 1; while (destPtr < destEndPtr) { cmd = *compressedData++; // Direct color lookup if (!(cmd & 0x80)) { color = palette[cmd]; // Write pixel if allowed by the current checkerboard page if ((page == 0 && (((uintptr)destPtr & 2) != 0)) || (page == 1 && (((uintptr)destPtr & 2) == 0))) { *destPtr = color; } destPtr++; // Update checkerboard page page = ((destPtr - pixels) / (0x500 / sizeof(PixMap))) & 1; continue; } // Handle compression commands if (cmd & 0x40) { // Pattern command if (cmd & 0x20) { // Extract pattern length count = cmd & 0x1F; count += skipValue; // Write pattern: color, skip, color color = palette[1]; // Write pixel if allowed by the current checkerboard page if ((page == 0 && (((uintptr)destPtr & 2) == 0)) || (page == 1 && (((uintptr)destPtr & 2) != 0))) { *destPtr = color; } destPtr++; destPtr += count; // Update checkerboard page page = ((destPtr - pixels) / (0x500 / sizeof(PixMap))) & 1; // Write pixel if allowed by the current checkerboard page if ((page == 0 && (((uintptr)destPtr & 2) == 0)) || (page == 1 && (((uintptr)destPtr & 2) != 0))) { *destPtr = color; } destPtr++; } else { // Complex pattern command count = cmd & 0x1F; if (count & 0x10) { count = (((count & 0x0F) << 8) | *compressedData); compressedData++; // Skip pixels if ((count & 0x800) != 0) { count &= 0x7FF; destPtr += count; // Update checkerboard page page = ((destPtr - pixels) / (0x500 / sizeof(PixMap))) & 1; } else { // Write pattern: color, skip, color color = palette[1]; // Write pixel if allowed by the current checkerboard page if ((page == 0 && (((uintptr)destPtr & 2) == 0)) || (page == 1 && (((uintptr)destPtr & 2) != 0))) { *destPtr = color; } destPtr++; destPtr += count; // Update checkerboard page page = ((destPtr - pixels) / (0x500 / sizeof(PixMap))) & 1; // Write pixel if allowed by the current checkerboard page if ((page == 0 && (((uintptr)destPtr & 2) == 0)) || (page == 1 && (((uintptr)destPtr & 2) != 0))) { *destPtr = color; } destPtr++; } } else { // Write pattern: color, skip, color color = palette[1]; // Write pixel if allowed by the current checkerboard page if ((page == 0 && (((uintptr)destPtr & 2) == 0)) || (page == 1 && (((uintptr)destPtr & 2) != 0))) { *destPtr = color; } destPtr++; destPtr += count; // Update checkerboard page page = ((destPtr - pixels) / (0x500 / sizeof(PixMap))) & 1; // Write pixel if allowed by the current checkerboard page if ((page == 0 && (((uintptr)destPtr & 2) == 0)) || (page == 1 && (((uintptr)destPtr & 2) != 0))) { *destPtr = color; } destPtr++; } } } else { // Run-length encoding (repeat color) count = cmd & 0x3F; cmd = *compressedData++; color = palette[cmd]; // Write pixel if allowed by the current checkerboard page if ((page == 0 && (((uintptr)destPtr & 2) == 0)) || (page == 1 && (((uintptr)destPtr & 2) != 0))) { destPtr++; count--; if (count == 0) continue; } // First, determine how many full pairs (colored pixel + empty pixel) we have... int halfCount = count >> 1; bool hasRemainder = (count & 1) != 0; // Process full pairs... if (halfCount > 0) { do { *destPtr = color; destPtr += 2; // Move two positions to maintain checkerboard... halfCount--; } while (halfCount > 0); } // Handle remaining pixel if count was odd... if (hasRemainder) { *destPtr = color; destPtr++; } // Update page number page = ((destPtr - pixels) / (0x500 / sizeof(PixMap))) & 1; } } } void GraphicsManager::bitBltWane32(Sprite *sprite, PixMap *pixels) { uint16 *colorPalette; PixMap *destEndPtr; PixMap *destPtr; byte *compData; byte cmd; uint16 value; byte count; PixMap color; int page; colorPalette = sprite->colorPalette; destEndPtr = pixels + sprite->rect.height / sizeof(PixMap); destPtr = pixels + sprite->rect.width / sizeof(PixMap); compData = sprite->compData; // Calculate initial page for checkerboard pattern page = ((destPtr - pixels) / (0x500 / sizeof(PixMap))) & 1; while (destPtr < destEndPtr) { cmd = *compData++; // Handle run-length encoding if ((cmd & 0x1F) != 0) { count = cmd >> 5; if (count == 0) { count = *compData++; } color = colorPalette[cmd & 0x1F]; // Write pixel if allowed by the current checkerboard page if ((page == 0 && (((uintptr)destPtr & 2) == 0)) || (page == 1 && (((uintptr)destPtr & 2) != 0))) { destPtr++; count--; if (count == 0) continue; } // First, determine how many full pairs (colored pixel + empty pixel) we have... int halfCount = count >> 1; bool hasRemainder = (count & 1) != 0; if (halfCount > 0) { do { *destPtr = color; destPtr += 2; // Move two positions to maintain checkerboard... halfCount--; } while (halfCount > 0); } // Handle remaining pixel if count was odd... if (hasRemainder) { *destPtr = color; destPtr++; } // Update page number page = ((destPtr - pixels) / (0x500 / sizeof(PixMap))) & 1; } else { // Handle other compression commands value = cmd << 3; byte secondByte = *compData++; value = (value & 0xFF00) | secondByte; if (value & 0x400) { // Skip pixels command value &= 0x3FF; destPtr += value; // Update page number page = ((destPtr - pixels) / (0x500 / sizeof(PixMap))) & 1; } else { // Pattern command: color, skip, color color = colorPalette[1]; // Write pixel if allowed by the current checkerboard page if ((page == 0 && (((uintptr)destPtr & 2) == 0)) || (page == 1 && (((uintptr)destPtr & 2) != 0))) { *destPtr = color; } destPtr++; destPtr += value; // Update page number page = ((destPtr - pixels) / (0x500 / sizeof(PixMap))) & 1; // Write pixel if allowed by the current checkerboard page if ((page == 0 && (((uintptr)destPtr & 2) == 0)) || (page == 1 && (((uintptr)destPtr & 2) != 0))) { *destPtr = color; } destPtr++; } } } } void GraphicsManager::bitBltWane16(Sprite *sprite, PixMap *pixels) { uint16 *colorPalette; PixMap *destPtr; byte *compData; PixMap *destEndPtr; int skipValue; byte cmd; PixMap value; PixMap color; byte count; int page; colorPalette = sprite->colorPalette; destPtr = pixels + sprite->rect.width / sizeof(PixMap); compData = sprite->compData; destEndPtr = pixels + sprite->rect.height / sizeof(PixMap); skipValue = 639 - (sprite->rect.right - sprite->rect.left); // Calculate initial page for checkerboard pattern page = ((destPtr - pixels) / (0x500 / sizeof(PixMap))) & 1; while (destPtr < destEndPtr) { cmd = *compData++; if (!(cmd & 0x80)) { // Handle run-length encoding count = cmd >> 4; if (count == 0) { count = *compData++; } color = colorPalette[cmd & 0xF]; // Write pixel if allowed by the current checkerboard page if ((page == 0 && (((uintptr)destPtr & 2) == 0)) || (page == 1 && (((uintptr)destPtr & 2) != 0))) { destPtr++; count--; if (count == 0) continue; } // First, determine how many full pairs (colored pixel + empty pixel) we have... int halfCount = count >> 1; bool hasRemainder = (count & 1) != 0; if (halfCount > 0) { do { *destPtr = color; destPtr += 2; // Move two positions to maintain checkerboard... halfCount--; } while (halfCount > 0); } // Handle remaining pixel if count was odd... if (hasRemainder) { *destPtr = color; destPtr++; } // Update page number page = ((destPtr - pixels) / (0x500 / sizeof(PixMap))) & 1; } else { // Handle other compression commands if (cmd & 0x40) { // Pattern command value = cmd & 0x3F; value += skipValue; // Pattern: color, skip by a certain amount, color color = colorPalette[1]; // Write pixel if allowed by the current checkerboard page if ((page == 0 && (((uintptr)destPtr & 2) == 0)) || (page == 1 && (((uintptr)destPtr & 2) != 0))) { *destPtr = color; } destPtr++; destPtr += value; // Update page number page = ((destPtr - pixels) / (0x500 / sizeof(PixMap))) & 1; // Write pixel if allowed by the current checkerboard page if ((page == 0 && (((uintptr)destPtr & 2) == 0)) || (page == 1 && (((uintptr)destPtr & 2) != 0))) { *destPtr = color; } destPtr++; } else { value = cmd & 0x3F; if ((value & 0x20) && ((value = ((value & 0x1F) << 8) | *compData++, (value & 0x1000)))) { // Skip pixels command value &= 0xFFF; destPtr += value; // Update page number page = ((destPtr - pixels) / (0x500 / sizeof(PixMap))) & 1; } else { // Pattern: color, skip, color color = colorPalette[1]; // Write pixel if allowed by the current checkerboard page if ((page == 0 && (((uintptr)destPtr & 2) == 0)) || (page == 1 && (((uintptr)destPtr & 2) != 0))) { *destPtr = color; } destPtr++; destPtr += value; // Update page number page = ((destPtr - pixels) / (0x500 / sizeof(PixMap))) & 1; // Write pixel if allowed by the current checkerboard page if ((page == 0 && (((uintptr)destPtr & 2) == 0)) || (page == 1 && (((uintptr)destPtr & 2) != 0))) { *destPtr = color; } destPtr++; } } } } } void GraphicsManager::bitBltWane8(Sprite *sprite, PixMap *pixels) { uint16 *colorPalette; PixMap *destPtr; byte *compData; PixMap *destEndPtr; int skipValue; byte cmd; PixMap value; PixMap color; byte count; int page; colorPalette = sprite->colorPalette; destPtr = pixels + sprite->rect.width / sizeof(PixMap); compData = sprite->compData; destEndPtr = pixels + sprite->rect.height / sizeof(PixMap); skipValue = 639 - (sprite->rect.right - sprite->rect.left); // Calculate initial page for checkerboard pattern page = ((destPtr - pixels) / (0x500 / sizeof(PixMap))) & 1; while (destPtr < destEndPtr) { cmd = *compData++; // Handle run-length encoding if (!(cmd & 0x80)) { count = cmd >> 3; if (count == 0) { count = *compData++; } color = colorPalette[cmd & 0x7]; // First, determine how many full pairs (colored pixel + empty pixel) we have... if ((page == 0 && (((uintptr)destPtr & 2) == 0)) || (page == 1 && (((uintptr)destPtr & 2) != 0))) { destPtr++; count--; if (count == 0) continue; } // Process pixel runs with checkerboard pattern int halfCount = count >> 1; bool hasRemainder = (count & 1) != 0; if (halfCount > 0) { do { *destPtr = color; destPtr += 2; // Move two positions to maintain checkerboard... halfCount--; } while (halfCount > 0); } // Handle remaining pixel if count was odd... if (hasRemainder) { *destPtr = color; destPtr++; } // Update page number page = ((destPtr - pixels) / (0x500 / sizeof(PixMap))) & 1; } else { // Handle pattern commands if (cmd & 0x40) { value = cmd & 0x3F; value += skipValue; // Pattern: color, skip by a certain amount, color color = colorPalette[1]; // Write pixel if allowed by the current checkerboard page if ((page == 0 && (((uintptr)destPtr & 2) == 0)) || (page == 1 && (((uintptr)destPtr & 2) != 0))) { *destPtr = color; } destPtr++; destPtr += value; // Update page number page = ((destPtr - pixels) / (0x500 / sizeof(PixMap))) & 1; // Write pixel if allowed by the current checkerboard page if ((page == 0 && (((uintptr)destPtr & 2) == 0)) || (page == 1 && (((uintptr)destPtr & 2) != 0))) { *destPtr = color; } destPtr++; } else { value = cmd & 0x3F; if ((value & 0x20) && ((value = ((value & 0x1F) << 8) | *compData++, (value & 0x1000)))) { // Skip pixels command value &= 0xFFF; destPtr += value; // Update page number page = ((destPtr - pixels) / (0x500 / sizeof(PixMap))) & 1; } else { // Pattern: color, skip, color color = colorPalette[1]; // Write pixel if allowed by the current checkerboard page if ((page == 0 && (((uintptr)destPtr & 2) == 0)) || (page == 1 && (((uintptr)destPtr & 2) != 0))) { *destPtr = color; } destPtr++; destPtr += value; // Update page number page = ((destPtr - pixels) / (0x500 / sizeof(PixMap))) & 1; // Write pixel if allowed by the current checkerboard page if ((page == 0 && (((uintptr)destPtr & 2) == 0)) || (page == 1 && (((uintptr)destPtr & 2) != 0))) { *destPtr = color; } destPtr++; } } } } } void GraphicsManager::bitBltWax128(Sprite *sprite, PixMap *pixels) { uint16 *palette; PixMap *destPtr; byte *compressedData; PixMap *destEndPtr; int skipValue; byte cmd; PixMap color; int count; int page; palette = sprite->colorPalette; destPtr = pixels + sprite->rect.width / sizeof(PixMap); compressedData = sprite->compData; destEndPtr = pixels + sprite->rect.height / sizeof(PixMap); skipValue = 639 - (sprite->rect.right - sprite->rect.left); // Calculate initial page for checkerboard pattern page = ((destPtr - pixels) / (0x500 / sizeof(PixMap))) & 1; while (destPtr < destEndPtr) { cmd = *compressedData++; // Direct color lookup if (!(cmd & 0x80)) { color = palette[cmd]; // Write pixel if allowed by the current checkerboard page if ((page == 0 && (((uintptr)destPtr & 2) == 0)) || (page == 1 && (((uintptr)destPtr & 2) != 0))) { *destPtr = color; } destPtr++; // Update checkerboard page page = ((destPtr - pixels) / (0x500 / sizeof(PixMap))) & 1; continue; } // Handle compression commands if (cmd & 0x40) { // Pattern command if (cmd & 0x20) { // Extract pattern length count = cmd & 0x1F; count += skipValue; // Write pattern: color, skip, color color = palette[1]; // Write pixel if allowed by the current checkerboard page if ((page == 0 && (((uintptr)destPtr & 2) != 0)) || (page == 1 && (((uintptr)destPtr & 2) == 0))) { *destPtr = color; } destPtr++; destPtr += count; // Update checkerboard page page = ((destPtr - pixels) / (0x500 / sizeof(PixMap))) & 1; // Write pixel if allowed by the current checkerboard page if ((page == 0 && (((uintptr)destPtr & 2) != 0)) || (page == 1 && (((uintptr)destPtr & 2) == 0))) { *destPtr = color; } destPtr++; } else { // Complex pattern command count = cmd & 0x1F; if (count & 0x10) { count = (((count & 0x0F) << 8) | *compressedData); compressedData++; // Skip pixels if ((count & 0x800) != 0) { count &= 0x7FF; destPtr += count; // Update checkerboard page page = ((destPtr - pixels) / (0x500 / sizeof(PixMap))) & 1; } else { // Write pattern: color, skip, color color = palette[1]; // Write pixel if allowed by the current checkerboard page if ((page == 0 && (((uintptr)destPtr & 2) != 0)) || (page == 1 && (((uintptr)destPtr & 2) == 0))) { *destPtr = color; } destPtr++; destPtr += count; // Update checkerboard page page = ((destPtr - pixels) / (0x500 / sizeof(PixMap))) & 1; // Write pixel if allowed by the current checkerboard page if ((page == 0 && (((uintptr)destPtr & 2) != 0)) || (page == 1 && (((uintptr)destPtr & 2) == 0))) { *destPtr = color; } destPtr++; } } else { // Write pattern: color, skip, color color = palette[1]; // Write pixel if allowed by the current checkerboard page if ((page == 0 && (((uintptr)destPtr & 2) != 0)) || (page == 1 && (((uintptr)destPtr & 2) == 0))) { *destPtr = color; } destPtr++; destPtr += count; // Update checkerboard page page = ((destPtr - pixels) / (0x500 / sizeof(PixMap))) & 1; // Write pixel if allowed by the current checkerboard page if ((page == 0 && (((uintptr)destPtr & 2) != 0)) || (page == 1 && (((uintptr)destPtr & 2) == 0))) { *destPtr = color; } destPtr++; } } } else { // Run-length encoding (repeat color) count = cmd & 0x3F; cmd = *compressedData++; color = palette[cmd]; // Write pixel if allowed by the current checkerboard page if ((page == 0 && (((uintptr)destPtr & 2) != 0)) || (page == 1 && (((uintptr)destPtr & 2) == 0))) { destPtr++; count--; if (count == 0) continue; } // First, determine how many full pairs (empty pixel + colored pixel) we have... int halfCount = count >> 1; bool hasRemainder = (count & 1) != 0; // Process full pairs... if (halfCount > 0) { do { *destPtr = color; destPtr += 2; // Move two positions to maintain checkerboard... halfCount--; } while (halfCount > 0); } // Handle remaining pixel if count was even... if (hasRemainder) { *destPtr = color; destPtr++; } // Update page number page = ((destPtr - pixels) / (0x500 / sizeof(PixMap))) & 1; } } } void GraphicsManager::bitBltWax32(Sprite *sprite, PixMap *pixels) { uint16 *colorPalette; PixMap *destEndPtr; PixMap *destPtr; byte *compData; byte cmd; uint16 value; byte count; PixMap color; int page; colorPalette = sprite->colorPalette; destEndPtr = pixels + sprite->rect.height / sizeof(PixMap); destPtr = pixels + sprite->rect.width / sizeof(PixMap); compData = sprite->compData; // Calculate initial page for checkerboard pattern page = ((destPtr - pixels) / (0x500 / sizeof(PixMap))) & 1; while (destPtr < destEndPtr) { cmd = *compData++; // Handle run-length encoding if ((cmd & 0x1F) != 0) { count = cmd >> 5; if (count == 0) { count = *compData++; } color = colorPalette[cmd & 0x1F]; // Write pixel if allowed by the current checkerboard page if ((page == 0 && (((uintptr)destPtr & 2) != 0)) || (page == 1 && (((uintptr)destPtr & 2) == 0))) { destPtr++; count--; if (count == 0) continue; } // First, determine how many full pairs (empty pixel + colored pixel) we have... int halfCount = count >> 1; bool hasRemainder = (count & 1) != 0; if (halfCount > 0) { do { *destPtr = color; destPtr += 2; // Move two positions to maintain checkerboard... halfCount--; } while (halfCount > 0); } // Handle remaining pixel if count was even... if (hasRemainder) { *destPtr = color; destPtr++; } // Update page number page = ((destPtr - pixels) / (0x500 / sizeof(PixMap))) & 1; } else { // Handle other compression commands value = cmd << 3; byte secondByte = *compData++; value = (value & 0xFF00) | secondByte; if (value & 0x400) { // Skip pixels command value &= 0x3FF; destPtr += value; // Update page number page = ((destPtr - pixels) / (0x500 / sizeof(PixMap))) & 1; } else { // Pattern command: color, skip, color color = colorPalette[1]; // Write pixel if allowed by the current checkerboard page if ((page == 0 && (((uintptr)destPtr & 2) != 0)) || (page == 1 && (((uintptr)destPtr & 2) == 0))) { *destPtr = color; } destPtr++; destPtr += value; // Update page number page = ((destPtr - pixels) / (0x500 / sizeof(PixMap))) & 1; // Write pixel if allowed by the current checkerboard page if ((page == 0 && (((uintptr)destPtr & 2) != 0)) || (page == 1 && (((uintptr)destPtr & 2) == 0))) { *destPtr = color; } destPtr++; } } } } void GraphicsManager::bitBltWax16(Sprite *sprite, PixMap *pixels) { uint16 *colorPalette; PixMap *destPtr; byte *compData; PixMap *destEndPtr; int skipValue; byte cmd; PixMap value; PixMap color; byte count; int page; colorPalette = sprite->colorPalette; destPtr = pixels + sprite->rect.width / sizeof(PixMap); compData = sprite->compData; destEndPtr = pixels + sprite->rect.height / sizeof(PixMap); skipValue = 639 - (sprite->rect.right - sprite->rect.left); // Calculate initial page for checkerboard pattern page = ((destPtr - pixels) / (0x500 / sizeof(PixMap))) & 1; while (destPtr < destEndPtr) { cmd = *compData++; if (!(cmd & 0x80)) { // Handle run-length encoding count = cmd >> 4; if (count == 0) { count = *compData++; } color = colorPalette[cmd & 0xF]; // Write pixel if allowed by the current checkerboard page if ((page == 0 && (((uintptr)destPtr & 2) != 0)) || (page == 1 && (((uintptr)destPtr & 2) == 0))) { destPtr++; count--; if (count == 0) continue; } // First, determine how many full pairs (empty pixel + colored pixel) we have... int halfCount = count >> 1; bool hasRemainder = (count & 1) != 0; if (halfCount > 0) { do { *destPtr = color; destPtr += 2; // Move two positions to maintain checkerboard... halfCount--; } while (halfCount > 0); } // Handle remaining pixel if count was even... if (hasRemainder) { *destPtr = color; destPtr++; } // Update page number page = ((destPtr - pixels) / (0x500 / sizeof(PixMap))) & 1; } else { // Handle other compression commands if (cmd & 0x40) { // Pattern command value = cmd & 0x3F; value += skipValue; // Pattern: color, skip by a certain amount, color color = colorPalette[1]; // Write pixel if allowed by the current checkerboard page if ((page == 0 && (((uintptr)destPtr & 2) != 0)) || (page == 1 && (((uintptr)destPtr & 2) == 0))) { *destPtr = color; } destPtr++; destPtr += value; // Update page number page = ((destPtr - pixels) / (0x500 / sizeof(PixMap))) & 1; // Write pixel if allowed by the current checkerboard page if ((page == 0 && (((uintptr)destPtr & 2) != 0)) || (page == 1 && (((uintptr)destPtr & 2) == 0))) { *destPtr = color; } destPtr++; } else { value = cmd & 0x3F; if ((value & 0x20) && ((value = ((value & 0x1F) << 8) | *compData++, (value & 0x1000)))) { // Skip pixels command value &= 0xFFF; destPtr += value; // Update page number page = ((destPtr - pixels) / (0x500 / sizeof(PixMap))) & 1; } else { // Pattern: color, skip, color color = colorPalette[1]; // Write pixel if allowed by the current checkerboard page if ((page == 0 && (((uintptr)destPtr & 2) != 0)) || (page == 1 && (((uintptr)destPtr & 2) == 0))) { *destPtr = color; } destPtr++; destPtr += value; // Update page after moving page = ((destPtr - pixels) / (0x500 / sizeof(PixMap))) & 1; // Write pixel if allowed by the current checkerboard page if ((page == 0 && (((uintptr)destPtr & 2) != 0)) || (page == 1 && (((uintptr)destPtr & 2) == 0))) { *destPtr = color; } destPtr++; } } } } } void GraphicsManager::bitBltWax8(Sprite *sprite, PixMap *pixels) { uint16 *colorPalette; PixMap *destPtr; byte *compData; PixMap *destEndPtr; int skipValue; byte cmd; PixMap value; PixMap color; byte count; int page; colorPalette = sprite->colorPalette; destPtr = pixels + sprite->rect.width / sizeof(PixMap); compData = sprite->compData; destEndPtr = pixels + sprite->rect.height / sizeof(PixMap); skipValue = 639 - (sprite->rect.right - sprite->rect.left); // Calculate initial page for checkerboard pattern page = ((destPtr - pixels) / (0x500 / sizeof(PixMap))) & 1; while (destPtr < destEndPtr) { cmd = *compData++; // Handle run-length encoding if (!(cmd & 0x80)) { count = cmd >> 3; if (count == 0) { count = *compData++; } color = colorPalette[cmd & 0x7]; // First, determine how many full pairs (colored pixel + empty pixel) we have... if ((page == 0 && (((uintptr)destPtr & 2) != 0)) || (page == 1 && (((uintptr)destPtr & 2) == 0))) { destPtr++; count--; if (count == 0) continue; } // Process pixel runs with checkerboard pattern int halfCount = count >> 1; bool hasRemainder = (count & 1) != 0; if (halfCount > 0) { do { *destPtr = color; destPtr += 2; // Move two positions to maintain checkerboard... halfCount--; } while (halfCount > 0); } // Handle remaining pixel if count was even... if (hasRemainder) { *destPtr = color; destPtr++; } // Update page number page = ((destPtr - pixels) / (0x500 / sizeof(PixMap))) & 1; } else { // Handle pattern commands if (cmd & 0x40) { value = cmd & 0x3F; value += skipValue; // Pattern: color, skip by a certain amount, color color = colorPalette[1]; // Write pixel if allowed by the current checkerboard page if ((page == 0 && (((uintptr)destPtr & 2) != 0)) || (page == 1 && (((uintptr)destPtr & 2) == 0))) { *destPtr = color; } destPtr++; destPtr += value; // Update page number page = ((destPtr - pixels) / (0x500 / sizeof(PixMap))) & 1; // Write pixel if allowed by the current checkerboard page if ((page == 0 && (((uintptr)destPtr & 2) != 0)) || (page == 1 && (((uintptr)destPtr & 2) == 0))) { *destPtr = color; } destPtr++; } else { value = cmd & 0x3F; if ((value & 0x20) && ((value = ((value & 0x1F) << 8) | *compData++, (value & 0x1000)))) { // Skip pixels command value &= 0xFFF; destPtr += value; // Update page number page = ((destPtr - pixels) / (0x500 / sizeof(PixMap))) & 1; } else { // Pattern: color, skip, color color = colorPalette[1]; // Write pixel if allowed by the current checkerboard page if ((page == 0 && (((uintptr)destPtr & 2) != 0)) || (page == 1 && (((uintptr)destPtr & 2) == 0))) { *destPtr = color; } destPtr++; destPtr += value; // Update page number page = ((destPtr - pixels) / (0x500 / sizeof(PixMap))) & 1; // Write pixel if allowed by the current checkerboard page if ((page == 0 && (((uintptr)destPtr & 2) != 0)) || (page == 1 && (((uintptr)destPtr & 2) == 0))) { *destPtr = color; } destPtr++; } } } } } void GraphicsManager::eraseSprite(byte *data) { if (acquireSurface()) { doErase(data); unlockSurface(); } } void GraphicsManager::dissolve(int32 location, int32 width, int32 height, PixMap *buf) { if (acquireSurface()) { goDissolve(location, width, height, buf); unlockSurface(); } } void GraphicsManager::doErase(byte *data) { byte *screenSurface = (byte *)_screenSurface.getPixels(); byte *endOfSurface = screenSurface + (640 * 480 * sizeof(PixMap)); byte *previousScreenBuffer = (byte *)_backBuffer; uint16 *eraseMask = (uint16 *)data; // Apply the old screen buffer on the erase mask do { if (READ_LE_UINT16(eraseMask)) { memcpy(screenSurface, previousScreenBuffer, 4 * READ_LE_UINT16(eraseMask)); previousScreenBuffer += 4 * READ_LE_UINT16(eraseMask); screenSurface += 4 * READ_LE_UINT16(eraseMask); } int skipSize = READ_LE_UINT16(eraseMask + 1) << 2; screenSurface += skipSize; previousScreenBuffer += skipSize; eraseMask += 2; } while (screenSurface < endOfSurface); } void GraphicsManager::modifyPalette(uint16 *data, uint32 size) { if (_luminosityIsInit) { // Apply gamma correction unless gamma level is 3 (i.e. no gamma correction)... if (_gammaLevel != 3) { uint16 *dataPtr = data; for (uint32 i = 0; i < size; i++) { uint16 pixel = *dataPtr; uint16 blue = (pixel & 0x1F); uint16 green = (pixel & 0x3E0) >> 5; uint16 red = (pixel & 0x7C00) >> 10; // Edit the color components... blue = _luminosityData[32 * _gammaLevel + blue]; green = _luminosityData[32 * _gammaLevel + green]; red = _luminosityData[32 * _gammaLevel + red]; // ...and recombine them *dataPtr = blue + (green << 5) + (red << 10); dataPtr++; } } // Adjust brightness... for (uint32 i = 0; i < size; i++) { uint16 pixel = data[i]; uint16 maskedPart = _brightnessMaskedBits & pixel; uint16 unmaskedPart = _brightnessUnmaskedBits & pixel; // Apply brightness (double the masked part)... maskedPart *= 2; // ...and recombine data[i] = unmaskedPart | maskedPart; } } } int GraphicsManager::getGammaLevel() { return _gammaLevel; } void GraphicsManager::setGammaLevel(int level) { if (level < 0) level = 0; if (level > 6) level = 6; _gammaLevel = level; _engine->getSubtitleManager()->initSubtitles(); _engine->getArchiveManager()->loadMice(); } void GraphicsManager::initLuminosityValues(int16 rMask, int16 gMask, int16 bMask) { if (bMask != 0x1F) { _luminosityIsInit = true; _brightnessUnmaskedBits = 0; _brightnessData[1] = 0xF7BC; _brightnessData[2] = 0xE738; _brightnessData[3] = 0xC630; _brightnessData[4] = 0x8420; _brightnessMaskedBits = rMask | gMask | bMask; _engine->getSubtitleManager()->initSubtitles(); _engine->getArchiveManager()->loadMice(); return; } if (gMask != 0x3E0) { _luminosityIsInit = true; _brightnessData[1] = 0xF79E; _brightnessData[2] = 0xE71C; _brightnessData[3] = 0xC618; _brightnessData[4] = 0x8410; _brightnessMaskedBits = rMask | gMask; _brightnessUnmaskedBits = 0x1F; _engine->getSubtitleManager()->initSubtitles(); _engine->getArchiveManager()->loadMice(); return; } if (rMask != 0x7C00) { _luminosityIsInit = true; _brightnessData[1] = 0xF3DE; _brightnessData[2] = 0xE39C; _brightnessData[3] = 0xC398; _brightnessData[4] = 0x8210; _brightnessMaskedBits = rMask; _brightnessUnmaskedBits = 0x3FF; _engine->getSubtitleManager()->initSubtitles(); _engine->getArchiveManager()->loadMice(); return; } } void GraphicsManager::initDecomp(PixMap *data, TBM *tbm) { _bgDecompTargetRect = tbm; _bgDecompOutBuffer = (byte *)data; _bgDecompFlags = 0; } void GraphicsManager::decompR(byte *data, int32 size) { int remainingSize = size; while (remainingSize > 0) { // Determine the next command byte byte commandByte; if (_bgDecompFlags & 0x10) { // Use saved byte from previous incomplete decompression _bgDecompFlags &= ~0x10; commandByte = _bgLastCompItem; } else { // Read next byte from input commandByte = *data; data++; remainingSize--; } // Check if this is a copy command if ((commandByte & 0x80) != 0) { // Check if we have enough data to process if (remainingSize == 0) { // Save state for next call _bgDecompFlags |= 0x10; _bgLastCompItem = commandByte; return; } // Read offset byte and decrement remaining size int offsetByte = *data; data++; remainingSize--; // Calculate parameters for copy command int count = ((commandByte & 0x70) >> 4) + 3; byte *sourcePtr = &_bgDecompOutBufferTemp[2 * (((commandByte & 0xF) << 8) + offsetByte) - 0x2000]; // Copy pixels from previous output for (int i = 0; i < count; i++) { *_bgDecompOutBufferTemp = *sourcePtr; _bgDecompOutBufferTemp += 2; sourcePtr += 2; } } else { // This is a run command (repeating value) int count = (commandByte >> 5) + 1; byte value = (commandByte & 0x1F) << 2; // Output repeated value for (int i = 0; i < count; i++) { *_bgDecompOutBufferTemp = value; _bgDecompOutBufferTemp += 2; } } } } void GraphicsManager::decompG(byte *data, int32 size) { int remainingSize = size; byte *outBuffer = _bgDecompOutBufferTemp; while (remainingSize > 0) { // Determine the next command byte byte commandByte; if (_bgDecompFlags & 0x10) { // Use saved byte from previous incomplete decompression _bgDecompFlags &= ~0x10; commandByte = _bgLastCompItem; } else { // Read next byte from input commandByte = *data; data++; remainingSize--; } // Check if this is a copy command if ((commandByte & 0x80) != 0) { // Check if we have enough data to process if (remainingSize == 0) { // Save state for next call _bgLastCompItem = commandByte; _bgDecompOutBufferTemp = outBuffer; _bgDecompFlags |= 0x10; return; } // Read offset byte and decrement remaining size int offsetByte = *data; data++; remainingSize--; // Calculate parameters for copy command int count = ((commandByte & 0x70) >> 4) + 3; int16 *sourcePtr = (int16 *)&outBuffer[2 * (((commandByte & 0xF) << 8) + offsetByte) - 0x2000]; // Copy green component (bits 5-9) from previous output for (int i = 0; i < count; i++) { *((int16 *)outBuffer) |= (*sourcePtr & 0x3E0); outBuffer += 2; sourcePtr++; } } else { // This is a run command (repeating value) int count = (commandByte >> 5) + 1; int16 greenValue = 32 * (commandByte & 0x1F); // Shift to green position (bits 5-9) // Output repeated value for (int i = 0; i < count; i++) { *((int16 *)outBuffer) |= greenValue; outBuffer += 2; } } } _bgDecompOutBufferTemp = outBuffer; } void GraphicsManager::decompB(byte *data, int32 size) { int remainingSize = size; while (remainingSize > 0) { // Determine the next command byte byte commandByte; if (_bgDecompFlags & 0x10) { // Use saved byte from previous incomplete decompression _bgDecompFlags &= ~0x10; commandByte = _bgLastCompItem; } else { // Read next byte from input commandByte = *data; data++; remainingSize--; } // Check if this is a copy command if ((commandByte & 0x80) != 0) { // Check if we have enough data to process if (remainingSize == 0) { // Save state for next call _bgDecompFlags |= 0x10; _bgLastCompItem = commandByte; return; } // Read offset byte and decrement remaining size int offsetByte = *data; data++; remainingSize--; // Calculate parameters for copy command int count = ((commandByte & 0x70) >> 4) + 3; byte *sourcePtr = &_bgDecompOutBufferTemp[2 * (((commandByte & 0xF) << 8) + offsetByte) - 0x2000]; // Copy blue component from previous output for (int i = 0; i < count; i++) { *_bgDecompOutBufferTemp = *sourcePtr; _bgDecompOutBufferTemp += 2; sourcePtr += 2; } } else { // This is a run command (repeating value) int count = (commandByte >> 5) + 1; byte blueValue = commandByte & 0x1F; // Blue component (bits 0-4) // Output repeated value for (int i = 0; i < count; i++) { *_bgDecompOutBufferTemp = blueValue; _bgDecompOutBufferTemp += 2; } } } } bool GraphicsManager::decomp16(byte *data, int32 size) { byte *srcBufData = data; int32 effSize = size; if (_bgDecompFlags == 0) { _bgDecompFlags = 0x1000; #ifdef SCUMM_LITTLE_ENDIAN _bgDecompOutBufferTemp = (_bgDecompOutBuffer + 1); // High byte for red #else _bgDecompOutBufferTemp = _bgDecompOutBuffer; // Low byte for red on BE #endif _bgDecompTargetRect->x = READ_LE_UINT32(data); _bgDecompTargetRect->y = READ_LE_UINT32(data + 1 * sizeof(uint32)); _bgDecompTargetRect->width = READ_LE_UINT32(data + 2 * sizeof(uint32)); _bgDecompTargetRect->height = READ_LE_UINT32(data + 3 * sizeof(uint32)); _bgOutChannelDataSizes[0] = READ_LE_UINT32(data + 4 * sizeof(uint32)); _bgOutChannelDataSizes[1] = READ_LE_UINT32(data + 5 * sizeof(uint32)); _bgOutChannelDataSizes[2] = READ_LE_UINT32(data + 6 * sizeof(uint32)); srcBufData = data + 28; effSize = size - 28; } while (effSize > 0) { int32 channel = _bgDecompFlags & 3; int32 remainingDataSize = _bgOutChannelDataSizes[channel]; if (effSize < remainingDataSize) { remainingDataSize = effSize; } else { _bgDecompFlags |= 0x100; } _bgOutChannelDataSizes[channel] -= remainingDataSize; if (channel == 0) { decompR(srcBufData, remainingDataSize); } else if (channel == 1) { decompB(srcBufData, remainingDataSize); } else if (channel == 2) { decompG(srcBufData, remainingDataSize); } srcBufData += remainingDataSize; effSize -= remainingDataSize; if ((_bgDecompFlags & 0x100) != 0) { _bgDecompFlags &= ~0x100; _bgDecompOutBufferTemp = _bgDecompOutBuffer; #ifndef SCUMM_LITTLE_ENDIAN if ((_bgDecompFlags & 3) == 0) _bgDecompOutBufferTemp++; #endif _bgDecompFlags++; // Go to the next channel if ((_bgDecompFlags & 3) == 3) return false; } } return true; } void GraphicsManager::drawItem(int cursor, int32 x, int32 y) { PixMap *cursorPtr = &_iconsBitmapData[1024 * cursor]; if (acquireSurface()) { PixMap *screenPtr = (PixMap *)_screenSurface.getPixels() + (640 * y + x); // Draw the item row by row... for (int row = 0; row < 32; row++) { memcpy(screenPtr, cursorPtr, 32 * sizeof(PixMap)); cursorPtr += 32; screenPtr += 640; } unlockSurface(); } } void GraphicsManager::drawItemDim(int cursor, int32 x, int32 y, int brightness) { PixMap *cursorPtr = &_iconsBitmapData[1024 * cursor]; if (acquireSurface()) { PixMap *screenPtr = (PixMap *)_screenSurface.getPixels() + (640 * y + x); // Draw the item row by row, with brightness adjustment... for (int row = 0; row < 32; row++) { for (int col = 0; col < 32; col++) { // Apply brightness mask and shift to dim the pixel... *screenPtr = (*cursorPtr & _brightnessData[brightness]) >> brightness; screenPtr++; cursorPtr++; } screenPtr += (640 - 32); } unlockSurface(); } } void GraphicsManager::drawMouse() { _mouseScreenBufStart = _mouseAreaRect.x + 640 * _mouseAreaRect.y; storeMouse(); if (acquireSurface()) { PixMap *cursorPtr = &_iconsBitmapData[1024 * _engine->_cursorType + 32 * _engine->_cursorYOffscreenOffset] + _engine->_cursorXOffscreenOffset; PixMap *screenPtr = (PixMap *)_screenSurface.getPixels() + _mouseScreenBufStart; // Draw cursor row by row... for (int row = 0; row < _mouseAreaRect.height; row++) { for (int col = 0; col < _mouseAreaRect.width; col++) { if (*cursorPtr) { *screenPtr = *cursorPtr; } cursorPtr++; screenPtr++; } cursorPtr += (32 - _mouseAreaRect.width); screenPtr += (640 - _mouseAreaRect.width); } unlockSurface(); } } void GraphicsManager::storeMouse() { if (acquireSurface()) { PixMap *mousePtr = _mouseBackBuffer; PixMap *screenPtr = (PixMap *)_screenSurface.getPixels() + _mouseScreenBufStart; for (int row = 0; row < _mouseAreaRect.height; row++) { memcpy(mousePtr, screenPtr, 2 * _mouseAreaRect.width); mousePtr += 32; screenPtr += 640; } unlockSurface(); } } void GraphicsManager::restoreMouse() { if (acquireSurface()) { PixMap *mousePtr = _mouseBackBuffer; PixMap *screenPtr = (PixMap *)_screenSurface.getPixels() + _mouseScreenBufStart; for (int row = 0; row < _mouseAreaRect.height; row++) { memcpy(screenPtr, mousePtr, 2 * _mouseAreaRect.width); mousePtr += 32; screenPtr += 640; } unlockSurface(); } } void GraphicsManager::newMouseLoc() { if (_engine->_cursorX > 639) _engine->_cursorX = 639; if (_engine->_cursorY > 479) _engine->_cursorY = 479; CursorHeader *cursorHotspot = &_cursorsDataHeader[_engine->_cursorType]; if (_engine->_cursorX - cursorHotspot->hotspotX >= 0) { _engine->_cursorXOffscreenOffset = 0; _mouseAreaRect.x = _engine->_cursorX - cursorHotspot->hotspotX; } else { _mouseAreaRect.x = 0; _engine->_cursorXOffscreenOffset = cursorHotspot->hotspotX - _engine->_cursorX; } if (_engine->_cursorY - cursorHotspot->hotspotY >= 0) { _engine->_cursorYOffscreenOffset = 0; _mouseAreaRect.y = _engine->_cursorY - cursorHotspot->hotspotY; } else { _mouseAreaRect.y = 0; _engine->_cursorYOffscreenOffset = cursorHotspot->hotspotY - _engine->_cursorY; } if (640 - _mouseAreaRect.x >= (32 - _engine->_cursorXOffscreenOffset)) { _mouseAreaRect.width = 32 - _engine->_cursorXOffscreenOffset; } else { _mouseAreaRect.width = 640 - _mouseAreaRect.x; } if (480 - _mouseAreaRect.y >= (32 - _engine->_cursorYOffscreenOffset)) { _mouseAreaRect.height = 32 - _engine->_cursorYOffscreenOffset; } else { _mouseAreaRect.height = 480 - _mouseAreaRect.y; } } void GraphicsManager::burstMouseArea(bool updateScreen) { burstBox(_mouseAreaRect.x, _mouseAreaRect.y, _mouseAreaRect.width, _mouseAreaRect.height, updateScreen); } void GraphicsManager::burstBox(int32 x, int32 y, int32 width, int32 height, bool updateScreen) { bool subtitlesDrawn = false; bool mouseDrawn = false; if (x < 640 && y < 480 && x + width <= 640 && y + height <= 480) { if ((_engine->getSubtitleManager()->_flags & 1) != 0 && x + width >= 80 && x <= 560 && y + height >= 420 && y <= 458) { _engine->getSubtitleManager()->vSubOn(); subtitlesDrawn = true; } if (canDrawMouse() && _mouseAreaRect.x <= x + width && _mouseAreaRect.x + _mouseAreaRect.width >= x && _mouseAreaRect.y <= y + height && _mouseAreaRect.y + _mouseAreaRect.height >= y) { drawMouse(); mouseDrawn = true; } g_system->copyRectToScreen(_screenSurface.getBasePtr(x, y), 640 * 2, x, y, width, height); // The original always draws to screen. The check here is to prevent redrawing the mouse to screen // when the mouse is disabled, something which happens constantly in the original and causes flicker // on slower platforms. if (updateScreen) { g_system->updateScreen(); } if (mouseDrawn) restoreMouse(); if (subtitlesDrawn) _engine->getSubtitleManager()->vSubOff(); } } bool GraphicsManager::canDrawMouse() { return _canDrawMouse; } void GraphicsManager::setMouseDrawable(bool drawable) { _canDrawMouse = drawable; } } // End of namespace LastExpress