Files
2026-02-02 04:50:13 +01:00

2261 lines
56 KiB
C++

/* 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 <http://www.gnu.org/licenses/>.
*
*/
#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