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

185 lines
6.7 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 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