Files
scummvm-cursorfix/engines/sci/graphics/drivers/cga.cpp
2026-02-02 04:50:13 +01:00

225 lines
7.1 KiB
C++

/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "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