Initial commit

This commit is contained in:
2026-02-02 04:50:13 +01:00
commit 5b11698731
22592 changed files with 7677434 additions and 0 deletions

View File

@@ -0,0 +1,224 @@
/* 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 "common/file.h"
#include "common/system.h"
#include "graphics/cursorman.h"
#include "sci/graphics/drivers/gfxdriver_intern.h"
namespace Sci {
class SCI0_CGADriver final : public SCI0_DOSPreVGADriver {
public:
SCI0_CGADriver(bool emulateCGAModeOnEGACard, bool rgbRendering);
~SCI0_CGADriver() override;
void copyRectToScreen(const byte *src, int srcX, int srcY, int pitch, int destX, int destY, int w, int h, const PaletteMod*, const byte*) override;
void replaceCursor(const void *cursor, uint w, uint h, int hotspotX, int hotspotY, uint32 keycolor) override;
static bool validateMode(Common::Platform p) { return (p == Common::kPlatformDOS) && checkDriver(&_driverFile, 1); }
private:
void setupRenderProc() override;
uint16 *_cgaPatterns;
byte _palette[12];
const bool _disableMode5;
typedef void (*LineProc)(byte*&, const byte*, int, int, int, const uint16*, const byte*);
LineProc _renderLine;
static const char *_driverFile;
};
SCI0_CGADriver::SCI0_CGADriver(bool emulateCGAModeOnEGACard, bool rgbRendering) : SCI0_DOSPreVGADriver(4, 320, 200, rgbRendering), _cgaPatterns(nullptr), _disableMode5(emulateCGAModeOnEGACard), _renderLine(nullptr) {
static const byte cgaColors[48] = {
/*
// Canonical CGA palette
0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0x00, 0xAA, 0x00, 0x00, 0xAA, 0xAA,
0xAA, 0x00, 0x00, 0xAA, 0x00, 0xAA, 0xAA, 0x55, 0x00, 0xAA, 0xAA, 0xAA,
0x55, 0x55, 0x55, 0x55, 0x55, 0xFF, 0x55, 0xFF, 0x55, 0x55, 0xFF, 0xFF,
0xFF, 0x55, 0x55, 0xFF, 0x55, 0xFF, 0xFF, 0xFF, 0x55, 0xFF, 0xFF, 0xFF
*/
// Improved palette model taken from https://int10h.org/blog/2022/06/ibm-5153-color-true-cga-palette/
0x00, 0x00, 0x00, 0x00, 0x00, 0xC4, 0x00, 0xC4, 0x00, 0x00, 0xC4, 0xC4,
0xC4, 0x00, 0x00, 0xC4, 0x00, 0xC4, 0xC4, 0x7E, 0x00, 0xC4, 0xC4, 0xC4,
0x4E, 0x4E, 0x4E, 0x4E, 0x4E, 0xDC, 0x4E, 0xDC, 0x4E, 0x4E, 0xF3, 0xF3,
0xDC, 0x4E, 0x4E, 0xF3, 0x4E, 0xF3, 0xF3, 0xF3, 0x4E, 0xFF, 0xFF, 0xFF
};
static const byte modeColorMap[3][4] = {
{ 0, 2, 4, 6 },
{ 0, 3, 5, 7 },
{ 0, 3, 4, 7 }
};
Common::File drv;
if (!drv.open(_driverFile))
GFXDRV_ERR_OPEN(_driverFile);
byte palIndex = 1;
byte palIntensity = 1;
byte mode = 4;
byte colMap[4];
memset(colMap, 0, sizeof(colMap));
uint16 eprcOffs = 0;
uint32 cmd = drv.readUint32LE();
if ((cmd & 0xFF) == 0xE9)
eprcOffs = ((cmd >> 8) & 0xFFFF) + 3;
if (!eprcOffs || drv.readUint32LE() != 0x87654321 || !drv.skip(1) || !drv.seek(drv.readByte(), SEEK_CUR) || !drv.seek(drv.readByte(), SEEK_CUR))
GFXDRV_ERR_VERSION(_driverFile);
drv.skip(drv.readByte() == 0x90 ? 2 : 1);
uint16 op1st = drv.readUint16LE();
int op1len = drv.readUint16LE() - op1st;
// sanity check
assert(op1len > 0 && op1len < 0x100);
drv.seek(op1st, SEEK_SET);
byte *buf = new byte[op1len]();
drv.read(buf, op1len);
// Try figuring out the correct settings...
for (int i = 0; i < op1len - 7; ++i) {
uint32 cfg = READ_BE_UINT32(buf + i);
cmd = READ_BE_UINT32(buf + 4 + i);
if ((cmd >> 16) == 0xCD10 && (cfg & 0xff00ff) == 0xB80000) {
mode = (cfg >> 8) & 0xff;
} else if (cmd == 0xB40BCD10) {
if (cfg >> 8 == 0x00B701B3) {
palIndex = cfg & 1;
palIntensity = (cfg >> 4) & 1;
} else if (cfg >> 8 == 0x00B700B3) {
colMap[0] = (cfg & 0x0f) + ((cfg & 0x10) >> 1);
}
}
}
delete[] buf;
assert(palIndex <= 1);
assert(palIntensity <= 1);
for (int i = 1; i < 4; ++i)
colMap[i] = modeColorMap[(!_disableMode5 && mode == 5) ? 2 : palIndex][i] + (palIntensity << 3);
memset (_palette, 0, sizeof(_palette));
for (int i = 0; i < 4; ++i) {
for (int ii = 0; ii < 3; ++ii)
_palette[i * 3 + ii] = cgaColors[colMap[i] * 3 + ii];
}
assignPalette(_palette);
_cgaPatterns = new uint16[256]();
// The pattern map is always located right before the driver entry point proc.
drv.seek(eprcOffs - 512, SEEK_SET);
for (int i = 0; i < 256; ++i)
_cgaPatterns[i] = drv.readUint16LE();
drv.close();
}
SCI0_CGADriver::~SCI0_CGADriver() {
delete[] _cgaPatterns;
}
void SCI0_CGADriver::copyRectToScreen(const byte *src, int srcX, int srcY, int pitch, int destX, int destY, int w, int h, const PaletteMod*, const byte*) {
GFXDRV_ASSERT_READY;
byte diff = srcX & 1;
srcX &= ~1;
destX &= ~1;
w = (w + diff + 1) & ~1;
src += (srcY * pitch + srcX);
byte *dst = _compositeBuffer;
int ty = destY;
for (int i = 0; i < h; ++i) {
_renderLine(dst, src, w, srcX & 3, ++ty, _cgaPatterns, _internalPalette);
src += pitch;
}
g_system->copyRectToScreen(_compositeBuffer, w * _pixelSize, destX, destY, w, h);
}
void SCI0_CGADriver::replaceCursor(const void *cursor, uint w, uint h, int hotspotX, int hotspotY, uint32 keycolor) {
GFXDRV_ASSERT_READY;
// Instead of implementing the original cursor rendering code, we rely on the 8 bit cursor
// that has already been generated by the engine. We simply convert the colors as needed...
assert(keycolor == 1);
const byte *s = reinterpret_cast<const byte*>(cursor);
byte *d = _compositeBuffer;
for (uint i = w * h; i; --i)
*d++ = *s++ & 3;
CursorMan.replaceCursor(_compositeBuffer, w, h, hotspotX, hotspotY, keycolor);
}
template <typename T> void cgaRenderLine(byte *&dst, const byte *src, int w, int tx, int ty, const uint16 *patterns, const byte *pal) {
T *d = reinterpret_cast<T*>(dst);
const T *p = reinterpret_cast<const T*>(pal);
w >>= 1;
for (int i = 0; i < w; ++i) {
uint16 pattern = patterns[((src[0] & 0x0f) << 4) | (src[1] & 0x0f)];
src += 2;
byte sh = (ty & 3) << 1;
byte lo = ((pattern & 0xff) >> sh) | ((pattern & 0xff) << (8 - sh));
byte hi = (pattern >> (8 + sh)) | ((pattern >> 8) << (8 - sh));
if (sizeof(T) == 1) {
*d++ = (lo >> (6 - (tx << 1))) & 3;
*d++ = (hi >> (4 - (tx << 1))) & 3;
} else {
*d++ = p[(lo >> (6 - (tx << 1))) & 3];
*d++ = p[(hi >> (4 - (tx << 1))) & 3];
}
tx ^= 2;
}
dst = reinterpret_cast<byte*>(d);
}
void SCI0_CGADriver::setupRenderProc() {
static const LineProc lineProcs[] = {
&cgaRenderLine<byte>,
&cgaRenderLine<uint16>,
&cgaRenderLine<uint32>
};
assert((_pixelSize >> 1) < ARRAYSIZE(lineProcs));
_renderLine = lineProcs[_pixelSize >> 1];
}
const char *SCI0_CGADriver::_driverFile = "CGA320C.DRV";
SCI_GFXDRV_VALIDATE_IMPL(SCI0_CGA)
GfxDriver *SCI0_CGADriver_create(int rgbRendering, ...) {
return new SCI0_CGADriver(false, rgbRendering != 0);
}
} // End of namespace Sci

View File

@@ -0,0 +1,222 @@
/* 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 "common/file.h"
#include "common/system.h"
#include "graphics/cursorman.h"
#include "sci/graphics/drivers/gfxdriver_intern.h"
namespace Sci {
class SCI0_CGABWDriver final : public SCI0_DOSPreVGADriver {
public:
SCI0_CGABWDriver(uint32 monochromeColor, bool rgbRendering);
~SCI0_CGABWDriver() override;
void copyRectToScreen(const byte *src, int srcX, int srcY, int pitch, int destX, int destY, int w, int h, const PaletteMod*, const byte*) override;
void replaceCursor(const void *cursor, uint w, uint h, int hotspotX, int hotspotY, uint32 keycolor) override;
Common::Point getMousePos() const override;
void setMousePos(const Common::Point &pos) const override;
void setShakePos(int shakeXOffset, int shakeYOffset) const override;
void clearRect(const Common::Rect &r) const override;
Common::Point getRealCoords(Common::Point &pos) const override;
static bool validateMode(Common::Platform p) { return (p == Common::kPlatformDOS) && checkDriver(_driverFiles, 2); }
private:
void setupRenderProc() override;
byte _monochromePalette[6];
const byte *_monochromePatterns;
bool _earlyVersion;
typedef void (*LineProc)(byte*&, const byte*, int, int, int, const byte*, const byte*);
LineProc _renderLine;
static const char *_driverFiles[2];
};
SCI0_CGABWDriver::SCI0_CGABWDriver(uint32 monochromeColor, bool rgbRendering) : SCI0_DOSPreVGADriver(2, 640, 400, rgbRendering), _monochromePatterns(nullptr), _earlyVersion(false), _renderLine(nullptr) {
_monochromePalette[0] = _monochromePalette[1] = _monochromePalette[2] = 0;
_monochromePalette[3] = (monochromeColor >> 16) & 0xff;
_monochromePalette[4] = (monochromeColor >> 8) & 0xff;
_monochromePalette[5] = monochromeColor & 0xff;
assignPalette(_monochromePalette);
if (!(_monochromePatterns = SciGfxDrvInternal::monochrInit(_driverFiles[0], _earlyVersion)) && !(_monochromePatterns = SciGfxDrvInternal::monochrInit(_driverFiles[1], _earlyVersion)))
error("Failed to open '%s' or '%s'", _driverFiles[0], _driverFiles[1]);
}
SCI0_CGABWDriver::~SCI0_CGABWDriver() {
delete[] _monochromePatterns;
}
void SCI0_CGABWDriver::copyRectToScreen(const byte *src, int srcX, int srcY, int pitch, int destX, int destY, int w, int h, const PaletteMod*, const byte*) {
GFXDRV_ASSERT_READY;
byte *dst = _compositeBuffer;
int ty = destY & 7;
if (_earlyVersion) {
++ty;
byte diff = srcX & 1;
srcX &= ~1;
destX &= ~1;
w = (w + diff + 1) & ~1;
}
src += (srcY * pitch + srcX);
for (int i = 0; i < h; ++i) {
_renderLine(dst, src, w, srcX & 3, ty, _monochromePatterns, _internalPalette);
ty = (ty + 1) & 7;
src += pitch;
}
g_system->copyRectToScreen(_compositeBuffer, (w << 1) * _pixelSize, destX << 1, destY << 1, w << 1, h << 1);
}
void SCI0_CGABWDriver::replaceCursor(const void *cursor, uint w, uint h, int hotspotX, int hotspotY, uint32 keycolor) {
GFXDRV_ASSERT_READY;
// Instead of implementing the original cursor rendering code, we rely on the 8 bit cursor that
// has already been generated by the engine. We simply convert the colors as needed and scale the cursor...
assert(keycolor == 1);
keycolor = 0x0f;
w <<= 1;
const byte *s = reinterpret_cast<const byte*>(cursor);
byte *d0 = _compositeBuffer;
byte *d1 = _compositeBuffer + w;
for (uint i = 0; i < h; ++i) {
for (uint ii = 0; ii < w; ++ii) {
*d0++ = *d1++ = *s ? (*s ^ 0x0e) : 0;
if (ii & 1)
++s;
}
d0 += w;
d1 += w;
}
CursorMan.replaceCursor(_compositeBuffer, w, h << 1, hotspotX << 1, hotspotY << 1, keycolor);
}
Common::Point SCI0_CGABWDriver::getMousePos() const {
Common::Point res = GfxDriver::getMousePos();
res.x >>= 1;
res.y >>= 1;
return res;
}
void SCI0_CGABWDriver::setMousePos(const Common::Point &pos) const {
g_system->warpMouse(pos.x << 1, pos.y << 1);
}
void SCI0_CGABWDriver::setShakePos(int shakeXOffset, int shakeYOffset) const {
g_system->setShakePos(shakeXOffset << 1, shakeYOffset << 1);
}
void SCI0_CGABWDriver::clearRect(const Common::Rect &r) const {
Common::Rect r2(r.left << 1, r.top << 1, r.right << 1, r.bottom << 1);
GfxDriver::clearRect(r2);
}
Common::Point SCI0_CGABWDriver::getRealCoords(Common::Point &pos) const {
return Common::Point(pos.x << 1, pos.y << 1);
}
template <typename T> void cgabwRenderLine_v1(byte *&dst, const byte *src, int w, int tx, int ty, const byte *patterns, const byte *pal) {
const T *p = reinterpret_cast<const T*>(pal);
const uint16 *patterns16 = reinterpret_cast<const uint16*>(patterns);
T *d1 = reinterpret_cast<T*>(dst);
T *d2 = d1 + (w << 1);
w >>= 1;
for (int i = 0; i < w; ++i) {
uint16 pt = patterns16[((src[0] & 0x0f) << 4) | (src[1] & 0x0f)];
src += 2;
byte sh = (ty & 3) << 1;
byte lo = ((pt & 0xff) >> sh) | ((pt & 0xff) << (8 - sh));
byte hi = (pt >> (8 + sh)) | ((pt >> 8) << (8 - sh));
if (sizeof(T) == 1) {
*d1++ = *d2++ = ((lo >> (6 - (tx << 1))) >> 1) & 1;
*d1++ = *d2++ = (lo >> (6 - (tx << 1))) & 1;
*d1++ = *d2++ = ((hi >> (4 - (tx << 1))) >> 1) & 1;
*d1++ = *d2++ = (hi >> (4 - (tx << 1))) & 1;
} else {
*d1++ = *d2++ = p[((lo >> (6 - (tx << 1))) >> 1) & 1];
*d1++ = *d2++ = p[(lo >> (6 - (tx << 1))) & 1];
*d1++ = *d2++ = p[((hi >> (4 - (tx << 1))) >> 1) & 1];
*d1++ = *d2++ = p[(hi >> (4 - (tx << 1))) & 1];
}
tx ^= 2;
}
dst = reinterpret_cast<byte*>(d2);
}
template <typename T> void cgabwRenderLine_v2(byte *&dst, const byte *src, int w, int tx, int ty, const byte *patterns, const byte *pal) {
const T *p = reinterpret_cast<const T*>(pal);
T *d1 = reinterpret_cast<T*>(dst);
T *d2 = d1 + (w << 1);
for (int i = 0; i < w; ++i) {
byte pt = patterns[((*src++ & 0x0f) << 3) + ty] >> (6 - (tx << 1));
if (sizeof(T) == 1) {
*d1++ = *d2++ = (pt >> 1) & 1;
*d1++ = *d2++ = pt & 1;
} else {
*d1++ = *d2++ = p[(pt >> 1) & 1];
*d1++ = *d2++ = p[pt & 1];
}
tx = (tx + 1) & 3;
}
dst = reinterpret_cast<byte*>(d2);
}
void SCI0_CGABWDriver::setupRenderProc() {
static const LineProc lineProcs[] = {
&cgabwRenderLine_v1<byte>,
&cgabwRenderLine_v1<uint16>,
&cgabwRenderLine_v1<uint32>,
&cgabwRenderLine_v2<byte>,
&cgabwRenderLine_v2<uint16>,
&cgabwRenderLine_v2<uint32>
};
int t = _pixelSize >> 1;
if (!_earlyVersion)
t += 3;
assert(t < ARRAYSIZE(lineProcs));
_renderLine = lineProcs[t];
}
const char *SCI0_CGABWDriver::_driverFiles[2] = { "CGA320BW.DRV", "CGA320M.DRV" };
SCI_GFXDRV_VALIDATE_IMPL(SCI0_CGABW)
GfxDriver *SCI0_CGABWDriver_create(int rgbRendering, ...) {
static const uint32 monochromeColors[] = { 0xffbf66, 0x66ff66, 0xffffff };
va_list args;
va_start(args, rgbRendering);
int config = CLIP<int>(va_arg(args, int), 0, ARRAYSIZE(monochromeColors) - 1);
va_end(args);
return new SCI0_CGABWDriver(monochromeColors[config], rgbRendering != 0);
}
} // End of namespace Sci

View File

@@ -0,0 +1,368 @@
/* 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 "common/events.h"
#include "common/file.h"
#include "common/system.h"
#include "engines/util.h"
#include "graphics/cursorman.h"
#include "graphics/paletteman.h"
#include "sci/graphics/drivers/gfxdriver_intern.h"
#include "sci/sci.h"
namespace Sci {
Common::Point GfxDriver::getMousePos() const {
return g_system->getEventManager()->getMousePos();
}
void GfxDriver::setMousePos(const Common::Point &pos) const {
g_system->warpMouse(pos.x, pos.y);
}
void GfxDriver::setShakePos(int shakeXOffset, int shakeYOffset) const {
g_system->setShakePos(shakeXOffset, shakeYOffset);
}
void GfxDriver::clearRect(const Common::Rect &r) const {
GFXDRV_ASSERT_READY;
g_system->fillScreen(r, 0);
}
void GfxDriver::copyCurrentPalette(byte *dest, int start, int num) const {
assert(dest);
assert(start + num <= 256);
g_system->getPaletteManager()->grabPalette(dest, start, num);
}
bool GfxDriver::checkDriver(const char *const *driverNames, int listSize) {
Common::String missing;
while (listSize-- && *driverNames) {
if (Common::File::exists(*driverNames))
return true;
if (!missing.empty())
missing += " or ";
missing += "'" + Common::String(*driverNames) + "'";
++driverNames;
}
warning("Driver file %s not found. Starting game in default mode", missing.c_str());
return false;
}
SCI0_DOSPreVGADriver::SCI0_DOSPreVGADriver(int numColors, int screenW, int screenH, bool rgbRendering) :
GfxDriver(screenW, screenH, numColors), _requestRGBMode(rgbRendering), _colors(nullptr), _compositeBuffer(nullptr), _internalPalette(nullptr) {
}
SCI0_DOSPreVGADriver::~SCI0_DOSPreVGADriver() {
delete[] _compositeBuffer;
delete[] _internalPalette;
}
void SCI0_DOSPreVGADriver::assignPalette(const byte *colors) {
_colors = colors;
}
void SCI0_DOSPreVGADriver::initScreen(const Graphics::PixelFormat*) {
Graphics::PixelFormat format(Graphics::PixelFormat::createFormatCLUT8());
initGraphics(_screenW, _screenH, _requestRGBMode ? nullptr : &format);
format = g_system->getScreenFormat();
_pixelSize = format.bytesPerPixel;
if (_requestRGBMode && _pixelSize == 1)
warning("SCI0_DOSPreVGADriver::initScreen(): RGB rendering not available in this ScummVM build");
delete[] _compositeBuffer;
delete[] _internalPalette;
_internalPalette = nullptr;
_compositeBuffer = nullptr;
assert(_colors);
if (_pixelSize == 1) {
g_system->getPaletteManager()->setPalette(_colors, 0, _numColors);
} else {
byte *rgbpal = new byte[_numColors * _pixelSize]();
assert(rgbpal);
if (_pixelSize == 2)
SciGfxDrvInternal::updateRGBPalette<uint16>(rgbpal, _colors, 0, _numColors, format);
else if (_pixelSize == 4)
SciGfxDrvInternal::updateRGBPalette<uint32>(rgbpal, _colors, 0, _numColors, format);
else
error("SCI0_DOSPreVGADriver::initScreen(): Unsupported screen format");
_internalPalette = rgbpal;
CursorMan.replaceCursorPalette(_colors, 0, _numColors);
}
_compositeBuffer = new byte[_screenW * _screenH * _pixelSize]();
assert(_compositeBuffer);
setupRenderProc();
_ready = true;
}
void SCI0_DOSPreVGADriver::replaceMacCursor(const Graphics::Cursor*) {
// This is not needed for SCI0 (and not for any PC version of games at all)
error("SCI0_DOSPreVGADriver::replaceMacCursor(Graphics::Cursor*): Not implemented");
}
void SCI0_DOSPreVGADriver::copyCurrentBitmap(byte*, uint32) const {
// This is not needed for SCI0
error("SCI0_DOSPreVGADriver::copyCurrentBitmap(): Not implemented");
}
void SCI0_DOSPreVGADriver::copyCurrentPalette(byte *dest, int start, int num) const {
GFXDRV_ASSERT_READY;
if (_pixelSize == 1) {
GfxDriver::copyCurrentPalette(dest, start, num);
return;
}
assert(dest);
memcpy(dest + start * 3, _colors + start * 3, MIN<int>(num, _numColors) * 3);
}
void SCI0_DOSPreVGADriver::drawTextFontGlyph(const byte*, int, int, int, int, int, int, const PaletteMod*, const byte*) {
// This is only needed for scaling drivers with unscaled hires fonts.
error("SCI0_DOSPreVGADriver::drawTextFontGlyph(): Not implemented");
}
} // End of namespace Sci
namespace SciGfxDrvInternal {
void updateBitmapBuffer(byte *dst, int dstPitch, const byte *src, int srcPitch, int x, int y, int w, int h) {
if (!dst)
return;
if (w == srcPitch && w == dstPitch) {
memcpy(dst + y * w, src, w * h);
} else {
const byte *s = src;
byte *d = dst + y * dstPitch + x;
for (int i = 0; i < h; ++i) {
memcpy(d, s, w);
s += srcPitch;
d += dstPitch;
}
}
}
template <typename T> void updateRGBPalette(byte *dest, const byte *src, uint start, uint num, Graphics::PixelFormat &f) {
T *dst = &reinterpret_cast<T*>(dest)[start];
for (uint i = 0; i < num; ++i) {
*dst++ = f.RGBToColor(src[0], src[1], src[2]);
src += 3;
}
}
template void updateRGBPalette<byte>(byte *dest, const byte *src, uint start, uint num, Graphics::PixelFormat &f);
template void updateRGBPalette<uint16>(byte *dest, const byte *src, uint start, uint num, Graphics::PixelFormat &f);
template void updateRGBPalette<uint32>(byte *dest, const byte *src, uint start, uint num, Graphics::PixelFormat &f);
template <typename T> void scale2x(byte *dst, const byte *src, int pitch, int w, int h) {
const T *s = reinterpret_cast<const T*>(src);
int dstPitch = pitch << 1;
T *d1 = reinterpret_cast<T*>(dst);
T *d2 = d1 + dstPitch;
pitch -= w;
dstPitch += (pitch << 1);
while (h--) {
for (int i = 0; i < w; ++i) {
d1[0] = d1[1] = d2[0] = d2[1] = *s++;
d1 += 2;
d2 += 2;
}
s += pitch;
d1 += dstPitch;
d2 += dstPitch;
}
}
template void scale2x<byte>(byte *dst, const byte *src, int pitch, int w, int h);
template void scale2x<uint16>(byte *dst, const byte *src, int pitch, int w, int h);
template void scale2x<uint32>(byte *dst, const byte *src, int pitch, int w, int h);
byte findColorInPalette(uint32 rgbTriplet, const byte *palette, int numColors) {
byte color[3];
for (int i = 2; i >= 0; --i) {
color[i] = rgbTriplet & 0xFF;
rgbTriplet >>= 8;
}
int min = 65025;
byte match = 0;
for (int i = 0; i < numColors && min; ++i) {
const byte *rgb = &palette[i * 3];
int t = (color[0] - rgb[0]) * (color[0] - rgb[0]) + (color[1] - rgb[1]) * (color[1] - rgb[1]) + (color[2] - rgb[2]) * (color[2] - rgb[2]);
if (t < min) {
min = t;
match = i;
}
}
return match;
}
void renderWinMonochromeCursor(byte *dst, const void *src, const byte *palette, uint &w, uint &h, int &hotX, int &hotY, byte blackColor, byte whiteColor, uint32 &keycolor, bool noScale) {
const byte *s = reinterpret_cast<const byte*>(src);
uint16 min = 65025;
uint16 max = 0;
byte newKeyColor = 0;
while (newKeyColor == blackColor || newKeyColor == whiteColor)
++newKeyColor;
for (uint i = 0; i < w * h; ++i) {
byte col = *s++;
if (col == keycolor)
continue;
const byte *rgb = &palette[col * 3];
uint16 t = rgb[0] * 28 + rgb[1] * 150 + rgb[2] * 28;
if (t > max)
max = t;
if (t < min)
min = t;
}
#if 0
// The original interpreter will accidentally let the value overflow like this,
// making most cursors completely white. I have fixed it.
uint16 med = (uint16)(min + max) >> 1;
#else
uint16 med = (min + max) >> 1;
#endif
uint16 lim1 = max - (max - min) / 3;
uint16 lim2 = min + max - lim1;
s = reinterpret_cast<const byte*>(src);
if (w < 17 && h < 17 && !noScale) {
// Small cursors (like the insignia ring in KQ6) get scaled and dithered.
byte *dst2 = dst + (w << 1);
for (uint i = 0; i < h; ++i) {
for (uint ii = 0; ii < w; ++ii) {
byte col = *s++;
if (col == keycolor) {
*dst++ = *dst2++ = newKeyColor;
*dst++ = *dst2++ = newKeyColor;
continue;
}
const byte *rgb = &palette[col * 3];
uint16 t = rgb[0] * 28 + rgb[1] * 150 + rgb[2] * 28;
dst[0] = dst2[1] = t > lim2 ? whiteColor : blackColor;
dst2[0] = dst[1] = t > lim1 ? whiteColor : blackColor;
dst += 2;
dst2 += 2;
};
dst += (w << 1);
dst2 += (w << 1);
}
w <<= 1;
h <<= 1;
hotX <<= 1;
hotY <<= 1;
} else {
for (uint i = 0; i < w * h; ++i) {
byte col = *s++;
if (col == keycolor) {
*dst++ = newKeyColor;
continue;
}
const byte *rgb = &palette[col * 3];
uint16 t = rgb[0] * 28 + rgb[1] * 150 + rgb[2] * 28;
*dst++ = t > med ? whiteColor : blackColor;
}
}
keycolor = newKeyColor;
}
void renderPC98GlyphFat(byte *dst, int dstPitch, const byte *src, int srcPitch, int w, int h, int transpCol) {
dstPitch -= w;
srcPitch -= w;
while (h--) {
for (int i = 0; i < w - 1; ++i) {
uint8 a = *src++;
uint8 b = *src;
if (a != transpCol)
*dst = a;
else if (b != transpCol)
*dst = b;
++dst;
}
byte l = *src++;
if (l != transpCol)
*dst = l;
++dst;
src += srcPitch;
dst += dstPitch;
}
}
const byte *monochrInit(const char *drvFile, bool &earlyVersion) {
Common::File drv;
if (!drv.open(drvFile))
return nullptr;
uint16 eprcOffs = 0;
uint32 cmd = drv.readUint32LE();
if ((cmd & 0xFF) == 0xE9)
eprcOffs = ((cmd >> 8) & 0xFFFF) + 3;
if (!eprcOffs || drv.readUint32LE() != 0x87654321 || !drv.skip(1) || !drv.seek(drv.readByte(), SEEK_CUR) || !drv.seek(drv.readByte(), SEEK_CUR))
GFXDRV_ERR_VERSION(drv.getName());
// This is a safe assumption, as the early version pattern map is 4 times the size of the later one.
earlyVersion = (eprcOffs > 0x500);
uint16 size = earlyVersion ? 512 : 128;
byte *result = new byte[size];
// For CGA, the pattern map is always located before the entry point dispatcher proc.
drv.seek(eprcOffs - size, SEEK_SET);
drv.read(result, size);
// For Hercules there are some extra vars in between, all with initial values
// of zero. The last entry of the pattern map is definitely never zero...
int xtraOffs = 0;
while (result[size - 1 - xtraOffs] == 0)
++xtraOffs;
if (xtraOffs != 0) {
drv.seek(eprcOffs - size - xtraOffs, SEEK_SET);
drv.read(result, size);
}
drv.close();
if (earlyVersion) {
uint16 *r = reinterpret_cast<uint16*>(result);
for (int i = 0; i < 256; ++i)
r[i] = FROM_LE_16(r[i]);
}
return result;
}
} // End of namespace SciGfxDrvInternal

View File

@@ -0,0 +1,270 @@
/* 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 "common/system.h"
#include "engines/util.h"
#include "graphics/cursorman.h"
#include "graphics/paletteman.h"
#include "sci/graphics/drivers/gfxdriver_intern.h"
#include "sci/resource/resource.h"
namespace Sci {
GfxDefaultDriver::GfxDefaultDriver(uint16 screenWidth, uint16 screenHeight, bool isSCI0, bool rgbRendering) : GfxDriver(screenWidth, screenHeight, 0), _cursorUsesScreenPalette(true), _colorConv(nullptr), _colorConvMod(nullptr),
_srcPixelSize(1), _requestRGBMode(rgbRendering), _compositeBuffer(nullptr), _currentBitmap(nullptr), _internalPalette(nullptr), _currentPalette(nullptr), _virtualW(screenWidth), _virtualH(screenHeight), _alwaysCreateBmpBuffer(!isSCI0) {
switch (g_sci->getResMan()->getViewType()) {
case kViewEga:
_numColors = 16; // QFG PC-98 with 8 colors also reports 16 here
break;
case kViewAmiga:
_numColors = 32;
break;
case kViewAmiga64:
_numColors = 64;
break;
case kViewVga:
case kViewVga11:
_numColors = 256;
break;
default:
break;
}
if (_numColors == 0)
error("GfxDefaultDriver: Unknown view type");
}
GfxDefaultDriver::~GfxDefaultDriver() {
delete[] _compositeBuffer;
delete[] _currentBitmap;
delete[] _internalPalette;
delete[] _currentPalette;
}
template <typename T> void colorConvert(byte *dst, const byte *src, int pitch, int w, int h, const byte *pal) {
T *d = reinterpret_cast<T*>(dst);
const T *p = reinterpret_cast<const T*>(pal);
const byte *s = src;
pitch -= w;
while (h--) {
for (int i = 0; i < w; ++i)
*d++ = p[*s++];
s += pitch;
}
}
#define applyMod(a, b) MIN<uint>(a * (128 + b) / 128, 255)
template <typename T> void colorConvertMod(byte *dst, const byte *src, int pitch, int w, int h, const byte *srcPal, const byte *internalPal, Graphics::PixelFormat &f, const PaletteMod *mods, const byte *modMapping) {
T *d = reinterpret_cast<T*>(dst);
const T *p = reinterpret_cast<const T*>(internalPal);
const byte *s1 = src;
const byte *s2 = modMapping;
pitch -= w;
while (h--) {
for (int i = 0; i < w; ++i) {
byte m = *s2++;
if (m) {
const byte *col = &srcPal[*s1++ * 3];
*d++ = f.RGBToColor(applyMod(col[0], mods[m].r), applyMod(col[1], mods[m].g), applyMod(col[2], mods[m].b));
} else {
*d++ = p[*s1++];
}
}
s1 += pitch;
s2 += pitch;
}
}
#undef applyMod
void GfxDefaultDriver::initScreen(const Graphics::PixelFormat *srcRGBFormat) {
Graphics::PixelFormat format8bt(Graphics::PixelFormat::createFormatCLUT8());
initGraphics(_screenW, _screenH, srcRGBFormat ? srcRGBFormat : (_requestRGBMode ? nullptr : &format8bt));
_format = g_system->getScreenFormat();
int srcPixelSize = srcRGBFormat ? _format.bytesPerPixel : 1;
if (srcPixelSize != _srcPixelSize || _pixelSize != _format.bytesPerPixel) {
delete[] _compositeBuffer;
delete[] _currentBitmap;
delete[] _internalPalette;
delete[] _currentPalette;
_compositeBuffer = _currentBitmap = _internalPalette = _currentPalette = nullptr;
}
_pixelSize = _format.bytesPerPixel;
_srcPixelSize = srcPixelSize;
if (_requestRGBMode && _pixelSize == 1)
warning("GfxDefaultDriver::initScreen(): RGB rendering not available in this ScummVM build");
if (_pixelSize != _srcPixelSize) {
uint32 bufferSize = _screenW * _screenH * _pixelSize;
_compositeBuffer = new byte[bufferSize]();
assert(_compositeBuffer);
}
// Not needed for SCI0, except for rgb rendering. Unfortunately, SCI_VERSION_01
// does need it and we can't tell the version from the number of colors there.
// That's why we have the _alwaysCreateBmpBuffer flag...
if (_alwaysCreateBmpBuffer || _numColors > 16 || _pixelSize > 1) {
_currentBitmap = new byte[_virtualW * _virtualH * _srcPixelSize]();
assert(_currentBitmap);
}
if (_numColors > 16 || _pixelSize > 1) {
// Not needed for SCI0, except for rgb rendering
_currentPalette = new byte[256 * 3]();
assert(_currentPalette);
if (_pixelSize != _srcPixelSize) {
_internalPalette = new byte[256 * _pixelSize]();
assert(_internalPalette);
}
}
static const ColorConvProc colorConvProcs[] = {
&colorConvert<byte>,
&colorConvert<uint16>,
&colorConvert<uint32>
};
assert((_pixelSize >> 1) < ARRAYSIZE(colorConvProcs));
_colorConv = colorConvProcs[_pixelSize >> 1];
static const ColorConvModProc colorConvModProcs[] = {
&colorConvertMod<byte>,
&colorConvertMod<uint16>,
&colorConvertMod<uint32>
};
assert((_pixelSize >> 1) < ARRAYSIZE(colorConvModProcs));
_colorConvMod = colorConvModProcs[_pixelSize >> 1];
_ready = true;
}
void GfxDefaultDriver::setPalette(const byte *colors, uint start, uint num, bool update, const PaletteMod *palMods, const byte *palModMapping) {
GFXDRV_ASSERT_READY;
if (_pixelSize > 1) {
updatePalette(colors, start, num);
if (update)
copyRectToScreen(_currentBitmap, 0, 0, _virtualW, 0, 0, _virtualW, _virtualH, palMods, palModMapping);
if (_cursorUsesScreenPalette)
CursorMan.replaceCursorPalette(_currentPalette, 0, 256);
} else {
g_system->getPaletteManager()->setPalette(colors, start, num);
}
}
void GfxDefaultDriver::copyRectToScreen(const byte *src, int srcX, int srcY, int pitch, int destX, int destY, int w, int h, const PaletteMod *palMods, const byte *palModMapping) {
GFXDRV_ASSERT_READY;
assert (h >= 0 && w >= 0);
src += (srcY * pitch + srcX * _srcPixelSize);
if (src != _currentBitmap)
SciGfxDrvInternal::updateBitmapBuffer(_currentBitmap, _screenW * _srcPixelSize, src, pitch, destX * _srcPixelSize, destY, w * _srcPixelSize, h);
if (_pixelSize != _srcPixelSize) {
generateOutput(_compositeBuffer, src, pitch, w, h, palMods, palModMapping + destY * pitch + destX);
src = _compositeBuffer;
pitch = w * _pixelSize;
}
g_system->copyRectToScreen(src, pitch, destX, destY, w, h);
}
void GfxDefaultDriver::replaceCursor(const void *cursor, uint w, uint h, int hotspotX, int hotspotY, uint32 keycolor) {
GFXDRV_ASSERT_READY;
CursorMan.replaceCursor(cursor, w, h, hotspotX, hotspotY, keycolor);
if (_pixelSize > 1 && _currentPalette != nullptr)
CursorMan.replaceCursorPalette(_currentPalette, 0, 256);
}
void GfxDefaultDriver::replaceMacCursor(const Graphics::Cursor*) {
// This is not needed for any non-Mac version of games at all)
error("GfxDefaultDriver::replaceMacCursor(Graphics::Cursor*): Not implemented");
}
void GfxDefaultDriver::copyCurrentBitmap(byte *dest, uint32 size) const {
GFXDRV_ASSERT_READY;
assert(dest);
assert(size <= (uint32)(_screenW * _screenH));
// SCI 0 should not make calls to this method (except when using palette mods), but we have to know if it does...
if (!_currentBitmap)
error("GfxDefaultDriver::copyCurrentBitmap(): unexpected call");
// I have changed the implementation a bit from what the engine did before. For non-rgb rendering
// it would call OSystem::lockScreen() and then memcpy the data from there (which avoided the need
// for the extra bitmap buffer). However, OSystem::lockScreen() is meant more as an update method
// for the screen, the call to OSystem::unlockScreen() will turn the whole screen dirty (to be
// updated on the next OSysten::updateScreen() call. This is not what we need here, so I rather use
// the extra bitmap buffer (which is required for rgb rendering anyway).
memcpy(dest, _currentBitmap, size);
}
void GfxDefaultDriver::copyCurrentPalette(byte *dest, int start, int num) const {
GFXDRV_ASSERT_READY;
if (_pixelSize == 1) {
GfxDriver::copyCurrentPalette(dest, start, num);
return;
}
assert(dest);
assert(_currentPalette);
assert(start + num <= 256);
memcpy(dest + start * 3, _currentPalette + start * 3, num * 3);
}
void GfxDefaultDriver::drawTextFontGlyph(const byte*, int, int, int, int, int, int, const PaletteMod*, const byte*) {
// This is only needed for scaling drivers with unscaled hires fonts.
error("GfxDefaultDriver::drawTextFontGlyph(): Not implemented");
}
void GfxDefaultDriver::updatePalette(const byte *colors, uint start, uint num) {
memcpy(_currentPalette + start * 3, colors, num * 3);
if (_pixelSize == 4)
SciGfxDrvInternal::updateRGBPalette<uint32>(_internalPalette, colors, start, num, _format);
else if (_pixelSize == 2)
SciGfxDrvInternal::updateRGBPalette<uint16>(_internalPalette, colors, start, num, _format);
else
error("GfxDefaultDriver::updatePalette(): Unsupported pixel size %d", _pixelSize);
}
void GfxDefaultDriver::generateOutput(byte *dst, const byte *src, int pitch, int w, int h, const PaletteMod *palMods, const byte *palModMapping) {
if (palMods && palModMapping)
_colorConvMod(dst, src, pitch, w, h, _currentPalette, _internalPalette, _format, palMods, palModMapping);
else
_colorConv(dst, src, pitch, w, h, _internalPalette);
}
GfxDriver *GfxDefaultDriver_create(int rgbRendering, ...) {
va_list args;
va_start(args, rgbRendering);
int config = va_arg(args, int);
int width = va_arg(args, int);
int height = va_arg(args, int);
va_end(args);
return new GfxDefaultDriver(width, height, config == 0, rgbRendering != 0);
}
} // End of namespace Sci

View File

@@ -0,0 +1,304 @@
/* 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 "common/file.h"
#include "common/system.h"
#include "engines/util.h"
#include "graphics/cursorman.h"
#include "graphics/paletteman.h"
#include "sci/graphics/drivers/gfxdriver_intern.h"
namespace Sci {
SCI1_EGADriver::SCI1_EGADriver(bool rgbRendering) : GfxDriver(320, 200, 256), _requestRGBMode(rgbRendering), _egaColorPatterns(nullptr), _egaMatchTable(nullptr),
_currentBitmap(nullptr), _compositeBuffer(nullptr), _currentPalette(nullptr), _internalPalette(nullptr), _colAdjust(0), _renderLine(nullptr), _vScaleMult(2), _vScaleDiv(1) {
static const byte egaColors[48] = {
0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0x00, 0xAA, 0x00, 0x00, 0xAA, 0xAA,
0xAA, 0x00, 0x00, 0xAA, 0x00, 0xAA, 0xAA, 0x55, 0x00, 0xAA, 0xAA, 0xAA,
0x55, 0x55, 0x55, 0x55, 0x55, 0xFF, 0x55, 0xFF, 0x55, 0x55, 0xFF, 0xFF,
0xFF, 0x55, 0x55, 0xFF, 0x55, 0xFF, 0xFF, 0xFF, 0x55, 0xFF, 0xFF, 0xFF
};
_convPalette = egaColors;
}
SCI1_EGADriver::~SCI1_EGADriver() {
delete[] _egaMatchTable;
delete[] _egaColorPatterns;
delete[] _compositeBuffer;
delete[] _currentBitmap;
delete[] _currentPalette;
delete[] _internalPalette;
}
template <typename T> void ega640RenderLine(byte *&dst, const byte *src, int w, const byte *patterns, const byte *pal, bool) {
const T *p = reinterpret_cast<const T*>(pal);
T *d1 = reinterpret_cast<T*>(dst);
T *d2 = d1 + (w << 1);
for (int i = 0; i < w; ++i) {
byte pt = patterns[*src++];
if (sizeof(T) == 1) {
*d1++ = *d2++ = pt >> 4;
*d1++ = *d2++ = pt & 0x0f;
} else {
*d1++ = *d2++ = p[pt >> 4];
*d1++ = *d2++ = p[pt & 0x0f];
}
}
dst = reinterpret_cast<byte*>(d2);
}
void SCI1_EGADriver::initScreen(const Graphics::PixelFormat*) {
if (!_ready)
loadData();
Graphics::PixelFormat format(Graphics::PixelFormat::createFormatCLUT8());
initGraphics(_screenW << 1, _screenH * _vScaleMult / _vScaleDiv, _requestRGBMode ? nullptr : &format);
format = g_system->getScreenFormat();
_pixelSize = format.bytesPerPixel;
if (_requestRGBMode && _pixelSize == 1)
warning("SCI1_EGADriver::initScreen(): RGB rendering not available in this ScummVM build");
delete[] _egaColorPatterns;
delete[] _compositeBuffer;
delete[] _currentBitmap;
delete[] _currentPalette;
delete[] _internalPalette;
_internalPalette = nullptr;
_egaColorPatterns = _compositeBuffer = _currentBitmap = _currentPalette = nullptr;
if (_pixelSize == 1) {
g_system->getPaletteManager()->setPalette(_convPalette, 0, 16);
} else {
byte *rgbpal = new byte[_numColors * _pixelSize]();
assert(rgbpal);
if (_pixelSize == 2)
SciGfxDrvInternal::updateRGBPalette<uint16>(rgbpal, _convPalette, 0, 16, format);
else if (_pixelSize == 4)
SciGfxDrvInternal::updateRGBPalette<uint32>(rgbpal, _convPalette, 0, 16, format);
else
error("SCI1_EGADriver::initScreen(): Unsupported screen format");
_internalPalette = rgbpal;
CursorMan.replaceCursorPalette(_convPalette, 0, 16);
}
_compositeBuffer = new byte[(_screenW << 1) * (_screenH * _vScaleMult / _vScaleDiv) * _pixelSize]();
assert(_compositeBuffer);
_currentBitmap = new byte[_screenW * _screenH]();
assert(_currentBitmap);
_currentPalette = new byte[256 * 3]();
assert(_currentPalette);
_egaColorPatterns = new byte[256]();
assert(_egaColorPatterns);
static const LineProc lineProcs[] = {
&ega640RenderLine<byte>,
&ega640RenderLine<uint16>,
&ega640RenderLine<uint32>
};
assert((_pixelSize >> 1) < ARRAYSIZE(lineProcs));
_renderLine = lineProcs[_pixelSize >> 1];
_ready = true;
}
void SCI1_EGADriver::setPalette(const byte *colors, uint start, uint num, bool update, const PaletteMod*, const byte*) {
GFXDRV_ASSERT_READY;
memcpy(_currentPalette + start * 3, colors, num * 3);
byte *d = &_egaColorPatterns[start];
for (uint i = 0; i < num; ++i) {
*d++ = _egaMatchTable[((MIN<byte>((colors[0] >> 2) + _colAdjust, 63) & 0x38) << 3) | (MIN<byte>((colors[1] >> 2) + _colAdjust, 63) & 0x38) | (MIN<byte>((colors[2] >> 2) + _colAdjust, 63) >> 3)];
colors += 3;
}
if (update)
copyRectToScreen(_currentBitmap, 0, 0, _screenW, 0, 0, _screenW, _screenH, nullptr, nullptr);
}
void SCI1_EGADriver::copyRectToScreen(const byte *src, int srcX, int srcY, int pitch, int destX, int destY, int w, int h, const PaletteMod*, const byte*) {
GFXDRV_ASSERT_READY;
assert (h >= 0 && w >= 0);
src += (srcY * pitch + srcX);
if (src != _currentBitmap)
SciGfxDrvInternal::updateBitmapBuffer(_currentBitmap, _screenW, src, pitch, destX, destY, w, h);
uint16 realWidth, realHeight;
renderBitmap(_compositeBuffer, src, pitch, destY, w, h, _egaColorPatterns, _internalPalette, realWidth, realHeight);
Common::Point pos(destX, destY);
pos = getRealCoords(pos);
g_system->copyRectToScreen(_compositeBuffer, realWidth * _pixelSize, pos.x, pos.y, realWidth, realHeight);
}
void SCI1_EGADriver::replaceCursor(const void *cursor, uint w, uint h, int hotspotX, int hotspotY, uint32 keycolor) {
GFXDRV_ASSERT_READY;
const byte *s = reinterpret_cast<const byte*>(cursor);
int dstPitch = (w << 1);
byte *d1 = _compositeBuffer;
byte *d2 = _compositeBuffer + dstPitch;
uint32 newKeyColor = 0xFF;
for (uint i = 0; i < h; ++i) {
for (uint ii = 0; ii < w; ++ii) {
byte col = *s++;
if (col == keycolor) {
*d1++ = *d2++ = newKeyColor;
*d1++ = *d2++ = newKeyColor;
} else {
byte pt = _egaColorPatterns[col];
*d1++ = *d2++ = pt >> 4;
*d1++ = *d2++ = pt & 0x0f;
}
}
d1 += dstPitch;
d2 += dstPitch;
}
CursorMan.replaceCursor(_compositeBuffer, w << 1, h << 1, hotspotX << 1, hotspotY << 1, newKeyColor);
}
void SCI1_EGADriver::copyCurrentBitmap(byte *dest, uint32 size) const {
GFXDRV_ASSERT_READY;
assert(dest);
assert(size <= (uint32)(_screenW * _screenH));
memcpy(dest, _currentBitmap, size);
}
void SCI1_EGADriver::copyCurrentPalette(byte *dest, int start, int num) const {
GFXDRV_ASSERT_READY;
assert(dest);
assert(start + num <= 256);
memcpy(dest + start * 3, _currentPalette + start * 3, num * 3);
}
void SCI1_EGADriver::drawTextFontGlyph(const byte*, int, int, int, int, int, int, const PaletteMod*, const byte*) {
// This is only needed for scaling drivers with unscaled hires fonts.
error("SCI1_EGADriver::drawTextFontGlyph(): Not implemented");
}
Common::Point SCI1_EGADriver::getMousePos() const {
Common::Point res = GfxDriver::getMousePos();
res.x >>= 1;
res.y = res.y * _vScaleDiv / _vScaleMult;
return res;
}
void SCI1_EGADriver::setMousePos(const Common::Point &pos) const {
g_system->warpMouse(pos.x << 1, pos.y * _vScaleMult / _vScaleDiv);
}
void SCI1_EGADriver::setShakePos(int shakeXOffset, int shakeYOffset) const {
g_system->setShakePos(shakeXOffset << 1, shakeYOffset * _vScaleMult / _vScaleDiv);
}
void SCI1_EGADriver::clearRect(const Common::Rect &r) const {
Common::Rect r2(r.left << 1, r.top * _vScaleMult / _vScaleDiv, r.right << 1, r.bottom * _vScaleMult / _vScaleDiv);
GfxDriver::clearRect(r2);
}
Common::Point SCI1_EGADriver::getRealCoords(Common::Point &pos) const {
return Common::Point(pos.x << 1, pos.y * _vScaleMult / _vScaleDiv);
}
void SCI1_EGADriver::loadData() {
Common::File drv;
if (!drv.open(_driverFile))
GFXDRV_ERR_OPEN(_driverFile);
uint16 eprcOffs = 0;
uint32 cmd = drv.readUint32LE();
if ((cmd & 0xFF) == 0xE9)
eprcOffs = ((cmd >> 8) & 0xFFFF) + 3;
if (!eprcOffs || drv.readUint32LE() != 0x87654321 || !drv.skip(1) || !drv.seek(drv.readByte(), SEEK_CUR) || !drv.seek(drv.readByte(), SEEK_CUR) || drv.readUint32LE() != 0xFEDCBA98 || !drv.skip(4))
GFXDRV_ERR_VERSION(_driverFile);
uint32 pos = (drv.pos() + 1) & ~1;
drv.seek(pos);
drv.seek(drv.readUint16LE());
uint32 colResponse = drv.readUint32LE();
_numColors = (colResponse >> 8) & 0xffff;
if (_numColors < 16 || _numColors > 256 || (colResponse & 0xff0000ff) != 0xC30000B8)
error("SCI1_EGADriver: Failed to retrieve color info from '%s'", _driverFile);
drv.seek(pos + 20);
drv.seek(drv.readUint16LE());
byte *buff = new byte[128];
drv.read(buff, 128);
uint16 tableOffs = 0;
for (int i = 0; i < 120 && !tableOffs; ++i) {
uint32 c = READ_BE_UINT32(buff + i);
if (c == 0x8BD82E8A) {
if (buff[i + 4] == 0x87)
tableOffs = READ_LE_UINT16(buff + i + 5);
} else if (c == 0xD0E8D0E8) {
for (int ii = 4; ii < 14; ++ii) {
if (READ_BE_UINT16(buff + i + ii) == 0x83C0) {
c = READ_BE_UINT32(buff + i + ii + 2);
if ((c & 0xFFFFFF) == 0x83F83F)
_colAdjust = c >> 24;
}
}
}
}
delete[] buff;
if (!tableOffs)
error("SCI1_EGADriver: Failed to load color data from '%s'", _driverFile);
drv.seek(tableOffs);
byte *table = new byte[512]();
drv.read(table, 512);
_egaMatchTable = table;
if (drv.readUint16LE() != 152 || drv.readUint16LE() != 160)
GFXDRV_ERR_VERSION(_driverFile);
drv.close();
}
void SCI1_EGADriver::renderBitmap(byte *dst, const byte *src, int pitch, int, int w, int h, const byte *patterns, const byte *palette, uint16 &realWidth, uint16 &realHeight) {
for (int i = 0; i < h; ++i) {
_renderLine(dst, src, w, patterns, palette, 0);
src += pitch;
}
realWidth = w << 1;
realHeight = h << 1;
}
const char *SCI1_EGADriver::_driverFile = "EGA640.DRV";
SCI_GFXDRV_VALIDATE_IMPL(SCI1_EGA)
GfxDriver *SCI1_EGADriver_create(int rgbRendering, ...) {
return new SCI1_EGADriver(rgbRendering != 0);
}
} // End of namespace Sci

View File

@@ -0,0 +1,89 @@
/* 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/>.
*
*/
#ifndef SCI_GRAPHICS_DRIVERS_GFXDRIVER_H
#define SCI_GRAPHICS_DRIVERS_GFXDRIVER_H
#include "common/rendermode.h"
#include "common/rect.h"
#include "graphics/pixelformat.h"
#include "sci/detection.h"
namespace Graphics {
class Cursor;
}
namespace Sci {
struct PaletteMod;
class GfxDriver {
public:
enum DrawFlags : uint32 {
kHiResMode = 1 << 0,
kMovieMode = 1 << 1
};
GfxDriver(uint16 screenWidth, uint16 screenHeight, int numColors) : _screenW(screenWidth), _screenH(screenHeight), _numColors(numColors), _ready(false), _pixelSize(1) {}
virtual ~GfxDriver() {}
virtual void initScreen(const Graphics::PixelFormat *srcRGBFormat = nullptr) = 0; // srcRGBFormat: expect incoming data to have the specified rgb pixel format (used for Mac hicolor videos)
virtual void setPalette(const byte *colors, uint start, uint num, bool update, const PaletteMod *palMods, const byte *palModMapping) = 0;
virtual void copyRectToScreen(const byte *src, int srcX, int srcY, int pitch, int destX, int destY, int w, int h, const PaletteMod *palMods, const byte *palModMapping) = 0;
virtual void replaceCursor(const void *cursor, uint w, uint h, int hotspotX, int hotspotY, uint32 keycolor) = 0;
virtual void replaceMacCursor(const Graphics::Cursor *cursor) = 0;
virtual Common::Point getMousePos() const;
virtual void setMousePos(const Common::Point &pos) const;
virtual void setShakePos(int shakeXOffset, int shakeYOffset) const;
virtual void clearRect(const Common::Rect &r) const;
virtual void copyCurrentBitmap(byte *dest, uint32 size) const = 0;
virtual void copyCurrentPalette(byte *dest, int start, int num) const;
virtual void drawTextFontGlyph(const byte *src, int pitch, int hiresDestX, int hiresDestY, int hiresW, int hiresH, int transpColor, const PaletteMod *palMods, const byte *palModMapping) = 0;
virtual byte remapTextColor(byte color) const { return color; }
virtual void setColorMap(const byte *colorMap) {}
virtual Common::Point getRealCoords(Common::Point &pos) const { return pos; }
virtual void setFlags(uint32 flags) {}
virtual void clearFlags(uint32 flags) {}
virtual bool supportsPalIntensity() const = 0;
virtual bool supportsHiResGraphics() const = 0;
virtual bool driverBasedTextRendering() const = 0;
uint16 numColors() const { return _numColors; }
byte pixelSize() const { return _pixelSize; }
protected:
bool _ready;
static bool checkDriver(const char *const *driverNames, int listSize);
const uint16 _screenW;
const uint16 _screenH;
uint16 _numColors;
byte _pixelSize;
};
} // End of namespace Sci
namespace SciGfxDriver {
Common::RenderMode getRenderMode();
Sci::GfxDriver *create(Common::RenderMode renderMode, int width, int height);
} // End of namespace SciGfxDriver
#endif // SCI_GRAPHICS_DRIVERS_GFXDRIVER_H

View File

@@ -0,0 +1,194 @@
/* 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/>.
*
*/
#ifndef SCI_GRAPHICS_DRIVERS_GFXDRIVER_INTERN_H
#define SCI_GRAPHICS_DRIVERS_GFXDRIVER_INTERN_H
#include "common/platform.h"
#include "sci/graphics/drivers/gfxdriver.h"
namespace Sci {
class GfxDefaultDriver : public GfxDriver {
public:
GfxDefaultDriver(uint16 screenWidth, uint16 screenHeight, bool isSCI0, bool rgbRendering);
~GfxDefaultDriver() override;
void initScreen(const Graphics::PixelFormat *srcRGBFormat) override; // srcRGBFormat: expect incoming data to have the specified rgb pixel format (used for Mac hicolor videos)
void setPalette(const byte *colors, uint start, uint num, bool update, const PaletteMod *palMods, const byte *palModMapping) override;
void copyRectToScreen(const byte *src, int srcX, int srcY, int pitch, int destX, int destY, int w, int h, const PaletteMod *palMods, const byte *palModMapping) override;
void replaceCursor(const void *cursor, uint w, uint h, int hotspotX, int hotspotY, uint32 keycolor) override;
void replaceMacCursor(const Graphics::Cursor*) override;
void copyCurrentBitmap(byte *dest, uint32 size) const override;
void copyCurrentPalette(byte *dest, int start, int num) const override;
void drawTextFontGlyph(const byte*, int, int, int, int, int, int, const PaletteMod*, const byte*) override; // Only for HiRes fonts. Not implemented here.
bool supportsPalIntensity() const override { return true; }
bool supportsHiResGraphics() const override { return false; }
bool driverBasedTextRendering() const override { return false; }
protected:
void updatePalette(const byte *colors, uint start, uint num);
byte *_compositeBuffer;
byte *_currentBitmap;
byte *_currentPalette;
byte *_internalPalette;
uint16 _virtualW;
uint16 _virtualH;
Graphics::PixelFormat _format;
byte _srcPixelSize;
bool _cursorUsesScreenPalette;
const bool _alwaysCreateBmpBuffer;
const bool _requestRGBMode;
typedef void (*ColorConvProc)(byte*, const byte*, int, int, int, const byte*);
ColorConvProc _colorConv;
typedef void (*ColorConvModProc)(byte*, const byte*, int, int, int, const byte*, const byte*, Graphics::PixelFormat&, const PaletteMod*, const byte*);
ColorConvModProc _colorConvMod;
private:
void generateOutput(byte *dst, const byte *src, int pitch, int w, int h, const PaletteMod *palMods, const byte *palModMapping);
};
class SCI0_DOSPreVGADriver : public GfxDriver {
public:
SCI0_DOSPreVGADriver(int numColors, int screenW, int screenH, bool rgbRendering);
~SCI0_DOSPreVGADriver() override;
void initScreen(const Graphics::PixelFormat*) override;
void setPalette(const byte*, uint, uint, bool, const PaletteMod*, const byte*) override {}
void replaceMacCursor(const Graphics::Cursor*) override;
void copyCurrentBitmap(byte*, uint32) const override;
void drawTextFontGlyph(const byte*, int, int, int, int, int, int, const PaletteMod*, const byte*) override; // Only for HiRes fonts. Not implemented here.
void copyCurrentPalette(byte *dest, int start, int num) const override;
bool supportsPalIntensity() const override { return false; }
bool supportsHiResGraphics() const override { return false; }
bool driverBasedTextRendering() const override { return false; }
protected:
void assignPalette(const byte *colors);
byte *_compositeBuffer;
const byte *_internalPalette;
private:
virtual void setupRenderProc() = 0;
const bool _requestRGBMode;
const byte *_colors;
};
class UpscaledGfxDriver : public GfxDefaultDriver {
public:
UpscaledGfxDriver(int16 textAlignX, bool scaleCursor, bool rgbRendering);
~UpscaledGfxDriver() override;
void initScreen(const Graphics::PixelFormat *format) override;
void setPalette(const byte *colors, uint start, uint num, bool update, const PaletteMod *palMods, const byte *palModMapping) override;
void copyRectToScreen(const byte *src, int srcX, int srcY, int pitch, int destX, int destY, int w, int h, const PaletteMod *palMods, const byte *palModMapping) override;
void replaceCursor(const void *cursor, uint w, uint h, int hotspotX, int hotspotY, uint32 keycolor) override;
Common::Point getMousePos() const override;
void setMousePos(const Common::Point &pos) const override;
void setShakePos(int shakeXOffset, int shakeYOffset) const override;
void clearRect(const Common::Rect &r) const override;
Common::Point getRealCoords(Common::Point &pos) const override;
void drawTextFontGlyph(const byte *src, int pitch, int hiresDestX, int hiresDestY, int hiresW, int hiresH, int transpColor, const PaletteMod *palMods, const byte *palModMapping) override; // For HiRes fonts.
bool driverBasedTextRendering() const override { return true; }
protected:
UpscaledGfxDriver(uint16 scaledW, uint16 scaledH, int16 textAlignX, bool scaleCursor, bool rgbRendering);
void updateScreen(int destX, int destY, int w, int h, const PaletteMod *palMods, const byte *palModMapping);
void adjustCursorBuffer(uint16 newWidth, uint16 newHeight);
typedef void (*GlyphRenderProc)(byte*, int, const byte*, int, int, int, int);
GlyphRenderProc _renderGlyph;
typedef void (*ScaledRenderProc)(byte*, const byte*, int, int, int);
ScaledRenderProc _renderScaled;
uint16 _textAlignX;
uint16 _hScaleMult;
uint16 _vScaleMult;
uint16 _vScaleDiv;
byte *_scaledBitmap;
private:
virtual void renderBitmap(const byte *src, int pitch, int dx, int dy, int w, int h, int &realWidth, int &realHeight);
const bool _scaleCursor;
uint16 _cursorWidth;
uint16 _cursorHeight;
bool _needCursorBuffer;
};
class SCI1_EGADriver : public GfxDriver {
public:
SCI1_EGADriver(bool rgbRendering);
~SCI1_EGADriver() override;
void initScreen(const Graphics::PixelFormat*) override;
void setPalette(const byte *colors, uint start, uint num, bool update, const PaletteMod*, const byte*) override;
void copyRectToScreen(const byte *src, int srcX, int srcY, int pitch, int destX, int destY, int w, int h, const PaletteMod*, const byte*) override;
void replaceCursor(const void *cursor, uint w, uint h, int hotspotX, int hotspotY, uint32 keycolor) override;
void replaceMacCursor(const Graphics::Cursor *cursor) override {}
void copyCurrentBitmap(byte *dest, uint32 size) const override;
void copyCurrentPalette(byte *dest, int start, int num) const override;
void drawTextFontGlyph(const byte*, int, int, int, int, int, int, const PaletteMod*, const byte*) override; // Only for HiRes fonts. Not implemented here.
Common::Point getMousePos() const override;
void setMousePos(const Common::Point &pos) const override;
void setShakePos(int shakeXOffset, int shakeYOffset) const override;
void clearRect(const Common::Rect &r) const override;
Common::Point getRealCoords(Common::Point &pos) const override;
bool supportsPalIntensity() const override { return false; }
bool supportsHiResGraphics() const override { return false; }
bool driverBasedTextRendering() const override { return false; }
static bool validateMode(Common::Platform p) { return (p == Common::kPlatformDOS || p == Common::kPlatformWindows) && checkDriver(&_driverFile, 1); }
protected:
typedef void (*LineProc)(byte*&, const byte*, int, const byte*, const byte*, bool);
LineProc _renderLine;
const byte *_convPalette;
uint16 _vScaleMult, _vScaleDiv;
const byte *_egaMatchTable;
byte *_egaColorPatterns;
uint8 _colAdjust;
byte *_compositeBuffer;
byte *_currentPalette;
private:
virtual void loadData();
virtual void renderBitmap(byte *dst, const byte *src, int pitch, int y, int w, int h, const byte *patterns, const byte *palette, uint16 &realWidth, uint16 &realHeight);
byte *_currentBitmap;
const byte *_internalPalette;
const bool _requestRGBMode;
static const char *_driverFile;
};
#define GFXDRV_ASSERT_READY \
if (!_ready) \
error("%s(): initScreen() must be called before using this method", __FUNCTION__)
#define GFXDRV_ERR_OPEN(x) \
error("%s(): Failed to open '%s'", __FUNCTION__, x)
#define GFXDRV_ERR_VERSION(x) \
error("%s(): Driver file '%s' unknown version", __FUNCTION__, x)
#define SCI_GFXDRV_VALIDATE_IMPL(name) \
bool name##Driver_validateMode(Common::Platform platform) { \
return name##Driver::validateMode(platform); \
}
} // End of namespace Sci
namespace SciGfxDrvInternal {
template <typename T> void updateRGBPalette(byte *dest, const byte *src, uint start, uint num, Graphics::PixelFormat &f);
template <typename T> void scale2x(byte *dst, const byte *src, int pitch, int w, int h);
void updateBitmapBuffer(byte *dst, int dstPitch, const byte *src, int srcPitch, int x, int y, int w, int h);
byte findColorInPalette(uint32 rgbTriplet, const byte *palette, int numColors);
void renderWinMonochromeCursor(byte *dst, const void *src, const byte *palette, uint &w, uint &h, int &hotX, int &hotY, byte blackColor, byte whiteColor, uint32 &keycolor, bool noScale);
void renderPC98GlyphFat(byte *dst, int dstPitch, const byte *src, int srcPitch, int w, int h, int transpCol);
const byte *monochrInit(const char *drvFile, bool &earlyVersion);
} // End of namespace SciGfxDrvInternal
#endif // SCI_GRAPHICS_DRIVERS_GFXDRIVER_INTERN_H

View File

@@ -0,0 +1,196 @@
/* 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 "common/file.h"
#include "common/system.h"
#include "graphics/cursorman.h"
#include "sci/graphics/drivers/gfxdriver_intern.h"
namespace Sci {
class SCI0_HerculesDriver final : public SCI0_DOSPreVGADriver {
public:
SCI0_HerculesDriver(uint32 monochromeColor, bool rgbRendering, bool cropImage);
~SCI0_HerculesDriver() override;
void copyRectToScreen(const byte *src, int srcX, int srcY, int pitch, int destX, int destY, int w, int h, const PaletteMod*, const byte*) override;
void replaceCursor(const void *cursor, uint w, uint h, int hotspotX, int hotspotY, uint32 keycolor) override;
Common::Point getMousePos() const override;
void setMousePos(const Common::Point &pos) const override;
void setShakePos(int shakeXOffset, int shakeYOffset) const override;
void clearRect(const Common::Rect &r) const override;
Common::Point getRealCoords(Common::Point &pos) const override;
static bool validateMode(Common::Platform p) { return (p == Common::kPlatformDOS) && checkDriver(&_driverFile, 1); }
private:
void setupRenderProc() override;
const uint16 _centerX;
const uint16 _centerY;
byte _monochromePalette[6];
const byte *_monochromePatterns;
typedef void (*LineProc)(byte*&, const byte*, int, int, int, const byte*, const byte*);
LineProc _renderLine;
static const char *_driverFile;
};
SCI0_HerculesDriver::SCI0_HerculesDriver(uint32 monochromeColor, bool rgbRendering, bool cropImage) : SCI0_DOSPreVGADriver(2, cropImage ? 640 : 720, cropImage ? 300 : 350, rgbRendering),
_centerX(cropImage ? 0 : 40), _centerY(cropImage ? 0 : 25), _monochromePatterns(nullptr), _renderLine(nullptr) {
_monochromePalette[0] = _monochromePalette[1] = _monochromePalette[2] = 0;
_monochromePalette[3] = (monochromeColor >> 16) & 0xff;
_monochromePalette[4] = (monochromeColor >> 8) & 0xff;
_monochromePalette[5] = monochromeColor & 0xff;
assignPalette(_monochromePalette);
bool unused = false;
if (!(_monochromePatterns = SciGfxDrvInternal::monochrInit(_driverFile, unused)))
GFXDRV_ERR_OPEN(_driverFile);
}
SCI0_HerculesDriver::~SCI0_HerculesDriver() {
delete[] _monochromePatterns;
}
void SCI0_HerculesDriver::copyRectToScreen(const byte *src, int srcX, int srcY, int pitch, int destX, int destY, int w, int h, const PaletteMod*, const byte*) {
GFXDRV_ASSERT_READY;
byte *dst = _compositeBuffer;
byte sw = destY & 1;
src += (srcY * pitch + srcX);
destY = (destY & ~1) * 3 / 2 + (destY & 1);
int ty = destY & 7;
int rh = 0;
for (int i = 0; i < h; ++i) {
const byte *src2 = src;
_renderLine(dst, src2, w, srcX & 3, ty, _monochromePatterns, _internalPalette);
ty = (ty + 1) & 7;
++rh;
if (sw & 1)
sw ^= 2;
if (sw != 3) {
src += pitch;
sw ^= 1;
} else {
--i;
}
}
g_system->copyRectToScreen(_compositeBuffer, (w << 1) * _pixelSize, (destX << 1) + _centerX, destY + _centerY, w << 1, rh);
}
void SCI0_HerculesDriver::replaceCursor(const void *cursor, uint w, uint h, int hotspotX, int hotspotY, uint32 keycolor) {
GFXDRV_ASSERT_READY;
// Instead of implementing the original cursor rendering code, we rely on the 8 bit cursor that
// has already been generated by the engine. We simply convert the colors as needed and scale the cursor...
assert(keycolor == 1);
keycolor = 0x0f;
int alt = 0;
const byte *s = reinterpret_cast<const byte *>(cursor);
byte *d = _compositeBuffer;
for (uint i = 0; i < h; ++i) {
for (uint ii = 0; ii < (w << 1); ++ii) {
*d++ = *s ? (*s ^ 0x0e) : 0;
if (ii & 1)
++s;
}
if (i & 1) {
alt ^= 1;
if (alt) {
s -= w;
--i;
}
}
}
CursorMan.replaceCursor(_compositeBuffer, w << 1, (h & ~1) * 3 / 2 + (h & 1), hotspotX << 1, (hotspotY & ~1) * 3 / 2 + (hotspotY & 1), keycolor);
}
Common::Point SCI0_HerculesDriver::getMousePos() const {
Common::Point res = GfxDriver::getMousePos();
res.x = CLIP<int>(res.x - _centerX, 0, 639) >> 1;
res.y = (CLIP<int>(res.y - _centerY, 0, 299) * 2 + 1) / 3;
return res;
}
void SCI0_HerculesDriver::setMousePos(const Common::Point &pos) const {
g_system->warpMouse((pos.x << 1) + _centerX, (pos.y & ~1) * 3 / 2 + (pos.y & 1) + _centerY);
}
void SCI0_HerculesDriver::setShakePos(int shakeXOffset, int shakeYOffset) const {
g_system->setShakePos(shakeXOffset << 1, (shakeYOffset & ~1) * 3 / 2 + (shakeYOffset & 1));
}
void SCI0_HerculesDriver::clearRect(const Common::Rect &r) const {
Common::Rect r2((r.left << 1) + _centerX, (r.top & ~1) * 3 / 2 + (r.top & 1) + _centerY, (r.right << 1) + _centerX, (r.bottom & ~1) * 3 / 2 + (r.bottom & 1) + _centerY);
GfxDriver::clearRect(r2);
}
Common::Point SCI0_HerculesDriver::getRealCoords(Common::Point &pos) const {
return Common::Point((pos.x << 1) + _centerX, (pos.y & ~1) * 3 / 2 + (pos.y & 1) + _centerY);
}
template <typename T> void herculesRenderLine(byte *&dst, const byte *src, int w, int tx, int ty, const byte *patterns, const byte *pal) {
T *d = reinterpret_cast<T*>(dst);
const T *p = reinterpret_cast<const T*>(pal);
for (int i = 0; i < w; ++i) {
byte pt = patterns[((*src++ & 0x0f) << 3) + ty] >> (6 - (tx << 1));
if (sizeof(T) == 1) {
*d++ = (pt >> 1) & 1;
*d++ = pt & 1;
} else {
*d++ = p[(pt >> 1) & 1];
*d++ = p[pt & 1];
}
tx = (tx + 1) & 3;
}
dst = reinterpret_cast<byte*>(d);
}
void SCI0_HerculesDriver::setupRenderProc() {
static const LineProc lineProcs[] = {
&herculesRenderLine<byte>,
&herculesRenderLine<uint16>,
&herculesRenderLine<uint32>
};
assert((_pixelSize >> 1) < ARRAYSIZE(lineProcs));
_renderLine = lineProcs[_pixelSize >> 1];
}
const char *SCI0_HerculesDriver::_driverFile = "HERCMONO.DRV";
SCI_GFXDRV_VALIDATE_IMPL(SCI0_Hercules)
GfxDriver *SCI0_HerculesDriver_create(int rgbRendering, ...) {
static const uint32 monochromeColors[] = { 0xffbf66, 0x66ff66, 0xffffff };
va_list args;
va_start(args, rgbRendering);
int config = CLIP<int>(va_arg(args, int), 0, ARRAYSIZE(monochromeColors) - 1);
va_end(args);
return new SCI0_HerculesDriver(monochromeColors[config], rgbRendering != 0, false);
}
} // End of namespace Sci

View File

@@ -0,0 +1,168 @@
/* 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 "sci/graphics/drivers/gfxdriver.h"
#include "sci/sci.h"
#include "common/config-manager.h"
#include "common/language.h"
#include "common/platform.h"
namespace Sci {
#define SCI_GFXDRV_DCL1(name) \
extern GfxDriver *name##Driver_create(int, ...)
#define SCI_GFXDRV_DCL2(name) \
extern bool name##Driver_validateMode(Common::Platform); \
extern GfxDriver *name##Driver_create(int, ...)
SCI_GFXDRV_DCL1(GfxDefault);
SCI_GFXDRV_DCL1(UpscaledGfx);
SCI_GFXDRV_DCL1(PC98Gfx16Colors);
SCI_GFXDRV_DCL1(WindowsGfx256Colors);
SCI_GFXDRV_DCL2(SCI1_EGA);
SCI_GFXDRV_DCL2(SCI1_VGAGreyScale);
SCI_GFXDRV_DCL2(SCI0_CGA);
SCI_GFXDRV_DCL2(SCI0_CGABW);
SCI_GFXDRV_DCL2(SCI0_Hercules);
SCI_GFXDRV_DCL2(SCI0_PC98Gfx8Colors);
SCI_GFXDRV_DCL2(SCI1_PC98Gfx8Colors);
SCI_GFXDRV_DCL2(WindowsGfx16Colors);
#undef SCI_GFXDRV_DCL1
#undef SCI_GFXDRV_DCL2
enum HiresSetting {
kUnused = -1,
kDisable = 0,
kEnable = 1
};
struct GfxDriverInfo {
Common::RenderMode renderMode;
Common::Platform platform;
SciVersion versionMin;
SciVersion versionMax;
SciGameId gameId;
Common::Language language;
HiresSetting hires;
bool (*validateMode)(Common::Platform);
GfxDriver *(*createDriver)(int, ...);
int config;
};
#define INITPROCS1(x) nullptr, x##Driver_create
#define INITPROCS2(x) x##Driver_validateMode, x##Driver_create
static const GfxDriverInfo _gfxDriverInfos[] = {
// Selected modes
{ Common::kRenderEGA, Common::kPlatformUnknown, SCI_VERSION_1_EARLY, SCI_VERSION_1_1, GID_ALL, Common::UNK_LANG, kUnused, INITPROCS2(SCI1_EGA), 0 },
{ Common::kRenderVGAGrey, Common::kPlatformUnknown, SCI_VERSION_1_EARLY, SCI_VERSION_1_1, GID_ALL, Common::UNK_LANG, kUnused, INITPROCS2(SCI1_VGAGreyScale), 0 },
{ Common::kRenderCGA, Common::kPlatformUnknown, SCI_VERSION_0_EARLY, SCI_VERSION_1_EGA_ONLY, GID_ALL, Common::UNK_LANG, kUnused, INITPROCS2(SCI0_CGA), 0 },
{ Common::kRenderCGA_BW, Common::kPlatformUnknown, SCI_VERSION_0_EARLY, SCI_VERSION_1_EGA_ONLY, GID_ALL, Common::UNK_LANG, kUnused, INITPROCS2(SCI0_CGABW), 2 },
{ Common::kRenderHercA, Common::kPlatformUnknown, SCI_VERSION_0_EARLY, SCI_VERSION_1_EGA_ONLY, GID_ALL, Common::UNK_LANG, kUnused, INITPROCS2(SCI0_Hercules), 0 },
{ Common::kRenderHercG, Common::kPlatformUnknown, SCI_VERSION_0_EARLY, SCI_VERSION_1_EGA_ONLY, GID_ALL, Common::UNK_LANG, kUnused, INITPROCS2(SCI0_Hercules), 1 },
{ Common::kRenderPC98_8c, Common::kPlatformUnknown, SCI_VERSION_0_LATE, SCI_VERSION_0_LATE, GID_PQ2, Common::UNK_LANG, kUnused, INITPROCS2(SCI0_PC98Gfx8Colors), 1 },
{ Common::kRenderPC98_8c, Common::kPlatformUnknown, SCI_VERSION_01, SCI_VERSION_01, GID_ALL, Common::UNK_LANG, kUnused, INITPROCS2(SCI0_PC98Gfx8Colors), 0 },
{ Common::kRenderPC98_8c, Common::kPlatformUnknown, SCI_VERSION_1_LATE, SCI_VERSION_1_LATE, GID_ALL, Common::UNK_LANG, kUnused, INITPROCS2(SCI1_PC98Gfx8Colors), 0 },
{ Common::kRenderWin16c, Common::kPlatformUnknown, SCI_VERSION_1_1, SCI_VERSION_1_1, GID_ALL, Common::UNK_LANG, kUnused, INITPROCS2(WindowsGfx16Colors), 0 },
// Default modes
{ Common::kRenderDefault, Common::kPlatformPC98, SCI_VERSION_0_LATE, SCI_VERSION_0_LATE, GID_PQ2, Common::UNK_LANG, kUnused, INITPROCS1(PC98Gfx16Colors), 2 },
{ Common::kRenderDefault, Common::kPlatformPC98, SCI_VERSION_01, SCI_VERSION_01, GID_ALL, Common::UNK_LANG, kUnused, INITPROCS1(PC98Gfx16Colors), 0 },
{ Common::kRenderDefault, Common::kPlatformPC98, SCI_VERSION_1_EARLY, SCI_VERSION_1_LATE, GID_ALL, Common::UNK_LANG, kUnused, INITPROCS1(PC98Gfx16Colors), 1 },
{ Common::kRenderDefault, Common::kPlatformWindows, SCI_VERSION_1_1, SCI_VERSION_1_1, GID_ALL, Common::UNK_LANG, kDisable, INITPROCS1(WindowsGfx256Colors), 0 },
{ Common::kRenderDefault, Common::kPlatformWindows, SCI_VERSION_1_1, SCI_VERSION_1_1, GID_KQ6, Common::UNK_LANG, kEnable, INITPROCS1(WindowsGfx256Colors), 1 },
{ Common::kRenderDefault, Common::kPlatformDOS, SCI_VERSION_1_1, SCI_VERSION_1_1, GID_KQ6, Common::UNK_LANG, kEnable, INITPROCS1(WindowsGfx256Colors), 1 },
{ Common::kRenderDefault, Common::kPlatformUnknown, SCI_VERSION_0_EARLY, SCI_VERSION_1_1, GID_ALL, Common::KO_KOR, kUnused, INITPROCS1(UpscaledGfx), 0 },
{ Common::kRenderDefault, Common::kPlatformUnknown, SCI_VERSION_0_EARLY, SCI_VERSION_0_LATE, GID_ALL, Common::UNK_LANG, kUnused, INITPROCS1(GfxDefault), 0 },
{ Common::kRenderDefault, Common::kPlatformUnknown, SCI_VERSION_01, SCI_VERSION_1_1, GID_ALL, Common::UNK_LANG, kUnused, INITPROCS1(GfxDefault), 1 }
};
#undef INITPROCS1
#undef INITPROCS2
} // End of namespace Sci
namespace SciGfxDriver {
using namespace Sci;
Common::RenderMode getRenderMode() {
// Check if the selected render mode is available for the game. This is quite specific for each game. Sometime it
// is only EGA, sometimes only CGA b/w without CGA 4 colors, etc. Also set default mode if undithering is enabled.
bool undither = ConfMan.getBool("disable_dithering");
Common::RenderMode selectedMode = ConfMan.hasKey("render_mode") ? Common::parseRenderMode(ConfMan.get("render_mode")) : Common::kRenderDefault;
Common::RenderMode result = selectedMode;
Common::Language lang = g_sci->getLanguage();
Common::Platform platform = g_sci->getPlatform();
SciVersion version = getSciVersion();
// No extra modes supported for the Korean fan-patched games.
// Also set default mode if undithering is enabled for a 16 colors game and the render mode is not set to CGA, Hercules
// or PC-98 8 colors.
if (lang == Common::KO_KOR || (undither && version <= SCI_VERSION_1_EGA_ONLY && selectedMode != Common::kRenderCGA &&
selectedMode != Common::kRenderCGA_BW && selectedMode != Common::kRenderHercA && selectedMode != Common::kRenderHercG && selectedMode != Common::kRenderPC98_8c))
result = Common::kRenderDefault;
if (result == Common::kRenderDefault)
return result;
// Now we just go over the first part of the table and if we find a config for the selected render mode and check
// if the mode is usable. Otherwise we return the default mode.
for (const GfxDriverInfo *info = _gfxDriverInfos; info->renderMode != Common::kRenderDefault; ++info) {
if (info->renderMode == selectedMode && info->versionMin <= version && version <= info->versionMax && (info->gameId == GID_ALL || info->gameId == g_sci->getGameId())
&& (info->language == Common::UNK_LANG || info->language == lang) && (info->hires == kUnused || (info->hires == kEnable) == g_sci->useHiresGraphics())) {
result = (info->validateMode == nullptr || info->validateMode(platform)) ? selectedMode : Common::kRenderDefault;
break;
}
}
return result;
}
GfxDriver *create(Common::RenderMode renderMode, int width, int height) {
GfxDriver *result = nullptr;
int requestRGB = (int)(ConfMan.hasKey("palette_mods") && ConfMan.getBool("palette_mods")) || (ConfMan.hasKey("rgb_rendering") && ConfMan.getBool("rgb_rendering"));
SciVersion version = getSciVersion();
SciGameId gameId = g_sci->getGameId();
Common::Language lang = g_sci->getLanguage();
Common::Platform platform = g_sci->getPlatform();
bool hires = g_sci->useHiresGraphics();
bool undither = ConfMan.hasKey("disable_dithering") ? ConfMan.getBool("disable_dithering") : false;
bool winCursors = ConfMan.hasKey("windows_cursors") ? ConfMan.getBool("windows_cursors") : false;
// If a specific render mode is requested, we try to find a driver that supports it. Otherwise, we try to find a
// driver that supports the current platform, game and version.
for (const GfxDriverInfo *info = _gfxDriverInfos; info < &_gfxDriverInfos[ARRAYSIZE(_gfxDriverInfos)]; ++info) {
if (((info->renderMode == Common::kRenderDefault && (info->platform == Common::kPlatformUnknown || info->platform == platform)) || (renderMode != Common::kRenderDefault && info->renderMode == renderMode))
&& version >= info->versionMin && version <= info->versionMax && (info->gameId == GID_ALL || info->gameId == gameId) && (info->language == Common::UNK_LANG || info->language == lang) && (info->hires == kUnused || (info->hires == kEnable) == hires)) {
result = info->createDriver(requestRGB, info->config, width, height, undither, winCursors, hires);
break;
}
}
return result;
}
} // End of namespace SciGfxDriver

View File

@@ -0,0 +1,244 @@
/* 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 "common/system.h"
#include "graphics/cursorman.h"
#include "sci/graphics/drivers/gfxdriver_intern.h"
namespace Sci {
class PC98Gfx16ColorsDriver final : public UpscaledGfxDriver {
public:
enum SjisFontStyle {
kFontStyleNone,
kFontStyleTextMode,
kFontStyleSpecialSCI1
};
PC98Gfx16ColorsDriver(int textAlignX, bool cursorScaleWidth, bool cursorScaleHeight, SjisFontStyle sjisFontStyle, bool rgbRendering, bool needsUnditheringPalette);
~PC98Gfx16ColorsDriver() override;
void initScreen(const Graphics::PixelFormat *format) override;
void setPalette(const byte*, uint, uint, bool, const PaletteMod*, const byte*) override {}
void replaceCursor(const void *cursor, uint w, uint h, int hotspotX, int hotspotY, uint32 keycolor) override;
byte remapTextColor(byte color) const override;
private:
const byte *_convPalette;
const byte *_textModePalette;
const bool _cursorScaleHeightOnly;
SjisFontStyle _fontStyle;
};
PC98Gfx16ColorsDriver::PC98Gfx16ColorsDriver(int textAlignX, bool cursorScaleWidth, bool cursorScaleHeight, SjisFontStyle sjisFontStyle, bool rgbRendering, bool needsUnditheringPalette) :
UpscaledGfxDriver(textAlignX, cursorScaleWidth && cursorScaleHeight, rgbRendering), _textModePalette(nullptr), _fontStyle(sjisFontStyle),
_cursorScaleHeightOnly(!cursorScaleWidth && cursorScaleHeight), _convPalette(nullptr) {
// Palette taken from driver file (identical for all versions of the
// driver I have seen so far, also same for SCI0 and SCI1)
static const byte pc98colorsV16[] = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x07, 0x00, 0x00, 0x07, 0x07,
0x07, 0x00, 0x00, 0x07, 0x00, 0x07, 0x05, 0x07, 0x00, 0x09, 0x09, 0x09,
0x06, 0x06, 0x06, 0x00, 0x00, 0x0f, 0x07, 0x0f, 0x06, 0x00, 0x0f, 0x0f,
0x0f, 0x00, 0x00, 0x0f, 0x00, 0x0f, 0x0f, 0x0f, 0x00, 0x0f, 0x0f, 0x0f
};
byte *col = new byte[768]();
const byte *s = pc98colorsV16;
for (uint i = 0; i < sizeof(pc98colorsV16) / 3; ++i) {
int a = ((i & 6) == 4 || (i & 6) == 2 ? i ^ 6 : i) * 3;
col[a + 0] = (s[1] * 0x11);
col[a + 1] = (s[0] * 0x11);
col[a + 2] = (s[2] * 0x11);
s += 3;
}
if (_fontStyle == kFontStyleTextMode) {
byte *d = &col[48];
for (uint8 i = 0; i < 8; ++i) {
*d++ = (i & 4) ? 0xff : 0;
*d++ = (i & 2) ? 0xff : 0;
*d++ = (i & 1) ? 0xff : 0;
}
}
if (needsUnditheringPalette) {
// We store the text mode color separately, since we need the slots for the undithering.
if (_fontStyle == kFontStyleTextMode) {
byte *tpal = new byte[24]();
memcpy(tpal, &col[48], 24);
_textModePalette = tpal;
}
// For the undithered mode, we generate the missing colors using the same formula as for EGA.
byte *d = &col[48];
for (int i = 16; i < 256; i++) {
const byte *s1 = &col[(i & 0x0f) * 3];
const byte *s2 = &col[(i >> 4) * 3];
for (int ii = 0; ii < 3; ++ii)
*d++ = (byte)(0.5 + (pow(0.5 * ((pow(*s1++ / 255.0, 2.2 / 1.0) * 255.0) + (pow(*s2++ / 255.0, 2.2 / 1.0) * 255.0)) / 255.0, 1.0 / 2.2) * 255.0));
}
}
_convPalette = col;
}
PC98Gfx16ColorsDriver::~PC98Gfx16ColorsDriver() {
delete[] _convPalette;
delete[] _textModePalette;
}
void renderPC98GlyphSpecial(byte *dst, int dstPitch, const byte *src, int srcPitch, int w, int h, int transpCol) {
assert(h == 16); // This is really not suitable for anything but the special SCI1 PC98 glyph drawing
dstPitch -= w;
srcPitch -= w;
while (h--) {
if (h > 10 || h < 5) {
for (int i = 0; i < w - 1; ++i) {
uint8 a = *src++;
uint8 b = *src;
if (a != transpCol)
*dst = a;
else if (b != transpCol)
*dst = b;
++dst;
}
byte l = *src++;
if (l != transpCol)
*dst = l;
++dst;
} else {
for (int i = 0; i < w; ++i) {
byte in = *src++;
if (in != transpCol)
*dst = in;
++dst;
}
}
src += srcPitch;
dst += dstPitch;
}
}
void PC98Gfx16ColorsDriver::initScreen(const Graphics::PixelFormat *format) {
UpscaledGfxDriver::initScreen(format);
assert(_convPalette);
GfxDefaultDriver::setPalette(_convPalette, 0, 256, true, nullptr, nullptr);
if (_fontStyle == kFontStyleTextMode)
_renderGlyph = &SciGfxDrvInternal::renderPC98GlyphFat;
if (_fontStyle != kFontStyleSpecialSCI1)
return;
_renderGlyph = &renderPC98GlyphSpecial;
}
void PC98Gfx16ColorsDriver::replaceCursor(const void *cursor, uint w, uint h, int hotspotX, int hotspotY, uint32 keycolor) {
GFXDRV_ASSERT_READY;
if (!_cursorScaleHeightOnly) {
UpscaledGfxDriver::replaceCursor(cursor, w, h, hotspotX, hotspotY, keycolor);
return;
}
// Special case for PQ2 which scales the cursor height (but not the width)
adjustCursorBuffer(w, h << 1);
const byte *s = reinterpret_cast<const byte*>(cursor);
byte *d = _compositeBuffer;
for (uint i = 0; i < h; ++i) {
memcpy(d, s, w);
d += w;
memcpy(d, s, w);
d += w;
s += w;
}
CursorMan.replaceCursor(_compositeBuffer, w, h << 1, hotspotX, hotspotY << 1, keycolor);
}
byte PC98Gfx16ColorsDriver::remapTextColor(byte color) const {
// Always black for QFG and SCI1. For QFG, this is on purpose. The code just copies the inverted glyph data
// into all 4 vmem planes. For SCI1 the driver opcode actually has a pen color argument, but it isn't put
// to any use. Not sure if it is intended.
if (_fontStyle != kFontStyleTextMode)
return 0;
color &= 7;
// This seems to be a bug in the original PQ2 interpreter, which I replicate, so that we get the same colors.
// What they were trying to do is just getting the rgb bits in the right order (switch red and green). But
// instead, before checking and setting the bits, they also copy the full color byte to the target color. So,
// the extra bits come just on top. The result: All green and red colors are turned into yellow, all magenta
// and cyan colors are turned into white.
if (color & 2)
color |= 4;
if (color & 4)
color |= 2;
// This is the blue color that PQ2 uses basically for all Japanese text...
if (color == 0)
color = 1;
byte textCol = color;
color += 0x10;
if (_textModePalette) {
// If we have used up the whole space of the CLUT8 for the undithering, we try
// to relocate the color which will work for all text mode colors with the default
// palette that is used by the PC-98 ports...
for (int i = 0; i < 256; ++i) {
if (_convPalette[i * 3] != _textModePalette[textCol * 3] || _convPalette[i * 3 + 1] != _textModePalette[textCol * 3 + 1] || _convPalette[i * 3 + 2] != _textModePalette[textCol * 3 + 2])
continue;
color = i;
break;
}
if (color >= 16)
color = 0;
}
return color;
}
GfxDriver *PC98Gfx16ColorsDriver_create(int rgbRendering, ...) {
va_list args;
va_start(args, rgbRendering);
int config = va_arg(args, int);
va_arg(args, int);
va_arg(args, int);
bool undither = (va_arg(args, int) != 0);
va_end(args);
GfxDriver *result = nullptr;
if (config == 0)
result = new PC98Gfx16ColorsDriver(8, false, false, PC98Gfx16ColorsDriver::kFontStyleNone, rgbRendering != 0, true);
else if (config == 1)
result = new PC98Gfx16ColorsDriver(1, true, true, PC98Gfx16ColorsDriver::kFontStyleSpecialSCI1, rgbRendering != 0, true);
else if (config == 2) {
// PQ2 is a bit special, probably the oldest of the PC-98 ports. Unlike all the others, it uses text mode print,
// so the text color is a system color outside the normal 16 colors palette. The original does not even have a
// 16 colors mode driver. Only the 8 colors mode, where the colors are identical for text and graphics mode.
// But we do want to provide the 16 colors mode, since it is not a big deal (i.e., it does not require data
// from a driver file and the fat print is also already there for the 8 colors mode). So we just make the
// necessary adjustments.
result = new PC98Gfx16ColorsDriver(8, false, true, PC98Gfx16ColorsDriver::kFontStyleTextMode, rgbRendering != 0, undither);
}
return result;
}
} // End of namespace Sci

View File

@@ -0,0 +1,159 @@
/* 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 "common/system.h"
#include "graphics/cursorman.h"
#include "sci/graphics/drivers/gfxdriver_intern.h"
namespace Sci {
class SCI0_PC98Gfx8ColorsDriver final : public UpscaledGfxDriver {
public:
SCI0_PC98Gfx8ColorsDriver(bool cursorScaleHeight, bool useTextModeForSJISChars, bool rgbRendering);
~SCI0_PC98Gfx8ColorsDriver() override;
void initScreen(const Graphics::PixelFormat *format) override;
void setPalette(const byte*, uint, uint, bool, const PaletteMod*, const byte*) override {}
void replaceCursor(const void *cursor, uint w, uint h, int hotspotX, int hotspotY, uint32 keycolor) override;
byte remapTextColor(byte color) const override;
static bool validateMode(Common::Platform p) { return (p == Common::kPlatformPC98) && checkDriver(_driverFiles, 2); }
private:
const byte *_convPalette;
const bool _cursorScaleHeightOnly;
const bool _useTextMode;
static const char *_driverFiles[2];
};
SCI0_PC98Gfx8ColorsDriver::SCI0_PC98Gfx8ColorsDriver(bool cursorScaleHeight, bool useTextModeForSJISChars, bool rgbRendering) :
UpscaledGfxDriver(8, false, rgbRendering), _cursorScaleHeightOnly(cursorScaleHeight), _useTextMode(useTextModeForSJISChars), _convPalette(nullptr) {
byte *col = new byte[8 * 3]();
_convPalette = col;
for (uint8 i = 0; i < 8; ++i) {
*col++ = (i & 4) ? 0xff : 0;
*col++ = (i & 2) ? 0xff : 0;
*col++ = (i & 1) ? 0xff : 0;
}
}
SCI0_PC98Gfx8ColorsDriver::~SCI0_PC98Gfx8ColorsDriver() {
delete[] _convPalette;
}
void pc98SimpleDither(byte *dst, const byte *src, int pitch, int w, int h) {
int dstPitch = pitch << 1;
byte *d1 = dst;
byte *d2 = d1 + dstPitch;
pitch -= w;
dstPitch += (pitch << 1);
while (h--) {
for (int i = 0; i < w; ++i) {
uint8 v = *src++;
d1[0] = d2[0] = (v & 7);
d1[1] = d2[1] = (v & 8) ? (v & 7) : 0;
d1 += 2;
d2 += 2;
}
src += pitch;
d1 += dstPitch;
d2 += dstPitch;
}
}
void SCI0_PC98Gfx8ColorsDriver::initScreen(const Graphics::PixelFormat *format) {
UpscaledGfxDriver::initScreen(format);
_renderScaled = &pc98SimpleDither;
if (_useTextMode)
_renderGlyph = &SciGfxDrvInternal::renderPC98GlyphFat;
assert(_convPalette);
GfxDefaultDriver::setPalette(_convPalette, 0, 8, true, nullptr, nullptr);
}
void SCI0_PC98Gfx8ColorsDriver::replaceCursor(const void *cursor, uint w, uint h, int hotspotX, int hotspotY, uint32 keycolor) {
GFXDRV_ASSERT_READY;
adjustCursorBuffer(w, _cursorScaleHeightOnly ? h << 1 : h);
const byte *s = reinterpret_cast<const byte*>(cursor);
byte *d = _compositeBuffer;
uint32 newKeyColor = 0xFF;
for (uint i = 0; i < h; ++i) {
for (uint ii = 0; ii < w; ++ii) {
byte col = *s++;
*d++ = (col == keycolor) ? newKeyColor : (col & 7);
}
}
// Special case for PQ2 which 2x scales the cursor height
if (_cursorScaleHeightOnly) {
s = _compositeBuffer + h * w - w;
d = _compositeBuffer + h * w * 2 - w;
for (uint i = 0; i < h; ++i) {
memcpy(d, s, w);
d -= w;
memcpy(d, s, w);
d -= w;
s -= w;
}
h <<= 1;
hotspotX <<= 1;
}
CursorMan.replaceCursor(_compositeBuffer, w, h, hotspotX, hotspotY, newKeyColor);
}
byte SCI0_PC98Gfx8ColorsDriver::remapTextColor(byte color) const {
// Always black. For QFG, this is on purpose. The code just copies the inverted glyph data into all 4 vmem planes.
if (!_useTextMode)
return 0;
color &= 7;
// This seems to be a bug in the original PQ2 interpreter, which I replicate, so that we get the same colors.
// What they were trying to do is just getting the rgb bits in the right order (switch red and green). But
// instead, before checking and setting the bits, they also copy the full color byte to the target color. So,
// the extra bits come just on top. The result: All green and red colors are turned into yellow, all magenta
// and cyan colors are turned into white.
if (color & 2)
color |= 4;
if (color & 4)
color |= 2;
// This is the blue color that PQ2 uses basically for all Japanese text...
if (color == 0)
color = 1;
return color;
}
const char *SCI0_PC98Gfx8ColorsDriver::_driverFiles[2] = { "9801V8M.DRV", "9801VID.DRV" };
SCI_GFXDRV_VALIDATE_IMPL(SCI0_PC98Gfx8Colors)
GfxDriver *SCI0_PC98Gfx8ColorsDriver_create(int rgbRendering, ...) {
va_list args;
va_start(args, rgbRendering);
bool conf = (va_arg(args, int) != 0);
va_end(args);
return new SCI0_PC98Gfx8ColorsDriver(conf, conf, rgbRendering != 0);
}
} // End of namespace Sci

View File

@@ -0,0 +1,221 @@
/* 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 "common/file.h"
#include "common/system.h"
#include "graphics/cursorman.h"
#include "sci/graphics/drivers/gfxdriver_intern.h"
namespace Sci {
class SCI1_PC98Gfx8ColorsDriver final : public UpscaledGfxDriver {
public:
SCI1_PC98Gfx8ColorsDriver(bool rgbRendering);
~SCI1_PC98Gfx8ColorsDriver() override;
void initScreen(const Graphics::PixelFormat *format) override;
void setPalette(const byte*, uint, uint, bool, const PaletteMod*, const byte*) override {}
void copyRectToScreen(const byte *src, int srcX, int srcY, int pitch, int destX, int destY, int w, int h, const PaletteMod *palMods, const byte *palModMapping) override;
void replaceCursor(const void *cursor, uint w, uint h, int hotspotX, int hotspotY, uint32 keycolor) override;
byte remapTextColor(byte) const override;
static bool validateMode(Common::Platform p) { return (p == Common::kPlatformPC98) && checkDriver(&_driverFile, 1); }
private:
const byte *_ditheringTable;
const byte *_convPalette;
static const char *_driverFile;
};
SCI1_PC98Gfx8ColorsDriver::SCI1_PC98Gfx8ColorsDriver(bool rgbRendering) : UpscaledGfxDriver(1, true, rgbRendering), _ditheringTable(nullptr), _convPalette(nullptr) {
Common::File drv;
if (!drv.open(_driverFile))
GFXDRV_ERR_OPEN(_driverFile);
uint16 eprcOffs = 0;
uint32 cmd = drv.readUint32LE();
if ((cmd & 0xFF) == 0xE9)
eprcOffs = ((cmd >> 8) & 0xFFFF) + 3;
if (!eprcOffs || drv.readUint32LE() != 0x87654321 || !drv.skip(1) || !drv.seek(drv.readByte(), SEEK_CUR) || !drv.seek(drv.readByte(), SEEK_CUR))
GFXDRV_ERR_VERSION(_driverFile);
uint32 pos = (drv.pos() + 1) & ~1;
drv.seek(pos + 2);
drv.seek(drv.readUint16LE());
byte *buff = new byte[190];
drv.read(buff, 190);
uint16 tableOffs = 0;
int step = 0;
for (int i = 0; i < 182 && !tableOffs; ++i) {
uint32 c = READ_BE_UINT32(buff + i);
if (step == 0 && ((c & 0xFFF0FFF0) != 0xD1E0D1E0 || (READ_BE_UINT32(buff + i + 4) ^ c)))
continue;
if (step == 0) {
step = 1;
i += 7;
continue;
}
if (c >> 20 != 0x81C || ((READ_BE_UINT32(buff + i + 4) >> 20) ^ (c >> 20)))
continue;
if ((c & 0xFFFF) == READ_BE_UINT16(buff + i + 6))
tableOffs = FROM_BE_16(c);
}
delete[] buff;
if (!tableOffs)
error("SCI1_PC98Gfx8ColorsDriver: Failed to load dithering data from '%s'", _driverFile);
drv.seek(tableOffs);
byte *dmx = new byte[96]();
drv.read(dmx, 96);
if (drv.readUint16LE() != 0xA800 || drv.readUint16LE() != 0xB000)
GFXDRV_ERR_VERSION(_driverFile);
drv.close();
byte *dt = new byte[1536]();
_ditheringTable = dt;
for (uint16 i = 0; i < 256; ++i) {
for (int ii = 0; ii < 6; ++ii)
*dt++ = (dmx[(i >> 4) * 6 + ii] & 0xCC) | (dmx[(i & 0x0f) * 6 + ii] & 0x33);
}
delete[] dmx;
_textAlignX = 1;
byte *col = new byte[8 * 3]();
_convPalette = col;
for (uint8 i = 0; i < 8; ++i) {
*col++ = (i & 2) ? 0xff : 0;
*col++ = (i & 1) ? 0xff : 0;
*col++ = (i & 4) ? 0xff : 0;
}
}
SCI1_PC98Gfx8ColorsDriver::~SCI1_PC98Gfx8ColorsDriver() {
delete[] _ditheringTable;
delete[] _convPalette;
}
void renderPlanarMatrix(byte *dst, const byte *src, int pitch, int w, int h, const byte *tbl) {
int dstPitch = pitch << 1;
byte *d1 = dst;
byte *d2 = d1 + dstPitch;
pitch -= w;
dstPitch += (pitch << 1);
while (h--) {
byte sh = 0;
for (int i = 0; i < (w >> 1); ++i) {
const byte *c = &tbl[(src[0] << 4 | src[1]) * 6];
for (int ii = sh; ii < sh + 4; ++ii) {
*d1++ = (((c[0] >> (7 - ii)) & 1) << 2) | (((c[1] >> (7 - ii)) & 1) << 1) | ((c[2] >> (7 - ii)) & 1);
*d2++ = (((c[3] >> (7 - ii)) & 1) << 2) | (((c[4] >> (7 - ii)) & 1) << 1) | ((c[5] >> (7 - ii)) & 1);
}
src += 2;
sh ^= 4;
}
src += pitch;
d1 += dstPitch;
d2 += dstPitch;
}
}
void SCI1_PC98Gfx8ColorsDriver::initScreen(const Graphics::PixelFormat *format) {
UpscaledGfxDriver::initScreen(format);
_renderGlyph = &SciGfxDrvInternal::renderPC98GlyphFat;
assert(_convPalette);
GfxDefaultDriver::setPalette(_convPalette, 0, 8, true, nullptr, nullptr);
}
void SCI1_PC98Gfx8ColorsDriver::copyRectToScreen(const byte *src, int srcX, int srcY, int pitch, int destX, int destY, int w, int h, const PaletteMod *palMods, const byte *palModMapping) {
GFXDRV_ASSERT_READY;
assert (h >= 0 && w >= 0);
byte diff = srcX & 7;
srcX &= ~7;
destX &= ~7;
w = (w + diff + 7) & ~7;
src += (srcY * pitch + srcX * _srcPixelSize);
if (src != _currentBitmap)
SciGfxDrvInternal::updateBitmapBuffer(_currentBitmap, _virtualW * _srcPixelSize, src, pitch, destX * _srcPixelSize, destY, w * _srcPixelSize, h);
byte *scb = _scaledBitmap + (destY << 1) * _screenW * _srcPixelSize + (destX << 1) * _srcPixelSize;
renderPlanarMatrix(scb, src, pitch, w, h, _ditheringTable);
updateScreen(destX << 1, destY << 1, w << 1, h << 1, palMods, palModMapping);
}
void SCI1_PC98Gfx8ColorsDriver::replaceCursor(const void *cursor, uint w, uint h, int hotspotX, int hotspotY, uint32 keycolor) {
GFXDRV_ASSERT_READY;
adjustCursorBuffer(w << 1, h << 1);
const byte *s = reinterpret_cast<const byte*>(cursor);
byte *d1 = _compositeBuffer;
uint32 newKeyColor = 0xFF;
int dstPitch = (w << 1);
byte *d2 = _compositeBuffer + dstPitch;
for (uint i = 0; i < h; ++i) {
for (uint ii = 0; ii < w; ++ii) {
byte col = *s++;
if (col == keycolor) {
*d1++ = *d2++ = newKeyColor;
*d1++ = *d2++ = newKeyColor;
} else {
*d1++ = *d2++ = (col & 7);
*d1++ = *d2++ = (col & 8) ? (col & 7) : 0;
}
}
d1 += dstPitch;
d2 += dstPitch;
}
CursorMan.replaceCursor(_compositeBuffer, w << 1, h << 1, hotspotX << 1, hotspotY << 1, newKeyColor);
}
byte SCI1_PC98Gfx8ColorsDriver::remapTextColor(byte) const {
// Always black. The driver opcode actually has a pen color argument, but it isn't put to any use. Not sure if it is intended.
return 0;
}
const char *SCI1_PC98Gfx8ColorsDriver::_driverFile = "9801V8.DRV";
SCI_GFXDRV_VALIDATE_IMPL(SCI1_PC98Gfx8Colors)
GfxDriver *SCI1_PC98Gfx8ColorsDriver_create(int rgbRendering, ...) {
return new SCI1_PC98Gfx8ColorsDriver(rgbRendering != 0);
}
} // End of namespace Sci

View File

@@ -0,0 +1,193 @@
/* 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 "common/system.h"
#include "graphics/cursorman.h"
#include "sci/graphics/drivers/gfxdriver_intern.h"
namespace Sci {
UpscaledGfxDriver::UpscaledGfxDriver(int16 textAlignX, bool scaleCursor, bool rgbRendering) :
UpscaledGfxDriver(640, 400, textAlignX, scaleCursor, rgbRendering) {
}
UpscaledGfxDriver::UpscaledGfxDriver(uint16 scaledW, uint16 scaledH, int16 textAlignX, bool scaleCursor, bool rgbRendering) :
GfxDefaultDriver(scaledW, scaledH, false, rgbRendering), _textAlignX(textAlignX), _scaleCursor(scaleCursor), _needCursorBuffer(false),
_scaledBitmap(nullptr), _renderScaled(nullptr), _renderGlyph(nullptr), _cursorWidth(0), _cursorHeight(0), _hScaleMult(2), _vScaleMult(2), _vScaleDiv(1) {
_virtualW = 320;
_virtualH = 200;
}
UpscaledGfxDriver::~UpscaledGfxDriver() {
delete[] _scaledBitmap;
}
void renderGlyph(byte *dst, int dstPitch, const byte *src, int srcPitch, int w, int h, int transpCol) {
dstPitch -= w;
srcPitch -= w;
while (h--) {
for (int i = 0; i < w; ++i) {
byte in = *src++;
if (in != transpCol)
*dst = in;
++dst;
}
src += srcPitch;
dst += dstPitch;
}
}
void UpscaledGfxDriver::initScreen(const Graphics::PixelFormat *format) {
GfxDefaultDriver::initScreen(format);
_scaledBitmap = new byte[_screenW * _screenH * _srcPixelSize]();
static const ScaledRenderProc scaledRenderProcs[] = {
&SciGfxDrvInternal::scale2x<byte>,
&SciGfxDrvInternal::scale2x<uint16>,
&SciGfxDrvInternal::scale2x<uint32>
};
assert((_srcPixelSize >> 1) < ARRAYSIZE(scaledRenderProcs));
_renderScaled = scaledRenderProcs[_srcPixelSize >> 1];
_renderGlyph = &renderGlyph;
}
void UpscaledGfxDriver::setPalette(const byte *colors, uint start, uint num, bool update, const PaletteMod *palMods, const byte *palModMapping) {
GFXDRV_ASSERT_READY;
if (_pixelSize == 1) {
GfxDefaultDriver::setPalette(colors, start, num, update, palMods, palModMapping);
return;
}
updatePalette(colors, start, num);
if (update)
updateScreen(0, 0, _screenW, _screenH, palMods, palModMapping);
if (_cursorUsesScreenPalette)
CursorMan.replaceCursorPalette(_currentPalette, 0, 256);
}
void UpscaledGfxDriver::copyRectToScreen(const byte *src, int srcX, int srcY, int pitch, int destX, int destY, int w, int h, const PaletteMod *palMods, const byte *palModMapping) {
GFXDRV_ASSERT_READY;
assert (h >= 0 && w >= 0);
src += (srcY * pitch + srcX * _srcPixelSize);
if (src != _currentBitmap)
SciGfxDrvInternal::updateBitmapBuffer(_currentBitmap, _virtualW * _srcPixelSize, src, pitch, destX * _srcPixelSize, destY, w * _srcPixelSize, h);
int realWidth = 0;
int realHeight = 0;
// We need to scale and color convert the bitmap in separate functions, because we want
// to keep the scaled non-color-modified bitmap for palette updates in rgb rendering mode.
renderBitmap(src, pitch, destX, destY, w, h, realWidth, realHeight);
Common::Point p(destX, destY);
p = getRealCoords(p);
updateScreen(p.x, p.y, realWidth, realHeight, palMods, palModMapping);
}
void UpscaledGfxDriver::replaceCursor(const void *cursor, uint w, uint h, int hotspotX, int hotspotY, uint32 keycolor) {
GFXDRV_ASSERT_READY;
if (_scaleCursor) {
adjustCursorBuffer(w << 1, h << 1);
SciGfxDrvInternal::scale2x<byte>(_compositeBuffer, reinterpret_cast<const byte*>(cursor), w, w, h);
CursorMan.replaceCursor(_compositeBuffer, w << 1, h << 1, hotspotX << 1, hotspotY << 1, keycolor);
} else {
CursorMan.replaceCursor(cursor, w, h, hotspotX, hotspotY, keycolor);
}
}
Common::Point UpscaledGfxDriver::getMousePos() const {
Common::Point res = GfxDriver::getMousePos();
res.x /= _hScaleMult;
res.y = res.y * _vScaleDiv / _vScaleMult;
return res;
}
void UpscaledGfxDriver::setMousePos(const Common::Point &pos) const {
g_system->warpMouse(pos.x * _hScaleMult, pos.y * _vScaleMult / _vScaleDiv);
}
void UpscaledGfxDriver::setShakePos(int shakeXOffset, int shakeYOffset) const {
g_system->setShakePos(shakeXOffset * _hScaleMult, shakeYOffset * _vScaleMult / _vScaleDiv);
}
void UpscaledGfxDriver::clearRect(const Common::Rect &r) const {
Common::Rect r2(r.left * _hScaleMult, r.top * _vScaleMult / _vScaleDiv, r.right * _hScaleMult, r.bottom * _vScaleMult / _vScaleDiv);
GfxDriver::clearRect(r2);
}
Common::Point UpscaledGfxDriver::getRealCoords(Common::Point &pos) const {
return Common::Point(pos.x * _hScaleMult, pos.y * _vScaleMult / _vScaleDiv);
}
void UpscaledGfxDriver::drawTextFontGlyph(const byte *src, int pitch, int hiresDestX, int hiresDestY, int hiresW, int hiresH, int transpColor, const PaletteMod *palMods, const byte *palModMapping) {
GFXDRV_ASSERT_READY;
hiresDestX &= ~(_textAlignX - 1);
byte *scb = _scaledBitmap + hiresDestY * _screenW * _srcPixelSize + hiresDestX * _srcPixelSize;
_renderGlyph(scb, _screenW, src, pitch, hiresW, hiresH, transpColor);
updateScreen(hiresDestX, hiresDestY, hiresW, hiresH, palMods, palModMapping);
}
void UpscaledGfxDriver::updateScreen(int destX, int destY, int w, int h, const PaletteMod *palMods, const byte *palModMapping) {
byte *buff = _compositeBuffer;
int pitch = w * _pixelSize;
byte *scb = _scaledBitmap + destY * _screenW * _srcPixelSize + destX * _srcPixelSize;
if (palMods && palModMapping) {
_colorConvMod(buff, scb, _screenW, w, h, _currentPalette, _internalPalette, _format, palMods, palModMapping);
} else if (_pixelSize != _srcPixelSize) {
_colorConv(buff, scb, _screenW, w, h, _internalPalette);
} else {
buff = scb;
pitch = _screenW *_pixelSize;
}
g_system->copyRectToScreen(buff, pitch, destX, destY, w, h);
}
void UpscaledGfxDriver::adjustCursorBuffer(uint16 newWidth, uint16 newHeight) {
// For configs which need/have the composite buffer for other purposes, we can skip this.
if (!_compositeBuffer)
_needCursorBuffer = true;
else if (!_needCursorBuffer)
return;
if (_cursorWidth * _cursorHeight < newWidth * newHeight) {
delete[] _compositeBuffer;
_compositeBuffer = new byte[newWidth * newHeight * _srcPixelSize]();
_cursorWidth = newWidth;
_cursorHeight = newHeight;
}
}
void UpscaledGfxDriver::renderBitmap(const byte *src, int pitch, int dx, int dy, int w, int h, int &realWidth, int &realHeight) {
byte *scb = _scaledBitmap + (dy << 1) * _screenW * _srcPixelSize + (dx << 1) * _srcPixelSize;
_renderScaled(scb, src, pitch, w, h);
realWidth = w << 1;
realHeight = h << 1;
}
GfxDriver *UpscaledGfxDriver_create(int rgbRendering, ...) {
return new UpscaledGfxDriver(1, true, rgbRendering != 0);
}
} // End of namespace Sci

View File

@@ -0,0 +1,69 @@
/* 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 "common/system.h"
#include "sci/graphics/drivers/gfxdriver_intern.h"
namespace Sci {
class SCI1_VGAGreyScaleDriver final : public GfxDefaultDriver {
public:
SCI1_VGAGreyScaleDriver(bool rgbRendering);
~SCI1_VGAGreyScaleDriver() override;
void setPalette(const byte *colors, uint start, uint num, bool update, const PaletteMod *palMods, const byte *palModMapping) override;
static bool validateMode(Common::Platform p) { return (p == Common::kPlatformDOS || p == Common::kPlatformWindows) && checkDriver(&_driverFile, 1); }
private:
byte *_greyScalePalette;
static const char *_driverFile;
};
SCI1_VGAGreyScaleDriver::SCI1_VGAGreyScaleDriver(bool rgbRendering) : GfxDefaultDriver(320, 200, false, rgbRendering), _greyScalePalette(nullptr) {
_greyScalePalette = new byte[_numColors * 3]();
}
SCI1_VGAGreyScaleDriver::~SCI1_VGAGreyScaleDriver() {
delete[] _greyScalePalette;
}
void SCI1_VGAGreyScaleDriver::setPalette(const byte *colors, uint start, uint num, bool update, const PaletteMod *palMods, const byte *palModMapping) {
GFXDRV_ASSERT_READY;
byte *d = _greyScalePalette;
for (uint i = 0; i < num; ++i) {
// In the driver files I inspected there were never any other color distributions than this.
// So I guess it is safe to hardcode that instead of loading it from the driver file.
d[0] = d[1] = d[2] = (colors[0] * 77 + colors[1] * 150 + colors[2] * 28) >> 8;
colors += 3;
d += 3;
}
GfxDefaultDriver::setPalette(_greyScalePalette, start, num, update, palMods, palModMapping);
}
const char *SCI1_VGAGreyScaleDriver::_driverFile = "VGA320BW.DRV";
SCI_GFXDRV_VALIDATE_IMPL(SCI1_VGAGreyScale)
GfxDriver *SCI1_VGAGreyScaleDriver_create(int rgbRendering, ...) {
return new SCI1_VGAGreyScaleDriver(rgbRendering != 0);
}
} // End of namespace Sci

View File

@@ -0,0 +1,184 @@
/* 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 "common/file.h"
#include "common/system.h"
#include "graphics/cursorman.h"
#include "sci/graphics/drivers/gfxdriver_intern.h"
namespace Sci {
class WindowsGfx16ColorsDriver final : public SCI1_EGADriver {
public:
// The original does not take into account the extra lines required for the 200->440 vertical scaling. There is a noticeable dithering glitch every 11th line, as the
// two pixels of the checkerbox pattern appear in the wrong order. I have implemented a fix for this which can be activated with the fixDithering parameter.
WindowsGfx16ColorsDriver(bool fixDithering, bool rgbRendering);
~WindowsGfx16ColorsDriver() override {}
void initScreen(const Graphics::PixelFormat *format) override;
void replaceCursor(const void *cursor, uint w, uint h, int hotspotX, int hotspotY, uint32 keycolor) override;
Common::Point getRealCoords(Common::Point &pos) const override;
static bool validateMode(Common::Platform p) { return (p == Common::kPlatformWindows && checkDriver(&_driverFile, 1)); }
private:
void loadData() override;
void renderBitmap(byte *dst, const byte *src, int pitch, int y, int w, int h, const byte *patterns, const byte *palette, uint16 &realWidth, uint16 &realHeight) override;
LineProc _renderLine2;
const bool _enhancedDithering;
static const char *_driverFile;
};
WindowsGfx16ColorsDriver::WindowsGfx16ColorsDriver(bool enhancedDithering, bool rgbRendering) : SCI1_EGADriver(rgbRendering), _enhancedDithering(enhancedDithering), _renderLine2(nullptr) {
static const byte win16Colors[48] = {
0x00, 0x00, 0x00, 0xA8, 0x00, 0x57, 0x00, 0xA8, 0x57, 0xA8, 0xA8, 0x57,
0x00, 0x00, 0xA8, 0xA8, 0x57, 0xA8, 0x57, 0xA8, 0xA8, 0x87, 0x88, 0x8F,
0xC0, 0xC7, 0xC8, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0xFF, 0xFF, 0x00,
0x00, 0x00, 0xFF, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
};
_convPalette = win16Colors;
_vScaleMult = 11;
_vScaleDiv = 5;
}
template <typename T, bool extScale> void win16ColRenderLine(byte *&dst, const byte *src, int w, const byte *patterns, const byte *pal, bool swap) {
const T *p = reinterpret_cast<const T*>(pal);
T *d1 = reinterpret_cast<T*>(dst);
T *d2 = d1 + (w << 1);
T *d3 = d2 + (w << 1);
T *&d3r = swap ? d2 : d1;
if (swap)
SWAP(d1, d2);
for (int i = 0; i < w; ++i) {
byte pt = patterns[*src++];
if (sizeof(T) == 1) {
*d1++ = d2[1] = pt & 0x0F;
*d1++ = *d2++ = pt >> 4;
} else {
*d1++ = d2[1] = p[pt & 0x0F];
*d1++ = *d2++ = p[pt >> 4];
}
d2++;
if (extScale) {
*d3++ = *(d3r - 2);
*d3++ = *(d3r - 1);
}
}
dst = reinterpret_cast<byte*>(extScale ? d3 : (swap ? d1 : d2));
}
void WindowsGfx16ColorsDriver::initScreen(const Graphics::PixelFormat *format) {
SCI1_EGADriver::initScreen(format);
static const LineProc lineProcs[] = {
&win16ColRenderLine<byte, false>,
&win16ColRenderLine<byte, true>,
&win16ColRenderLine<uint16, false>,
&win16ColRenderLine<uint16, true>,
&win16ColRenderLine<uint32, false>,
&win16ColRenderLine<uint32, true>
};
assert((_pixelSize | 1) < ARRAYSIZE(lineProcs));
_renderLine = lineProcs[_pixelSize & ~1];
_renderLine2 = lineProcs[_pixelSize | 1];
}
void WindowsGfx16ColorsDriver::replaceCursor(const void *cursor, uint w, uint h, int hotspotX, int hotspotY, uint32 keycolor) {
GFXDRV_ASSERT_READY;
// The original windows interpreter always renders the cursor as b/w, regardless of which cursor views (the DOS
// cursors or the new Windows ones) the user selects. This is also regardless of color mode (16 or 256 colors).
byte col1 = SciGfxDrvInternal::findColorInPalette(0x00000000, _convPalette, _numColors);
byte col2 = SciGfxDrvInternal::findColorInPalette(0x00FFFFFF, _convPalette, _numColors);
SciGfxDrvInternal::renderWinMonochromeCursor(_compositeBuffer, cursor, _currentPalette, w, h, hotspotX, hotspotY, col1, col2, keycolor, false);
CursorMan.replaceCursor(_compositeBuffer, w, h, hotspotX, hotspotY, keycolor);
}
Common::Point WindowsGfx16ColorsDriver::getRealCoords(Common::Point &pos) const {
return Common::Point(pos.x << 1, (pos.y << 1) + (pos.y + 4) / 5);
}
void WindowsGfx16ColorsDriver::loadData() {
Common::File file;
if (!file.open(_driverFile))
GFXDRV_ERR_OPEN(_driverFile);
int64 sz = file.size();
byte *buf = new byte[sz];
file.read(buf, sz);
file.close();
// We can keep the search for the table simple, since there are only two supported executables (KQ6
// Windows and SQ4 Windows) and both contain the following value only within the pattern table...
uint32 srch = FROM_LE_32(0xCC4C4404);
const byte *tblOffs = nullptr;
for (const byte *pos = buf; pos < buf + sz - 67 && tblOffs == nullptr; ++pos) {
// We check three times, just to be sure. Checking once would actually suffice.
if (READ_UINT32(pos) != srch || READ_UINT32(pos + 8) != srch || READ_UINT32(pos + 64) != srch)
continue;
tblOffs = pos - 4;
}
if (tblOffs == nullptr)
error("%s(): Failed to load 16 colors match table", __FUNCTION__);
byte *tbl = new byte[512];
memcpy(tbl, tblOffs, 512);
_egaMatchTable = tbl;
delete[] buf;
_colAdjust = (_egaMatchTable[482] == 0x79) ? 4 : 0;
_numColors = 16;
}
void WindowsGfx16ColorsDriver::renderBitmap(byte *dst, const byte *src, int pitch, int y, int w, int h, const byte *patterns, const byte *palette, uint16 &realWidth, uint16 &realHeight) {
const byte *dst0 = dst;
byte mod = (y + 4) % 5;
byte swap = _enhancedDithering ? ((y + 4) / 5) & 1 : 0;
for (int i = 0; i < h; ++i) {
if (++mod == 5) {
_renderLine2(dst, src, w, patterns, palette, swap);
if (_enhancedDithering)
swap ^= 1;
mod = 0;
} else {
_renderLine(dst, src, w, patterns, palette, swap);
}
src += pitch;
}
realWidth = w << 1;
realHeight = (dst - dst0) / (realWidth * _pixelSize);
}
const char *WindowsGfx16ColorsDriver::_driverFile = "SCIWV.EXE";
SCI_GFXDRV_VALIDATE_IMPL(WindowsGfx16Colors)
GfxDriver *WindowsGfx16ColorsDriver_create(int rgbRendering, ...) {
return new WindowsGfx16ColorsDriver(true, rgbRendering != 0);
}
} // End of namespace Sci

View File

@@ -0,0 +1,259 @@
/* 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 "common/system.h"
#include "graphics/cursorman.h"
#include "sci/graphics/drivers/gfxdriver_intern.h"
namespace Sci {
class WindowsGfx256ColorsDriver final : public UpscaledGfxDriver {
public:
WindowsGfx256ColorsDriver(bool coloredDosStyleCursors, bool smallWindow, bool rgbRendering);
~WindowsGfx256ColorsDriver() override {}
void initScreen(const Graphics::PixelFormat *format) override;
void copyRectToScreen(const byte *src, int srcX, int srcY, int pitch, int destX, int destY, int w, int h, const PaletteMod *palMods, const byte *palModMapping) override;
void replaceCursor(const void *cursor, uint w, uint h, int hotspotX, int hotspotY, uint32 keycolor) override;
Common::Point getRealCoords(Common::Point &pos) const override;
void setColorMap(const byte *colorMap) override { _colorMap = colorMap; }
void setFlags(uint32 flags) override;
void clearFlags(uint32 flags) override;
bool supportsHiResGraphics() const override { return !_smallWindow; }
bool driverBasedTextRendering() const override { return false; }
protected:
typedef void (*LineProc)(byte*&, const byte*, int, int, int);
LineProc _renderLine;
private:
typedef void (*LineProcSpec)(byte*&, const byte*, int, int, const byte*);
LineProcSpec _renderLine2;
void renderBitmap(const byte *src, int pitch, int dx, int dy, int w, int h, int &realWidth, int &realHeight) override;
uint32 _flags;
const byte *_colorMap;
const bool _smallWindow;
const bool _dosStyleCursors;
uint16 _vScaleMult2;
};
WindowsGfx256ColorsDriver::WindowsGfx256ColorsDriver(bool coloredDosStyleCursors, bool smallWindow,bool rgbRendering) :
UpscaledGfxDriver(smallWindow ? 320 : 640, smallWindow ? 240 : 440, 1, coloredDosStyleCursors && !smallWindow, rgbRendering), _dosStyleCursors(coloredDosStyleCursors), _smallWindow(smallWindow),
_renderLine(nullptr), _renderLine2(nullptr), _flags(0), _colorMap(nullptr), _vScaleMult2(smallWindow ? 1 : 2) {
_virtualW = 320;
_virtualH = 200;
if (smallWindow)
_hScaleMult = 1;
_vScaleMult = _smallWindow ? 6 : 11;
_vScaleDiv = 5;
}
void largeWindowRenderLine(byte *&dst, const byte *src, int pitch, int w, int ty) {
int dstPitch = pitch;
int dstPitch2 = pitch - (w << 1);
byte *d1 = dst;
byte *d2 = d1 + dstPitch;
if (ty == 5) {
byte *d3 = d2 + dstPitch;
for (int i = 0; i < w; ++i) {
d1[0] = d1[1] = d2[0] = d2[1] = d3[0] = d3[1] = *src++;
d1 += 2;
d2 += 2;
d3 += 2;
}
dst = d3 + dstPitch2;
} else {
for (int i = 0; i < w; ++i) {
d1[0] = d1[1] = d2[0] = d2[1] = *src++;
d1 += 2;
d2 += 2;
}
dst = d2 + dstPitch2;
}
}
void largeWindowRenderLineMovie(byte *&dst, const byte *src, int pitch, int w, const byte*) {
int dstPitch = pitch;
int dstPitch2 = pitch - (w << 1);
byte *d1 = dst;
byte *d2 = d1 + dstPitch;
for (int i = 0; i < w; ++i) {
d1[0] = d1[1] = d2[0] = d2[1] = *src++;
d1 += 2;
d2 += 2;
}
dst = d2 + dstPitch2;
}
void smallWindowRenderLine(byte *&dst, const byte *src, int pitch, int w, int ty) {
int dstPitch = pitch;
int dstPitch2 = pitch - w;
byte *d1 = dst;
if (ty == 5) {
byte *d2 = d1 + dstPitch;
for (int i = 0; i < w; ++i)
*d1++ = *d2++ = *src++;
dst = d2 + dstPitch2;
} else {
for (int i = 0; i < w; ++i)
*d1++ = *src++;
dst = d1 + dstPitch2;
}
}
void smallWindowRenderLineMovie(byte *&dst, const byte *src, int pitch, int w, const byte*) {
int dstPitch = pitch - w;
byte *d1 = dst;
for (int i = 0; i < w; ++i)
*d1++ = *src++;
dst = d1 + dstPitch;
}
void hiresRenderLine(byte *&dst, const byte *src, int pitch, int w, const byte *colorMap) {
if (!colorMap) {
memcpy(dst, src, w);
} else {
byte *d = dst;
for (int i = 0; i < w; ++i)
*d++ = colorMap[*src++];
}
dst += pitch;
}
void renderLineDummy(byte *&, const byte* , int, int, const byte*) {
}
void WindowsGfx256ColorsDriver::initScreen(const Graphics::PixelFormat *format) {
UpscaledGfxDriver::initScreen(format);
_renderLine = _smallWindow ? &smallWindowRenderLine : &largeWindowRenderLine;
_renderLine2 = _smallWindow ? &renderLineDummy : &hiresRenderLine;
}
void WindowsGfx256ColorsDriver::copyRectToScreen(const byte *src, int srcX, int srcY, int pitch, int destX, int destY, int w, int h, const PaletteMod *palMods, const byte *palModMapping) {
GFXDRV_ASSERT_READY;
assert (h >= 0 && w >= 0);
if (!(_flags & (kHiResMode | kMovieMode))) {
UpscaledGfxDriver::copyRectToScreen(src, srcX, srcY, pitch, destX, destY, w, h, palMods, palModMapping);
return;
}
if (_flags & kMovieMode) {
destX = (_screenW >> 1) - (w & ~1) * _hScaleMult / 2;
destY = (_screenH >> 1) - (h & ~1) * _vScaleMult2 / 2;
}
src += (srcY * pitch + srcX * _srcPixelSize);
byte *dst = _scaledBitmap + destY * _screenW * _srcPixelSize + destX * _srcPixelSize;
for (int i = 0; i < h; ++i) {
_renderLine2(dst, src, _screenW, w, _colorMap);
src += pitch;
}
if (_flags & kMovieMode) {
w *= _hScaleMult;
h *= _vScaleMult2;
}
updateScreen(destX, destY, w, h, palMods, palModMapping);
}
void WindowsGfx256ColorsDriver::replaceCursor(const void *cursor, uint w, uint h, int hotspotX, int hotspotY, uint32 keycolor) {
GFXDRV_ASSERT_READY;
if (_dosStyleCursors) {
// The original windows interpreter always renders the cursor as b/w, regardless of which cursor views (the DOS
// cursors or the new Windows ones) the user selects. This is also regardless of color mode (16 or 256 colors).
// It is a technical limitation on Windows 95 with VGA hardware that mouse cursors have to be b/w.
// Instead, we use the colored DOS style cursors as a default, since there was consensus to do that.
UpscaledGfxDriver::replaceCursor(cursor, w, h, hotspotX, hotspotY, keycolor);
return;
}
adjustCursorBuffer(w << 1, h << 1);
if (_pixelSize == 1)
copyCurrentPalette(_currentPalette, 0, _numColors);
byte col1 = SciGfxDrvInternal::findColorInPalette(0x00000000, _currentPalette, _numColors);
byte col2 = SciGfxDrvInternal::findColorInPalette(0x00FFFFFF, _currentPalette, _numColors);
SciGfxDrvInternal::renderWinMonochromeCursor(_compositeBuffer, cursor, _currentPalette, w, h, hotspotX, hotspotY, col1, col2, keycolor, _smallWindow);
CursorMan.replaceCursor(_compositeBuffer, w, h, hotspotX, hotspotY, keycolor);
}
Common::Point WindowsGfx256ColorsDriver::getRealCoords(Common::Point &pos) const {
return Common::Point(pos.x * _hScaleMult, pos.y * _vScaleMult2 + (pos.y + 4) / 5);
}
void WindowsGfx256ColorsDriver::setFlags(uint32 flags) {
flags ^= (_flags & flags);
if (!flags)
return;
if (flags & kMovieMode)
_renderLine2 = _smallWindow ? &smallWindowRenderLineMovie : &largeWindowRenderLineMovie;
_flags |= flags;
}
void WindowsGfx256ColorsDriver::clearFlags(uint32 flags) {
flags &= _flags;
if (!flags)
return;
if (flags & kMovieMode)
_renderLine2 = _smallWindow ? &renderLineDummy : &hiresRenderLine;
_flags &= ~flags;
}
void WindowsGfx256ColorsDriver::renderBitmap(const byte *src, int pitch, int dx, int dy, int w, int h, int &realWidth, int &realHeight) {
assert(_renderLine);
byte *dst = _scaledBitmap + (dy * _vScaleMult2 + (dy + 4) / 5) * _screenW * _srcPixelSize + dx *_hScaleMult * _srcPixelSize;
const byte *dstart = dst;
dy = (dy + 4) % 5;
while (h--) {
_renderLine(dst, src, _screenW, w, ++dy);
dy %= 5;
src += pitch;
}
realWidth = w * _hScaleMult;
realHeight = (dst - dstart) / _screenW;
}
GfxDriver *WindowsGfx256ColorsDriver_create(int rgbRendering, ...) {
va_list args;
va_start(args, rgbRendering);
int config = va_arg(args, int);
va_arg(args, int);
va_arg(args, int);
va_arg(args, int);
bool winCursors = (va_arg(args, int) != 0);
va_end(args);
return new WindowsGfx256ColorsDriver(!winCursors, config == 0, rgbRendering != 0);
}
} // End of namespace Sci