aboutsummaryrefslogtreecommitdiff
path: root/engines/sci/graphics
diff options
context:
space:
mode:
Diffstat (limited to 'engines/sci/graphics')
-rw-r--r--engines/sci/graphics/controls16.cpp12
-rw-r--r--engines/sci/graphics/controls16.h6
-rw-r--r--engines/sci/graphics/cursor.cpp8
-rw-r--r--engines/sci/graphics/font.cpp8
-rw-r--r--engines/sci/graphics/font.h4
-rw-r--r--engines/sci/graphics/frameout.cpp10
-rw-r--r--engines/sci/graphics/paint16.cpp25
-rw-r--r--engines/sci/graphics/paint16.h2
-rw-r--r--engines/sci/graphics/paint32.cpp2
-rw-r--r--engines/sci/graphics/palette.cpp87
-rw-r--r--engines/sci/graphics/palette.h6
-rw-r--r--engines/sci/graphics/picture.cpp88
-rw-r--r--engines/sci/graphics/portrait.cpp105
-rw-r--r--engines/sci/graphics/portrait.h2
-rw-r--r--engines/sci/graphics/ports.cpp16
-rw-r--r--engines/sci/graphics/screen.cpp399
-rw-r--r--engines/sci/graphics/screen.h95
-rw-r--r--engines/sci/graphics/text16.cpp225
-rw-r--r--engines/sci/graphics/text16.h15
-rw-r--r--engines/sci/graphics/transitions.cpp6
-rw-r--r--engines/sci/graphics/view.cpp3
21 files changed, 746 insertions, 378 deletions
diff --git a/engines/sci/graphics/controls16.cpp b/engines/sci/graphics/controls16.cpp
index f2b2ccdfe6..e2e250cf9d 100644
--- a/engines/sci/graphics/controls16.cpp
+++ b/engines/sci/graphics/controls16.cpp
@@ -280,7 +280,7 @@ int GfxControls16::getPicNotValid() {
return _screen->_picNotValid;
}
-void GfxControls16::kernelDrawButton(Common::Rect rect, reg_t obj, const char *text, int16 fontId, int16 style, bool hilite) {
+void GfxControls16::kernelDrawButton(Common::Rect rect, reg_t obj, const char *text, uint16 languageSplitter, int16 fontId, int16 style, bool hilite) {
int16 sci0EarlyPen = 0, sci0EarlyBack = 0;
if (!hilite) {
if (getSciVersion() == SCI_VERSION_0_EARLY) {
@@ -295,7 +295,7 @@ void GfxControls16::kernelDrawButton(Common::Rect rect, reg_t obj, const char *t
_paint16->frameRect(rect);
rect.grow(-2);
_ports->textGreyedOutput(!(style & SCI_CONTROLS_STYLE_ENABLED));
- _text16->Box(text, false, rect, SCI_TEXT16_ALIGNMENT_CENTER, fontId);
+ _text16->Box(text, languageSplitter, false, rect, SCI_TEXT16_ALIGNMENT_CENTER, fontId);
_ports->textGreyedOutput(false);
rect.grow(1);
if (style & SCI_CONTROLS_STYLE_SELECTED)
@@ -318,12 +318,12 @@ void GfxControls16::kernelDrawButton(Common::Rect rect, reg_t obj, const char *t
}
}
-void GfxControls16::kernelDrawText(Common::Rect rect, reg_t obj, const char *text, int16 fontId, TextAlignment alignment, int16 style, bool hilite) {
+void GfxControls16::kernelDrawText(Common::Rect rect, reg_t obj, const char *text, uint16 languageSplitter, int16 fontId, TextAlignment alignment, int16 style, bool hilite) {
if (!hilite) {
rect.grow(1);
_paint16->eraseRect(rect);
rect.grow(-1);
- _text16->Box(text, false, rect, alignment, fontId);
+ _text16->Box(text, languageSplitter, false, rect, alignment, fontId);
if (style & SCI_CONTROLS_STYLE_SELECTED) {
_paint16->frameRect(rect);
}
@@ -335,7 +335,7 @@ void GfxControls16::kernelDrawText(Common::Rect rect, reg_t obj, const char *tex
}
}
-void GfxControls16::kernelDrawTextEdit(Common::Rect rect, reg_t obj, const char *text, int16 fontId, int16 mode, int16 style, int16 cursorPos, int16 maxChars, bool hilite) {
+void GfxControls16::kernelDrawTextEdit(Common::Rect rect, reg_t obj, const char *text, uint16 languageSplitter, int16 fontId, int16 mode, int16 style, int16 cursorPos, int16 maxChars, bool hilite) {
Common::Rect textRect = rect;
uint16 oldFontId = _text16->GetFontId();
@@ -343,7 +343,7 @@ void GfxControls16::kernelDrawTextEdit(Common::Rect rect, reg_t obj, const char
_texteditCursorVisible = false;
texteditCursorErase();
_paint16->eraseRect(rect);
- _text16->Box(text, false, textRect, SCI_TEXT16_ALIGNMENT_LEFT, fontId);
+ _text16->Box(text, languageSplitter, false, textRect, SCI_TEXT16_ALIGNMENT_LEFT, fontId);
_paint16->frameRect(rect);
if (style & SCI_CONTROLS_STYLE_SELECTED) {
_text16->SetFont(fontId);
diff --git a/engines/sci/graphics/controls16.h b/engines/sci/graphics/controls16.h
index 6a70c71aae..39ffa243fb 100644
--- a/engines/sci/graphics/controls16.h
+++ b/engines/sci/graphics/controls16.h
@@ -55,9 +55,9 @@ public:
GfxControls16(SegManager *segMan, GfxPorts *ports, GfxPaint16 *paint16, GfxText16 *text16, GfxScreen *screen);
~GfxControls16();
- void kernelDrawButton(Common::Rect rect, reg_t obj, const char *text, int16 fontId, int16 style, bool hilite);
- void kernelDrawText(Common::Rect rect, reg_t obj, const char *text, int16 fontId, int16 alignment, int16 style, bool hilite);
- void kernelDrawTextEdit(Common::Rect rect, reg_t obj, const char *text, int16 fontId, int16 mode, int16 style, int16 cursorPos, int16 maxChars, bool hilite);
+ void kernelDrawButton(Common::Rect rect, reg_t obj, const char *text, uint16 languageSplitter, int16 fontId, int16 style, bool hilite);
+ void kernelDrawText(Common::Rect rect, reg_t obj, const char *text, uint16 languageSplitter, int16 fontId, int16 alignment, int16 style, bool hilite);
+ void kernelDrawTextEdit(Common::Rect rect, reg_t obj, const char *text, uint16 languageSplitter, int16 fontId, int16 mode, int16 style, int16 cursorPos, int16 maxChars, bool hilite);
void kernelDrawIcon(Common::Rect rect, reg_t obj, GuiResourceId viewId, int16 loopNo, int16 celNo, int16 priority, int16 style, bool hilite);
void kernelDrawList(Common::Rect rect, reg_t obj, int16 maxChars, int16 count, const char **entries, GuiResourceId fontId, int16 style, int16 upperPos, int16 cursorPos, bool isAlias, bool hilite);
void kernelTexteditChange(reg_t controlObject, reg_t eventObject);
diff --git a/engines/sci/graphics/cursor.cpp b/engines/sci/graphics/cursor.cpp
index 048ec1e9b9..1a58de073c 100644
--- a/engines/sci/graphics/cursor.cpp
+++ b/engines/sci/graphics/cursor.cpp
@@ -47,7 +47,7 @@ GfxCursor::GfxCursor(ResourceManager *resMan, GfxPalette *palette, GfxScreen *sc
_isVisible = true;
// center mouse cursor
- setPosition(Common::Point(_screen->getWidth() / 2, _screen->getHeight() / 2));
+ setPosition(Common::Point(_screen->getScriptWidth() / 2, _screen->getScriptHeight() / 2));
_moveZoneActive = false;
_zoomZoneActive = false;
@@ -151,14 +151,14 @@ void GfxCursor::kernelSetShape(GuiResourceId resourceId) {
colorMapping[0] = 0; // Black is hardcoded
colorMapping[1] = _screen->getColorWhite(); // White is also hardcoded
colorMapping[2] = SCI_CURSOR_SCI0_TRANSPARENCYCOLOR;
- colorMapping[3] = _palette->matchColor(170, 170, 170); // Grey
+ colorMapping[3] = _palette->matchColor(170, 170, 170) & SCI_PALETTE_MATCH_COLORMASK; // Grey
// TODO: Figure out if the grey color is hardcoded
// HACK for the magnifier cursor in LB1, fixes its color (bug #3487092)
if (g_sci->getGameId() == GID_LAURABOW && resourceId == 1)
colorMapping[3] = _screen->getColorWhite();
// HACK for Longbow cursors, fixes the shade of grey they're using (bug #3489101)
if (g_sci->getGameId() == GID_LONGBOW)
- colorMapping[3] = _palette->matchColor(223, 223, 223); // Light Grey
+ colorMapping[3] = _palette->matchColor(223, 223, 223) & SCI_PALETTE_MATCH_COLORMASK; // Light Grey
// Seek to actual data
resourceData += 4;
@@ -481,7 +481,7 @@ void GfxCursor::kernelSetPos(Common::Point pos) {
void GfxCursor::kernelMoveCursor(Common::Point pos) {
_coordAdjuster->moveCursor(pos);
- if (pos.x > _screen->getWidth() || pos.y > _screen->getHeight()) {
+ if (pos.x > _screen->getScriptWidth() || pos.y > _screen->getScriptHeight()) {
warning("attempt to place cursor at invalid coordinates (%d, %d)", pos.y, pos.x);
return;
}
diff --git a/engines/sci/graphics/font.cpp b/engines/sci/graphics/font.cpp
index e4684ff134..2268ec0459 100644
--- a/engines/sci/graphics/font.cpp
+++ b/engines/sci/graphics/font.cpp
@@ -48,8 +48,8 @@ GfxFontFromResource::GfxFontFromResource(ResourceManager *resMan, GfxScreen *scr
// filling info for every char
for (int16 i = 0; i < _numChars; i++) {
_chars[i].offset = READ_SCI32ENDIAN_UINT16(_resourceData + 6 + i * 2);
- _chars[i].w = _resourceData[_chars[i].offset];
- _chars[i].h = _resourceData[_chars[i].offset + 1];
+ _chars[i].width = _resourceData[_chars[i].offset];
+ _chars[i].height = _resourceData[_chars[i].offset + 1];
}
}
@@ -66,10 +66,10 @@ byte GfxFontFromResource::getHeight() {
return _fontHeight;
}
byte GfxFontFromResource::getCharWidth(uint16 chr) {
- return chr < _numChars ? _chars[chr].w : 0;
+ return chr < _numChars ? _chars[chr].width : 0;
}
byte GfxFontFromResource::getCharHeight(uint16 chr) {
- return chr < _numChars ? _chars[chr].h : 0;
+ return chr < _numChars ? _chars[chr].height : 0;
}
byte *GfxFontFromResource::getCharData(uint16 chr) {
return chr < _numChars ? _resourceData + _chars[chr].offset + 2 : 0;
diff --git a/engines/sci/graphics/font.h b/engines/sci/graphics/font.h
index 58b2ba4813..451261f315 100644
--- a/engines/sci/graphics/font.h
+++ b/engines/sci/graphics/font.h
@@ -71,9 +71,11 @@ private:
byte *_resourceData;
struct Charinfo {
- byte w, h;
+ byte width;
+ byte height;
int16 offset;
};
+
byte _fontHeight;
uint16 _numChars;
Charinfo *_chars;
diff --git a/engines/sci/graphics/frameout.cpp b/engines/sci/graphics/frameout.cpp
index fafc734895..ccc362dc37 100644
--- a/engines/sci/graphics/frameout.cpp
+++ b/engines/sci/graphics/frameout.cpp
@@ -524,6 +524,10 @@ void GfxFrameout::showVideo() {
RobotDecoder *videoDecoder = g_sci->_robotDecoder;
uint16 x = videoDecoder->getPos().x;
uint16 y = videoDecoder->getPos().y;
+ uint16 screenWidth = _screen->getWidth();
+ uint16 screenHeight = _screen->getHeight();
+ uint16 outputWidth;
+ uint16 outputHeight;
if (videoDecoder->hasDirtyPalette())
g_system->getPaletteManager()->setPalette(videoDecoder->getPalette(), 0, 256);
@@ -532,7 +536,11 @@ void GfxFrameout::showVideo() {
if (videoDecoder->needsUpdate()) {
const Graphics::Surface *frame = videoDecoder->decodeNextFrame();
if (frame) {
- g_system->copyRectToScreen(frame->getPixels(), frame->pitch, x, y, frame->w, frame->h);
+ // We need to clip here
+ // At least Phantasmagoria shows a 640x390 video on a 630x450 screen during the intro
+ outputWidth = frame->w > screenWidth ? screenWidth : frame->w;
+ outputHeight = frame->h > screenHeight ? screenHeight : frame->h;
+ g_system->copyRectToScreen(frame->getPixels(), frame->pitch, x, y, outputWidth, outputHeight);
if (videoDecoder->hasDirtyPalette())
g_system->getPaletteManager()->setPalette(videoDecoder->getPalette(), 0, 256);
diff --git a/engines/sci/graphics/paint16.cpp b/engines/sci/graphics/paint16.cpp
index b835eb92ca..6004e9ce7a 100644
--- a/engines/sci/graphics/paint16.cpp
+++ b/engines/sci/graphics/paint16.cpp
@@ -471,12 +471,9 @@ void GfxPaint16::kernelGraphRedrawBox(Common::Rect rect) {
#define SCI_DISPLAY_WIDTH 106
#define SCI_DISPLAY_SAVEUNDER 107
#define SCI_DISPLAY_RESTOREUNDER 108
-#define SCI_DISPLAY_DUMMY1 114
-#define SCI_DISPLAY_DUMMY2 115
-#define SCI_DISPLAY_DUMMY3 117
#define SCI_DISPLAY_DONTSHOWBITS 121
-reg_t GfxPaint16::kernelDisplay(const char *text, int argc, reg_t *argv) {
+reg_t GfxPaint16::kernelDisplay(const char *text, uint16 languageSplitter, int argc, reg_t *argv) {
reg_t displayArg;
TextAlignment alignment = SCI_TEXT16_ALIGNMENT_LEFT;
int16 colorPen = -1, colorBack = -1, width = -1, bRedraw = 1;
@@ -543,22 +540,6 @@ reg_t GfxPaint16::kernelDisplay(const char *text, int argc, reg_t *argv) {
bRedraw = 0;
break;
- // The following three dummy calls are not supported by the Sierra SCI
- // interpreter, but are erroneously called in some game scripts.
- case SCI_DISPLAY_DUMMY1: // Longbow demo (all rooms) and QFG1 EGA demo (room 11)
- case SCI_DISPLAY_DUMMY2: // Longbow demo (all rooms)
- case SCI_DISPLAY_DUMMY3: // QFG1 EGA demo (room 11) and PQ2 (room 23)
- if (!(g_sci->getGameId() == GID_LONGBOW && g_sci->isDemo()) &&
- !(g_sci->getGameId() == GID_QFG1 && g_sci->isDemo()) &&
- !(g_sci->getGameId() == GID_PQ2))
- error("Unknown kDisplay argument %d", displayArg.getOffset());
-
- if (displayArg.getOffset() == SCI_DISPLAY_DUMMY2) {
- if (!argc)
- error("No parameter left for kDisplay(115)");
- argc--; argv++;
- }
- break;
default:
SciTrackOriginReply originReply;
SciWorkaroundSolution solution = trackOriginAndFindWorkaround(0, kDisplay_workarounds, &originReply);
@@ -572,7 +553,7 @@ reg_t GfxPaint16::kernelDisplay(const char *text, int argc, reg_t *argv) {
}
// now drawing the text
- _text16->Size(rect, text, -1, width);
+ _text16->Size(rect, text, languageSplitter, -1, width);
rect.moveTo(_ports->getPort()->curLeft, _ports->getPort()->curTop);
// Note: This code has been found in SCI1 middle and newer games. It was
// previously only for SCI1 late and newer, but the LSL1 interpreter contains
@@ -588,7 +569,7 @@ reg_t GfxPaint16::kernelDisplay(const char *text, int argc, reg_t *argv) {
result = bitsSave(rect, GFX_SCREEN_MASK_VISUAL);
if (colorBack != -1)
fillRect(rect, GFX_SCREEN_MASK_VISUAL, colorBack, 0, 0);
- _text16->Box(text, false, rect, alignment, -1);
+ _text16->Box(text, languageSplitter, false, rect, alignment, -1);
if (_screen->_picNotValid == 0 && bRedraw)
bitsShow(rect);
// restoring port and cursor pos
diff --git a/engines/sci/graphics/paint16.h b/engines/sci/graphics/paint16.h
index 882f311a5b..955cfdec8f 100644
--- a/engines/sci/graphics/paint16.h
+++ b/engines/sci/graphics/paint16.h
@@ -80,7 +80,7 @@ public:
void kernelGraphUpdateBox(const Common::Rect &rect, bool hiresMode);
void kernelGraphRedrawBox(Common::Rect rect);
- reg_t kernelDisplay(const char *text, int argc, reg_t *argv);
+ reg_t kernelDisplay(const char *text, uint16 languageSplitter, int argc, reg_t *argv);
reg_t kernelPortraitLoad(const Common::String &resourceName);
void kernelPortraitShow(const Common::String &resourceName, Common::Point position, uint16 resourceNum, uint16 noun, uint16 verb, uint16 cond, uint16 seq);
diff --git a/engines/sci/graphics/paint32.cpp b/engines/sci/graphics/paint32.cpp
index 7d106b5b02..a210a469f1 100644
--- a/engines/sci/graphics/paint32.cpp
+++ b/engines/sci/graphics/paint32.cpp
@@ -46,7 +46,7 @@ void GfxPaint32::fillRect(Common::Rect rect, byte color) {
Common::Rect clipRect = rect;
clipRect.clip(_screen->getWidth(), _screen->getHeight());
-
+
for (y = clipRect.top; y < clipRect.bottom; y++) {
for (x = clipRect.left; x < clipRect.right; x++) {
_screen->putPixel(x, y, GFX_SCREEN_MASK_VISUAL, color, 0, 0);
diff --git a/engines/sci/graphics/palette.cpp b/engines/sci/graphics/palette.cpp
index a3624c7959..59abef5550 100644
--- a/engines/sci/graphics/palette.cpp
+++ b/engines/sci/graphics/palette.cpp
@@ -65,14 +65,21 @@ GfxPalette::GfxPalette(ResourceManager *resMan, GfxScreen *screen)
// the real merging done in earlier games. If we use the copying over, we
// will get issues because some views have marked all colors as being used
// and those will overwrite the current palette in that case
- if (getSciVersion() < SCI_VERSION_1_1)
+ if (getSciVersion() < SCI_VERSION_1_1) {
_useMerging = true;
- else if (getSciVersion() == SCI_VERSION_1_1)
+ _use16bitColorMatch = true;
+ } else if (getSciVersion() == SCI_VERSION_1_1) {
// there are some games that use inbetween SCI1.1 interpreter, so we have
// to detect if the current game is merging or copying
_useMerging = _resMan->detectPaletteMergingSci11();
- else // SCI32
+ _use16bitColorMatch = _useMerging;
+ // Note: Laura Bow 2 floppy uses the new palette format and is detected
+ // as 8 bit color matching because of that.
+ } else {
+ // SCI32
_useMerging = false;
+ _use16bitColorMatch = false; // not verified that SCI32 uses 8-bit color matching
+ }
palVaryInit();
@@ -120,6 +127,10 @@ bool GfxPalette::isMerging() {
return _useMerging;
}
+bool GfxPalette::isUsing16bitColorMatch() {
+ return _use16bitColorMatch;
+}
+
// meant to get called only once during init of engine
void GfxPalette::setDefault() {
if (_resMan->getViewType() == kViewEga)
@@ -464,8 +475,8 @@ bool GfxPalette::merge(Palette *newPalette, bool force, bool forceRealMerge) {
// check if exact color could be matched
res = matchColor(newPalette->colors[i].r, newPalette->colors[i].g, newPalette->colors[i].b);
- if (res & 0x8000) { // exact match was found
- newPalette->mapping[i] = res & 0xFF;
+ if (res & SCI_PALETTE_MATCH_PERFECT) { // exact match was found
+ newPalette->mapping[i] = res & SCI_PALETTE_MATCH_COLORMASK;
continue;
}
@@ -486,8 +497,8 @@ bool GfxPalette::merge(Palette *newPalette, bool force, bool forceRealMerge) {
// if still no luck - set an approximate color
if (j == 256) {
- newPalette->mapping[i] = res & 0xFF;
- _sysPalette.colors[res & 0xFF].used |= 0x10;
+ newPalette->mapping[i] = res & SCI_PALETTE_MATCH_COLORMASK;
+ _sysPalette.colors[res & SCI_PALETTE_MATCH_COLORMASK].used |= 0x10;
}
}
@@ -509,29 +520,47 @@ void GfxPalette::drewPicture(GuiResourceId pictureId) {
}
}
-uint16 GfxPalette::matchColor(byte r, byte g, byte b) {
- byte found = 0xFF;
- int diff = 0x2FFFF, cdiff;
- int16 dr,dg,db;
-
- for (int i = 1; i < 255; i++) {
- if ((!_sysPalette.colors[i].used))
- continue;
- dr = _sysPalette.colors[i].r - r;
- dg = _sysPalette.colors[i].g - g;
- db = _sysPalette.colors[i].b - b;
-// minimum squares match
- cdiff = (dr*dr) + (dg*dg) + (db*db);
-// minimum sum match (Sierra's)
-// cdiff = ABS(dr) + ABS(dg) + ABS(db);
- if (cdiff < diff) {
- if (cdiff == 0)
- return i | 0x8000; // setting this flag to indicate exact match
- found = i;
- diff = cdiff;
+uint16 GfxPalette::matchColor(byte matchRed, byte matchGreen, byte matchBlue) {
+ int16 colorNr;
+ int16 differenceRed, differenceGreen, differenceBlue;
+ int16 differenceTotal = 0;
+ int16 bestDifference = 0x7FFF;
+ uint16 bestColorNr = 255;
+
+ if (_use16bitColorMatch) {
+ // used by SCI0 to SCI1, also by the first few SCI1.1 games
+ for (colorNr = 0; colorNr < 256; colorNr++) {
+ if ((!_sysPalette.colors[colorNr].used))
+ continue;
+ differenceRed = ABS(_sysPalette.colors[colorNr].r - matchRed);
+ differenceGreen = ABS(_sysPalette.colors[colorNr].g - matchGreen);
+ differenceBlue = ABS(_sysPalette.colors[colorNr].b - matchBlue);
+ differenceTotal = differenceRed + differenceGreen + differenceBlue;
+ if (differenceTotal <= bestDifference) {
+ bestDifference = differenceTotal;
+ bestColorNr = colorNr;
+ }
+ }
+ } else {
+ // SCI1.1, starting with QfG3 introduced a bug in the matching code
+ // we have to implement it as well, otherwise some colors will be "wrong" in comparison to the original interpreter
+ // See Space Quest 5 bug #6455
+ for (colorNr = 0; colorNr < 256; colorNr++) {
+ if ((!_sysPalette.colors[colorNr].used))
+ continue;
+ differenceRed = (uint8)ABS<int8>(_sysPalette.colors[colorNr].r - matchRed);
+ differenceGreen = (uint8)ABS<int8>(_sysPalette.colors[colorNr].g - matchGreen);
+ differenceBlue = (uint8)ABS<int8>(_sysPalette.colors[colorNr].b - matchBlue);
+ differenceTotal = differenceRed + differenceGreen + differenceBlue;
+ if (differenceTotal <= bestDifference) {
+ bestDifference = differenceTotal;
+ bestColorNr = colorNr;
+ }
}
}
- return found;
+ if (differenceTotal == 0) // original interpreter does not do this, instead it does 2 calls for merges in the worst case
+ return bestColorNr | SCI_PALETTE_MATCH_PERFECT; // we set this flag, so that we can optimize during palette merge
+ return bestColorNr;
}
void GfxPalette::getSys(Palette *pal) {
@@ -621,7 +650,7 @@ void GfxPalette::kernelSetIntensity(uint16 fromColor, uint16 toColor, uint16 int
}
int16 GfxPalette::kernelFindColor(uint16 r, uint16 g, uint16 b) {
- return matchColor(r, g, b) & 0xFF;
+ return matchColor(r, g, b) & SCI_PALETTE_MATCH_COLORMASK;
}
// Returns true, if palette got changed
diff --git a/engines/sci/graphics/palette.h b/engines/sci/graphics/palette.h
index 347695deb8..500a45eccf 100644
--- a/engines/sci/graphics/palette.h
+++ b/engines/sci/graphics/palette.h
@@ -31,6 +31,10 @@ namespace Sci {
class ResourceManager;
class GfxScreen;
+// Special flag implemented by us for optimization in palette merge
+#define SCI_PALETTE_MATCH_PERFECT 0x8000
+#define SCI_PALETTE_MATCH_COLORMASK 0xFF
+
enum ColorRemappingType {
kRemappingNone = 0,
kRemappingByRange = 1,
@@ -46,6 +50,7 @@ public:
~GfxPalette();
bool isMerging();
+ bool isUsing16bitColorMatch();
void setDefault();
void createFromData(byte *data, int bytesLeft, Palette *paletteOut);
@@ -124,6 +129,7 @@ private:
bool _sysPaletteChanged;
bool _useMerging;
+ bool _use16bitColorMatch;
Common::Array<PalSchedule> _schedules;
diff --git a/engines/sci/graphics/picture.cpp b/engines/sci/graphics/picture.cpp
index 434a490109..d7ef84dc1e 100644
--- a/engines/sci/graphics/picture.cpp
+++ b/engines/sci/graphics/picture.cpp
@@ -88,10 +88,13 @@ void GfxPicture::draw(int16 animationNr, bool mirroredFlag, bool addToFlag, int1
}
void GfxPicture::reset() {
+ int16 startY = _ports->getPort()->top;
+ int16 startX = 0;
int16 x, y;
- for (y = _ports->getPort()->top; y < _screen->getHeight(); y++) {
- for (x = 0; x < _screen->getWidth(); x++) {
- _screen->putPixel(x, y, GFX_SCREEN_MASK_ALL, 255, 0, 0);
+ _screen->vectorAdjustCoordinate(&startX, &startY);
+ for (y = startY; y < _screen->getHeight(); y++) {
+ for (x = startX; x < _screen->getWidth(); x++) {
+ _screen->vectorPutPixel(x, y, GFX_SCREEN_MASK_ALL, 255, 0, 0);
}
}
}
@@ -246,7 +249,7 @@ void GfxPicture::drawCelData(byte *inbuffer, int size, int headerPos, int rlePos
int16 y, lastY, x, leftX, rightX;
int pixelCount;
uint16 width, height;
-
+
// if the picture is not an overlay and we are also not in EGA mode, use priority 0
if (!isEGA && !_addToFlag)
priority = 0;
@@ -362,7 +365,7 @@ void GfxPicture::drawCelData(byte *inbuffer, int size, int headerPos, int rlePos
ptr = celBitmap;
ptr += skipCelBitmapPixels;
ptr += skipCelBitmapLines * width;
-
+
if ((!isEGA) || (priority < 16)) {
// VGA + EGA, EGA only checks priority, when given priority is below 16
if (!_mirroredFlag) {
@@ -482,6 +485,8 @@ enum {
PIC_OPX_VGA_PRIORITY_TABLE_EXPLICIT = 4
};
+//#define DEBUG_PICTURE_DRAW 1
+
#ifdef DEBUG_PICTURE_DRAW
const char *picOpcodeNames[] = {
"Set color",
@@ -589,6 +594,9 @@ void GfxPicture::drawVectorData(byte *data, int dataSize) {
while (curPos < dataSize) {
#ifdef DEBUG_PICTURE_DRAW
debug("Picture op: %X (%s) at %d", data[curPos], picOpcodeNames[data[curPos] - 0xF0], curPos);
+ _screen->copyToScreen();
+ g_system->updateScreen();
+ g_system->delayMillis(400);
#endif
switch (pic_op = data[curPos++]) {
case PIC_OP_SET_COLOR:
@@ -934,17 +942,17 @@ void GfxPicture::vectorFloodFill(int16 x, int16 y, byte color, byte priority, by
Common::Point p, p1;
byte screenMask = _screen->getDrawingMask(color, priority, control);
byte matchedMask, matchMask;
- int16 w, e, a_set, b_set;
bool isEGA = (_resMan->getViewType() == kViewEga);
p.x = x + curPort->left;
p.y = y + curPort->top;
- stack.push(p);
- byte searchColor = _screen->getVisual(p.x, p.y);
- byte searchPriority = _screen->getPriority(p.x, p.y);
- byte searchControl = _screen->getControl(p.x, p.y);
+ _screen->vectorAdjustCoordinate(&p.x, &p.y);
+
+ byte searchColor = _screen->vectorGetVisual(p.x, p.y);
+ byte searchPriority = _screen->vectorGetPriority(p.x, p.y);
+ byte searchControl = _screen->vectorGetControl(p.x, p.y);
if (isEGA) {
// In EGA games a pixel in the framebuffer is only 4 bits. We store
@@ -991,22 +999,31 @@ void GfxPicture::vectorFloodFill(int16 x, int16 y, byte color, byte priority, by
}
// hard borders for filling
- int l = curPort->rect.left + curPort->left;
- int t = curPort->rect.top + curPort->top;
- int r = curPort->rect.right + curPort->left - 1;
- int b = curPort->rect.bottom + curPort->top - 1;
+ int16 borderLeft = curPort->rect.left + curPort->left;
+ int16 borderTop = curPort->rect.top + curPort->top;
+ int16 borderRight = curPort->rect.right + curPort->left - 1;
+ int16 borderBottom = curPort->rect.bottom + curPort->top - 1;
+ int16 curToLeft, curToRight, a_set, b_set;
+
+ // Translate coordinates, if required (needed for Macintosh 480x300)
+ _screen->vectorAdjustCoordinate(&borderLeft, &borderTop);
+ _screen->vectorAdjustCoordinate(&borderRight, &borderBottom);
+ //return;
+
+ stack.push(p);
+
while (stack.size()) {
p = stack.pop();
- if ((matchedMask = _screen->isFillMatch(p.x, p.y, matchMask, searchColor, searchPriority, searchControl, isEGA)) == 0) // already filled
+ if ((matchedMask = _screen->vectorIsFillMatch(p.x, p.y, matchMask, searchColor, searchPriority, searchControl, isEGA)) == 0) // already filled
continue;
- _screen->putPixel(p.x, p.y, screenMask, color, priority, control);
- w = p.x;
- e = p.x;
+ _screen->vectorPutPixel(p.x, p.y, screenMask, color, priority, control);
+ curToLeft = p.x;
+ curToRight = p.x;
// moving west and east pointers as long as there is a matching color to fill
- while (w > l && (matchedMask = _screen->isFillMatch(w - 1, p.y, matchMask, searchColor, searchPriority, searchControl, isEGA)))
- _screen->putPixel(--w, p.y, screenMask, color, priority, control);
- while (e < r && (matchedMask = _screen->isFillMatch(e + 1, p.y, matchMask, searchColor, searchPriority, searchControl, isEGA)))
- _screen->putPixel(++e, p.y, screenMask, color, priority, control);
+ while (curToLeft > borderLeft && (matchedMask = _screen->vectorIsFillMatch(curToLeft - 1, p.y, matchMask, searchColor, searchPriority, searchControl, isEGA)))
+ _screen->vectorPutPixel(--curToLeft, p.y, screenMask, color, priority, control);
+ while (curToRight < borderRight && (matchedMask = _screen->vectorIsFillMatch(curToRight + 1, p.y, matchMask, searchColor, searchPriority, searchControl, isEGA)))
+ _screen->vectorPutPixel(++curToRight, p.y, screenMask, color, priority, control);
#if 0
// debug code for floodfill
_screen->copyToScreen();
@@ -1015,10 +1032,10 @@ void GfxPicture::vectorFloodFill(int16 x, int16 y, byte color, byte priority, by
#endif
// checking lines above and below for possible flood targets
a_set = b_set = 0;
- while (w <= e) {
- if (p.y > t && (matchedMask = _screen->isFillMatch(w, p.y - 1, matchMask, searchColor, searchPriority, searchControl, isEGA))) { // one line above
+ while (curToLeft <= curToRight) {
+ if (p.y > borderTop && (matchedMask = _screen->vectorIsFillMatch(curToLeft, p.y - 1, matchMask, searchColor, searchPriority, searchControl, isEGA))) { // one line above
if (a_set == 0) {
- p1.x = w;
+ p1.x = curToLeft;
p1.y = p.y - 1;
stack.push(p1);
a_set = 1;
@@ -1026,16 +1043,16 @@ void GfxPicture::vectorFloodFill(int16 x, int16 y, byte color, byte priority, by
} else
a_set = 0;
- if (p.y < b && (matchedMask = _screen->isFillMatch(w, p.y + 1, matchMask, searchColor, searchPriority, searchControl, isEGA))) { // one line below
+ if (p.y < borderBottom && (matchedMask = _screen->vectorIsFillMatch(curToLeft, p.y + 1, matchMask, searchColor, searchPriority, searchControl, isEGA))) { // one line below
if (b_set == 0) {
- p1.x = w;
+ p1.x = curToLeft;
p1.y = p.y + 1;
stack.push(p1);
b_set = 1;
}
} else
b_set = 0;
- w++;
+ curToLeft++;
}
}
}
@@ -1173,7 +1190,7 @@ void GfxPicture::vectorPatternBox(Common::Rect box, byte color, byte prio, byte
for (y = box.top; y < box.bottom; y++) {
for (x = box.left; x < box.right; x++) {
- _screen->putPixel(x, y, flag, color, prio, control);
+ _screen->vectorPutPixel(x, y, flag, color, prio, control);
}
}
}
@@ -1186,7 +1203,7 @@ void GfxPicture::vectorPatternTexturedBox(Common::Rect box, byte color, byte pri
for (y = box.top; y < box.bottom; y++) {
for (x = box.left; x < box.right; x++) {
if (*textureData) {
- _screen->putPixel(x, y, flag, color, prio, control);
+ _screen->vectorPutPixel(x, y, flag, color, prio, control);
}
textureData++;
}
@@ -1203,7 +1220,7 @@ void GfxPicture::vectorPatternCircle(Common::Rect box, byte size, byte color, by
for (y = box.top; y < box.bottom; y++) {
for (x = box.left; x < box.right; x++) {
if (bitmap & 1) {
- _screen->putPixel(x, y, flag, color, prio, control);
+ _screen->vectorPutPixel(x, y, flag, color, prio, control);
}
bitNo++;
if (bitNo == 8) {
@@ -1222,12 +1239,12 @@ void GfxPicture::vectorPatternTexturedCircle(Common::Rect box, byte size, byte c
byte bitNo = 0;
const bool *textureData = &vectorPatternTextures[vectorPatternTextureOffset[texture]];
int y, x;
-
+
for (y = box.top; y < box.bottom; y++) {
for (x = box.left; x < box.right; x++) {
if (bitmap & 1) {
if (*textureData) {
- _screen->putPixel(x, y, flag, color, prio, control);
+ _screen->vectorPutPixel(x, y, flag, color, prio, control);
}
textureData++;
}
@@ -1252,7 +1269,10 @@ void GfxPicture::vectorPattern(int16 x, int16 y, byte color, byte priority, byte
rect.top = y; rect.left = x;
rect.setHeight((size*2)+1); rect.setWidth((size*2)+2);
_ports->offsetRect(rect);
- rect.clip(_screen->getWidth(), _screen->getHeight());
+ rect.clip(_screen->getScriptWidth(), _screen->getScriptHeight());
+
+ _screen->vectorAdjustCoordinate(&rect.left, &rect.top);
+ _screen->vectorAdjustCoordinate(&rect.right, &rect.bottom);
if (code & SCI_PATTERN_CODE_RECTANGLE) {
// Rectangle
diff --git a/engines/sci/graphics/portrait.cpp b/engines/sci/graphics/portrait.cpp
index 488450485d..cb425f3be9 100644
--- a/engines/sci/graphics/portrait.cpp
+++ b/engines/sci/graphics/portrait.cpp
@@ -57,13 +57,39 @@ void Portrait::init() {
// 4 bytes paletteSize (base 1)
// -> 17 bytes
// paletteSize bytes paletteData
- // 14 bytes bitmap header
- // -> 4 bytes unknown
- // -> 2 bytes height
- // -> 2 bytes width
- // -> 6 bytes unknown
- // height * width bitmap data
- // another animation count times bitmap header and data
+ //
+ // bitmap-data follows, total of [animation count]
+ // 14 bytes bitmap header
+ // -> 4 bytes unknown
+ // -> 2 bytes height
+ // -> 2 bytes width
+ // -> 6 bytes unknown
+ // height * width bitmap data
+ //
+ // 4 bytes offset table size (may be larger than the actual known entries?!)
+ // 14 bytes all zeroes (dummy entry?!)
+ //
+ // 14 bytes for each entry
+ // -> 2 bytes displace X
+ // -> 2 bytes displace Y
+ // -> 2 bytes height (again)
+ // -> 2 bytes width (again)
+ // -> 6 bytes unknown (normally 01 00 00 00 00 00 for delta bitmaps, 00 00 00 00 00 00 for first bitmap)
+ // random data may be used as filler
+ //
+ // 4 bytes lip sync id table size (is [lip sync id count] * 4, should be 0x2E0 for all actors)
+ // 4 bytes per lip sync id
+ // -> 1 byte length of ID
+ // -> 3 bytes actual ID
+ //
+ // 4 bytes lip sync id data table size (seems to be the same for all actors, always 0x220 in size)
+ // 1 byte animation number or 0xFF as terminator
+ // 1 byte delay, if last byte was not terminator
+ // one array for every lip sync id
+ //
+ // 4 bytes appended, seem to be random
+ // 9E11120E for alex
+ // 9E9E9E9E for vizier
int32 fileSize = 0;
Common::SeekableReadStream *file =
SearchMan.createReadStreamForMember("actors/" + _resourceName + ".bin");
@@ -134,34 +160,34 @@ void Portrait::init() {
// raw lip-sync ID table follows
uint32 lipSyncIDTableSize;
-
+
lipSyncIDTableSize = READ_LE_UINT32(data);
data += 4;
assert( lipSyncIDTableSize == (_lipSyncIDCount * 4) );
_lipSyncIDTable = data;
data += lipSyncIDTableSize;
-
+
// raw lip-sync frame table follows
uint32 lipSyncDataTableSize;
uint32 lipSyncDataTableLastOffset;
byte lipSyncData;
uint16 lipSyncDataNr;
uint16 lipSyncCurOffset;
-
+
lipSyncDataTableSize = READ_LE_UINT32(data);
data += 4;
assert( lipSyncDataTableSize == 0x220 ); // always this size, just a safety-check
-
+
_lipSyncData = data;
lipSyncDataTableLastOffset = lipSyncDataTableSize - 1;
_lipSyncDataOffsetTable = new uint16[ _lipSyncIDCount ];
-
+
lipSyncDataNr = 0;
lipSyncCurOffset = 0;
while ( (lipSyncCurOffset < lipSyncDataTableSize) && (lipSyncDataNr < _lipSyncIDCount) ) {
// We are currently at the start of ID-frame data
_lipSyncDataOffsetTable[lipSyncDataNr] = lipSyncCurOffset;
-
+
// Look for end of ID-frame data
lipSyncData = *data++; lipSyncCurOffset++;
while ( (lipSyncData != 0xFF) && (lipSyncCurOffset < lipSyncDataTableLastOffset) ) {
@@ -195,15 +221,16 @@ void Portrait::doit(Common::Point position, uint16 resourceId, uint16 noun, uint
Resource *syncResource = _resMan->findResource(syncResourceId, true);
uint syncOffset = 0;
#endif
-
+
#ifdef DEBUG_PORTRAIT
// prints out the current lip sync ASCII data
char debugPrint[4000];
if (raveResource->size < 4000) {
memcpy(debugPrint, raveResource->data, raveResource->size);
debugPrint[raveResource->size] = 0; // set terminating NUL
+ debug("kPortrait: using actor %s", _resourceName.c_str());
debug("kPortrait (noun %d, verb %d, cond %d, seq %d)", noun, verb, cond, seq);
- debug("kPortrait: %s", debugPrint);
+ debug("kPortrait: rave data is '%s'", debugPrint);
}
#endif
@@ -246,7 +273,7 @@ void Portrait::doit(Common::Point position, uint16 resourceId, uint16 noun, uint
warning("kPortrait: no rave resource %d %X", resourceId, audioNumber);
return;
}
-
+
// Do animation depending on rave resource till audio is done playing
int16 raveTicks;
uint16 raveID;
@@ -264,7 +291,7 @@ void Portrait::doit(Common::Point position, uint16 resourceId, uint16 noun, uint
raveTicks = raveGetTicks(raveResource, &raveOffset);
if (raveTicks < 0)
break;
-
+
// get lipSyncID
raveID = raveGetID(raveResource, &raveOffset);
if (raveID) {
@@ -272,7 +299,15 @@ void Portrait::doit(Common::Point position, uint16 resourceId, uint16 noun, uint
} else {
raveLipSyncData = NULL;
}
-
+
+#ifdef DEBUG_PORTRAIT
+ if (raveID & 0x0ff) {
+ debug("kPortrait: rave '%c%c' after %d ticks", raveID >> 8, raveID & 0x0ff, raveTicks);
+ } else if (raveID) {
+ debug("kPortrait: rave '%c' after %d ticks", raveID >> 8, raveTicks);
+ }
+#endif
+
timerPosition += raveTicks;
// Wait till syncTime passed, then show specific animation bitmap
@@ -282,12 +317,13 @@ void Portrait::doit(Common::Point position, uint16 resourceId, uint16 noun, uint
curEvent = _event->getSciEvent(SCI_EVENT_ANY);
if (curEvent.type == SCI_EVENT_MOUSE_PRESS ||
(curEvent.type == SCI_EVENT_KEYBOARD && curEvent.data == SCI_KEY_ESC) ||
- g_sci->getEngineState()->abortScriptProcessing == kAbortQuitGame)
+ g_sci->getEngineState()->abortScriptProcessing == kAbortQuitGame ||
+ g_sci->getEngineState()->_delayedRestoreGame)
userAbort = true;
curPosition = _audio->getAudioPosition();
} while ((curPosition != -1) && (curPosition < timerPosition) && (!userAbort));
}
-
+
if (raveLipSyncData) {
// lip sync data is
// Tick:Byte, Bitmap-Nr:BYTE
@@ -295,6 +331,8 @@ void Portrait::doit(Common::Point position, uint16 resourceId, uint16 noun, uint
timerPositionWithin = timerPosition;
raveLipSyncTicks = *raveLipSyncData++;
while ( (raveLipSyncData < _lipSyncDataOffsetTableEnd) && (raveLipSyncTicks != 0xFF) ) {
+ if (raveLipSyncTicks)
+ raveLipSyncTicks--; // 1 -> wait 0 ticks, 2 -> wait 1 tick, etc.
timerPositionWithin += raveLipSyncTicks;
do {
@@ -308,7 +346,14 @@ void Portrait::doit(Common::Point position, uint16 resourceId, uint16 noun, uint
} while ((curPosition != -1) && (curPosition < timerPositionWithin) && (!userAbort));
raveLipSyncBitmapNr = *raveLipSyncData++;
-
+#ifdef DEBUG_PORTRAIT
+ if (!raveLipSyncTicks) {
+ debug("kPortrait: showing frame %d", raveLipSyncBitmapNr);
+ } else {
+ debug("kPortrait: showing frame %d after %d ticks", raveLipSyncBitmapNr, raveLipSyncTicks);
+ }
+#endif
+
// bitmap nr within sync data is base 1, we need base 0
raveLipSyncBitmapNr--;
@@ -319,7 +364,7 @@ void Portrait::doit(Common::Point position, uint16 resourceId, uint16 noun, uint
} else {
warning("kPortrait: rave lip sync data tried to draw non-existent bitmap %d", raveLipSyncBitmapNr);
}
-
+
raveLipSyncTicks = *raveLipSyncData++;
}
}
@@ -372,7 +417,7 @@ void Portrait::doit(Common::Point position, uint16 resourceId, uint16 noun, uint
}
}
#endif
-
+
// Reset the portrait bitmap to "closed mouth" state (rave.dll seems to do the same)
drawBitmap(0);
bitsShow();
@@ -393,10 +438,10 @@ int16 Portrait::raveGetTicks(Resource *resource, uint *offset) {
byte *curData = resource->data + curOffset;
byte curByte;
uint16 curValue = 0;
-
+
if (curOffset >= resource->size)
return -1;
-
+
while (curOffset < resource->size) {
curByte = *curData++; curOffset++;
if ( curByte == ' ' )
@@ -418,7 +463,7 @@ uint16 Portrait::raveGetID(Resource *resource, uint *offset) {
byte *curData = resource->data + curOffset;
byte curByte = 0;
uint16 curValue = 0;
-
+
while (curOffset < resource->size) {
curByte = *curData++; curOffset++;
if ( curByte == ' ' )
@@ -429,7 +474,7 @@ uint16 Portrait::raveGetID(Resource *resource, uint *offset) {
curValue |= curByte;
}
}
-
+
*offset = curOffset;
return curValue;
}
@@ -440,17 +485,17 @@ byte *Portrait::raveGetLipSyncData(uint16 raveID) {
byte *lipSyncIDPtr = _lipSyncIDTable;
byte lipSyncIDByte1, lipSyncIDByte2;
uint16 lipSyncID;
-
+
lipSyncIDPtr++; // skip over first byte
while (lipSyncIDNr < _lipSyncIDCount) {
lipSyncIDByte1 = *lipSyncIDPtr++;
lipSyncIDByte2 = *lipSyncIDPtr++;
lipSyncID = ( lipSyncIDByte1 << 8 ) | lipSyncIDByte2;
-
+
if ( lipSyncID == raveID ) {
return _lipSyncData + _lipSyncDataOffsetTable[lipSyncIDNr];
}
-
+
lipSyncIDNr++;
lipSyncIDPtr += 2; // ID is every 4 bytes
}
diff --git a/engines/sci/graphics/portrait.h b/engines/sci/graphics/portrait.h
index 877b253bcf..e0888daa86 100644
--- a/engines/sci/graphics/portrait.h
+++ b/engines/sci/graphics/portrait.h
@@ -72,7 +72,7 @@ private:
Common::String _resourceName;
byte *_fileData;
-
+
uint32 _lipSyncIDCount;
byte *_lipSyncIDTable;
diff --git a/engines/sci/graphics/ports.cpp b/engines/sci/graphics/ports.cpp
index 56c63a7b12..bcc991081e 100644
--- a/engines/sci/graphics/ports.cpp
+++ b/engines/sci/graphics/ports.cpp
@@ -63,10 +63,10 @@ void GfxPorts::init(bool usesOldGfxFunctions, GfxPaint16 *paint16, GfxText16 *te
openPort(_menuPort);
setPort(_menuPort);
_text16->SetFont(0);
- _menuPort->rect = Common::Rect(0, 0, _screen->getWidth(), _screen->getHeight());
- _menuBarRect = Common::Rect(0, 0, _screen->getWidth(), 9);
- _menuRect = Common::Rect(0, 0, _screen->getWidth(), 10);
- _menuLine = Common::Rect(0, 9, _screen->getWidth(), 10);
+ _menuPort->rect = Common::Rect(0, 0, _screen->getScriptWidth(), _screen->getScriptHeight());
+ _menuBarRect = Common::Rect(0, 0, _screen->getScriptWidth(), 9);
+ _menuRect = Common::Rect(0, 0, _screen->getScriptWidth(), 10);
+ _menuLine = Common::Rect(0, 9, _screen->getScriptWidth(), 10);
_wmgrPort = new Port(1);
_windowsById.resize(2);
@@ -122,13 +122,13 @@ void GfxPorts::init(bool usesOldGfxFunctions, GfxPaint16 *paint16, GfxText16 *te
} else {
_wmgrPort->rect.bottom = _screen->getHeight();
}
- _wmgrPort->rect.right = _screen->getWidth();
+ _wmgrPort->rect.right = _screen->getScriptWidth();
_wmgrPort->rect.moveTo(0, 0);
_wmgrPort->curTop = 0;
_wmgrPort->curLeft = 0;
_windowList.push_front(_wmgrPort);
- _picWind = addWindow(Common::Rect(0, offTop, _screen->getWidth(), _screen->getHeight()), 0, 0, SCI_WINDOWMGR_STYLE_TRANSPARENT | SCI_WINDOWMGR_STYLE_NOFRAME, 0, true);
+ _picWind = addWindow(Common::Rect(0, offTop, _screen->getScriptWidth(), _screen->getScriptHeight()), 0, 0, SCI_WINDOWMGR_STYLE_TRANSPARENT | SCI_WINDOWMGR_STYLE_NOFRAME, 0, true);
// For SCI0 games till kq4 (.502 - not including) we set _picWind top to offTop instead
// Because of the menu/status bar
if (_usesOldGfxFunctions)
@@ -321,13 +321,13 @@ Window *GfxPorts::addWindow(const Common::Rect &dims, const Common::Rect *restor
// their interpreter even in the newer VGA games.
r.left = r.left & 0xFFFE;
- if (r.width() > _screen->getWidth()) {
+ if (r.width() > _screen->getScriptWidth()) {
// We get invalid dimensions at least at the end of sq3 (script bug!).
// Same happens very often in lsl5, sierra sci didnt fix it but it looked awful.
// Also happens frequently in the demo of GK1.
warning("Fixing too large window, left: %d, right: %d", dims.left, dims.right);
r.left = 0;
- r.right = _screen->getWidth() - 1;
+ r.right = _screen->getScriptWidth() - 1;
if ((style != _styleUser) && !(style & SCI_WINDOWMGR_STYLE_NOFRAME))
r.right--;
}
diff --git a/engines/sci/graphics/screen.cpp b/engines/sci/graphics/screen.cpp
index c5c94d7991..ca5b5b3b8c 100644
--- a/engines/sci/graphics/screen.cpp
+++ b/engines/sci/graphics/screen.cpp
@@ -37,7 +37,15 @@ GfxScreen::GfxScreen(ResourceManager *resMan) : _resMan(resMan) {
// Scale the screen, if needed
_upscaledHires = GFX_SCREEN_UPSCALED_DISABLED;
-
+
+ // we default to scripts running at 320x200
+ _scriptWidth = 320;
+ _scriptHeight = 200;
+ _width = 0;
+ _height = 0;
+ _displayWidth = 0;
+ _displayHeight = 0;
+
// King's Quest 6 and Gabriel Knight 1 have hires content, gk1/cd was able
// to provide that under DOS as well, but as gk1/floppy does support
// upscaled hires scriptswise, but doesn't actually have the hires content
@@ -50,10 +58,34 @@ GfxScreen::GfxScreen(ResourceManager *resMan) : _resMan(resMan) {
_upscaledHires = GFX_SCREEN_UPSCALED_640x480;
#endif
}
+
+ // Japanese versions of games use hi-res font on upscaled version of the game.
+ if ((g_sci->getLanguage() == Common::JA_JPN) && (getSciVersion() <= SCI_VERSION_1_1))
+ _upscaledHires = GFX_SCREEN_UPSCALED_640x400;
+ // Macintosh SCI0 games used 480x300, while the scripts were running at 320x200
if (g_sci->getPlatform() == Common::kPlatformMacintosh) {
- if (getSciVersion() <= SCI_VERSION_01)
+ if (getSciVersion() <= SCI_VERSION_01) {
_upscaledHires = GFX_SCREEN_UPSCALED_480x300;
+ _width = 480;
+ _height = 300; // regular visual, priority and control map are 480x300 (this is different than other upscaled SCI games)
+ }
+
+ // Some Mac SCI1/1.1 games only take up 190 rows and do not
+ // have the menu bar.
+ // TODO: Verify that LSL1 and LSL5 use height 190
+ switch (g_sci->getGameId()) {
+ case GID_FREDDYPHARKAS:
+ case GID_KQ5:
+ case GID_KQ6:
+ case GID_LSL1:
+ case GID_LSL5:
+ case GID_SQ1:
+ _scriptHeight = 190;
+ break;
+ default:
+ break;
+ }
}
#ifdef ENABLE_SCI32
@@ -65,76 +97,77 @@ GfxScreen::GfxScreen(ResourceManager *resMan) : _resMan(resMan) {
#endif
if (_resMan->detectHires()) {
- _width = 640;
- _pitch = 640;
- _height = 480;
- } else {
- _width = 320;
- _pitch = 320;
- _height = getLowResScreenHeight();
+ _scriptWidth = 640;
+ _scriptHeight = 480;
}
#ifdef ENABLE_SCI32
- // Phantasmagoria 1 sets a window area of 630x450
+ // Phantasmagoria 1 effectively outputs 630x450
+ // Coordinate translation has to use this resolution as well
if (g_sci->getGameId() == GID_PHANTASMAGORIA) {
_width = 630;
_height = 450;
}
#endif
- // Japanese versions of games use hi-res font on upscaled version of the game.
- if ((g_sci->getLanguage() == Common::JA_JPN) && (getSciVersion() <= SCI_VERSION_1_1))
- _upscaledHires = GFX_SCREEN_UPSCALED_640x400;
+ // if not yet set, set those to script-width/height
+ if (!_width)
+ _width = _scriptWidth;
+ if (!_height)
+ _height = _scriptHeight;
- _pixels = _pitch * _height;
+ _pixels = _width * _height;
switch (_upscaledHires) {
case GFX_SCREEN_UPSCALED_480x300:
// Space Quest 3, Hoyle 1+2 on MAC use this one
- // TODO: Sierra's upscaling worked differently. We need to figure out the exact algo
_displayWidth = 480;
_displayHeight = 300;
- for (int i = 0; i <= _height; i++)
+ for (int i = 0; i <= _scriptHeight; i++)
_upscaledHeightMapping[i] = (i * 3) >> 1;
- for (int i = 0; i <= _width; i++)
+ for (int i = 0; i <= _scriptWidth; i++)
_upscaledWidthMapping[i] = (i * 3) >> 1;
break;
case GFX_SCREEN_UPSCALED_640x400:
// Police Quest 2 and Quest For Glory on PC9801 (Japanese)
_displayWidth = 640;
_displayHeight = 400;
- for (int i = 0; i <= _height; i++)
+ for (int i = 0; i <= _scriptHeight; i++)
_upscaledHeightMapping[i] = i * 2;
- for (int i = 0; i <= _width; i++)
+ for (int i = 0; i <= _scriptWidth; i++)
_upscaledWidthMapping[i] = i * 2;
break;
case GFX_SCREEN_UPSCALED_640x440:
// used by King's Quest 6 on Windows
_displayWidth = 640;
_displayHeight = 440;
- for (int i = 0; i <= _height; i++)
+ for (int i = 0; i <= _scriptHeight; i++)
_upscaledHeightMapping[i] = (i * 11) / 5;
- for (int i = 0; i <= _width; i++)
+ for (int i = 0; i <= _scriptWidth; i++)
_upscaledWidthMapping[i] = i * 2;
break;
case GFX_SCREEN_UPSCALED_640x480:
// Gabriel Knight 1 (VESA, Mac)
_displayWidth = 640;
_displayHeight = 480;
- for (int i = 0; i <= _height; i++)
+ for (int i = 0; i <= _scriptHeight; i++)
_upscaledHeightMapping[i] = (i * 12) / 5;
- for (int i = 0; i <= _width; i++)
+ for (int i = 0; i <= _scriptWidth; i++)
_upscaledWidthMapping[i] = i * 2;
break;
default:
- _displayWidth = _pitch;
- _displayHeight = _height;
+ if (!_displayWidth)
+ _displayWidth = _width;
+ if (!_displayHeight)
+ _displayHeight = _height;
memset(&_upscaledHeightMapping, 0, sizeof(_upscaledHeightMapping) );
memset(&_upscaledWidthMapping, 0, sizeof(_upscaledWidthMapping) );
break;
}
_displayPixels = _displayWidth * _displayHeight;
+
+ // Allocate visual, priority, control and display screen
_visualScreen = (byte *)calloc(_pixels, 1);
_priorityScreen = (byte *)calloc(_pixels, 1);
_controlScreen = (byte *)calloc(_pixels, 1);
@@ -179,6 +212,36 @@ GfxScreen::GfxScreen(ResourceManager *resMan) : _resMan(resMan) {
error("Unknown SCI1.1 Mac game");
} else
initGraphics(_displayWidth, _displayHeight, _displayWidth > 320);
+
+ // Initialize code pointers
+ _vectorAdjustCoordinatePtr = &GfxScreen::vectorAdjustCoordinateNOP;
+ _vectorAdjustLineCoordinatesPtr = &GfxScreen::vectorAdjustLineCoordinatesNOP;
+ _vectorIsFillMatchPtr = &GfxScreen::vectorIsFillMatchNormal;
+ _vectorPutPixelPtr = &GfxScreen::putPixelNormal;
+ _vectorPutLinePixelPtr = &GfxScreen::putPixel;
+ _vectorGetPixelPtr = &GfxScreen::getPixelNormal;
+ _putPixelPtr = &GfxScreen::putPixelNormal;
+ _getPixelPtr = &GfxScreen::getPixelNormal;
+
+ switch (_upscaledHires) {
+ case GFX_SCREEN_UPSCALED_480x300:
+ _vectorAdjustCoordinatePtr = &GfxScreen::vectorAdjustCoordinate480x300Mac;
+ _vectorAdjustLineCoordinatesPtr = &GfxScreen::vectorAdjustLineCoordinates480x300Mac;
+ // vectorPutPixel -> we already adjust coordinates for vector code, that's why we can set pixels directly
+ // vectorGetPixel -> see vectorPutPixel
+ _vectorPutLinePixelPtr = &GfxScreen::vectorPutLinePixel480x300Mac;
+ _putPixelPtr = &GfxScreen::putPixelAllUpscaled;
+ _getPixelPtr = &GfxScreen::getPixelUpscaled;
+ break;
+ case GFX_SCREEN_UPSCALED_640x400:
+ case GFX_SCREEN_UPSCALED_640x440:
+ case GFX_SCREEN_UPSCALED_640x480:
+ _vectorPutPixelPtr = &GfxScreen::putPixelDisplayUpscaled;
+ _putPixelPtr = &GfxScreen::putPixelDisplayUpscaled;
+ break;
+ case GFX_SCREEN_UPSCALED_DISABLED:
+ break;
+ }
}
GfxScreen::~GfxScreen() {
@@ -188,6 +251,18 @@ GfxScreen::~GfxScreen() {
free(_displayScreen);
}
+// should not be used regularly; only meant for restore game
+void GfxScreen::clearForRestoreGame() {
+ // reset all screen data
+ memset(_visualScreen, 0, _pixels);
+ memset(_priorityScreen, 0, _pixels);
+ memset(_controlScreen, 0, _pixels);
+ memset(_displayScreen, 0, _displayPixels);
+ memset(&_ditheredPicColors, 0, sizeof(_ditheredPicColors));
+ _fontIsUpscaled = false;
+ copyToScreen();
+}
+
void GfxScreen::copyToScreen() {
g_system->copyRectToScreen(_activeScreen, _displayWidth, 0, 0, _displayWidth, _displayHeight);
}
@@ -232,7 +307,7 @@ void GfxScreen::copyRectToScreen(const Common::Rect &rect, int16 x, int16 y) {
} else {
int rectHeight = _upscaledHeightMapping[rect.bottom] - _upscaledHeightMapping[rect.top];
int rectWidth = _upscaledWidthMapping[rect.right] - _upscaledWidthMapping[rect.left];
-
+
g_system->copyRectToScreen(_activeScreen + _upscaledHeightMapping[rect.top] * _displayWidth + _upscaledWidthMapping[rect.left], _displayWidth, _upscaledWidthMapping[x], _upscaledHeightMapping[y], rectWidth, rectHeight);
}
}
@@ -248,43 +323,162 @@ byte GfxScreen::getDrawingMask(byte color, byte prio, byte control) {
return flag;
}
-void GfxScreen::putPixel(int x, int y, byte drawMask, byte color, byte priority, byte control) {
- int offset = y * _pitch + x;
+void GfxScreen::vectorAdjustCoordinateNOP(int16 *x, int16 *y) {
+}
- if (drawMask & GFX_SCREEN_MASK_VISUAL) {
- _visualScreen[offset] = color;
- if (!_upscaledHires) {
- _displayScreen[offset] = color;
+void GfxScreen::vectorAdjustCoordinate480x300Mac(int16 *x, int16 *y) {
+ *x = _upscaledWidthMapping[*x];
+ *y = _upscaledHeightMapping[*y];
+}
+
+void GfxScreen::vectorAdjustLineCoordinatesNOP(int16 *left, int16 *top, int16 *right, int16 *bottom, byte drawMask, byte color, byte priority, byte control) {
+}
+
+void GfxScreen::vectorAdjustLineCoordinates480x300Mac(int16 *left, int16 *top, int16 *right, int16 *bottom, byte drawMask, byte color, byte priority, byte control) {
+ int16 displayLeft = _upscaledWidthMapping[*left];
+ int16 displayRight = _upscaledWidthMapping[*right];
+ int16 displayTop = _upscaledHeightMapping[*top];
+ int16 displayBottom = _upscaledHeightMapping[*bottom];
+
+ if (displayLeft < displayRight) {
+ // one more pixel to the left, one more pixel to the right
+ if (displayLeft > 0)
+ vectorPutLinePixel(displayLeft - 1, displayTop, drawMask, color, priority, control);
+ vectorPutLinePixel(displayRight + 1, displayBottom, drawMask, color, priority, control);
+ } else if (displayLeft > displayRight) {
+ if (displayRight > 0)
+ vectorPutLinePixel(displayRight - 1, displayBottom, drawMask, color, priority, control);
+ vectorPutLinePixel(displayLeft + 1, displayTop, drawMask, color, priority, control);
+ }
+ *left = displayLeft;
+ *top = displayTop;
+ *right = displayRight;
+ *bottom = displayBottom;
+}
+
+byte GfxScreen::vectorIsFillMatchNormal(int16 x, int16 y, byte screenMask, byte checkForColor, byte checkForPriority, byte checkForControl, bool isEGA) {
+ int offset = y * _width + x;
+ byte match = 0;
+
+ if (screenMask & GFX_SCREEN_MASK_VISUAL) {
+ if (!isEGA) {
+ if (*(_visualScreen + offset) == checkForColor)
+ match |= GFX_SCREEN_MASK_VISUAL;
} else {
- putScaledPixelOnDisplay(x, y, color);
+ // In EGA games a pixel in the framebuffer is only 4 bits. We store
+ // a full byte per pixel to allow undithering, but when comparing
+ // pixels for flood-fill purposes, we should only compare the
+ // visible color of a pixel.
+
+ byte EGAcolor = *(_visualScreen + offset);
+ if ((x ^ y) & 1)
+ EGAcolor = (EGAcolor ^ (EGAcolor >> 4)) & 0x0F;
+ else
+ EGAcolor = EGAcolor & 0x0F;
+ if (EGAcolor == checkForColor)
+ match |= GFX_SCREEN_MASK_VISUAL;
}
}
+ if ((screenMask & GFX_SCREEN_MASK_PRIORITY) && *(_priorityScreen + offset) == checkForPriority)
+ match |= GFX_SCREEN_MASK_PRIORITY;
+ if ((screenMask & GFX_SCREEN_MASK_CONTROL) && *(_controlScreen + offset) == checkForControl)
+ match |= GFX_SCREEN_MASK_CONTROL;
+ return match;
+}
+
+// Special 480x300 Mac putPixel for vector line drawing, also draws an additional pixel below the actual one
+void GfxScreen::vectorPutLinePixel480x300Mac(int16 x, int16 y, byte drawMask, byte color, byte priority, byte control) {
+ int offset = y * _width + x;
+
+ if (drawMask & GFX_SCREEN_MASK_VISUAL) {
+ _visualScreen[offset] = color;
+ _visualScreen[offset + _width] = color;
+ _displayScreen[offset] = color;
+ // also set pixel below actual pixel
+ _displayScreen[offset + _displayWidth] = color;
+ }
+ if (drawMask & GFX_SCREEN_MASK_PRIORITY) {
+ _priorityScreen[offset] = priority;
+ _priorityScreen[offset + _width] = priority;
+ }
+ if (drawMask & GFX_SCREEN_MASK_CONTROL) {
+ _controlScreen[offset] = control;
+ _controlScreen[offset + _width] = control;
+ }
+}
+
+// Directly sets a pixel on various screens, display is not upscaled
+void GfxScreen::putPixelNormal(int16 x, int16 y, byte drawMask, byte color, byte priority, byte control) {
+ int offset = y * _width + x;
+
+ if (drawMask & GFX_SCREEN_MASK_VISUAL) {
+ _visualScreen[offset] = color;
+ _displayScreen[offset] = color;
+ }
if (drawMask & GFX_SCREEN_MASK_PRIORITY)
_priorityScreen[offset] = priority;
if (drawMask & GFX_SCREEN_MASK_CONTROL)
_controlScreen[offset] = control;
}
+// Directly sets a pixel on various screens, display IS upscaled
+void GfxScreen::putPixelDisplayUpscaled(int16 x, int16 y, byte drawMask, byte color, byte priority, byte control) {
+ int offset = y * _width + x;
+
+ if (drawMask & GFX_SCREEN_MASK_VISUAL) {
+ _visualScreen[offset] = color;
+ putScaledPixelOnScreen(_displayScreen, x, y, color);
+ }
+ if (drawMask & GFX_SCREEN_MASK_PRIORITY)
+ _priorityScreen[offset] = priority;
+ if (drawMask & GFX_SCREEN_MASK_CONTROL)
+ _controlScreen[offset] = control;
+}
+
+// Directly sets a pixel on various screens, ALL screens ARE upscaled
+void GfxScreen::putPixelAllUpscaled(int16 x, int16 y, byte drawMask, byte color, byte priority, byte control) {
+ if (drawMask & GFX_SCREEN_MASK_VISUAL) {
+ putScaledPixelOnScreen(_visualScreen, x, y, color);
+ putScaledPixelOnScreen(_displayScreen, x, y, color);
+ }
+ if (drawMask & GFX_SCREEN_MASK_PRIORITY)
+ putScaledPixelOnScreen(_priorityScreen, x, y, priority);
+ if (drawMask & GFX_SCREEN_MASK_CONTROL)
+ putScaledPixelOnScreen(_controlScreen, x, y, control);
+}
+
/**
* This is used to put font pixels onto the screen - we adjust differently, so that we won't
* do triple pixel lines in any case on upscaled hires. That way the font will not get distorted
* Sierra SCI didn't do this
*/
-void GfxScreen::putFontPixel(int startingY, int x, int y, byte color) {
- int actualY = startingY + y;
+void GfxScreen::putFontPixel(int16 startingY, int16 x, int16 y, byte color) {
+ int16 actualY = startingY + y;
if (_fontIsUpscaled) {
// Do not scale ourselves, but put it on the display directly
putPixelOnDisplay(x, actualY, color);
} else {
- int offset = actualY * _pitch + x;
+ int offset = actualY * _width + x;
_visualScreen[offset] = color;
switch (_upscaledHires) {
case GFX_SCREEN_UPSCALED_DISABLED:
_displayScreen[offset] = color;
break;
+ case GFX_SCREEN_UPSCALED_640x400:
+ case GFX_SCREEN_UPSCALED_640x440:
+ case GFX_SCREEN_UPSCALED_640x480: {
+ // to 1-> 4 pixels upscaling for all of those, so that fonts won't look weird
+ int displayOffset = (_upscaledHeightMapping[startingY] + y * 2) * _displayWidth + x * 2;
+ _displayScreen[displayOffset] = color;
+ _displayScreen[displayOffset + 1] = color;
+ displayOffset += _displayWidth;
+ _displayScreen[displayOffset] = color;
+ _displayScreen[displayOffset + 1] = color;
+ break;
+ }
default:
- putScaledPixelOnDisplay(x, actualY, color);
+ putScaledPixelOnScreen(_displayScreen, x, actualY, color);
break;
}
}
@@ -295,12 +489,15 @@ void GfxScreen::putFontPixel(int startingY, int x, int y, byte color) {
* only used on upscaled-Hires games where hires content needs to get drawn ONTO
* the upscaled display screen (like japanese fonts, hires portraits, etc.).
*/
-void GfxScreen::putPixelOnDisplay(int x, int y, byte color) {
+void GfxScreen::putPixelOnDisplay(int16 x, int16 y, byte color) {
int offset = y * _displayWidth + x;
_displayScreen[offset] = color;
}
-void GfxScreen::putScaledPixelOnDisplay(int x, int y, byte color) {
+//void GfxScreen::putScaledPixelOnDisplay(int16 x, int16 y, byte color) {
+//}
+
+void GfxScreen::putScaledPixelOnScreen(byte *screen, int16 x, int16 y, byte data) {
int displayOffset = _upscaledHeightMapping[y] * _displayWidth + _upscaledWidthMapping[x];
int heightOffsetBreak = (_upscaledHeightMapping[y + 1] - _upscaledHeightMapping[y]) * _displayWidth;
int heightOffset = 0;
@@ -308,7 +505,7 @@ void GfxScreen::putScaledPixelOnDisplay(int x, int y, byte color) {
do {
int widthOffset = 0;
do {
- _displayScreen[displayOffset + heightOffset + widthOffset] = color;
+ screen[displayOffset + heightOffset + widthOffset] = data;
widthOffset++;
} while (widthOffset != widthOffsetBreak);
heightOffset += _displayWidth;
@@ -329,16 +526,18 @@ void GfxScreen::drawLine(Common::Point startPoint, Common::Point endPoint, byte
int16 top = CLIP<int16>(startPoint.y, 0, maxHeight);
int16 right = CLIP<int16>(endPoint.x, 0, maxWidth);
int16 bottom = CLIP<int16>(endPoint.y, 0, maxHeight);
-
+
//set_drawing_flag
byte drawMask = getDrawingMask(color, priority, control);
+ vectorAdjustLineCoordinates(&left, &top, &right, &bottom, drawMask, color, priority, control);
+
// horizontal line
if (top == bottom) {
if (right < left)
SWAP(right, left);
for (int i = left; i <= right; i++)
- putPixel(i, top, drawMask, color, priority, control);
+ vectorPutLinePixel(i, top, drawMask, color, priority, control);
return;
}
// vertical line
@@ -346,20 +545,20 @@ void GfxScreen::drawLine(Common::Point startPoint, Common::Point endPoint, byte
if (top > bottom)
SWAP(top, bottom);
for (int i = top; i <= bottom; i++)
- putPixel(left, i, drawMask, color, priority, control);
+ vectorPutLinePixel(left, i, drawMask, color, priority, control);
return;
}
// sloped line - draw with Bresenham algorithm
- int dy = bottom - top;
- int dx = right - left;
- int stepy = dy < 0 ? -1 : 1;
- int stepx = dx < 0 ? -1 : 1;
+ int16 dy = bottom - top;
+ int16 dx = right - left;
+ int16 stepy = dy < 0 ? -1 : 1;
+ int16 stepx = dx < 0 ? -1 : 1;
dy = ABS(dy) << 1;
dx = ABS(dx) << 1;
// setting the 1st and last pixel
- putPixel(left, top, drawMask, color, priority, control);
- putPixel(right, bottom, drawMask, color, priority, control);
+ vectorPutLinePixel(left, top, drawMask, color, priority, control);
+ vectorPutLinePixel(right, bottom, drawMask, color, priority, control);
// drawing the line
if (dx > dy) { // going horizontal
int fraction = dy - (dx >> 1);
@@ -370,7 +569,7 @@ void GfxScreen::drawLine(Common::Point startPoint, Common::Point endPoint, byte
}
left += stepx;
fraction += dy;
- putPixel(left, top, drawMask, color, priority, control);
+ vectorPutLinePixel(left, top, drawMask, color, priority, control);
}
} else { // going vertical
int fraction = dx - (dy >> 1);
@@ -381,7 +580,7 @@ void GfxScreen::drawLine(Common::Point startPoint, Common::Point endPoint, byte
}
top += stepy;
fraction += dx;
- putPixel(left, top, drawMask, color, priority, control);
+ vectorPutLinePixel(left, top, drawMask, color, priority, control);
}
}
}
@@ -394,46 +593,14 @@ void GfxScreen::putKanjiChar(Graphics::FontSJIS *commonFont, int16 x, int16 y, u
commonFont->drawChar(displayPtr, chr, _displayWidth, 1, color, 0, -1, -1);
}
-byte GfxScreen::getVisual(int x, int y) {
- return _visualScreen[y * _pitch + x];
+byte GfxScreen::getPixelNormal(byte *screen, int16 x, int16 y) {
+ return screen[y * _width + x];
}
-byte GfxScreen::getPriority(int x, int y) {
- return _priorityScreen[y * _pitch + x];
-}
-
-byte GfxScreen::getControl(int x, int y) {
- return _controlScreen[y * _pitch + x];
-}
-
-byte GfxScreen::isFillMatch(int16 x, int16 y, byte screenMask, byte t_color, byte t_pri, byte t_con, bool isEGA) {
- int offset = y * _pitch + x;
- byte match = 0;
-
- if (screenMask & GFX_SCREEN_MASK_VISUAL) {
- if (!isEGA) {
- if (*(_visualScreen + offset) == t_color)
- match |= GFX_SCREEN_MASK_VISUAL;
- } else {
- // In EGA games a pixel in the framebuffer is only 4 bits. We store
- // a full byte per pixel to allow undithering, but when comparing
- // pixels for flood-fill purposes, we should only compare the
- // visible color of a pixel.
-
- byte c = *(_visualScreen + offset);
- if ((x ^ y) & 1)
- c = (c ^ (c >> 4)) & 0x0F;
- else
- c = c & 0x0F;
- if (c == t_color)
- match |= GFX_SCREEN_MASK_VISUAL;
- }
- }
- if ((screenMask & GFX_SCREEN_MASK_PRIORITY) && *(_priorityScreen + offset) == t_pri)
- match |= GFX_SCREEN_MASK_PRIORITY;
- if ((screenMask & GFX_SCREEN_MASK_CONTROL) && *(_controlScreen + offset) == t_con)
- match |= GFX_SCREEN_MASK_CONTROL;
- return match;
+byte GfxScreen::getPixelUpscaled(byte *screen, int16 x, int16 y) {
+ int16 mappedX = _upscaledWidthMapping[x];
+ int16 mappedY = _upscaledHeightMapping[y];
+ return screen[mappedY * _width + mappedX];
}
int GfxScreen::bitsGetDataSize(Common::Rect rect, byte mask) {
@@ -469,14 +636,14 @@ void GfxScreen::bitsSave(Common::Rect rect, byte mask, byte *memoryPtr) {
memcpy(memoryPtr, (void *)&mask, sizeof(mask)); memoryPtr += sizeof(mask);
if (mask & GFX_SCREEN_MASK_VISUAL) {
- bitsSaveScreen(rect, _visualScreen, _pitch, memoryPtr);
+ bitsSaveScreen(rect, _visualScreen, _width, memoryPtr);
bitsSaveDisplayScreen(rect, memoryPtr);
}
if (mask & GFX_SCREEN_MASK_PRIORITY) {
- bitsSaveScreen(rect, _priorityScreen, _pitch, memoryPtr);
+ bitsSaveScreen(rect, _priorityScreen, _width, memoryPtr);
}
if (mask & GFX_SCREEN_MASK_CONTROL) {
- bitsSaveScreen(rect, _controlScreen, _pitch, memoryPtr);
+ bitsSaveScreen(rect, _controlScreen, _width, memoryPtr);
}
if (mask & GFX_SCREEN_MASK_DISPLAY) {
if (!_upscaledHires)
@@ -530,14 +697,14 @@ void GfxScreen::bitsRestore(byte *memoryPtr) {
memcpy((void *)&mask, memoryPtr, sizeof(mask)); memoryPtr += sizeof(mask);
if (mask & GFX_SCREEN_MASK_VISUAL) {
- bitsRestoreScreen(rect, memoryPtr, _visualScreen, _pitch);
+ bitsRestoreScreen(rect, memoryPtr, _visualScreen, _width);
bitsRestoreDisplayScreen(rect, memoryPtr);
}
if (mask & GFX_SCREEN_MASK_PRIORITY) {
- bitsRestoreScreen(rect, memoryPtr, _priorityScreen, _pitch);
+ bitsRestoreScreen(rect, memoryPtr, _priorityScreen, _width);
}
if (mask & GFX_SCREEN_MASK_CONTROL) {
- bitsRestoreScreen(rect, memoryPtr, _controlScreen, _pitch);
+ bitsRestoreScreen(rect, memoryPtr, _controlScreen, _width);
}
if (mask & GFX_SCREEN_MASK_DISPLAY) {
if (!_upscaledHires)
@@ -612,21 +779,22 @@ void GfxScreen::dither(bool addToFlag) {
byte color, ditheredColor;
byte *visualPtr = _visualScreen;
byte *displayPtr = _displayScreen;
-
+
if (!_unditheringEnabled) {
// Do dithering on visual and display-screen
for (y = 0; y < _height; y++) {
- for (x = 0; x < _pitch; x++) {
+ for (x = 0; x < _width; x++) {
color = *visualPtr;
if (color & 0xF0) {
color ^= color << 4;
color = ((x^y) & 1) ? color >> 4 : color & 0x0F;
switch (_upscaledHires) {
case GFX_SCREEN_UPSCALED_DISABLED:
+ case GFX_SCREEN_UPSCALED_480x300:
*displayPtr = color;
break;
default:
- putScaledPixelOnDisplay(x, y, color);
+ putScaledPixelOnScreen(_displayScreen, x, y, color);
break;
}
*visualPtr = color;
@@ -639,7 +807,7 @@ void GfxScreen::dither(bool addToFlag) {
memset(&_ditheredPicColors, 0, sizeof(_ditheredPicColors));
// Do dithering on visual screen and put decoded but undithered byte onto display-screen
for (y = 0; y < _height; y++) {
- for (x = 0; x < _pitch; x++) {
+ for (x = 0; x < _width; x++) {
color = *visualPtr;
if (color & 0xF0) {
color ^= color << 4;
@@ -654,10 +822,11 @@ void GfxScreen::dither(bool addToFlag) {
}
switch (_upscaledHires) {
case GFX_SCREEN_UPSCALED_DISABLED:
+ case GFX_SCREEN_UPSCALED_480x300:
*displayPtr = ditheredColor;
break;
default:
- putScaledPixelOnDisplay(x, y, ditheredColor);
+ putScaledPixelOnScreen(_displayScreen, x, y, ditheredColor);
break;
}
color = ((x^y) & 1) ? color >> 4 : color & 0x0F;
@@ -685,8 +854,8 @@ int16 *GfxScreen::unditherGetDitheredBgColors() {
}
void GfxScreen::debugShowMap(int mapNo) {
- // We cannot really support changing maps when in upscaledHires mode
- if (_upscaledHires)
+ // We cannot really support changing maps when display screen has a different resolution than visual screen
+ if ((_width != _displayWidth) || (_height != _displayHeight))
return;
switch (mapNo) {
@@ -779,8 +948,8 @@ void GfxScreen::adjustBackUpscaledCoordinates(int16 &y, int16 &x, Sci32ViewNativ
switch (_upscaledHires) {
case GFX_SCREEN_UPSCALED_480x300:
- x = (x << 1) / 3;
- y = (y << 1) / 3;
+ x = (x * 4) / 6;
+ y = (y * 4) / 6;
break;
case GFX_SCREEN_UPSCALED_640x400:
x /= 2;
@@ -816,26 +985,4 @@ int16 GfxScreen::kernelPicNotValid(int16 newPicNotValid) {
return oldPicNotValid;
}
-uint16 GfxScreen::getLowResScreenHeight() {
- // Some Mac SCI1/1.1 games only take up 190 rows and do not
- // have the menu bar.
- // TODO: Verify that LSL1 and LSL5 use height 190
- if (g_sci->getPlatform() == Common::kPlatformMacintosh) {
- switch (g_sci->getGameId()) {
- case GID_FREDDYPHARKAS:
- case GID_KQ5:
- case GID_KQ6:
- case GID_LSL1:
- case GID_LSL5:
- case GID_SQ1:
- return 190;
- default:
- break;
- }
- }
-
- // Everything else is 200
- return 200;
-}
-
} // End of namespace Sci
diff --git a/engines/sci/graphics/screen.h b/engines/sci/graphics/screen.h
index e266a4ed16..1c946ef02f 100644
--- a/engines/sci/graphics/screen.h
+++ b/engines/sci/graphics/screen.h
@@ -69,11 +69,14 @@ public:
uint16 getWidth() { return _width; }
uint16 getHeight() { return _height; }
+ uint16 getScriptWidth() { return _scriptWidth; }
+ uint16 getScriptHeight() { return _scriptHeight; }
uint16 getDisplayWidth() { return _displayWidth; }
uint16 getDisplayHeight() { return _displayHeight; }
byte getColorWhite() { return _colorWhite; }
byte getColorDefaultVectorData() { return _colorDefaultVectorData; }
+ void clearForRestoreGame();
void copyToScreen();
void copyFromScreen(byte *buffer);
void kernelSyncWithFramebuffer();
@@ -81,11 +84,51 @@ public:
void copyDisplayRectToScreen(const Common::Rect &rect);
void copyRectToScreen(const Common::Rect &rect, int16 x, int16 y);
+ // calls to code pointers
+ void inline vectorAdjustCoordinate (int16 *x, int16 *y) {
+ (this->*_vectorAdjustCoordinatePtr)(x, y);
+ }
+ void inline vectorAdjustLineCoordinates (int16 *left, int16 *top, int16 *right, int16 *bottom, byte drawMask, byte color, byte priority, byte control) {
+ (this->*_vectorAdjustLineCoordinatesPtr)(left, top, right, bottom, drawMask, color, priority, control);
+ }
+ byte inline vectorIsFillMatch (int16 x, int16 y, byte screenMask, byte t_color, byte t_pri, byte t_con, bool isEGA) {
+ return (this->*_vectorIsFillMatchPtr)(x, y, screenMask, t_color, t_pri, t_con, isEGA);
+ }
+ void inline vectorPutPixel(int16 x, int16 y, byte drawMask, byte color, byte priority, byte control) {
+ (this->*_vectorPutPixelPtr)(x, y, drawMask, color, priority, control);
+ }
+ void inline vectorPutLinePixel(int16 x, int16 y, byte drawMask, byte color, byte priority, byte control) {
+ (this->*_vectorPutLinePixelPtr)(x, y, drawMask, color, priority, control);
+ }
+ byte inline vectorGetVisual(int16 x, int16 y) {
+ return (this->*_vectorGetPixelPtr)(_visualScreen, x, y);
+ }
+ byte inline vectorGetPriority(int16 x, int16 y) {
+ return (this->*_vectorGetPixelPtr)(_priorityScreen, x, y);
+ }
+ byte inline vectorGetControl(int16 x, int16 y) {
+ return (this->*_vectorGetPixelPtr)(_controlScreen, x, y);
+ }
+
+
+ void inline putPixel(int16 x, int16 y, byte drawMask, byte color, byte priority, byte control) {
+ (this->*_putPixelPtr)(x, y, drawMask, color, priority, control);
+ }
+
+ byte inline getVisual(int16 x, int16 y) {
+ return (this->*_getPixelPtr)(_visualScreen, x, y);
+ }
+ byte inline getPriority(int16 x, int16 y) {
+ return (this->*_getPixelPtr)(_priorityScreen, x, y);
+ }
+ byte inline getControl(int16 x, int16 y) {
+ return (this->*_getPixelPtr)(_controlScreen, x, y);
+ }
+
byte getDrawingMask(byte color, byte prio, byte control);
- void putPixel(int x, int y, byte drawMask, byte color, byte prio, byte control);
- void putFontPixel(int startingY, int x, int y, byte color);
- void putPixelOnDisplay(int x, int y, byte color);
- void putScaledPixelOnDisplay(int x, int y, byte color);
+ //void putPixel(int16 x, int16 y, byte drawMask, byte color, byte prio, byte control);
+ void putFontPixel(int16 startingY, int16 x, int16 y, byte color);
+ void putPixelOnDisplay(int16 x, int16 y, byte color);
void drawLine(Common::Point startPoint, Common::Point endPoint, byte color, byte prio, byte control);
void drawLine(int16 left, int16 top, int16 right, int16 bottom, byte color, byte prio, byte control) {
drawLine(Common::Point(left, top), Common::Point(right, bottom), color, prio, control);
@@ -101,10 +144,6 @@ public:
void enableUndithering(bool flag);
void putKanjiChar(Graphics::FontSJIS *commonFont, int16 x, int16 y, uint16 chr, byte color);
- byte getVisual(int x, int y);
- byte getPriority(int x, int y);
- byte getControl(int x, int y);
- byte isFillMatch(int16 x, int16 y, byte drawMask, byte t_color, byte t_pri, byte t_con, bool isEGA);
int bitsGetDataSize(Common::Rect rect, byte mask);
void bitsSave(Common::Rect rect, byte mask, byte *memoryPtr);
@@ -135,9 +174,10 @@ public:
private:
uint16 _width;
- uint16 _pitch;
uint16 _height;
uint _pixels;
+ uint16 _scriptWidth;
+ uint16 _scriptHeight;
uint16 _displayWidth;
uint16 _displayHeight;
uint _displayPixels;
@@ -190,8 +230,8 @@ private:
* This here holds a translation for vertical+horizontal coordinates between native
* (visual) and actual (display) screen.
*/
- int _upscaledHeightMapping[SCI_SCREEN_UPSCALEDMAXHEIGHT + 1];
- int _upscaledWidthMapping[SCI_SCREEN_UPSCALEDMAXWIDTH + 1];
+ int16 _upscaledHeightMapping[SCI_SCREEN_UPSCALEDMAXHEIGHT + 1];
+ int16 _upscaledWidthMapping[SCI_SCREEN_UPSCALEDMAXWIDTH + 1];
/**
* This defines whether or not the font we're drawing is already scaled
@@ -199,7 +239,38 @@ private:
*/
bool _fontIsUpscaled;
- uint16 getLowResScreenHeight();
+ // dynamic code
+ void (GfxScreen::*_vectorAdjustCoordinatePtr) (int16 *x, int16 *y);
+ void vectorAdjustCoordinateNOP (int16 *x, int16 *y);
+ void vectorAdjustCoordinate480x300Mac (int16 *x, int16 *y);
+
+ void (GfxScreen::*_vectorAdjustLineCoordinatesPtr) (int16 *left, int16 *top, int16 *right, int16 *bottom, byte drawMask, byte color, byte priority, byte control);
+ void vectorAdjustLineCoordinatesNOP (int16 *left, int16 *top, int16 *right, int16 *bottom, byte drawMask, byte color, byte priority, byte control);
+ void vectorAdjustLineCoordinates480x300Mac (int16 *left, int16 *top, int16 *right, int16 *bottom, byte drawMask, byte color, byte priority, byte control);
+
+ byte (GfxScreen::*_vectorIsFillMatchPtr) (int16 x, int16 y, byte screenMask, byte t_color, byte t_pri, byte t_con, bool isEGA);
+ byte vectorIsFillMatchNormal (int16 x, int16 y, byte screenMask, byte t_color, byte t_pri, byte t_con, bool isEGA);
+ byte vectorIsFillMatch480x300Mac (int16 x, int16 y, byte screenMask, byte t_color, byte t_pri, byte t_con, bool isEGA);
+
+ void (GfxScreen::*_vectorPutPixelPtr) (int16 x, int16 y, byte drawMask, byte color, byte priority, byte control);
+ void vectorPutPixel480x300Mac (int16 x, int16 y, byte drawMask, byte color, byte priority, byte control);
+
+ void (GfxScreen::*_vectorPutLinePixelPtr) (int16 x, int16 y, byte drawMask, byte color, byte priority, byte control);
+ void vectorPutLinePixel480x300Mac (int16 x, int16 y, byte drawMask, byte color, byte priority, byte control);
+
+ byte (GfxScreen::*_vectorGetPixelPtr) (byte *screen, int16 x, int16 y);
+
+ void (GfxScreen::*_putPixelPtr) (int16 x, int16 y, byte drawMask, byte color, byte priority, byte control);
+ void putPixelNormal (int16 x, int16 y, byte drawMask, byte color, byte priority, byte control);
+ void putPixelDisplayUpscaled (int16 x, int16 y, byte drawMask, byte color, byte priority, byte control);
+ void putPixelAllUpscaled (int16 x, int16 y, byte drawMask, byte color, byte priority, byte control);
+
+ byte (GfxScreen::*_getPixelPtr) (byte *screen, int16 x, int16 y);
+ byte getPixelNormal (byte *screen, int16 x, int16 y);
+ byte getPixelUpscaled (byte *screen, int16 x, int16 y);
+
+ // pixel helper
+ void putScaledPixelOnScreen(byte *screen, int16 x, int16 y, byte color);
};
} // End of namespace Sci
diff --git a/engines/sci/graphics/text16.cpp b/engines/sci/graphics/text16.cpp
index 245d6996cb..f463dff4b1 100644
--- a/engines/sci/graphics/text16.cpp
+++ b/engines/sci/graphics/text16.cpp
@@ -143,18 +143,30 @@ int16 GfxText16::CodeProcessing(const char *&text, GuiResourceId orgFontId, int1
return textCodeSize;
}
-static const uint16 text16_punctuationSjis[] = {
- 0x9F82, 0xA182, 0xA382, 0xA582, 0xA782, 0xC182, 0xA782, 0xC182, 0xE182, 0xE382, 0xE582, 0xEC82,
- 0x4083, 0x4283, 0x4483, 0x4683, 0x4883, 0x6283, 0x8383, 0x8583, 0x8783, 0x8E83, 0x9583, 0x9683,
- 0x5B81, 0x4181, 0x4281, 0x7681, 0x7881, 0x4981, 0x4881, 0
+// Has actually punctuation and characters in it, that may not be the first in a line
+static const uint16 text16_shiftJIS_punctuation[] = {
+ 0x9F82, 0xA182, 0xA382, 0xA582, 0xA782, 0xC182, 0xE182, 0xE382, 0xE582, 0xEC82, 0x4083, 0x4283,
+ 0x4483, 0x4683, 0x4883, 0x6283, 0x8383, 0x8583, 0x8783, 0x8E83, 0x9583, 0x9683, 0x5B81, 0x4181,
+ 0x4281, 0x7681, 0x7881, 0x4981, 0x4881, 0
};
// return max # of chars to fit maxwidth with full words, does not include
// breaking space
-int16 GfxText16::GetLongest(const char *text, int16 maxWidth, GuiResourceId orgFontId) {
+// Also adjusts text pointer to the new position for the caller
+//
+// Special cases in games:
+// Laura Bow 2 - Credits in the game menu - all the text lines start with spaces (bug #5159)
+// Act 6 Coroner questionaire - the text of all control buttons has trailing spaces
+// "Detective Ryan Hanrahan O'Riley" contains even more spaces (bug #5334)
+// Conquests of Camelot - talking with Cobb - one text box of the dialogue contains a longer word,
+// that will be broken into 2 lines (bug #5159)
+int16 GfxText16::GetLongest(const char *&textPtr, int16 maxWidth, GuiResourceId orgFontId) {
uint16 curChar = 0;
- int16 maxChars = 0, curCharCount = 0;
- uint16 width = 0;
+ const char *textStartPtr = textPtr;
+ const char *lastSpacePtr = NULL;
+ int16 lastSpaceCharCount = 0;
+ int16 curCharCount = 0, resultCharCount = 0;
+ uint16 curWidth = 0, tempWidth = 0;
GuiResourceId previousFontId = GetFontId();
int16 previousPenColor = _ports->_curPort->penClr;
@@ -162,35 +174,38 @@ int16 GfxText16::GetLongest(const char *text, int16 maxWidth, GuiResourceId orgF
if (!_font)
return 0;
- while (width <= maxWidth) {
- curChar = (*(const byte *)text++);
+ while (1) {
+ curChar = (*(const byte *)textPtr);
if (_font->isDoubleByte(curChar)) {
- curChar |= (*(const byte *)text++) << 8;
- curCharCount++;
+ curChar |= (*(const byte *)(textPtr + 1)) << 8;
}
switch (curChar) {
case 0x7C:
if (getSciVersion() >= SCI_VERSION_1_1) {
- curCharCount++;
- curCharCount += CodeProcessing(text, orgFontId, previousPenColor, false);
+ curCharCount++; textPtr++;
+ curCharCount += CodeProcessing(textPtr, orgFontId, previousPenColor, false);
continue;
}
break;
// We need to add 0xD, 0xA and 0xD 0xA to curCharCount and then exit
- // which means, we split text like
- // 'Mature, experienced software analyst available.' 0xD 0xA
- // 'Bug installation a proven speciality. "No version too clean."' (normal game text, this is from lsl2)
- // and 0xA '-------' 0xA (which is the official sierra subtitle separator)
+ // which means, we split text like for example
+ // - 'Mature, experienced software analyst available.' 0xD 0xA
+ // 'Bug installation a proven speciality. "No version too clean."' (normal game text, this is from lsl2)
+ // - 0xA '-------' 0xA (which is the official sierra subtitle separator) (found in multilingual versions)
// Sierra did it the same way.
case 0xD:
// Check, if 0xA is following, if so include it as well
- if ((*(const unsigned char *)text) == 0xA)
- curCharCount++;
+ if ((*(const byte *)(textPtr + 1)) == 0xA) {
+ curCharCount++; textPtr++;
+ }
// it's meant to pass through here
case 0xA:
case 0x9781: // this one is used by SQ4/japanese as line break as well
- curCharCount++;
+ curCharCount++; textPtr++;
+ if (curChar > 0xFF) {
+ curCharCount++; textPtr++;
+ }
// and it's also meant to pass through here
case 0:
SetFont(previousFontId);
@@ -198,55 +213,86 @@ int16 GfxText16::GetLongest(const char *text, int16 maxWidth, GuiResourceId orgF
return curCharCount;
case ' ':
- maxChars = curCharCount; // return count up to (but not including) breaking space
+ lastSpaceCharCount = curCharCount; // return count up to (but not including) breaking space
+ lastSpacePtr = textPtr + 1; // remember position right after the current space
break;
}
- // Sometimes this can go off the screen, like for example bug #3040161.
- // However, we only perform this for non-Japanese games, as these require
- // special handling, done after this loop.
- if (width + _font->getCharWidth(curChar) > maxWidth && g_sci->getLanguage() != Common::JA_JPN)
+ tempWidth += _font->getCharWidth(curChar);
+
+ // Width is too large? -> break out
+ if (tempWidth > maxWidth)
break;
- width += _font->getCharWidth(curChar);
- curCharCount++;
+
+ // still fits, remember width
+ curWidth = tempWidth;
+
+ // go to next character
+ curCharCount++; textPtr++;
+ if (curChar > 0xFF) {
+ // Double-Byte
+ curCharCount++; textPtr++;
+ }
}
- // Text without spaces, probably Kanji/Japanese
- if (maxChars == 0) {
- maxChars = curCharCount;
+ if (lastSpaceCharCount) {
+ // Break and at least one space was found before that
+ resultCharCount = lastSpaceCharCount;
- uint16 nextChar;
+ // additionally skip over all spaces, that are following that space, but don't count them for displaying purposes
+ textPtr = lastSpacePtr;
+ while (*textPtr == ' ')
+ textPtr++;
- // We remove the last char only, if maxWidth was actually equal width
- // before adding the last char. Otherwise we won't get the same cutting
- // as in sierra pc98 sci.
- if (maxWidth == (width - _font->getCharWidth(curChar))) {
- maxChars--;
- if (curChar > 0xFF)
- maxChars--;
- nextChar = curChar;
- } else {
- nextChar = (*(const byte *)text++);
- if (_font->isDoubleByte(nextChar))
- nextChar |= (*(const byte *)text++) << 8;
- }
- // sierra checked the following character against a punctuation kanji table
- if (nextChar > 0xFF) {
- // if the character is punctuation, we go back one character
- uint nonBreakingNr = 0;
- while (text16_punctuationSjis[nonBreakingNr]) {
- if (text16_punctuationSjis[nonBreakingNr] == nextChar) {
- maxChars--;
- if (curChar > 0xFF)
- maxChars--; // go back 2 chars, when last char was double byte
+ } else {
+ // Break without spaces found, we split the very first word - may also be Kanji/Japanese
+ if (curChar > 0xFF) {
+ // current charracter is Japanese
+
+ // PC-9801 SCI actually added the last character, which shouldn't fit anymore, still onto the
+ // screen in case maxWidth wasn't fully reached with the last character
+ if (( maxWidth - 1 ) > curWidth) {
+ curCharCount += 2; textPtr += 2;
+
+ curChar = (*(const byte *)textPtr);
+ if (_font->isDoubleByte(curChar)) {
+ curChar |= (*(const byte *)(textPtr + 1)) << 8;
+ }
+ }
+
+ // But it also checked, if the current character is not inside a punctuation table and it even
+ // went backwards in case it found multiple ones inside that table.
+ uint nonBreakingPos = 0;
+
+ while (1) {
+ // Look up if character shouldn't be the first on a new line
+ nonBreakingPos = 0;
+ while (text16_shiftJIS_punctuation[nonBreakingPos]) {
+ if (text16_shiftJIS_punctuation[nonBreakingPos] == curChar)
+ break;
+ nonBreakingPos++;
+ }
+ if (!text16_shiftJIS_punctuation[nonBreakingPos]) {
+ // character is fine
break;
}
- nonBreakingNr++;
+ // Character is not acceptable, seek backward in the text
+ curCharCount -= 2; textPtr -= 2;
+ if (textPtr < textStartPtr)
+ error("Seeking back went too far, data corruption?");
+
+ curChar = (*(const byte *)textPtr);
+ if (!_font->isDoubleByte(curChar))
+ error("Non double byte while seeking back");
+ curChar |= (*(const byte *)(textPtr + 1)) << 8;
}
}
+
+ // We split the word in that case
+ resultCharCount = curCharCount;
}
SetFont(previousFontId);
_ports->penColor(previousPenColor);
- return maxChars;
+ return resultCharCount;
}
void GfxText16::Width(const char *text, int16 from, int16 len, GuiResourceId orgFontId, int16 &textWidth, int16 &textHeight, bool restoreFont) {
@@ -303,7 +349,7 @@ void GfxText16::DrawString(const char *str, GuiResourceId orgFontId, int16 orgPe
Draw(str, 0, (int16)strlen(str), orgFontId, orgPenColor);
}
-int16 GfxText16::Size(Common::Rect &rect, const char *text, GuiResourceId fontId, int16 maxWidth) {
+int16 GfxText16::Size(Common::Rect &rect, const char *text, uint16 languageSplitter, GuiResourceId fontId, int16 maxWidth) {
GuiResourceId previousFontId = GetFontId();
int16 previousPenColor = _ports->_curPort->penClr;
int16 charCount;
@@ -315,12 +361,12 @@ int16 GfxText16::Size(Common::Rect &rect, const char *text, GuiResourceId fontId
else
fontId = previousFontId;
- if (g_sci->getLanguage() == Common::JA_JPN)
- SwitchToFont900OnSjis(text);
-
rect.top = rect.left = 0;
if (maxWidth < 0) { // force output as single line
+ if (g_sci->getLanguage() == Common::JA_JPN)
+ SwitchToFont900OnSjis(text, languageSplitter);
+
StringWidth(text, fontId, textWidth, textHeight);
rect.bottom = textHeight;
rect.right = textWidth;
@@ -328,17 +374,20 @@ int16 GfxText16::Size(Common::Rect &rect, const char *text, GuiResourceId fontId
// rect.right=found widest line with RTextWidth and GetLongest
// rect.bottom=num. lines * GetPointSize
rect.right = (maxWidth ? maxWidth : 192);
- const char *curPos = text;
- while (*curPos) {
- charCount = GetLongest(curPos, rect.right, fontId);
+ const char *curTextPos = text; // in work position for GetLongest()
+ const char *curTextLine = text; // starting point of current line
+ while (*curTextPos) {
+ // We need to check for Shift-JIS every line
+ if (g_sci->getLanguage() == Common::JA_JPN)
+ SwitchToFont900OnSjis(curTextPos, languageSplitter);
+
+ charCount = GetLongest(curTextPos, rect.right, fontId);
if (charCount == 0)
break;
- Width(curPos, 0, charCount, fontId, textWidth, textHeight, false);
+ Width(curTextLine, 0, charCount, fontId, textWidth, textHeight, false);
maxTextWidth = MAX(textWidth, maxTextWidth);
totalHeight += textHeight;
- curPos += charCount;
- while (*curPos == ' ')
- curPos++; // skip over breaking spaces
+ curTextLine = curTextPos;
}
rect.bottom = totalHeight;
rect.right = maxWidth ? maxWidth : MIN(rect.right, maxTextWidth);
@@ -405,34 +454,38 @@ void GfxText16::Show(const char *text, int16 from, int16 len, GuiResourceId orgF
}
// Draws a text in rect.
-void GfxText16::Box(const char *text, bool show, const Common::Rect &rect, TextAlignment alignment, GuiResourceId fontId) {
+void GfxText16::Box(const char *text, uint16 languageSplitter, bool show, const Common::Rect &rect, TextAlignment alignment, GuiResourceId fontId) {
int16 textWidth, maxTextWidth, textHeight, charCount;
int16 offset = 0;
int16 hline = 0;
GuiResourceId previousFontId = GetFontId();
int16 previousPenColor = _ports->_curPort->penClr;
bool doubleByteMode = false;
+ const char *curTextPos = text;
+ const char *curTextLine = text;
if (fontId != -1)
SetFont(fontId);
else
fontId = previousFontId;
- if (g_sci->getLanguage() == Common::JA_JPN) {
- if (SwitchToFont900OnSjis(text))
- doubleByteMode = true;
- }
-
// Reset reference code rects
_codeRefRects.clear();
_codeRefTempRect.left = _codeRefTempRect.top = -1;
maxTextWidth = 0;
- while (*text) {
- charCount = GetLongest(text, rect.width(), fontId);
+ while (*curTextPos) {
+ // We need to check for Shift-JIS every line
+ // Police Quest 2 PC-9801 often draws English + Japanese text during the same call
+ if (g_sci->getLanguage() == Common::JA_JPN) {
+ if (SwitchToFont900OnSjis(curTextPos, languageSplitter))
+ doubleByteMode = true;
+ }
+
+ charCount = GetLongest(curTextPos, rect.width(), fontId);
if (charCount == 0)
break;
- Width(text, 0, charCount, fontId, textWidth, textHeight, true);
+ Width(curTextLine, 0, charCount, fontId, textWidth, textHeight, true);
maxTextWidth = MAX<int16>(maxTextWidth, textWidth);
switch (alignment) {
case SCI_TEXT16_ALIGNMENT_RIGHT:
@@ -451,15 +504,13 @@ void GfxText16::Box(const char *text, bool show, const Common::Rect &rect, TextA
_ports->moveTo(rect.left + offset, rect.top + hline);
if (show) {
- Show(text, 0, charCount, fontId, previousPenColor);
+ Show(curTextLine, 0, charCount, fontId, previousPenColor);
} else {
- Draw(text, 0, charCount, fontId, previousPenColor);
+ Draw(curTextLine, 0, charCount, fontId, previousPenColor);
}
hline += textHeight;
- text += charCount;
- while (*text == ' ')
- text++; // skip over breaking spaces
+ curTextLine = curTextPos;
}
SetFont(previousFontId);
_ports->penColor(previousPenColor);
@@ -521,11 +572,13 @@ void GfxText16::DrawStatus(const char *text) {
// Sierra did this in their PC98 interpreter only, they identify a text as being
// sjis and then switch to font 900
-bool GfxText16::SwitchToFont900OnSjis(const char *text) {
+bool GfxText16::SwitchToFont900OnSjis(const char *text, uint16 languageSplitter) {
byte firstChar = (*(const byte *)text++);
- if (((firstChar >= 0x81) && (firstChar <= 0x9F)) || ((firstChar >= 0xE0) && (firstChar <= 0xEF))) {
- SetFont(900);
- return true;
+ if (languageSplitter != 0x6a23) { // #j prefix as language splitter
+ if (((firstChar >= 0x81) && (firstChar <= 0x9F)) || ((firstChar >= 0xE0) && (firstChar <= 0xEF))) {
+ SetFont(900);
+ return true;
+ }
}
return false;
}
@@ -554,9 +607,9 @@ reg_t GfxText16::allocAndFillReferenceRectArray() {
return NULL_REG;
}
-void GfxText16::kernelTextSize(const char *text, int16 font, int16 maxWidth, int16 *textWidth, int16 *textHeight) {
+void GfxText16::kernelTextSize(const char *text, uint16 languageSplitter, int16 font, int16 maxWidth, int16 *textWidth, int16 *textHeight) {
Common::Rect rect(0, 0, 0, 0);
- Size(rect, text, font, maxWidth);
+ Size(rect, text, languageSplitter, font, maxWidth);
*textWidth = rect.width();
*textHeight = rect.height();
}
diff --git a/engines/sci/graphics/text16.h b/engines/sci/graphics/text16.h
index ab0cb13a64..2724d97347 100644
--- a/engines/sci/graphics/text16.h
+++ b/engines/sci/graphics/text16.h
@@ -51,15 +51,20 @@ public:
void ClearChar(int16 chr);
- int16 GetLongest(const char *text, int16 maxWidth, GuiResourceId orgFontId);
+ int16 GetLongest(const char *&text, int16 maxWidth, GuiResourceId orgFontId);
void Width(const char *text, int16 from, int16 len, GuiResourceId orgFontId, int16 &textWidth, int16 &textHeight, bool restoreFont);
void StringWidth(const char *str, GuiResourceId orgFontId, int16 &textWidth, int16 &textHeight);
void ShowString(const char *str, GuiResourceId orgFontId, int16 orgPenColor);
void DrawString(const char *str, GuiResourceId orgFontId, int16 orgPenColor);
- int16 Size(Common::Rect &rect, const char *text, GuiResourceId fontId, int16 maxWidth);
+ int16 Size(Common::Rect &rect, const char *text, uint16 textLanguage, GuiResourceId fontId, int16 maxWidth);
void Draw(const char *text, int16 from, int16 len, GuiResourceId orgFontId, int16 orgPenColor);
void Show(const char *text, int16 from, int16 len, GuiResourceId orgFontId, int16 orgPenColor);
- void Box(const char *text, bool show, const Common::Rect &rect, TextAlignment alignment, GuiResourceId fontId);
+ void Box(const char *text, uint16 languageSplitter, bool show, const Common::Rect &rect, TextAlignment alignment, GuiResourceId fontId);
+
+ void Box(const char *text, bool show, const Common::Rect &rect, TextAlignment alignment, GuiResourceId fontId) {
+ Box(text, 0, show, rect, alignment, fontId);
+ }
+
void DrawString(const char *text);
void DrawStatus(const char *text);
@@ -67,13 +72,13 @@ public:
reg_t allocAndFillReferenceRectArray();
- void kernelTextSize(const char *text, int16 font, int16 maxWidth, int16 *textWidth, int16 *textHeight);
+ void kernelTextSize(const char *text, uint16 textLanguage, int16 font, int16 maxWidth, int16 *textWidth, int16 *textHeight);
void kernelTextFonts(int argc, reg_t *argv);
void kernelTextColors(int argc, reg_t *argv);
private:
void init();
- bool SwitchToFont900OnSjis(const char *text);
+ bool SwitchToFont900OnSjis(const char *text, uint16 languageSplitter);
GfxCache *_cache;
GfxPorts *_ports;
diff --git a/engines/sci/graphics/transitions.cpp b/engines/sci/graphics/transitions.cpp
index 5e7dbc6c15..ccc7a4389a 100644
--- a/engines/sci/graphics/transitions.cpp
+++ b/engines/sci/graphics/transitions.cpp
@@ -339,10 +339,10 @@ void GfxTransitions::pixelation(bool blackoutFlag) {
do {
mask = (mask & 1) ? (mask >> 1) ^ 0xB400 : mask >> 1;
- if (mask >= _screen->getWidth() * _screen->getHeight())
+ if (mask >= _screen->getScriptWidth() * _screen->getScriptHeight())
continue;
- pixelRect.left = mask % _screen->getWidth(); pixelRect.right = pixelRect.left + 1;
- pixelRect.top = mask / _screen->getWidth(); pixelRect.bottom = pixelRect.top + 1;
+ pixelRect.left = mask % _screen->getScriptWidth(); pixelRect.right = pixelRect.left + 1;
+ pixelRect.top = mask / _screen->getScriptWidth(); pixelRect.bottom = pixelRect.top + 1;
pixelRect.clip(_picRect);
if (!pixelRect.isEmpty())
copyRectToScreen(pixelRect, blackoutFlag);
diff --git a/engines/sci/graphics/view.cpp b/engines/sci/graphics/view.cpp
index f3f352e5b8..da61ecf4c3 100644
--- a/engines/sci/graphics/view.cpp
+++ b/engines/sci/graphics/view.cpp
@@ -283,6 +283,7 @@ void GfxView::initData(GuiResourceId resourceId) {
_isScaleable = false;
break;
case 0x40:
+ case 0x4F: // LSL6 Polish, seems to be garbage - bug #6718
case 0:
break; // don't do anything, we already have _isScaleable set
default:
@@ -366,7 +367,7 @@ void GfxView::initData(GuiResourceId resourceId) {
default:
error("ViewType was not detected, can't continue");
}
-
+
// Inject our own views
// Currently only used for Dual mode (speech + text) for games, that do not have a "dual" icon already
// Which is Laura Bow 2 + King's Quest 6