aboutsummaryrefslogtreecommitdiff
path: root/engines/kyra/graphics
diff options
context:
space:
mode:
Diffstat (limited to 'engines/kyra/graphics')
-rw-r--r--engines/kyra/graphics/animator_hof.cpp313
-rw-r--r--engines/kyra/graphics/animator_lok.cpp651
-rw-r--r--engines/kyra/graphics/animator_lok.h127
-rw-r--r--engines/kyra/graphics/animator_mr.cpp456
-rw-r--r--engines/kyra/graphics/animator_tim.cpp235
-rw-r--r--engines/kyra/graphics/animator_v2.cpp187
-rw-r--r--engines/kyra/graphics/screen.cpp3971
-rw-r--r--engines/kyra/graphics/screen.h736
-rw-r--r--engines/kyra/graphics/screen_eob.cpp1974
-rw-r--r--engines/kyra/graphics/screen_eob.h177
-rw-r--r--engines/kyra/graphics/screen_hof.cpp96
-rw-r--r--engines/kyra/graphics/screen_hof.h50
-rw-r--r--engines/kyra/graphics/screen_lok.cpp447
-rw-r--r--engines/kyra/graphics/screen_lok.h111
-rw-r--r--engines/kyra/graphics/screen_lol.cpp893
-rw-r--r--engines/kyra/graphics/screen_lol.h105
-rw-r--r--engines/kyra/graphics/screen_mr.cpp130
-rw-r--r--engines/kyra/graphics/screen_mr.h53
-rw-r--r--engines/kyra/graphics/screen_v2.cpp461
-rw-r--r--engines/kyra/graphics/screen_v2.h81
-rw-r--r--engines/kyra/graphics/vqa.cpp667
-rw-r--r--engines/kyra/graphics/vqa.h161
-rw-r--r--engines/kyra/graphics/wsamovie.cpp463
-rw-r--r--engines/kyra/graphics/wsamovie.h134
24 files changed, 12679 insertions, 0 deletions
diff --git a/engines/kyra/graphics/animator_hof.cpp b/engines/kyra/graphics/animator_hof.cpp
new file mode 100644
index 0000000000..0b8db62a9b
--- /dev/null
+++ b/engines/kyra/graphics/animator_hof.cpp
@@ -0,0 +1,313 @@
+/* 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 2
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "kyra/engine/kyra_hof.h"
+#include "kyra/graphics/wsamovie.h"
+
+#include "common/system.h"
+
+namespace Kyra {
+
+void KyraEngine_HoF::restorePage3() {
+ screen()->copyBlockToPage(2, 0, 0, 320, 144, _gamePlayBuffer);
+}
+
+void KyraEngine_HoF::clearAnimObjects() {
+ _animObjects[0].index = 0;
+ _animObjects[0].type = 0;
+ _animObjects[0].enabled = 1;
+ _animObjects[0].flags = 0x800;
+ _animObjects[0].width = 32;
+ _animObjects[0].height = 49;
+ _animObjects[0].width2 = 4;
+ _animObjects[0].height2 = 10;
+
+ for (int i = 1; i < 11; ++i) {
+ _animObjects[i].index = i;
+ _animObjects[i].type = 2;
+ }
+
+ for (int i = 11; i <= 40; ++i) {
+ _animObjects[i].index = i;
+ _animObjects[i].type = 1;
+ _animObjects[i].flags = 0x800;
+ _animObjects[i].width = 16;
+ _animObjects[i].height = 16;
+ }
+}
+
+void KyraEngine_HoF::drawAnimObjects() {
+ for (AnimObj *curObject = _animList; curObject; curObject = curObject->nextObject) {
+ if (!curObject->enabled)
+ continue;
+
+ int x = curObject->xPos2 - (_screen->getScreenDim(2)->sx << 3);
+ int y = curObject->yPos2 - _screen->getScreenDim(2)->sy;
+ int layer = 7;
+
+ if (curObject->flags & 0x800) {
+ if (curObject->animFlags)
+ layer = 0;
+ else
+ layer = getDrawLayer(curObject->xPos1, curObject->yPos1);
+ }
+ curObject->flags |= 0x800;
+
+ if (curObject->index)
+ drawSceneAnimObject(curObject, x, y, layer);
+ else
+ drawCharacterAnimObject(curObject, x, y, layer);
+ }
+}
+
+void KyraEngine_HoF::refreshAnimObjects(int force) {
+ for (AnimObj *curObject = _animList; curObject; curObject = curObject->nextObject) {
+ if (!curObject->enabled)
+ continue;
+ if (!curObject->needRefresh && !force)
+ continue;
+
+ int x = curObject->xPos2 - curObject->width2;
+ if (x < 0)
+ x = 0;
+ if (x >= 320)
+ x = 319;
+ int y = curObject->yPos2 - curObject->height2;
+ if (y < 0)
+ y = 0;
+ if (y >= 143)
+ y = 142;
+
+ int width = curObject->width + curObject->width2 + 8;
+ int height = curObject->height + curObject->height2*2;
+ if (width + x > 320)
+ width -= width + x - 322;
+ if (height + y > 143)
+ height -= height + y - 144;
+
+ _screen->copyRegion(x, y, x, y, width, height, 2, 0, Screen::CR_NO_P_CHECK);
+
+ curObject->needRefresh = false;
+ }
+}
+
+void KyraEngine_HoF::updateItemAnimations() {
+ bool nextFrame = false;
+
+ if (_itemAnimDefinition[0].itemIndex == -1 || _inventorySaved)
+ return;
+
+ const ItemAnimDefinition *s = &_itemAnimDefinition[_nextAnimItem];
+ ActiveItemAnim *a = &_activeItemAnim[_nextAnimItem];
+ _nextAnimItem = (_nextAnimItem + 1) % _itemAnimDefinitionSize;
+
+ if (_system->getMillis() < a->nextFrameTime)
+ return;
+
+ uint16 shpIdx = s->frames[a->currentFrame].index + 64;
+ if (s->itemIndex == _mouseState && s->itemIndex == _itemInHand && _screen->isMouseVisible()) {
+ nextFrame = true;
+ _screen->setMouseCursor(8, 15, getShapePtr(shpIdx));
+ }
+
+ for (int i = 0; i < 10; i++) {
+ if (s->itemIndex == _mainCharacter.inventory[i]) {
+ nextFrame = true;
+ _screen->drawShape(2, getShapePtr(240 + i), 304, 184, 0, 0);
+ _screen->drawShape(2, getShapePtr(shpIdx), 304, 184, 0, 0);
+ _screen->copyRegion(304, 184, _inventoryX[i], _inventoryY[i], 16, 16, 2, 0);
+ }
+ }
+
+ _screen->updateScreen();
+
+ for (int i = 11; i < 40; i++) {
+ AnimObj *animObject = &_animObjects[i];
+ if (animObject->shapeIndex2 == s->itemIndex + 64) {
+ if (s->itemIndex == 121) {
+ int f = findItem(_mainCharacter.sceneId, 121);
+ int nx = _itemList[f].x - 4;
+ if (nx > 12) {
+ if (lineIsPassable(nx, _itemList[f].y)) {
+ animObject->xPos2 -= 4;
+ _itemList[f].x -= 4;
+ }
+ }
+ }
+ animObject->shapePtr = getShapePtr(shpIdx);
+ animObject->shapeIndex1 = shpIdx;
+ animObject->needRefresh = 1;
+ nextFrame = true;
+ }
+ }
+
+ if (nextFrame) {
+ a->nextFrameTime = _system->getMillis() + (s->frames[a->currentFrame].delay * _tickLength);
+ a->currentFrame = (a->currentFrame + 1) % s->numFrames;
+ }
+}
+
+void KyraEngine_HoF::updateCharFacing() {
+ if (_mainCharacter.x1 > _mouseX)
+ _mainCharacter.facing = 5;
+ else
+ _mainCharacter.facing = 3;
+
+ _mainCharacter.animFrame = _characterFrameTable[_mainCharacter.facing];
+ updateCharacterAnim(0);
+ refreshAnimObjectsIfNeed();
+}
+
+void KyraEngine_HoF::updateCharacterAnim(int) {
+ Character *c = &_mainCharacter;
+ AnimObj *animState = _animObjects;
+
+ animState->needRefresh = 1;
+ animState->specialRefresh = 1;
+
+ if (c->facing >= 1 && c->facing <= 3)
+ animState->flags |= 1;
+ else if (c->facing >= 5 && c->facing <= 7)
+ animState->flags &= ~1;
+
+ animState->xPos2 = animState->xPos1 = c->x1;
+ animState->yPos2 = animState->yPos1 = c->y1;
+ animState->shapePtr = getShapePtr(c->animFrame);
+ animState->shapeIndex1 = animState->shapeIndex2 = c->animFrame;
+
+ int xAdd = _shapeDescTable[c->animFrame-9].xAdd;
+ int yAdd = _shapeDescTable[c->animFrame-9].yAdd;
+
+ _charScale = getScale(c->x1, c->y1);
+
+ animState->xPos2 += (xAdd * _charScale) >> 8;
+ animState->yPos2 += (yAdd * _charScale) >> 8;
+ animState->width2 = 8;
+ animState->height2 = 10;
+
+ _animList = deleteAnimListEntry(_animList, animState);
+ if (_animList)
+ _animList = addToAnimListSorted(_animList, animState);
+ else
+ _animList = initAnimList(_animList, animState);
+
+ updateCharPal(1);
+}
+
+void KyraEngine_HoF::updateSceneAnim(int anim, int newFrame) {
+ AnimObj *animObject = &_animObjects[1+anim];
+ if (!animObject->enabled)
+ return;
+
+ animObject->needRefresh = 1;
+ animObject->specialRefresh = 1;
+ animObject->flags = 0;
+
+ if (_sceneAnims[anim].flags & 2)
+ animObject->flags |= 0x800;
+ else
+ animObject->flags &= ~0x800;
+
+ if (_sceneAnims[anim].flags & 4)
+ animObject->flags |= 1;
+ else
+ animObject->flags &= ~1;
+
+ if (_sceneAnims[anim].flags & 0x20) {
+ animObject->shapePtr = _sceneShapeTable[newFrame];
+ animObject->shapeIndex2 = 0xFFFF;
+ animObject->shapeIndex3 = 0xFFFF;
+ animObject->animNum = 0xFFFF;
+ } else {
+ animObject->shapePtr = 0;
+ animObject->shapeIndex3 = newFrame;
+ animObject->animNum = anim;
+ }
+
+ animObject->xPos1 = _sceneAnims[anim].x;
+ animObject->yPos1 = _sceneAnims[anim].y;
+ animObject->xPos2 = _sceneAnims[anim].x2;
+ animObject->yPos2 = _sceneAnims[anim].y2;
+
+ if (_sceneAnims[anim].flags & 2) {
+ _animList = deleteAnimListEntry(_animList, animObject);
+ if (!_animList)
+ _animList = initAnimList(_animList, animObject);
+ else
+ _animList = addToAnimListSorted(_animList, animObject);
+ }
+}
+
+void KyraEngine_HoF::drawSceneAnimObject(AnimObj *obj, int x, int y, int layer) {
+ if (obj->type == 1) {
+ if (obj->shapeIndex1 == 0xFFFF)
+ return;
+ int scale = getScale(obj->xPos1, obj->yPos1);
+ _screen->drawShape(2, getShapePtr(obj->shapeIndex1), x, y, 2, obj->flags | 4, layer, scale, scale);
+ return;
+ }
+
+ if (obj->shapePtr) {
+ _screen->drawShape(2, obj->shapePtr, x, y, 2, obj->flags, layer);
+ } else {
+ if (obj->shapeIndex3 == 0xFFFF || obj->animNum == 0xFFFF)
+ return;
+
+ int flags = 0x4000;
+ if (obj->flags & 0x800)
+ flags |= 0x8000;
+
+ if (_sceneAnims[obj->animNum].wsaFlag) {
+ x = y = 0;
+ } else {
+ x = obj->xPos2;
+ y = obj->yPos2;
+ }
+
+ _sceneAnimMovie[obj->animNum]->displayFrame(obj->shapeIndex3, 2, x, y, int(flags | layer), 0, 0);
+ }
+}
+
+void KyraEngine_HoF::drawCharacterAnimObject(AnimObj *obj, int x, int y, int layer) {
+ if (_drawNoShapeFlag || obj->shapeIndex1 == 0xFFFF)
+ return;
+ _screen->drawShape(2, getShapePtr(obj->shapeIndex1), x, y, 2, obj->flags | 4, layer, _charScale, _charScale);
+}
+
+void KyraEngine_HoF::setCharacterAnimDim(int w, int h) {
+ restorePage3();
+
+ _animObj0Width = _animObjects[0].width;
+ _animObj0Height = _animObjects[0].height;
+
+ _animObjects[0].width = w;
+ _animObjects[0].height = h;
+}
+
+void KyraEngine_HoF::resetCharacterAnimDim() {
+ restorePage3();
+
+ _animObjects[0].width = _animObj0Width;
+ _animObjects[0].height = _animObj0Height;
+}
+
+} // End of namespace Kyra
diff --git a/engines/kyra/graphics/animator_lok.cpp b/engines/kyra/graphics/animator_lok.cpp
new file mode 100644
index 0000000000..9f4236b32b
--- /dev/null
+++ b/engines/kyra/graphics/animator_lok.cpp
@@ -0,0 +1,651 @@
+/* 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 2
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "kyra/engine/kyra_lok.h"
+#include "kyra/graphics/screen.h"
+#include "kyra/graphics/animator_lok.h"
+#include "kyra/engine/sprites.h"
+
+namespace Kyra {
+
+Animator_LoK::Animator_LoK(KyraEngine_LoK *vm, OSystem *system) {
+ _vm = vm;
+ _screen = vm->screen();
+ _initOk = false;
+ _system = system;
+ _screenObjects = _actors = _items = _sprites = _objectQueue = 0;
+ _noDrawShapesFlag = 0;
+
+ _actorBkgBackUp[0] = new uint8[_screen->getRectSize(8, 69)];
+ memset(_actorBkgBackUp[0], 0, _screen->getRectSize(8, 69));
+ _actorBkgBackUp[1] = new uint8[_screen->getRectSize(8, 69)];
+ memset(_actorBkgBackUp[1], 0, _screen->getRectSize(8, 69));
+}
+
+Animator_LoK::~Animator_LoK() {
+ close();
+ delete[] _actorBkgBackUp[0];
+ delete[] _actorBkgBackUp[1];
+}
+
+void Animator_LoK::init(int actors_, int items_, int sprites_) {
+ _screenObjects = new AnimObject[actors_ + items_ + sprites_];
+ assert(_screenObjects);
+ memset(_screenObjects, 0, sizeof(AnimObject) * (actors_ + items_ + sprites_));
+ _actors = _screenObjects;
+ _sprites = &_screenObjects[actors_];
+ _items = &_screenObjects[actors_ + items_];
+ _brandonDrawFrame = 113;
+
+ _initOk = true;
+}
+
+void Animator_LoK::close() {
+ if (_initOk) {
+ _initOk = false;
+ delete[] _screenObjects;
+ _screenObjects = _actors = _items = _sprites = _objectQueue = 0;
+ }
+}
+
+void Animator_LoK::initAnimStateList() {
+ AnimObject *animStates = _screenObjects;
+ animStates[0].index = 0;
+ animStates[0].active = 1;
+ animStates[0].flags = 0x800;
+ animStates[0].background = _actorBkgBackUp[0];
+ animStates[0].rectSize = _screen->getRectSize(4, 48);
+ animStates[0].width = 4;
+ animStates[0].height = 48;
+ animStates[0].width2 = 4;
+ animStates[0].height2 = 3;
+
+ for (int i = 1; i <= 4; ++i) {
+ animStates[i].index = i;
+ animStates[i].active = 0;
+ animStates[i].flags = 0x800;
+ animStates[i].background = _actorBkgBackUp[1];
+ animStates[i].rectSize = _screen->getRectSize(4, 64);
+ animStates[i].width = 4;
+ animStates[i].height = 48;
+ animStates[i].width2 = 4;
+ animStates[i].height2 = 3;
+ }
+
+ for (int i = 5; i < 16; ++i) {
+ animStates[i].index = i;
+ animStates[i].active = 0;
+ animStates[i].flags = 0;
+ }
+
+ for (int i = 16; i < 28; ++i) {
+ animStates[i].index = i;
+ animStates[i].flags = 0;
+ animStates[i].background = _vm->_shapes[345 + i];
+ animStates[i].rectSize = _screen->getRectSize(3, 24);
+ animStates[i].width = 3;
+ animStates[i].height = 16;
+ animStates[i].width2 = 0;
+ animStates[i].height2 = 0;
+ }
+}
+
+void Animator_LoK::preserveAllBackgrounds() {
+ uint8 curPage = _screen->_curPage;
+ _screen->_curPage = 2;
+
+ AnimObject *curObject = _objectQueue;
+ while (curObject) {
+ if (curObject->active && !curObject->disable) {
+ preserveOrRestoreBackground(curObject, false);
+ curObject->bkgdChangeFlag = 0;
+ }
+ curObject = curObject->nextAnimObject;
+ }
+ _screen->_curPage = curPage;
+}
+
+void Animator_LoK::flagAllObjectsForBkgdChange() {
+ AnimObject *curObject = _objectQueue;
+ while (curObject) {
+ curObject->bkgdChangeFlag = 1;
+ curObject = curObject->nextAnimObject;
+ }
+}
+
+void Animator_LoK::flagAllObjectsForRefresh() {
+ AnimObject *curObject = _objectQueue;
+ while (curObject) {
+ curObject->refreshFlag = 1;
+ curObject = curObject->nextAnimObject;
+ }
+}
+
+void Animator_LoK::restoreAllObjectBackgrounds() {
+ AnimObject *curObject = _objectQueue;
+ _screen->_curPage = 2;
+
+ while (curObject) {
+ if (curObject->active && !curObject->disable) {
+ preserveOrRestoreBackground(curObject, true);
+ curObject->x2 = curObject->x1;
+ curObject->y2 = curObject->y1;
+ }
+ curObject = curObject->nextAnimObject;
+ }
+
+ _screen->_curPage = 0;
+}
+
+void Animator_LoK::preserveAnyChangedBackgrounds() {
+ AnimObject *curObject = _objectQueue;
+ _screen->_curPage = 2;
+
+ while (curObject) {
+ if (curObject->active && !curObject->disable && curObject->bkgdChangeFlag) {
+ preserveOrRestoreBackground(curObject, false);
+ curObject->bkgdChangeFlag = 0;
+ }
+ curObject = curObject->nextAnimObject;
+ }
+
+ _screen->_curPage = 0;
+}
+
+void Animator_LoK::preserveOrRestoreBackground(AnimObject *obj, bool restore) {
+ int x = 0, y = 0, width = obj->width, height = obj->height;
+
+ if (restore) {
+ x = obj->x2 >> 3;
+ y = obj->y2;
+ } else {
+ x = obj->x1 >> 3;
+ y = obj->y1;
+ }
+
+ if (x < 0)
+ x = 0;
+ if (y < 0)
+ y = 0;
+
+ int temp;
+
+ temp = x + width;
+ if (temp >= 39)
+ x = 39 - width;
+ temp = y + height;
+ if (temp >= 136)
+ y = 136 - height;
+
+ if (restore)
+ _screen->copyBlockToPage(_screen->_curPage, x << 3, y, width << 3, height, obj->background);
+ else
+ _screen->copyRegionToBuffer(_screen->_curPage, x << 3, y, width << 3, height, obj->background);
+}
+
+void Animator_LoK::prepDrawAllObjects() {
+ AnimObject *curObject = _objectQueue;
+ int drawPage = 2;
+ int flagUnk1 = 0, flagUnk2 = 0, flagUnk3 = 0;
+ if (_noDrawShapesFlag)
+ return;
+ if (_vm->_brandonStatusBit & 0x20)
+ flagUnk1 = 0x200;
+ if (_vm->_brandonStatusBit & 0x40)
+ flagUnk2 = 0x4000;
+
+ while (curObject) {
+ if (curObject->active) {
+ int xpos = curObject->x1;
+ int ypos = curObject->y1;
+
+ int drawLayer = 0;
+ if (!(curObject->flags & 0x800))
+ drawLayer = 7;
+ else if (curObject->disable)
+ drawLayer = 0;
+ else
+ drawLayer = _vm->_sprites->getDrawLayer(curObject->drawY);
+
+ // talking head functionallity
+ if (_vm->_talkingCharNum != -1 && (_vm->_currentCharacter->currentAnimFrame != 88 || curObject->index != 0)) {
+ const int16 baseAnimFrameTable1[] = { 0x11, 0x35, 0x59, 0x00, 0x00, 0x00 };
+ const int16 baseAnimFrameTable2[] = { 0x15, 0x39, 0x5D, 0x00, 0x00, 0x00 };
+ const int8 xOffsetTable1[] = { 2, 4, 0, 5, 2, 0, 0, 0 };
+ const int8 xOffsetTable2[] = { 6, 4, 8, 3, 6, 0, 0, 0 };
+ const int8 yOffsetTable1[] = { 0, 8, 1, 1, 0, 0, 0, 0 };
+ const int8 yOffsetTable2[] = { 0, 8, 1, 1, 0, 0, 0, 0 };
+ if (curObject->index == 0 || curObject->index <= 4) {
+ int shapesIndex = 0;
+ if (curObject->index == _vm->_charSayUnk3) {
+ shapesIndex = _vm->_currHeadShape + baseAnimFrameTable1[curObject->index];
+ } else {
+ shapesIndex = baseAnimFrameTable2[curObject->index];
+ int temp2 = 0;
+ if (curObject->index == 2) {
+ if (_vm->_characterList[2].sceneId == 77 || _vm->_characterList[2].sceneId == 86)
+ temp2 = 1;
+ else
+ temp2 = 0;
+ } else {
+ temp2 = 1;
+ }
+
+ if (!temp2)
+ shapesIndex = -1;
+ }
+
+ xpos = curObject->x1;
+ ypos = curObject->y1;
+
+ int tempX = 0, tempY = 0;
+ if (curObject->flags & 0x1) {
+ tempX = (xOffsetTable1[curObject->index] * _brandonScaleX) >> 8;
+ tempY = yOffsetTable1[curObject->index];
+ } else {
+ tempX = (xOffsetTable2[curObject->index] * _brandonScaleX) >> 8;
+ tempY = yOffsetTable2[curObject->index];
+ }
+ tempY = (tempY * _brandonScaleY) >> 8;
+ xpos += tempX;
+ ypos += tempY;
+
+ if (_vm->_scaleMode && _brandonScaleX != 256)
+ ++xpos;
+
+ if (curObject->index == 0 && shapesIndex != -1) {
+ if (!(_vm->_brandonStatusBit & 2)) {
+ flagUnk3 = 0x100;
+ if ((flagUnk1 & 0x200) || (flagUnk2 & 0x4000))
+ flagUnk3 = 0;
+
+ int tempFlags = 0;
+ if (flagUnk3 & 0x100) {
+ tempFlags = curObject->flags & 1;
+ tempFlags |= 0x800 | flagUnk1 | 0x100;
+ }
+
+ if (!(flagUnk3 & 0x100) && (flagUnk2 & 0x4000)) {
+ tempFlags = curObject->flags & 1;
+ tempFlags |= 0x900 | flagUnk1 | 0x4000;
+ _screen->drawShape(drawPage, _vm->_shapes[shapesIndex], xpos, ypos, 2, tempFlags | 4, _vm->_brandonPoisonFlagsGFX, int(1), int(_vm->_brandonInvFlag), drawLayer, _brandonScaleX, _brandonScaleY);
+ } else {
+ if (!(flagUnk2 & 0x4000)) {
+ tempFlags = curObject->flags & 1;
+ tempFlags |= 0x900 | flagUnk1;
+ }
+
+ _screen->drawShape(drawPage, _vm->_shapes[shapesIndex], xpos, ypos, 2, tempFlags | 4, _vm->_brandonPoisonFlagsGFX, int(1), drawLayer, _brandonScaleX, _brandonScaleY);
+ }
+ }
+ } else {
+ if (shapesIndex != -1) {
+ int tempFlags = 0;
+ if (curObject->flags & 1)
+ tempFlags = 1;
+ _screen->drawShape(drawPage, _vm->_shapes[shapesIndex], xpos, ypos, 2, tempFlags | 0x800, drawLayer);
+ }
+ }
+ }
+ }
+
+ xpos = curObject->x1;
+ ypos = curObject->y1;
+
+ curObject->flags |= 0x800;
+ if (curObject->index == 0) {
+ flagUnk3 = 0x100;
+
+ if (flagUnk1 & 0x200 || flagUnk2 & 0x4000)
+ flagUnk3 = 0;
+
+ if (_vm->_brandonStatusBit & 2)
+ curObject->flags &= 0xFFFFFFFE;
+
+ if (!_vm->_scaleMode) {
+ if (flagUnk3 & 0x100)
+ _screen->drawShape(drawPage, curObject->sceneAnimPtr, xpos, ypos, 2, curObject->flags | flagUnk1 | 0x100, (uint8 *)_vm->_brandonPoisonFlagsGFX, int(1), drawLayer);
+ else if (flagUnk2 & 0x4000)
+ _screen->drawShape(drawPage, curObject->sceneAnimPtr, xpos, ypos, 2, curObject->flags | flagUnk1 | 0x4000, int(_vm->_brandonInvFlag), drawLayer);
+ else
+ _screen->drawShape(drawPage, curObject->sceneAnimPtr, xpos, ypos, 2, curObject->flags | flagUnk1, drawLayer);
+ } else {
+ if (flagUnk3 & 0x100)
+ _screen->drawShape(drawPage, curObject->sceneAnimPtr, xpos, ypos, 2, curObject->flags | flagUnk1 | 0x104, (uint8 *)_vm->_brandonPoisonFlagsGFX, int(1), drawLayer, _brandonScaleX, _brandonScaleY);
+ else if (flagUnk2 & 0x4000)
+ _screen->drawShape(drawPage, curObject->sceneAnimPtr, xpos, ypos, 2, curObject->flags | flagUnk1 | 0x4004, int(_vm->_brandonInvFlag), drawLayer, _brandonScaleX, _brandonScaleY);
+ else
+ _screen->drawShape(drawPage, curObject->sceneAnimPtr, xpos, ypos, 2, curObject->flags | flagUnk1 | 0x4, drawLayer, _brandonScaleX, _brandonScaleY);
+ }
+ } else {
+ if (curObject->index >= 16 && curObject->index <= 27)
+ _screen->drawShape(drawPage, curObject->sceneAnimPtr, xpos, ypos, 2, curObject->flags | 4, drawLayer, (int)_vm->_scaleTable[curObject->drawY], (int)_vm->_scaleTable[curObject->drawY]);
+ else
+ _screen->drawShape(drawPage, curObject->sceneAnimPtr, xpos, ypos, 2, curObject->flags, drawLayer);
+ }
+ }
+ curObject = curObject->nextAnimObject;
+ }
+}
+
+void Animator_LoK::copyChangedObjectsForward(int refreshFlag) {
+ for (AnimObject *curObject = _objectQueue; curObject; curObject = curObject->nextAnimObject) {
+ if (curObject->active) {
+ if (curObject->refreshFlag || refreshFlag) {
+ int xpos = 0, ypos = 0, width = 0, height = 0;
+ xpos = (curObject->x1 >> 3) - (curObject->width2 >> 3) - 1;
+ ypos = curObject->y1 - curObject->height2;
+ width = curObject->width + (curObject->width2 >> 3) + 2;
+ height = curObject->height + curObject->height2 * 2;
+
+ if (xpos < 1)
+ xpos = 1;
+ else if (xpos > 39)
+ continue;
+
+ if (xpos + width > 39)
+ width = 39 - xpos;
+
+ if (ypos < 8)
+ ypos = 8;
+ else if (ypos > 136)
+ continue;
+
+ if (ypos + height > 136)
+ height = 136 - ypos;
+
+ _screen->copyRegion(xpos << 3, ypos, xpos << 3, ypos, width << 3, height, 2, 0);
+ curObject->refreshFlag = 0;
+ }
+ }
+ }
+
+ _screen->updateScreen();
+}
+
+void Animator_LoK::updateAllObjectShapes() {
+ restoreAllObjectBackgrounds();
+ preserveAnyChangedBackgrounds();
+ prepDrawAllObjects();
+ copyChangedObjectsForward(0);
+}
+
+void Animator_LoK::animRemoveGameItem(int index) {
+ restoreAllObjectBackgrounds();
+
+ AnimObject *animObj = &_items[index];
+ animObj->sceneAnimPtr = 0;
+ animObj->animFrameNumber = -1;
+ animObj->refreshFlag = 1;
+ animObj->bkgdChangeFlag = 1;
+ updateAllObjectShapes();
+ animObj->active = 0;
+
+ objectRemoveQueue(_objectQueue, animObj);
+}
+
+void Animator_LoK::animAddGameItem(int index, uint16 sceneId) {
+ restoreAllObjectBackgrounds();
+ assert(sceneId < _vm->_roomTableSize);
+ Room *currentRoom = &_vm->_roomTable[sceneId];
+ AnimObject *animObj = &_items[index];
+ animObj->active = 1;
+ animObj->refreshFlag = 1;
+ animObj->bkgdChangeFlag = 1;
+ animObj->drawY = currentRoom->itemsYPos[index];
+ animObj->sceneAnimPtr = _vm->_shapes[216 + currentRoom->itemsTable[index]];
+ animObj->animFrameNumber = -1;
+ animObj->x1 = currentRoom->itemsXPos[index];
+ animObj->y1 = currentRoom->itemsYPos[index];
+ animObj->x1 -= fetchAnimWidth(animObj->sceneAnimPtr, _vm->_scaleTable[animObj->drawY]) >> 1;
+ animObj->y1 -= fetchAnimHeight(animObj->sceneAnimPtr, _vm->_scaleTable[animObj->drawY]);
+ animObj->x2 = animObj->x1;
+ animObj->y2 = animObj->y1;
+ animObj->width2 = 0;
+ animObj->height2 = 0;
+ _objectQueue = objectQueue(_objectQueue, animObj);
+ preserveAnyChangedBackgrounds();
+ animObj->refreshFlag = 1;
+ animObj->bkgdChangeFlag = 1;
+}
+
+void Animator_LoK::animAddNPC(int character) {
+ restoreAllObjectBackgrounds();
+ AnimObject *animObj = &_actors[character];
+ const Character *ch = &_vm->_characterList[character];
+
+ animObj->active = 1;
+ animObj->refreshFlag = 1;
+ animObj->bkgdChangeFlag = 1;
+ animObj->drawY = ch->y1;
+ animObj->sceneAnimPtr = _vm->_shapes[ch->currentAnimFrame];
+ animObj->x1 = animObj->x2 = ch->x1 + _vm->_defaultShapeTable[ch->currentAnimFrame - 7].xOffset;
+ animObj->y1 = animObj->y2 = ch->y1 + _vm->_defaultShapeTable[ch->currentAnimFrame - 7].yOffset;
+
+ if (ch->facing >= 1 && ch->facing <= 3)
+ animObj->flags |= 1;
+ else if (ch->facing >= 5 && ch->facing <= 7)
+ animObj->flags &= 0xFFFFFFFE;
+
+ _objectQueue = objectQueue(_objectQueue, animObj);
+ preserveAnyChangedBackgrounds();
+ animObj->refreshFlag = 1;
+ animObj->bkgdChangeFlag = 1;
+}
+
+Animator_LoK::AnimObject *Animator_LoK::objectRemoveQueue(AnimObject *queue, AnimObject *rem) {
+ AnimObject *cur = queue;
+ AnimObject *prev = queue;
+
+ while (cur != rem && cur) {
+ AnimObject *temp = cur->nextAnimObject;
+ if (!temp)
+ break;
+ prev = cur;
+ cur = temp;
+ }
+
+ if (cur == queue) {
+ if (!cur)
+ return 0;
+ return cur->nextAnimObject;
+ }
+
+ if (!cur->nextAnimObject) {
+ if (cur == rem) {
+ if (!prev)
+ return 0;
+ else
+ prev->nextAnimObject = 0;
+ }
+ } else {
+ if (cur == rem)
+ prev->nextAnimObject = rem->nextAnimObject;
+ }
+
+ return queue;
+}
+
+Animator_LoK::AnimObject *Animator_LoK::objectAddHead(AnimObject *queue, AnimObject *head) {
+ head->nextAnimObject = queue;
+ return head;
+}
+
+Animator_LoK::AnimObject *Animator_LoK::objectQueue(AnimObject *queue, AnimObject *add) {
+ if (!queue || add->drawY <= queue->drawY) {
+ add->nextAnimObject = queue;
+ return add;
+ }
+ AnimObject *cur = queue;
+ AnimObject *prev = queue;
+ while (add->drawY > cur->drawY) {
+ AnimObject *temp = cur->nextAnimObject;
+ if (!temp)
+ break;
+ prev = cur;
+ cur = temp;
+ }
+
+ if (add->drawY <= cur->drawY) {
+ prev->nextAnimObject = add;
+ add->nextAnimObject = cur;
+ } else {
+ cur->nextAnimObject = add;
+ add->nextAnimObject = 0;
+ }
+ return queue;
+}
+
+void Animator_LoK::addObjectToQueue(AnimObject *object) {
+ if (!_objectQueue)
+ _objectQueue = objectAddHead(0, object);
+ else
+ _objectQueue = objectQueue(_objectQueue, object);
+}
+
+void Animator_LoK::refreshObject(AnimObject *object) {
+ _objectQueue = objectRemoveQueue(_objectQueue, object);
+ if (_objectQueue)
+ _objectQueue = objectQueue(_objectQueue, object);
+ else
+ _objectQueue = objectAddHead(0, object);
+}
+
+void Animator_LoK::makeBrandonFaceMouse() {
+ Common::Point mouse = _vm->getMousePos();
+ if (mouse.x >= _vm->_currentCharacter->x1)
+ _vm->_currentCharacter->facing = 3;
+ else
+ _vm->_currentCharacter->facing = 5;
+ animRefreshNPC(0);
+ updateAllObjectShapes();
+}
+
+int16 Animator_LoK::fetchAnimWidth(const uint8 *shape, int16 mult) {
+ if (_vm->gameFlags().useAltShapeHeader)
+ shape += 2;
+ return (((int16)READ_LE_UINT16((shape + 3))) * mult) >> 8;
+}
+
+int16 Animator_LoK::fetchAnimHeight(const uint8 *shape, int16 mult) {
+ if (_vm->gameFlags().useAltShapeHeader)
+ shape += 2;
+ return (int16)(((int8)*(shape + 2)) * mult) >> 8;
+}
+
+void Animator_LoK::setBrandonAnimSeqSize(int width, int height) {
+ restoreAllObjectBackgrounds();
+ _brandonAnimSeqSizeWidth = _actors[0].width;
+ _brandonAnimSeqSizeHeight = _actors[0].height;
+ _actors[0].width = width + 1;
+ _actors[0].height = height;
+ preserveAllBackgrounds();
+}
+
+void Animator_LoK::resetBrandonAnimSeqSize() {
+ restoreAllObjectBackgrounds();
+ _actors[0].width = _brandonAnimSeqSizeWidth;
+ _actors[0].height = _brandonAnimSeqSizeHeight;
+ preserveAllBackgrounds();
+}
+
+void Animator_LoK::animRefreshNPC(int character) {
+ AnimObject *animObj = &_actors[character];
+ Character *ch = &_vm->characterList()[character];
+
+ animObj->refreshFlag = 1;
+ animObj->bkgdChangeFlag = 1;
+ int facing = ch->facing;
+ if (facing >= 1 && facing <= 3)
+ animObj->flags |= 1;
+ else if (facing >= 5 && facing <= 7)
+ animObj->flags &= 0xFFFFFFFE;
+
+ animObj->drawY = ch->y1;
+ animObj->sceneAnimPtr = _vm->shapes()[ch->currentAnimFrame];
+ animObj->animFrameNumber = ch->currentAnimFrame;
+ if (character == 0) {
+ if (_vm->brandonStatus() & 10) {
+ animObj->animFrameNumber = 88;
+ ch->currentAnimFrame = 88;
+ }
+ if (_vm->brandonStatus() & 2) {
+ animObj->animFrameNumber = _brandonDrawFrame;
+ ch->currentAnimFrame = _brandonDrawFrame;
+ animObj->sceneAnimPtr = _vm->shapes()[_brandonDrawFrame];
+ if (_vm->_brandonStatusBit0x02Flag) {
+ ++_brandonDrawFrame;
+ // TODO: check this
+ if (_brandonDrawFrame >= 122) {
+ _brandonDrawFrame = 113;
+ _vm->_brandonStatusBit0x02Flag = 0;
+ }
+ }
+ }
+ }
+
+ int xOffset = _vm->_defaultShapeTable[ch->currentAnimFrame - 7].xOffset;
+ int yOffset = _vm->_defaultShapeTable[ch->currentAnimFrame - 7].yOffset;
+
+ if (_vm->_scaleMode) {
+ animObj->x1 = ch->x1;
+ animObj->y1 = ch->y1;
+
+ int newScale = _vm->_scaleTable[ch->y1];
+ _brandonScaleX = newScale;
+ _brandonScaleY = newScale;
+
+ animObj->x1 += (_brandonScaleX * xOffset) >> 8;
+ animObj->y1 += (_brandonScaleY * yOffset) >> 8;
+ } else {
+ animObj->x1 = ch->x1 + xOffset;
+ animObj->y1 = ch->y1 + yOffset;
+ }
+ animObj->width2 = 4;
+ animObj->height2 = 3;
+
+ refreshObject(animObj);
+}
+
+void Animator_LoK::setCharacterDefaultFrame(int character) {
+ static const uint16 initFrameTable[] = {
+ 7, 41, 77, 0, 0
+ };
+ assert(character < ARRAYSIZE(initFrameTable));
+ Character *edit = &_vm->characterList()[character];
+ edit->sceneId = 0xFFFF;
+ edit->facing = 0;
+ edit->currentAnimFrame = initFrameTable[character];
+ // edit->unk6 = 1;
+}
+
+void Animator_LoK::setCharactersHeight() {
+ static const int8 initHeightTable[] = {
+ 48, 40, 48, 47, 56,
+ 44, 42, 47, 38, 35,
+ 40
+ };
+ for (int i = 0; i < 11; ++i)
+ _vm->characterList()[i].height = initHeightTable[i];
+}
+
+} // End of namespace Kyra
diff --git a/engines/kyra/graphics/animator_lok.h b/engines/kyra/graphics/animator_lok.h
new file mode 100644
index 0000000000..55c4d571fd
--- /dev/null
+++ b/engines/kyra/graphics/animator_lok.h
@@ -0,0 +1,127 @@
+/* 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 2
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef KYRA_ANIMATOR_LOK_H
+#define KYRA_ANIMATOR_LOK_H
+
+namespace Kyra {
+class KyraEngine_LoK;
+class Screen;
+
+class Animator_LoK {
+public:
+ struct AnimObject {
+ uint8 index;
+ uint32 active;
+ uint32 refreshFlag;
+ uint32 bkgdChangeFlag;
+ bool disable;
+ uint32 flags;
+ int16 drawY;
+ uint8 *sceneAnimPtr;
+ int16 animFrameNumber;
+ uint8 *background;
+ uint16 rectSize;
+ int16 x1, y1;
+ int16 x2, y2;
+ uint16 width;
+ uint16 height;
+ uint16 width2;
+ uint16 height2;
+ AnimObject *nextAnimObject;
+ };
+
+ Animator_LoK(KyraEngine_LoK *vm, OSystem *system);
+ virtual ~Animator_LoK();
+
+ operator bool() const { return _initOk; }
+
+ void init(int actors, int items, int sprites);
+ void close();
+
+ AnimObject *objects() { return _screenObjects; }
+ AnimObject *actors() { return _actors; }
+ AnimObject *items() { return _items; }
+ AnimObject *sprites() { return _sprites; }
+
+ void initAnimStateList();
+ void preserveAllBackgrounds();
+ void flagAllObjectsForBkgdChange();
+ void flagAllObjectsForRefresh();
+ void restoreAllObjectBackgrounds();
+ void preserveAnyChangedBackgrounds();
+ virtual void prepDrawAllObjects();
+ void copyChangedObjectsForward(int refreshFlag);
+
+ void updateAllObjectShapes();
+ void animRemoveGameItem(int index);
+ void animAddGameItem(int index, uint16 sceneId);
+ void animAddNPC(int character);
+ void animRefreshNPC(int character);
+
+ void clearQueue() { _objectQueue = 0; }
+ void addObjectToQueue(AnimObject *object);
+ void refreshObject(AnimObject *object);
+
+ void makeBrandonFaceMouse();
+ void setBrandonAnimSeqSize(int width, int height);
+ void resetBrandonAnimSeqSize();
+ void setCharacterDefaultFrame(int character);
+ void setCharactersHeight();
+
+ int16 fetchAnimWidth(const uint8 *shape, int16 mult);
+ int16 fetchAnimHeight(const uint8 *shape, int16 mult);
+
+ int _noDrawShapesFlag;
+ uint16 _brandonDrawFrame;
+ int _brandonScaleX;
+ int _brandonScaleY;
+
+protected:
+ KyraEngine_LoK *_vm;
+ Screen *_screen;
+ OSystem *_system;
+ bool _initOk;
+
+ AnimObject *_screenObjects;
+
+ AnimObject *_actors;
+ AnimObject *_items;
+ AnimObject *_sprites;
+
+ uint8 *_actorBkgBackUp[2];
+
+ AnimObject *objectRemoveQueue(AnimObject *queue, AnimObject *rem);
+ AnimObject *objectAddHead(AnimObject *queue, AnimObject *head);
+ AnimObject *objectQueue(AnimObject *queue, AnimObject *add);
+
+ void preserveOrRestoreBackground(AnimObject *obj, bool restore);
+
+ AnimObject *_objectQueue;
+
+ int _brandonAnimSeqSizeWidth;
+ int _brandonAnimSeqSizeHeight;
+};
+
+} // End of namespace Kyra
+
+#endif
diff --git a/engines/kyra/graphics/animator_mr.cpp b/engines/kyra/graphics/animator_mr.cpp
new file mode 100644
index 0000000000..11b1bcb45a
--- /dev/null
+++ b/engines/kyra/graphics/animator_mr.cpp
@@ -0,0 +1,456 @@
+/* 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 2
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "kyra/engine/kyra_mr.h"
+#include "kyra/resource/resource.h"
+#include "kyra/graphics/wsamovie.h"
+
+#include "common/system.h"
+
+namespace Kyra {
+
+void KyraEngine_MR::restorePage3() {
+ screen()->copyBlockToPage(2, 0, 0, 320, 200, _gamePlayBuffer);
+}
+
+void KyraEngine_MR::clearAnimObjects() {
+ for (int i = 0; i < 67; ++i)
+ _animObjects[i].enabled = false;
+
+ _animObjects[0].index = 0;
+ _animObjects[0].type = 0;
+ _animObjects[0].enabled = true;
+ _animObjects[0].specialRefresh = 1;
+ _animObjects[0].flags = 0x800;
+ _animObjects[0].width = 57;
+ _animObjects[0].height = 91;
+ _animObjects[0].width2 = 4;
+ _animObjects[0].height2 = 10;
+
+ for (int i = 1; i < 17; ++i) {
+ _animObjects[i].index = i;
+ _animObjects[i].type = 2;
+ _animObjects[i].flags = 0;
+ _animObjects[i].enabled = false;
+ _animObjects[i].needRefresh = 0;
+ _animObjects[i].specialRefresh = 1;
+ }
+
+ for (int i = 17; i <= 66; ++i) {
+ _animObjects[i].index = i;
+ _animObjects[i].type = 1;
+ _animObjects[i].specialRefresh = 1;
+ _animObjects[i].flags = 0x800;
+ _animObjects[i].width = 24;
+ _animObjects[i].height = 20;
+ _animObjects[i].width2 = 0;
+ _animObjects[i].height2 = 0;
+ }
+}
+
+void KyraEngine_MR::animSetupPaletteEntry(AnimObj *anim) {
+ int layer = _screen->getLayer(anim->xPos1, anim->yPos1) - 1;
+ int16 count = 0;
+ for (int i = 0; i < 3; ++i)
+ count += _sceneDatPalette[layer*3+i];
+ count /= 3;
+ count *= -1;
+ count = MAX<int16>(0, MIN<int16>(count, 10));
+ anim->palette = count / 3;
+}
+
+void KyraEngine_MR::drawAnimObjects() {
+ for (AnimObj *curObject = _animList; curObject; curObject = curObject->nextObject) {
+ if (!curObject->enabled)
+ continue;
+
+ int x = curObject->xPos2 - (_screen->getScreenDim(2)->sx << 3);
+ int y = curObject->yPos2 - _screen->getScreenDim(2)->sy;
+ int layer = 7;
+
+ if (curObject->flags & 0x800) {
+ if (!curObject->specialRefresh)
+ layer = 0;
+ else
+ layer = getDrawLayer(curObject->xPos1, curObject->yPos1);
+ }
+
+ if (curObject->index)
+ drawSceneAnimObject(curObject, x, y, layer);
+ else
+ drawCharacterAnimObject(curObject, x, y, layer);
+ }
+}
+
+void KyraEngine_MR::drawSceneAnimObject(AnimObj *obj, int x, int y, int layer) {
+ if (obj->type == 1) {
+ if (obj->shapeIndex1 == 0xFFFF)
+ return;
+ int scale = getScale(obj->xPos1, obj->yPos1);
+ _screen->drawShape(2, getShapePtr(obj->shapeIndex1), x, y, 2, obj->flags | 0x104, _paletteOverlay, obj->palette, layer, scale, scale);
+ } else {
+ if (obj->shapePtr) {
+ _screen->drawShape(2, obj->shapePtr, x, y, 2, obj->flags, 7);
+ } else {
+ if (obj->shapeIndex3 == 0xFFFF || obj->animNum == 0xFFFF)
+ return;
+ uint16 flags = 0x4000;
+ if (obj->flags & 0x800)
+ flags |= 0x8000;
+ x = obj->xPos2 - _sceneAnimMovie[obj->animNum]->xAdd();
+ y = obj->yPos2 - _sceneAnimMovie[obj->animNum]->yAdd();
+ _sceneAnimMovie[obj->animNum]->displayFrame(obj->shapeIndex3, 2, x, y, flags | layer, 0, 0);
+ }
+ }
+}
+
+void KyraEngine_MR::drawCharacterAnimObject(AnimObj *obj, int x, int y, int layer) {
+ if (_drawNoShapeFlag)
+ return;
+
+ if (_mainCharacter.animFrame < 9)
+ _mainCharacter.animFrame = 87;
+
+ if (obj->shapeIndex1 == 0xFFFF || _mainCharacter.animFrame == 87)
+ return;
+
+ _screen->drawShape(2, getShapePtr(421), _mainCharacter.x3, _mainCharacter.y3, 2, obj->flags | 0x304, _paletteOverlay, 3, layer, _charScale, _charScale);
+ uint8 *shape = getShapePtr(_mainCharacter.animFrame);
+ if (shape)
+ _screen->drawShape(2, shape, x, y, 2, obj->flags | 4, layer, _charScale, _charScale);
+}
+
+void KyraEngine_MR::refreshAnimObjects(int force) {
+ for (AnimObj *curObject = _animList; curObject; curObject = curObject->nextObject) {
+ if (!curObject->enabled)
+ continue;
+ if (!curObject->needRefresh && !force)
+ continue;
+
+ const int scale = (curObject->index == 0) ? _charScale : 0;
+
+ int x = curObject->xPos2 - curObject->width2;
+ if (scale)
+ x -= (0x100 - scale) >> 4;
+
+ if (x < 0)
+ x = 0;
+ if (x >= 320)
+ x = 319;
+
+ int y = curObject->yPos2 - curObject->height2;
+ if (scale)
+ y -= (0x100 - scale) >> 3;
+ if (y < 0)
+ y = 0;
+ if (y >= 187)
+ y = 186;
+
+ int width = curObject->width + curObject->width2 + 8;
+ int height = curObject->height + curObject->height2*2;
+ if (width + x > 320)
+ width -= width + x - 322;
+
+ const int maxY = _inventoryState ? 143 : 187;
+ if (height + y > maxY)
+ height -= height + y - (maxY + 1);
+
+ if (height > 0) {
+ _screen->copyRegion(x, y, x, y, width, height, 2, 0, Screen::CR_NO_P_CHECK);
+ }
+
+ curObject->needRefresh = false;
+ }
+}
+
+void KyraEngine_MR::updateItemAnimations() {
+ bool nextFrame = false;
+
+ if (_itemAnimDefinition[0].itemIndex == -1)
+ return;
+
+ const ItemAnimDefinition *s = &_itemAnimDefinition[_nextAnimItem];
+ ActiveItemAnim *a = &_activeItemAnim[_nextAnimItem];
+ _nextAnimItem = (_nextAnimItem + 1) % 10;
+
+ if (_system->getMillis() < a->nextFrameTime)
+ return;
+
+ uint16 shpIdx = s->frames[a->currentFrame].index + 248;
+ if (s->itemIndex == _mouseState && s->itemIndex == _itemInHand && _screen->isMouseVisible()) {
+ nextFrame = true;
+ _screen->setMouseCursor(12, 19, getShapePtr(shpIdx));
+ }
+
+ if (_inventoryState) {
+ for (int i = 0; i < 10; i++) {
+ if (s->itemIndex == _mainCharacter.inventory[i]) {
+ nextFrame = true;
+ _screen->drawShape(2, getShapePtr(422 + i), 9, 0, 0, 0);
+ _screen->drawShape(2, getShapePtr(shpIdx), 9, 0, 0, 0);
+ _screen->copyRegion(9, 0, _inventoryX[i], _inventoryY[i], 24, 20, 2, 0, Screen::CR_NO_P_CHECK);
+ }
+ }
+ }
+
+ _screen->updateScreen();
+
+ for (int i = 17; i < 66; i++) {
+ AnimObj *animObject = &_animObjects[i];
+ if (animObject->shapeIndex2 == s->itemIndex + 248) {
+ animObject->shapePtr = getShapePtr(shpIdx);
+ animObject->shapeIndex1 = shpIdx;
+ animObject->needRefresh = true;
+ nextFrame = true;
+ }
+ }
+
+ if (nextFrame) {
+ a->nextFrameTime = _system->getMillis() + (s->frames[a->currentFrame].delay * _tickLength);
+ a->currentFrame = (a->currentFrame + 1) % s->numFrames;
+ }
+}
+
+void KyraEngine_MR::updateCharacterAnim(int charId) {
+ AnimObj *obj = &_animObjects[0];
+ obj->needRefresh = true;
+ obj->flags &= ~1;
+ obj->xPos1 = _mainCharacter.x1;
+ obj->yPos1 = _mainCharacter.y1;
+ obj->shapePtr = getShapePtr(_mainCharacter.animFrame);
+ obj->shapeIndex1 = obj->shapeIndex2 = _mainCharacter.animFrame;
+
+ int shapeOffsetX = 0, shapeOffsetY = 0;
+ if (_mainCharacter.animFrame >= 50 && _mainCharacter.animFrame <= 87) {
+ shapeOffsetX = _malcolmShapeXOffset;
+ shapeOffsetY = _malcolmShapeYOffset;
+ } else {
+ shapeOffsetX = _animShapeXAdd;
+ shapeOffsetY = _animShapeYAdd;
+ }
+
+ obj->xPos2 = _mainCharacter.x1;
+ obj->yPos2 = _mainCharacter.y1;
+ _charScale = getScale(_mainCharacter.x1, _mainCharacter.y1);
+ obj->xPos2 += (shapeOffsetX * _charScale) >> 8;
+ obj->yPos2 += (shapeOffsetY * _charScale) >> 8;
+ _mainCharacter.x3 = _mainCharacter.x1 - (_charScale >> 4) - 1;
+ _mainCharacter.y3 = _mainCharacter.y1 - (_charScale >> 6) - 1;
+ if (_charBackUpWidth2 == -1) {
+ obj->width2 = 4;
+ obj->height2 = 10;
+ }
+
+ for (int i = 1; i <= 16; ++i) {
+ if (_animObjects[i].enabled && _animObjects[i].specialRefresh)
+ _animObjects[i].needRefresh = true;
+ }
+
+ _animList = deleteAnimListEntry(_animList, obj);
+ if (_animList)
+ _animList = addToAnimListSorted(_animList, obj);
+ else
+ _animList = initAnimList(_animList, obj);
+
+ if (!_loadingState)
+ updateCharPal(1);
+}
+
+void KyraEngine_MR::updateSceneAnim(int anim, int newFrame) {
+ AnimObj *animObject = &_animObjects[1+anim];
+ if (!animObject->enabled)
+ return;
+
+ animObject->needRefresh = true;
+
+ if (_sceneAnims[anim].flags & 2)
+ animObject->flags |= 1;
+ else
+ animObject->flags &= ~1;
+
+ if (_sceneAnims[anim].flags & 4) {
+ animObject->shapePtr = _sceneShapes[newFrame];
+ animObject->shapeIndex2 = 0xFFFF;
+ animObject->shapeIndex3 = 0xFFFF;
+ animObject->animNum = 0xFFFF;
+ } else {
+ animObject->shapePtr = 0;
+ animObject->shapeIndex3 = newFrame;
+ animObject->animNum = anim;
+ }
+
+ animObject->xPos1 = _sceneAnims[anim].x;
+ animObject->yPos1 = _sceneAnims[anim].y;
+ animObject->xPos2 = _sceneAnims[anim].x2;
+ animObject->yPos2 = _sceneAnims[anim].y2;
+
+ if (_sceneAnims[anim].flags & 0x20) {
+ _animList = deleteAnimListEntry(_animList, animObject);
+ if (!_animList)
+ _animList = initAnimList(_animList, animObject);
+ else
+ _animList = addToAnimListSorted(_animList, animObject);
+ }
+}
+
+void KyraEngine_MR::setupSceneAnimObject(int animId, uint16 flags, int x, int y, int x2, int y2, int w,
+ int h, int unk10, int specialSize, int unk14, int shape, const char *filename) {
+ restorePage3();
+ SceneAnim &anim = _sceneAnims[animId];
+ anim.flags = flags;
+ anim.x = x;
+ anim.y = y;
+ anim.x2 = x2;
+ anim.y2 = y2;
+ anim.width = w;
+ anim.height = h;
+ anim.specialSize = specialSize;
+ anim.shapeIndex = shape;
+ if (filename)
+ strcpy(anim.filename, filename);
+
+ if (flags & 8) {
+ _sceneAnimMovie[animId]->open(filename, 1, 0);
+ if (_sceneAnimMovie[animId]->opened()) {
+ anim.wsaFlag = 1;
+ if (x2 == -1)
+ x2 = _sceneAnimMovie[animId]->xAdd();
+ if (y2 == -1)
+ y2 = _sceneAnimMovie[animId]->yAdd();
+ if (w == -1)
+ w = _sceneAnimMovie[animId]->width();
+ if (h == -1)
+ h = _sceneAnimMovie[animId]->height();
+ if (x == -1)
+ x = (w >> 1) + x2;
+ if (y == -1)
+ y = y2 + h - 1;
+
+ anim.x = x;
+ anim.y = y;
+ anim.x2 = x2;
+ anim.y2 = y2;
+ anim.width = w;
+ anim.height = h;
+ }
+ }
+
+ AnimObj *obj = &_animObjects[1+animId];
+ obj->enabled = true;
+ obj->needRefresh = true;
+
+ obj->specialRefresh = (anim.flags & 0x20) ? 1 : 0;
+ obj->flags = (anim.flags & 0x10) ? 0x800 : 0;
+ if (anim.flags & 2)
+ obj->flags |= 1;
+
+ obj->xPos1 = anim.x;
+ obj->yPos1 = anim.y;
+
+ if ((anim.flags & 4) && anim.shapeIndex != -1)
+ obj->shapePtr = _sceneShapes[anim.shapeIndex];
+ else
+ obj->shapePtr = 0;
+
+ if (anim.flags & 8) {
+ obj->shapeIndex3 = anim.shapeIndex;
+ obj->animNum = animId;
+ } else {
+ obj->shapeIndex3 = 0xFFFF;
+ obj->animNum = 0xFFFF;
+ }
+
+ obj->xPos3 = obj->xPos2 = anim.x2;
+ obj->yPos3 = obj->yPos2 = anim.y2;
+ obj->width = anim.width;
+ obj->height = anim.height;
+ obj->width2 = obj->height2 = anim.specialSize;
+
+ if (_animList)
+ _animList = addToAnimListSorted(_animList, obj);
+ else
+ _animList = initAnimList(_animList, obj);
+}
+
+void KyraEngine_MR::removeSceneAnimObject(int anim, int refresh) {
+ AnimObj *obj = &_animObjects[anim+1];
+ restorePage3();
+ obj->shapeIndex3 = 0xFFFF;
+ obj->animNum = 0xFFFF;
+ obj->needRefresh = true;
+
+ if (refresh)
+ refreshAnimObjectsIfNeed();
+
+ obj->enabled = false;
+ _animList = deleteAnimListEntry(_animList, obj);
+ _sceneAnimMovie[anim]->close();
+}
+
+void KyraEngine_MR::setCharacterAnimDim(int w, int h) {
+ restorePage3();
+ _charBackUpWidth = _animObjects[0].width;
+ _charBackUpWidth2 = _animObjects[0].width2;
+ _charBackUpHeight = _animObjects[0].height;
+ _charBackUpHeight2 = _animObjects[0].height2;
+
+ _animObjects[0].width2 = (w - _charBackUpWidth) / 2;
+ _animObjects[0].height2 = h - _charBackUpHeight;
+ _animObjects[0].width = w;
+ _animObjects[0].height = h;
+}
+
+void KyraEngine_MR::resetCharacterAnimDim() {
+ restorePage3();
+ _animObjects[0].width2 = _charBackUpWidth2;
+ _animObjects[0].height2 = _charBackUpHeight2;
+ _animObjects[0].width = _charBackUpWidth;
+ _animObjects[0].height = _charBackUpHeight;
+ _charBackUpWidth2 = _charBackUpHeight2 = -1;
+ _charBackUpWidth = _charBackUpHeight = -1;
+}
+
+void KyraEngine_MR::showIdleAnim() {
+ if (_mainCharacter.sceneId == 20 || _mainCharacter.sceneId == 21
+ || _mainCharacter.sceneId == 12 || _mainCharacter.sceneId == 11)
+ return;
+
+ if (_mainCharacter.animFrame == 87)
+ return;
+
+ if (!_nextIdleType && !talkObjectsInCurScene()) {
+ randomSceneChat();
+ } else {
+ static const char *const facingTable[] = {
+ "A", "R", "R", "FR", "FX", "FL", "L", "L"
+ };
+
+ Common::String filename = Common::String::format( "MI0%s%.02d.EMC", facingTable[_mainCharacter.facing], _characterShapeFile);
+
+ if (_res->exists(filename.c_str()))
+ runAnimationScript(filename.c_str(), 1, 1, 1, 1);
+ }
+
+ _nextIdleType = !_nextIdleType;
+}
+
+} // End of namespace Kyra
diff --git a/engines/kyra/graphics/animator_tim.cpp b/engines/kyra/graphics/animator_tim.cpp
new file mode 100644
index 0000000000..160524e6ca
--- /dev/null
+++ b/engines/kyra/graphics/animator_tim.cpp
@@ -0,0 +1,235 @@
+/* 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 2
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "kyra/script/script_tim.h"
+#include "kyra/graphics/wsamovie.h"
+#include "kyra/graphics/screen_lol.h"
+
+#ifdef ENABLE_LOL
+#include "kyra/engine/lol.h"
+#else
+#include "kyra/graphics/screen_v2.h"
+#endif
+
+#include "common/system.h"
+
+namespace Kyra {
+
+#ifdef ENABLE_LOL
+TimAnimator::TimAnimator(LoLEngine *engine, Screen_v2 *screen_v2, OSystem *system, bool useParts) : _vm(engine), _screen(screen_v2), _system(system), _useParts(useParts) {
+#else
+TimAnimator::TimAnimator(KyraEngine_v1 *engine, Screen_v2 *screen_v2, OSystem *system, bool useParts) : _vm(engine), _screen(screen_v2), _system(system), _useParts(useParts) {
+#endif
+ _animations = new Animation[TIM::kWSASlots];
+ memset(_animations, 0, TIM::kWSASlots * sizeof(Animation));
+
+ if (_useParts) {
+ for (int i = 0; i < TIM::kWSASlots; i++) {
+ _animations[i].parts = new AnimPart[TIM::kAnimParts];
+ memset(_animations[i].parts, 0, TIM::kAnimParts * sizeof(AnimPart));
+ }
+ }
+}
+
+TimAnimator::~TimAnimator() {
+ for (int i = 0; i < TIM::kWSASlots; i++) {
+ delete _animations[i].wsa;
+ if (_useParts)
+ delete[] _animations[i].parts;
+ }
+
+ delete[] _animations;
+}
+
+void TimAnimator::init(int animIndex, Movie *wsa, int x, int y, int wsaCopyParams, int frameDelay) {
+ Animation *anim = &_animations[animIndex];
+ anim->wsa = wsa;
+ anim->x = x;
+ anim->y = y;
+ anim->wsaCopyParams = wsaCopyParams;
+ anim->frameDelay = frameDelay;
+ anim->enable = 0;
+ anim->lastPart = -1;
+}
+
+void TimAnimator::reset(int animIndex, bool clearStruct) {
+ Animation *anim = &_animations[animIndex];
+ if (!anim)
+ return;
+ anim->field_D = 0;
+ anim->enable = 0;
+ delete anim->wsa;
+ anim->wsa = 0;
+
+ if (clearStruct) {
+ if (_useParts)
+ delete[] anim->parts;
+
+ memset(anim, 0, sizeof(Animation));
+
+ if (_useParts) {
+ anim->parts = new AnimPart[TIM::kAnimParts];
+ memset(anim->parts, 0, TIM::kAnimParts * sizeof(AnimPart));
+ }
+ }
+}
+
+void TimAnimator::displayFrame(int animIndex, int page, int frame, int flags) {
+ Animation *anim = &_animations[animIndex];
+ if ((anim->wsaCopyParams & 0x4000) != 0)
+ page = 2;
+ // WORKAROUND for some bugged scripts that will try to display frames of non-existent animations
+ if (anim->wsa)
+ anim->wsa->displayFrame(frame, page, anim->x, anim->y, (flags == -1) ? (anim->wsaCopyParams & 0xF0FF) : flags, 0, 0);
+ if (!page)
+ _screen->updateScreen();
+}
+
+#ifdef ENABLE_LOL
+void TimAnimator::setupPart(int animIndex, int part, int firstFrame, int lastFrame, int cycles, int nextPart, int partDelay, int f, int sfxIndex, int sfxFrame) {
+ AnimPart *a = &_animations[animIndex].parts[part];
+ a->firstFrame = firstFrame;
+ a->lastFrame = lastFrame;
+ a->cycles = cycles;
+ a->nextPart = nextPart;
+ a->partDelay = partDelay;
+ a->field_A = f;
+ a->sfxIndex = sfxIndex;
+ a->sfxFrame = sfxFrame;
+}
+
+void TimAnimator::start(int animIndex, int part) {
+ if (!_vm || !_system || !_screen)
+ return;
+
+ Animation *anim = &_animations[animIndex];
+ anim->curPart = part;
+ AnimPart *p = &anim->parts[part];
+ anim->enable = 1;
+ anim->nextFrame = _system->getMillis() + anim->frameDelay * _vm->_tickLength;
+ anim->curFrame = p->firstFrame;
+ anim->cyclesCompleted = 0;
+
+ // WORKAROUND for some bugged scripts that will try to display frames of non-existent animations
+ if (anim->wsa)
+ anim->wsa->displayFrame(anim->curFrame - 1, 0, anim->x, anim->y, 0, 0, 0);
+}
+
+void TimAnimator::stop(int animIndex) {
+ Animation *anim = &_animations[animIndex];
+ anim->enable = 0;
+ anim->field_D = 0;
+ if (animIndex == 5) {
+ delete anim->wsa;
+ anim->wsa = 0;
+ }
+}
+
+void TimAnimator::update(int animIndex) {
+ if (!_vm || !_system || !_screen)
+ return;
+
+ Animation *anim = &_animations[animIndex];
+ if (!anim->enable || anim->nextFrame >= _system->getMillis())
+ return;
+
+ AnimPart *p = &anim->parts[anim->curPart];
+ anim->nextFrame = 0;
+
+ int step = 0;
+ if (p->lastFrame >= p->firstFrame) {
+ step = 1;
+ anim->curFrame++;
+ } else {
+ step = -1;
+ anim->curFrame--;
+ }
+
+ if (anim->curFrame == (p->lastFrame + step)) {
+ anim->cyclesCompleted++;
+
+ if ((anim->cyclesCompleted > p->cycles) || anim->field_D) {
+ anim->lastPart = anim->curPart;
+
+ if ((p->nextPart == -1) || (anim->field_D && p->field_A)) {
+ anim->enable = 0;
+ anim->field_D = 0;
+ return;
+ }
+
+ anim->nextFrame += (p->partDelay * _vm->_tickLength);
+ anim->curPart = p->nextPart;
+
+ p = &anim->parts[anim->curPart];
+ anim->curFrame = p->firstFrame;
+ anim->cyclesCompleted = 0;
+
+ } else {
+ anim->curFrame = p->firstFrame;
+ }
+ }
+
+ if (p->sfxIndex != -1 && p->sfxFrame == anim->curFrame)
+ _vm->snd_playSoundEffect(p->sfxIndex, -1);
+
+ anim->nextFrame += (anim->frameDelay * _vm->_tickLength);
+
+ anim->wsa->displayFrame(anim->curFrame - 1, 0, anim->x, anim->y, 0, 0, 0);
+ anim->nextFrame += _system->getMillis();
+}
+
+void TimAnimator::playPart(int animIndex, int firstFrame, int lastFrame, int delay) {
+ if (!_vm || !_system || !_screen)
+ return;
+
+ Animation *anim = &_animations[animIndex];
+ // WORKAROUND for some bugged scripts that will try to play invalid animations
+ if (!anim->wsa)
+ return;
+
+ int step = (lastFrame >= firstFrame) ? 1 : -1;
+ for (int i = firstFrame; i != (lastFrame + step); i += step) {
+ uint32 next = _system->getMillis() + delay * _vm->_tickLength;
+ if (anim->wsaCopyParams & 0x4000) {
+ _screen->copyRegion(112, 0, 112, 0, 176, 120, 6, 2);
+ anim->wsa->displayFrame(i - 1, 2, anim->x, anim->y, anim->wsaCopyParams & 0x1000 ? 0x5000 : 0x4000, _vm->_transparencyTable1, _vm->_transparencyTable2);
+ _screen->copyRegion(112, 0, 112, 0, 176, 120, 2, 0);
+ _screen->updateScreen();
+ } else {
+ anim->wsa->displayFrame(i - 1, 0, anim->x, anim->y, 0, 0, 0);
+ _screen->updateScreen();
+ }
+ int32 del = (int32)(next - _system->getMillis());
+ if (del > 0)
+ _vm->delay(del, true);
+ }
+}
+
+int TimAnimator::resetLastPart(int animIndex) {
+ Animation *anim = &_animations[animIndex];
+ int8 res = -1;
+ SWAP(res, anim->lastPart);
+ return res;
+}
+#endif
+
+} // End of namespace Kyra
diff --git a/engines/kyra/graphics/animator_v2.cpp b/engines/kyra/graphics/animator_v2.cpp
new file mode 100644
index 0000000000..a7a8d70962
--- /dev/null
+++ b/engines/kyra/graphics/animator_v2.cpp
@@ -0,0 +1,187 @@
+/* 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 2
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "kyra/engine/kyra_v2.h"
+#include "kyra/graphics/screen_v2.h"
+
+namespace Kyra {
+
+void KyraEngine_v2::allocAnimObjects(int actors, int anims, int items) {
+ _animObjects = new AnimObj[actors + anims + items];
+ assert(_animObjects);
+
+ memset(_animObjects, 0, sizeof(AnimObj) * (actors + anims + items));
+
+ _animActor = _animObjects;
+ _animAnims = _animObjects + actors;
+ _animItems = _animObjects + actors + anims;
+}
+
+KyraEngine_v2::AnimObj *KyraEngine_v2::initAnimList(AnimObj *list, AnimObj *entry) {
+ entry->nextObject = list;
+ return entry;
+}
+
+KyraEngine_v2::AnimObj *KyraEngine_v2::addToAnimListSorted(AnimObj *list, AnimObj *add) {
+ add->nextObject = 0;
+
+ if (!list)
+ return add;
+
+ if (add->yPos1 <= list->yPos1) {
+ add->nextObject = list;
+ return add;
+ }
+
+ AnimObj *cur = list;
+ AnimObj *prev = list;
+ while (add->yPos1 > cur->yPos1) {
+ AnimObj *temp = cur->nextObject;
+ if (!temp)
+ break;
+ prev = cur;
+ cur = temp;
+ }
+
+ if (add->yPos1 <= cur->yPos1) {
+ prev->nextObject = add;
+ add->nextObject = cur;
+ } else {
+ cur->nextObject = add;
+ add->nextObject = 0;
+ }
+ return list;
+}
+
+KyraEngine_v2::AnimObj *KyraEngine_v2::deleteAnimListEntry(AnimObj *list, AnimObj *entry) {
+ if (!list)
+ return 0;
+
+ AnimObj *old = 0;
+ AnimObj *cur = list;
+
+ while (true) {
+ if (cur == entry)
+ break;
+ if (!cur->nextObject)
+ break;
+ old = cur;
+ cur = cur->nextObject;
+ }
+
+ if (cur != entry)
+ return list;
+
+ if (cur == list) {
+ if (!cur->nextObject)
+ return 0;
+ cur = cur->nextObject;
+ return cur;
+ }
+
+ if (!cur->nextObject) {
+ if (!old)
+ return 0;
+ old->nextObject = 0;
+ return list;
+ }
+
+ if (cur != entry)
+ return list;
+
+ old->nextObject = entry->nextObject;
+ return list;
+}
+
+void KyraEngine_v2::refreshAnimObjectsIfNeed() {
+ for (AnimObj *curEntry = _animList; curEntry; curEntry = curEntry->nextObject) {
+ if (curEntry->enabled && curEntry->needRefresh) {
+ restorePage3();
+ drawAnimObjects();
+ refreshAnimObjects(0);
+ screen()->updateScreen();
+ return;
+ }
+ }
+}
+
+void KyraEngine_v2::flagAnimObjsForRefresh() {
+ for (AnimObj *curEntry = _animList; curEntry; curEntry = curEntry->nextObject)
+ curEntry->needRefresh = 1;
+}
+
+void KyraEngine_v2::flagAnimObjsSpecialRefresh() {
+ for (AnimObj *curEntry = _animList; curEntry; curEntry = curEntry->nextObject)
+ curEntry->specialRefresh = 1;
+}
+
+void KyraEngine_v2::addItemToAnimList(int item) {
+ assert(item >= 0 && item < _itemListSize);
+
+ restorePage3();
+
+ AnimObj *animObj = _animItems + item;
+
+ animObj->enabled = 1;
+ animObj->needRefresh = 1;
+
+ int itemId = _itemList[item].id;
+
+ animObj->xPos2 = animObj->xPos1 = _itemList[item].x;
+ animObj->yPos2 = animObj->yPos1 = _itemList[item].y;
+
+ animObj->shapePtr = getShapePtr(itemId + _desc.itemShapeStart);
+ animSetupPaletteEntry(animObj);
+ animObj->shapeIndex2 = animObj->shapeIndex1 = itemId + _desc.itemShapeStart;
+
+ int scaleY, scaleX;
+ scaleY = scaleX = getScale(animObj->xPos1, animObj->yPos1);
+
+ uint8 *shapePtr = getShapePtr(itemId + _desc.itemShapeStart);
+ animObj->xPos3 = (animObj->xPos2 -= (screen_v2()->getShapeScaledWidth(shapePtr, scaleX) >> 1));
+ animObj->yPos3 = (animObj->yPos2 -= screen_v2()->getShapeScaledHeight(shapePtr, scaleY));
+
+ animObj->width2 = animObj->height2 = 0;
+
+ _animList = addToAnimListSorted(_animList, animObj);
+ animObj->needRefresh = 1;
+}
+
+void KyraEngine_v2::deleteItemAnimEntry(int item) {
+ assert(item < _itemListSize);
+
+ AnimObj *animObj = _animItems + item;
+
+ restorePage3();
+
+ animObj->shapePtr = 0;
+ animObj->shapeIndex1 = 0xFFFF;
+ animObj->shapeIndex2 = 0xFFFF;
+ animObj->needRefresh = 1;
+
+ refreshAnimObjectsIfNeed();
+
+ animObj->enabled = 0;
+ _animList = deleteAnimListEntry(_animList, animObj);
+}
+
+} // End of namespace Kyra
diff --git a/engines/kyra/graphics/screen.cpp b/engines/kyra/graphics/screen.cpp
new file mode 100644
index 0000000000..a07e437d5f
--- /dev/null
+++ b/engines/kyra/graphics/screen.cpp
@@ -0,0 +1,3971 @@
+/* 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 2
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "kyra/graphics/screen.h"
+#include "kyra/kyra_v1.h"
+#include "kyra/resource/resource.h"
+
+#include "common/endian.h"
+#include "common/memstream.h"
+#include "common/system.h"
+#include "common/config-manager.h"
+
+#include "engines/util.h"
+
+#include "graphics/cursorman.h"
+#include "graphics/palette.h"
+#include "graphics/sjis.h"
+
+namespace Kyra {
+
+Screen::Screen(KyraEngine_v1 *vm, OSystem *system, const ScreenDim *dimTable, const int dimTableSize)
+ : _system(system), _vm(vm), _sjisInvisibleColor(0), _dimTable(dimTable), _dimTableCount(dimTableSize),
+ _cursorColorKey((vm->game() == GI_KYRA1 || vm->game() == GI_EOB1 || vm->game() == GI_EOB2) ? 0xFF : 0) {
+ _debugEnabled = false;
+ _maskMinY = _maskMaxY = -1;
+
+ _drawShapeVar1 = 0;
+ _drawShapeVar3 = 1;
+ _drawShapeVar4 = 0;
+ _drawShapeVar5 = 0;
+
+ memset(_fonts, 0, sizeof(_fonts));
+
+ memset(_pagePtrs, 0, sizeof(_pagePtrs));
+ // In VGA mode the odd and even page pointers point to the same buffers.
+ for (int i = 0; i < SCREEN_PAGE_NUM; i++)
+ _pageMapping[i] = i & ~1;
+
+ _renderMode = Common::kRenderDefault;
+ _sjisMixedFontMode = false;
+
+ _useHiColorScreen = _vm->gameFlags().useHiColorMode;
+ _screenPageSize = SCREEN_PAGE_SIZE;
+ _16bitPalette = 0;
+ _16bitConversionPalette = 0;
+ _16bitShadingLevel = 0;
+ _bytesPerPixel = 1;
+
+ _currentFont = FID_8_FNT;
+ _paletteChanged = true;
+ _curDim = 0;
+}
+
+Screen::~Screen() {
+ for (int i = 0; i < SCREEN_OVLS_NUM; ++i)
+ delete[] _sjisOverlayPtrs[i];
+
+ delete[] _pagePtrs[0];
+
+ for (int f = 0; f < ARRAYSIZE(_fonts); ++f)
+ delete _fonts[f];
+
+ delete _screenPalette;
+ delete _internFadePalette;
+ delete[] _decodeShapeBuffer;
+ delete[] _animBlockPtr;
+ delete[] _16bitPalette;
+ delete[] _16bitConversionPalette;
+
+ for (uint i = 0; i < _palettes.size(); ++i)
+ delete _palettes[i];
+
+ for (int i = 0; i < _dimTableCount; ++i)
+ delete _customDimTable[i];
+ delete[] _customDimTable;
+}
+
+bool Screen::init() {
+ _debugEnabled = false;
+
+ memset(_sjisOverlayPtrs, 0, sizeof(_sjisOverlayPtrs));
+ _useOverlays = false;
+ _useSJIS = false;
+ _use16ColorMode = _vm->gameFlags().use16ColorMode;
+ _isAmiga = (_vm->gameFlags().platform == Common::kPlatformAmiga);
+
+ // We only check the "render_mode" setting for both Eye of the Beholder
+ // games here, since all the other games do not support the render_mode
+ // setting or handle it differently, like Kyra 1 PC-98. This avoids
+ // graphics glitches and crashes in other games, when the user sets his
+ // global render_mode setting to EGA for example.
+ // TODO/FIXME: It would be nice not to hardcode this. But there is no
+ // trivial/non annoying way to do mode checks in an easy fashion right
+ // now.
+ // In a more general sense, we might want to think about a way to only
+ // pass valid config values, as in values which the engine can work with,
+ // to the engines. We already limit the selection via our GUIO flags in
+ // the game specific settings, but this is not enough due to global
+ // settings allowing everything.
+ if (_vm->game() == GI_EOB1 || _vm->game() == GI_EOB2) {
+ if (ConfMan.hasKey("render_mode"))
+ _renderMode = Common::parseRenderMode(ConfMan.get("render_mode"));
+ }
+
+ // CGA and EGA modes use additional pages to do the CGA/EGA specific graphics conversions.
+ if (_vm->game() == GI_EOB1 && (_renderMode == Common::kRenderCGA || _renderMode == Common::kRenderEGA)) {
+ for (int i = 0; i < 8; i++)
+ _pageMapping[i] = i;
+ }
+
+ memset(_fonts, 0, sizeof(_fonts));
+
+ _useOverlays = (_vm->gameFlags().useHiRes && _renderMode != Common::kRenderEGA);
+
+ if (_useOverlays) {
+ _useSJIS = (_vm->gameFlags().lang == Common::JA_JPN);
+ _sjisInvisibleColor = (_vm->game() == GI_KYRA1) ? 0x80 : 0xF6;
+ _sjisMixedFontMode = !_use16ColorMode;
+
+ if (!_sjisOverlayPtrs[0]) {
+ // We alway assume 2 bytes per pixel here when the backend is in hicolor mode, since this is the surface that is passed to the backend.
+ // We do this regardsless of the paramater sent to enableHiColorMode() so as not to have to change the backend color mode.
+ // Conversions from 8bit to 16bit have to take place when copying data to this surface here.
+ int bpp = _useHiColorScreen ? 2 : 1;
+ _sjisOverlayPtrs[0] = new uint8[SCREEN_OVL_SJIS_SIZE * bpp];
+ assert(_sjisOverlayPtrs[0]);
+ memset(_sjisOverlayPtrs[0], _sjisInvisibleColor, SCREEN_OVL_SJIS_SIZE * bpp);
+ }
+
+ for (int i = 1; i < SCREEN_OVLS_NUM; ++i) {
+ if (!_sjisOverlayPtrs[i]) {
+ _sjisOverlayPtrs[i] = new uint8[SCREEN_OVL_SJIS_SIZE];
+ assert(_sjisOverlayPtrs[i]);
+ memset(_sjisOverlayPtrs[i], _sjisInvisibleColor, SCREEN_OVL_SJIS_SIZE);
+ }
+ }
+
+ if (_useSJIS) {
+ Graphics::FontSJIS *font = Graphics::FontSJIS::createFont(_vm->gameFlags().platform);
+
+ if (!font)
+ error("Could not load any SJIS font, neither the original nor ScummVM's 'SJIS.FNT'");
+
+ _fonts[FID_SJIS_FNT] = new SJISFont(font, _sjisInvisibleColor, _use16ColorMode, !_use16ColorMode && _vm->game() != GI_LOL && _vm->game() != GI_EOB2, _vm->game() == GI_EOB2 && _vm->gameFlags().platform == Common::kPlatformFMTowns, !_use16ColorMode && _vm->game() == GI_LOL ? 1 : 0);
+ }
+ }
+
+ _curPage = 0;
+
+ enableHiColorMode(false);
+
+ memset(_shapePages, 0, sizeof(_shapePages));
+
+ const int paletteCount = _isAmiga ? 13 : 4;
+ // We allow 256 color palettes in EGA mode, since original EOB II code does the same and requires it
+ const int numColors = _use16ColorMode ? 16 : (_isAmiga ? 32 : (_renderMode == Common::kRenderCGA ? 4 : 256));
+
+ _interfacePaletteEnabled = false;
+
+ _screenPalette = new Palette(numColors);
+ assert(_screenPalette);
+
+ _palettes.resize(paletteCount);
+ for (int i = 0; i < paletteCount; ++i) {
+ _palettes[i] = new Palette(numColors);
+ assert(_palettes[i]);
+ }
+
+ // Setup CGA colors (if CGA mode is selected)
+ if (_renderMode == Common::kRenderCGA) {
+ Palette pal(5);
+ pal.setCGAPalette(1, Palette::kIntensityHigh);
+ // create additional black color 4 for use with the mouse cursor manager
+ pal.fill(4, 1, 0);
+ Screen::setScreenPalette(pal);
+ }
+
+ _internFadePalette = new Palette(numColors);
+ assert(_internFadePalette);
+
+ setScreenPalette(getPalette(0));
+
+ // We setup the PC98 text mode palette at [16, 24], since that will be used
+ // for KANJI characters in Lands of Lore.
+ if (_use16ColorMode && _vm->gameFlags().platform == Common::kPlatformPC98) {
+ uint8 palette[8 * 3];
+
+ for (int i = 0; i < 8; ++i) {
+ palette[i * 3 + 0] = ((i >> 1) & 1) * 0xFF;
+ palette[i * 3 + 1] = ((i >> 2) & 1) * 0xFF;
+ palette[i * 3 + 2] = ((i >> 0) & 1) * 0xFF;
+ }
+
+ _system->getPaletteManager()->setPalette(palette, 16, 8);
+ }
+
+ _customDimTable = new ScreenDim *[_dimTableCount];
+ memset(_customDimTable, 0, sizeof(ScreenDim *) * _dimTableCount);
+
+ _curDimIndex = -1;
+ _curDim = 0;
+ _charWidth = 0;
+ _charOffset = 0;
+ for (int i = 0; i < ARRAYSIZE(_textColorsMap); ++i)
+ _textColorsMap[i] = i;
+ _textColorsMap16bit[0] = _textColorsMap16bit[1] = 0;
+ _decodeShapeBuffer = NULL;
+ _decodeShapeBufferSize = 0;
+ _animBlockPtr = NULL;
+ _animBlockSize = 0;
+ _mouseLockCount = 1;
+ CursorMan.showMouse(false);
+
+ _forceFullUpdate = false;
+
+ return true;
+}
+
+bool Screen::enableScreenDebug(bool enable) {
+ bool temp = _debugEnabled;
+
+ if (_debugEnabled != enable) {
+ _debugEnabled = enable;
+ setResolution();
+ _forceFullUpdate = true;
+ updateScreen();
+ }
+
+ return temp;
+}
+
+void Screen::setResolution() {
+ byte palette[3 * 256];
+ if (!_useHiColorScreen)
+ _system->getPaletteManager()->grabPalette(palette, 0, 256);
+
+ int width = 320, height = 200;
+
+ if (_vm->gameFlags().useHiRes) {
+ height = 400;
+
+ if (_debugEnabled)
+ width = 960;
+ else
+ width = 640;
+ } else {
+ if (_debugEnabled)
+ width = 640;
+ else
+ width = 320;
+ }
+
+ if (_useHiColorScreen) {
+ Graphics::PixelFormat px(2, 5, 5, 5, 0, 10, 5, 0, 0);
+ Common::List<Graphics::PixelFormat> tryModes = _system->getSupportedFormats();
+ for (Common::List<Graphics::PixelFormat>::iterator g = tryModes.begin(); g != tryModes.end(); ++g) {
+ if (g->bytesPerPixel != 2 || g->aBits()) {
+ g = tryModes.reverse_erase(g);
+ } else if (*g == px) {
+ tryModes.clear();
+ tryModes.push_back(px);
+ break;
+ }
+ }
+ initGraphics(width, height, tryModes);
+ if (_system->getScreenFormat().bytesPerPixel != 2)
+ error("Required graphics mode not supported by platform.");
+
+ } else {
+ initGraphics(width, height);
+ _system->getPaletteManager()->setPalette(palette, 0, 256);
+ }
+}
+
+void Screen::enableHiColorMode(bool enabled) {
+ if (_useHiColorScreen && enabled) {
+ if (!_16bitPalette)
+ _16bitPalette = new uint16[1024];
+ memset(_16bitPalette, 0, 1024 * sizeof(uint16));
+ delete[] _16bitConversionPalette;
+ _16bitConversionPalette = 0;
+ _bytesPerPixel = 2;
+ } else {
+ if (_useHiColorScreen) {
+ if (!_16bitConversionPalette)
+ _16bitConversionPalette = new uint16[256];
+ memset(_16bitConversionPalette, 0, 256 * sizeof(uint16));
+ }
+
+ delete[] _16bitPalette;
+ _16bitPalette = 0;
+ _bytesPerPixel = 1;
+ }
+
+ resetPagePtrsAndBuffers(SCREEN_PAGE_SIZE * _bytesPerPixel);
+}
+
+void Screen::updateScreen() {
+ bool needRealUpdate = _forceFullUpdate || !_dirtyRects.empty() || _paletteChanged;
+ _paletteChanged = false;
+
+ if (_useOverlays)
+ updateDirtyRectsOvl();
+ else if (_isAmiga && _interfacePaletteEnabled)
+ updateDirtyRectsAmiga();
+ else
+ updateDirtyRects();
+
+ if (_debugEnabled) {
+ needRealUpdate = true;
+
+ if (!_useOverlays)
+ _system->copyRectToScreen(getPagePtr(2), SCREEN_W, 320, 0, SCREEN_W, SCREEN_H);
+ else
+ _system->copyRectToScreen(getPagePtr(2), SCREEN_W, 640, 0, SCREEN_W, SCREEN_H);
+ }
+
+ if (needRealUpdate)
+ _system->updateScreen();
+}
+
+void Screen::updateDirtyRects() {
+ if (_forceFullUpdate) {
+ _system->copyRectToScreen(getCPagePtr(0), SCREEN_W, 0, 0, SCREEN_W, SCREEN_H);
+ } else {
+ const byte *page0 = getCPagePtr(0);
+ Common::List<Common::Rect>::iterator it;
+ for (it = _dirtyRects.begin(); it != _dirtyRects.end(); ++it) {
+ _system->copyRectToScreen(page0 + it->top * SCREEN_W + it->left, SCREEN_W, it->left, it->top, it->width(), it->height());
+ }
+ }
+ _forceFullUpdate = false;
+ _dirtyRects.clear();
+}
+
+void Screen::updateDirtyRectsAmiga() {
+ if (_forceFullUpdate) {
+ _system->copyRectToScreen(getCPagePtr(0), SCREEN_W, 0, 0, SCREEN_W, 136);
+
+ // Page 8 is not used by Kyra 1 AMIGA, thus we can use it to adjust the colors
+ copyRegion(0, 136, 0, 0, 320, 64, 0, 8, CR_NO_P_CHECK);
+
+ uint8 *dst = getPagePtr(8);
+ for (int y = 0; y < 64; ++y)
+ for (int x = 0; x < 320; ++x)
+ *dst++ += 32;
+
+ _system->copyRectToScreen(getCPagePtr(8), SCREEN_W, 0, 136, SCREEN_W, 64);
+ } else {
+ const byte *page0 = getCPagePtr(0);
+ Common::List<Common::Rect>::iterator it;
+
+ for (it = _dirtyRects.begin(); it != _dirtyRects.end(); ++it) {
+ if (it->bottom <= 136) {
+ _system->copyRectToScreen(page0 + it->top * SCREEN_W + it->left, SCREEN_W, it->left, it->top, it->width(), it->height());
+ } else {
+ // Check whether the rectangle is part of both the screen and the interface
+ if (it->top < 136) {
+ // The rectangle covers both screen part and interface part
+
+ const int screenHeight = 136 - it->top;
+ const int interfaceHeight = it->bottom - 136;
+
+ const int width = it->width();
+ const int lineAdd = SCREEN_W - width;
+
+ // Copy the screen part verbatim
+ _system->copyRectToScreen(page0 + it->top * SCREEN_W + it->left, SCREEN_W, it->left, it->top, width, screenHeight);
+
+ // Adjust the interface part
+ copyRegion(it->left, 136, 0, 0, width, interfaceHeight, 0, 8, Screen::CR_NO_P_CHECK);
+
+ uint8 *dst = getPagePtr(8);
+ for (int y = 0; y < interfaceHeight; ++y) {
+ for (int x = 0; x < width; ++x)
+ *dst++ += 32;
+ dst += lineAdd;
+ }
+
+ _system->copyRectToScreen(getCPagePtr(8), SCREEN_W, it->left, 136, width, interfaceHeight);
+ } else {
+ // The rectangle only covers the interface part
+
+ const int width = it->width();
+ const int height = it->height();
+ const int lineAdd = SCREEN_W - width;
+
+ copyRegion(it->left, it->top, 0, 0, width, height, 0, 8, Screen::CR_NO_P_CHECK);
+
+ uint8 *dst = getPagePtr(8);
+ for (int y = 0; y < height; ++y) {
+ for (int x = 0; x < width; ++x)
+ *dst++ += 32;
+ dst += lineAdd;
+ }
+
+ _system->copyRectToScreen(getCPagePtr(8), SCREEN_W, it->left, it->top, width, height);
+ }
+ }
+ }
+ }
+
+ _forceFullUpdate = false;
+ _dirtyRects.clear();
+}
+
+void Screen::updateDirtyRectsOvl() {
+ if (_forceFullUpdate) {
+ const byte *src = getCPagePtr(0);
+ byte *dst = _sjisOverlayPtrs[0];
+ scale2x(dst, 640, src, SCREEN_W, SCREEN_W, SCREEN_H);
+ mergeOverlay(0, 0, 640, 400);
+ _system->copyRectToScreen(dst, _useHiColorScreen ? 1280 : 640, 0, 0, 640, 400);
+ } else {
+ const byte *page0 = getCPagePtr(0);
+ byte *ovl0 = _sjisOverlayPtrs[0];
+ int dstBpp = _useHiColorScreen ? 2 : 1;
+
+ Common::List<Common::Rect>::iterator it;
+ for (it = _dirtyRects.begin(); it != _dirtyRects.end(); ++it) {
+ byte *dst = ovl0 + it->top * 1280 * dstBpp + (it->left << dstBpp);
+ const byte *src = page0 + it->top * SCREEN_W * _bytesPerPixel + it->left * _bytesPerPixel;
+
+ scale2x(dst, 640, src, SCREEN_W, it->width(), it->height());
+ mergeOverlay(it->left<<1, it->top<<1, it->width()<<1, it->height()<<1);
+ _system->copyRectToScreen(dst, _useHiColorScreen ? 1280 : 640, it->left << 1, it->top << 1, it->width() << 1, it->height() << 1);
+ }
+ }
+
+ _forceFullUpdate = false;
+ _dirtyRects.clear();
+}
+
+void Screen::scale2x(byte *dst, int dstPitch, const byte *src, int srcPitch, int w, int h) {
+ int srcBpp = _bytesPerPixel;
+ int dstBpp = _useHiColorScreen ? 2 : 1;
+
+ byte *dstL1 = dst;
+ byte *dstL2 = dst + dstPitch * dstBpp;
+
+ int dstAdd = (dstPitch * 2 - w * 2) * dstBpp;
+ int srcAdd = (srcPitch - w) * srcBpp;
+ int dstInc = 2 * dstBpp;
+
+ while (h--) {
+ for (int x = 0; x < w; x++, src += srcBpp, dstL1 += dstInc, dstL2 += dstInc) {
+ if (dstBpp == 1) {
+ uint16 col = *src;
+ col |= col << 8;
+ *(uint16 *)(dstL1) = *(uint16 *)(dstL2) = col;
+ } else if (dstBpp == srcBpp) {
+ uint32 col = *(const uint16 *)src;
+ col |= col << 16;
+ *(uint32 *)(dstL1) = *(uint32 *)(dstL2) = col;
+ } else if (dstBpp == 2) {
+ uint32 col = _16bitConversionPalette[*src];
+ col |= col << 16;
+ *(uint32 *)(dstL1) = *(uint32 *)(dstL2) = col;
+ }
+ }
+ dstL1 += dstAdd; dstL2 += dstAdd;
+ src += srcAdd;
+ }
+}
+
+void Screen::mergeOverlay(int x, int y, int w, int h) {
+ int bpp = _useHiColorScreen ? 2 : 1;
+ byte *dst = _sjisOverlayPtrs[0] + y * 640 * bpp + x * bpp;
+ const byte *src = _sjisOverlayPtrs[1] + y * 640 + x;
+ uint16 *p16 = _16bitPalette ? _16bitPalette : (_16bitConversionPalette ? _16bitConversionPalette : 0);
+
+ int add = 640 - w;
+
+ while (h--) {
+ for (x = 0; x < w; ++x, dst += bpp) {
+ byte col = *src++;
+ if (col != _sjisInvisibleColor) {
+ if (bpp == 2)
+ *(uint16*)dst = p16[col];
+ else
+ *dst = col;
+ }
+ }
+ dst += add * bpp;
+ src += add;
+ }
+}
+
+const ScreenDim *Screen::getScreenDim(int dim) const {
+ assert(dim < _dimTableCount);
+ return _customDimTable[dim] ? _customDimTable[dim] : &_dimTable[dim];
+}
+
+void Screen::modifyScreenDim(int dim, int x, int y, int w, int h) {
+ if (!_customDimTable[dim])
+ _customDimTable[dim] = new ScreenDim;
+
+ memcpy(_customDimTable[dim], &_dimTable[dim], sizeof(ScreenDim));
+ _customDimTable[dim]->sx = x;
+ _customDimTable[dim]->sy = y;
+ _customDimTable[dim]->w = w;
+ _customDimTable[dim]->h = h;
+ if (dim == _curDimIndex || _vm->game() == GI_LOL)
+ setScreenDim(dim);
+}
+
+void Screen::setScreenDim(int dim) {
+ _curDim = getScreenDim(dim);
+ _curDimIndex = dim;
+}
+
+void Screen::resetPagePtrsAndBuffers(int pageSize) {
+ _screenPageSize = pageSize;
+
+ delete[] _pagePtrs[0];
+ memset(_pagePtrs, 0, sizeof(_pagePtrs));
+
+ Common::Array<uint8> realPages;
+ for (int i = 0; i < SCREEN_PAGE_NUM; i++) {
+ if (Common::find(realPages.begin(), realPages.end(), _pageMapping[i]) == realPages.end())
+ realPages.push_back(_pageMapping[i]);
+ }
+
+ int numPages = realPages.size();
+ uint32 bufferSize = numPages * _screenPageSize;
+
+ uint8 *pagePtr = new uint8[bufferSize];
+ memset(pagePtr, 0, bufferSize);
+
+ memset(_pagePtrs, 0, sizeof(_pagePtrs));
+ for (int i = 0; i < SCREEN_PAGE_NUM; i++) {
+ if (_pagePtrs[_pageMapping[i]]) {
+ _pagePtrs[i] = _pagePtrs[_pageMapping[i]];
+ } else {
+ _pagePtrs[i] = pagePtr;
+ pagePtr += _screenPageSize;
+ }
+ }
+}
+
+uint8 *Screen::getPagePtr(int pageNum) {
+ assert(pageNum < SCREEN_PAGE_NUM);
+ return _pagePtrs[pageNum];
+}
+
+const uint8 *Screen::getCPagePtr(int pageNum) const {
+ assert(pageNum < SCREEN_PAGE_NUM);
+ return _pagePtrs[pageNum];
+}
+
+uint8 *Screen::getPageRect(int pageNum, int x, int y, int w, int h) {
+ assert(pageNum < SCREEN_PAGE_NUM);
+ if (pageNum == 0 || pageNum == 1)
+ addDirtyRect(x, y, w, h);
+ return _pagePtrs[pageNum] + y * SCREEN_W + x;
+}
+
+void Screen::clearPage(int pageNum) {
+ assert(pageNum < SCREEN_PAGE_NUM);
+ if (pageNum == 0 || pageNum == 1)
+ _forceFullUpdate = true;
+ memset(getPagePtr(pageNum), 0, _screenPageSize);
+ clearOverlayPage(pageNum);
+}
+
+int Screen::setCurPage(int pageNum) {
+ assert(pageNum < SCREEN_PAGE_NUM);
+ int previousPage = _curPage;
+ _curPage = pageNum;
+ return previousPage;
+}
+
+void Screen::clearCurPage() {
+ if (_curPage == 0 || _curPage == 1)
+ _forceFullUpdate = true;
+ memset(getPagePtr(_curPage), 0, _screenPageSize);
+ clearOverlayPage(_curPage);
+}
+
+void Screen::copyWsaRect(int x, int y, int w, int h, int dimState, int plotFunc, const uint8 *src,
+ int unk1, const uint8 *unkPtr1, const uint8 *unkPtr2) {
+ uint8 *dstPtr = getPagePtr(_curPage);
+ uint8 *origDst = dstPtr;
+
+ const ScreenDim *dim = getScreenDim(dimState);
+ int dimX1 = dim->sx << 3;
+ int dimX2 = dim->w << 3;
+ dimX2 += dimX1;
+
+ int dimY1 = dim->sy;
+ int dimY2 = dim->h;
+ dimY2 += dimY1;
+
+ int temp = y - dimY1;
+ if (temp < 0) {
+ if ((temp += h) <= 0)
+ return;
+ else {
+ SWAP(temp, h);
+ y += temp - h;
+ src += (temp - h) * w;
+ }
+ }
+
+ temp = dimY2 - y;
+ if (temp <= 0)
+ return;
+
+ if (temp < h)
+ h = temp;
+
+ int srcOffset = 0;
+ temp = x - dimX1;
+ if (temp < 0) {
+ temp = -temp;
+ srcOffset = temp;
+ x += temp;
+ w -= temp;
+ }
+
+ int srcAdd = 0;
+
+ temp = dimX2 - x;
+ if (temp <= 0)
+ return;
+
+ if (temp < w) {
+ SWAP(w, temp);
+ temp -= w;
+ srcAdd = temp;
+ }
+
+ dstPtr += y * SCREEN_W + x;
+ uint8 *dst = dstPtr;
+
+ if (_curPage == 0 || _curPage == 1)
+ addDirtyRect(x, y, w, h);
+
+ if (!_use16ColorMode)
+ clearOverlayRect(_curPage, x, y, w, h);
+
+ temp = h;
+ int curY = y;
+ while (h--) {
+ src += srcOffset;
+ ++curY;
+ int cW = w;
+
+ switch (plotFunc) {
+ case 0:
+ memcpy(dst, src, cW);
+ dst += cW; src += cW;
+ break;
+
+ case 1:
+ while (cW--) {
+ uint8 d = *src++;
+ uint8 t = unkPtr1[d];
+ if (t != 0xFF)
+ d = unkPtr2[*dst + (t << 8)];
+ *dst++ = d;
+ }
+ break;
+
+ case 4:
+ while (cW--) {
+ uint8 d = *src++;
+ if (d)
+ *dst = d;
+ ++dst;
+ }
+ break;
+
+ case 5:
+ while (cW--) {
+ uint8 d = *src++;
+ if (d) {
+ uint8 t = unkPtr1[d];
+ if (t != 0xFF)
+ d = unkPtr2[*dst + (t << 8)];
+ *dst = d;
+ }
+ ++dst;
+ }
+ break;
+
+ case 8:
+ case 9:
+ while (cW--) {
+ uint8 d = *src++;
+ uint8 t = _shapePages[0][dst - origDst] & 7;
+ if (unk1 < t && (curY > _maskMinY && curY < _maskMaxY))
+ d = _shapePages[1][dst - origDst];
+ *dst++ = d;
+ }
+ break;
+
+ case 12:
+ case 13:
+ while (cW--) {
+ uint8 d = *src++;
+ if (d) {
+ uint8 t = _shapePages[0][dst - origDst] & 7;
+ if (unk1 < t && (curY > _maskMinY && curY < _maskMaxY))
+ d = _shapePages[1][dst - origDst];
+ *dst++ = d;
+ } else {
+ d = _shapePages[1][dst - origDst];
+ *dst++ = d;
+ }
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ dst = (dstPtr += SCREEN_W);
+ src += srcAdd;
+ }
+}
+
+int Screen::getPagePixel(int pageNum, int x, int y) {
+ assert(pageNum < SCREEN_PAGE_NUM);
+ assert(x >= 0 && x < SCREEN_W && y >= 0 && y < SCREEN_H);
+ if (_bytesPerPixel == 1)
+ return _pagePtrs[pageNum][y * SCREEN_W + x];
+ else
+ return ((uint16*)_pagePtrs[pageNum])[y * SCREEN_W + x];
+}
+
+void Screen::setPagePixel(int pageNum, int x, int y, uint8 color) {
+ assert(pageNum < SCREEN_PAGE_NUM);
+ assert(x >= 0 && x < SCREEN_W && y >= 0 && y < SCREEN_H);
+
+ if (pageNum == 0 || pageNum == 1)
+ addDirtyRect(x, y, 1, 1);
+
+ if (_use16ColorMode) {
+ color &= 0x0F;
+ color |= (color << 4);
+ } else if (_renderMode == Common::kRenderCGA) {
+ color &= 0x03;
+ } else if (_renderMode == Common::kRenderEGA && !_useHiResEGADithering) {
+ color &= 0x0F;
+ }
+
+ if (_bytesPerPixel == 2) {
+ ((uint16*)_pagePtrs[pageNum])[y * SCREEN_W + x] = _16bitPalette[color];
+ } else {
+ _pagePtrs[pageNum][y * SCREEN_W + x] = color;
+ }
+}
+
+void Screen::fadeFromBlack(int delay, const UpdateFunctor *upFunc) {
+ fadePalette(getPalette(0), delay, upFunc);
+}
+
+void Screen::fadeToBlack(int delay, const UpdateFunctor *upFunc) {
+ if (_renderMode == Common::kRenderEGA)
+ return;
+
+ Palette pal(getPalette(0).getNumColors());
+ fadePalette(pal, delay, upFunc);
+}
+
+void Screen::fadePalette(const Palette &pal, int delay, const UpdateFunctor *upFunc) {
+ if (_renderMode == Common::kRenderEGA || _bytesPerPixel == 2)
+ setScreenPalette(pal);
+
+ updateScreen();
+
+ if (_renderMode == Common::kRenderCGA || _renderMode == Common::kRenderEGA || _bytesPerPixel == 2)
+ return;
+
+ int diff = 0, delayInc = 0;
+ getFadeParams(pal, delay, delayInc, diff);
+
+ int delayAcc = 0;
+ while (!_vm->shouldQuit()) {
+ delayAcc += delayInc;
+
+ int refreshed = fadePalStep(pal, diff);
+
+ if (upFunc && upFunc->isValid())
+ (*upFunc)();
+ else if (_useHiColorScreen)
+ updateScreen();
+ else
+ _system->updateScreen();
+
+ if (!refreshed)
+ break;
+
+ _vm->delay((delayAcc >> 8) * 1000 / 60);
+ delayAcc &= 0xFF;
+ }
+
+ // In case we should quit we setup the final palette here. This avoids
+ // ugly palette glitches when quitting while fading. This can for example
+ // be noticed when quitting while viewing the family album in Kyra3.
+ if (_vm->shouldQuit()) {
+ setScreenPalette(pal);
+ }
+}
+
+void Screen::getFadeParams(const Palette &pal, int delay, int &delayInc, int &diff) {
+ uint8 maxDiff = 0;
+
+ for (int i = 0; i < pal.getNumColors() * 3; ++i) {
+ diff = ABS(pal[i] - (*_screenPalette)[i]);
+ maxDiff = MAX<uint8>(maxDiff, diff);
+ }
+
+ delayInc = (delay << 8) & 0x7FFF;
+ if (maxDiff != 0)
+ delayInc /= maxDiff;
+
+ delay = delayInc;
+ for (diff = 1; diff <= maxDiff; ++diff) {
+ if (delayInc >= 512)
+ break;
+ delayInc += delay;
+ }
+}
+
+int Screen::fadePalStep(const Palette &pal, int diff) {
+ _internFadePalette->copy(*_screenPalette);
+
+ bool needRefresh = false;
+
+ for (int i = 0; i < pal.getNumColors() * 3; ++i) {
+ int c1 = pal[i];
+ int c2 = (*_internFadePalette)[i];
+ if (c1 != c2) {
+ needRefresh = true;
+ if (c1 > c2) {
+ c2 += diff;
+ if (c1 < c2)
+ c2 = c1;
+ }
+
+ if (c1 < c2) {
+ c2 -= diff;
+ if (c1 > c2)
+ c2 = c1;
+ }
+
+ (*_internFadePalette)[i] = (uint8)c2;
+ }
+ }
+
+ if (needRefresh)
+ setScreenPalette(*_internFadePalette);
+
+ return needRefresh ? 1 : 0;
+}
+
+void Screen::setPaletteIndex(uint8 index, uint8 red, uint8 green, uint8 blue) {
+ Palette &pal = getPalette(0);
+
+ const int offset = index * 3;
+
+ if (pal[offset + 0] == red && pal[offset + 1] == green && pal[offset + 2] == blue)
+ return;
+
+ pal[offset + 0] = red;
+ pal[offset + 1] = green;
+ pal[offset + 2] = blue;
+
+ setScreenPalette(pal);
+}
+
+void Screen::getRealPalette(int num, uint8 *dst) {
+ const int colors = _use16ColorMode ? 16 : (_isAmiga ? 32 : 256);
+ const uint8 *palData = getPalette(num).getData();
+
+ if (!palData) {
+ memset(dst, 0, colors * 3);
+ return;
+ }
+
+ for (int i = 0; i < colors; ++i) {
+ dst[0] = (palData[0] * 0xFF) / 0x3F;
+ dst[1] = (palData[1] * 0xFF) / 0x3F;
+ dst[2] = (palData[2] * 0xFF) / 0x3F;
+ dst += 3;
+ palData += 3;
+ }
+}
+
+void Screen::setScreenPalette(const Palette &pal) {
+ uint8 screenPal[256 * 3];
+ _screenPalette->copy(pal);
+
+ for (int i = 0; i < pal.getNumColors(); ++i) {
+ screenPal[3 * i + 0] = (pal[i * 3 + 0] * 0xFF) / 0x3F;
+ screenPal[3 * i + 1] = (pal[i * 3 + 1] * 0xFF) / 0x3F;
+ screenPal[3 * i + 2] = (pal[i * 3 + 2] * 0xFF) / 0x3F;
+ }
+
+ _paletteChanged = true;
+
+ if (_useHiColorScreen) {
+ if (_16bitPalette)
+ memcpy(_16bitPalette, pal.getData(), 512);
+
+ // Generate 16bit palette for the 8bit/16 bit conversion in scale2x()
+ if (_16bitConversionPalette) {
+ Graphics::PixelFormat pixelFormat = _system->getScreenFormat();
+ for (int i = 0; i < 256; ++i)
+ _16bitConversionPalette[i] = pixelFormat.RGBToColor(screenPal[i * 3], screenPal[i * 3 + 1], screenPal[i * 3 + 2]);
+ // The whole Surface has to be converted again after each palette chance
+ _forceFullUpdate = true;
+ }
+ return;
+ }
+
+ _system->getPaletteManager()->setPalette(screenPal, 0, pal.getNumColors());
+}
+
+void Screen::enableInterfacePalette(bool e) {
+ _interfacePaletteEnabled = e;
+
+ _forceFullUpdate = true;
+ _dirtyRects.clear();
+
+ // TODO: We might need to reset the mouse cursor
+
+ updateScreen();
+}
+
+void Screen::setInterfacePalette(const Palette &pal, uint8 r, uint8 g, uint8 b) {
+ if (!_isAmiga)
+ return;
+
+ uint8 screenPal[32 * 3];
+
+ assert(32 <= pal.getNumColors());
+
+ for (int i = 0; i < pal.getNumColors(); ++i) {
+ if (i != 0x10) {
+ screenPal[3 * i + 0] = (pal[i * 3 + 0] * 0xFF) / 0x3F;
+ screenPal[3 * i + 1] = (pal[i * 3 + 1] * 0xFF) / 0x3F;
+ screenPal[3 * i + 2] = (pal[i * 3 + 2] * 0xFF) / 0x3F;
+ } else {
+ screenPal[3 * i + 0] = (r * 0xFF) / 0x3F;
+ screenPal[3 * i + 1] = (g * 0xFF) / 0x3F;
+ screenPal[3 * i + 2] = (b * 0xFF) / 0x3F;
+ }
+ }
+
+ _paletteChanged = true;
+ _system->getPaletteManager()->setPalette(screenPal, 32, pal.getNumColors());
+}
+
+void Screen::copyToPage0(int y, int h, uint8 page, uint8 *seqBuf) {
+ assert(y + h <= SCREEN_H);
+ const uint8 *src = getPagePtr(page) + y * SCREEN_W;
+ uint8 *dstPage = getPagePtr(0) + y * SCREEN_W;
+ for (int i = 0; i < h; ++i) {
+ for (int x = 0; x < SCREEN_W; ++x) {
+ if (seqBuf[x] != src[x]) {
+ seqBuf[x] = src[x];
+ dstPage[x] = src[x];
+ }
+ }
+ src += SCREEN_W;
+ seqBuf += SCREEN_W;
+ dstPage += SCREEN_W;
+ }
+ addDirtyRect(0, y, SCREEN_W, h);
+ // This would remove the text in the end sequence of
+ // the (Kyrandia 1) FM-TOWNS version.
+ // Since this method is just used for the Seqplayer
+ // this shouldn't be a problem anywhere else, so it's
+ // safe to disable the call here.
+ //clearOverlayRect(0, 0, y, SCREEN_W, h);
+}
+
+void Screen::copyRegion(int x1, int y1, int x2, int y2, int w, int h, int srcPage, int dstPage, int flags) {
+ if (x2 < 0) {
+ if (x2 <= -w)
+ return;
+ w += x2;
+ x1 -= x2;
+ x2 = 0;
+ } else if (x2 + w >= SCREEN_W) {
+ if (x2 > SCREEN_W)
+ return;
+ w = SCREEN_W - x2;
+ }
+
+ if (y2 < 0) {
+ if (y2 <= -h)
+ return;
+ h += y2;
+ y1 -= y2;
+ y2 = 0;
+ } else if (y2 + h >= SCREEN_H) {
+ if (y2 > SCREEN_H)
+ return;
+ h = SCREEN_H - y2;
+ }
+
+ const uint8 *src = getPagePtr(srcPage) + y1 * SCREEN_W * _bytesPerPixel + x1 * _bytesPerPixel;
+ uint8 *dst = getPagePtr(dstPage) + y2 * SCREEN_W * _bytesPerPixel + x2 * _bytesPerPixel;
+
+ if (src == dst)
+ return;
+
+ if (dstPage == 0 || dstPage == 1)
+ addDirtyRect(x2, y2, w, h);
+
+ copyOverlayRegion(x1, y1, x2, y2, w, h, srcPage, dstPage);
+
+ if (flags & CR_NO_P_CHECK) {
+ while (h--) {
+ memmove(dst, src, w * _bytesPerPixel);
+ src += SCREEN_W * _bytesPerPixel;
+ dst += SCREEN_W * _bytesPerPixel;
+ }
+ } else {
+ while (h--) {
+ for (int i = 0; i < w; ++i) {
+ if (_bytesPerPixel == 2) {
+ uint px = *(const uint16*)&src[i << 1];
+ if (px)
+ *(uint16*)&dst[i << 1] = px;
+ } else {
+ if (src[i])
+ dst[i] = src[i];
+ }
+ }
+ src += SCREEN_W * _bytesPerPixel;
+ dst += SCREEN_W * _bytesPerPixel;
+ }
+ }
+}
+
+void Screen::copyRegionToBuffer(int pageNum, int x, int y, int w, int h, uint8 *dest) {
+ if (y < 0) {
+ dest += (-y) * w * _bytesPerPixel;
+ h += y;
+ y = 0;
+ } else if (y + h > SCREEN_H) {
+ h = SCREEN_H - y;
+ }
+
+ if (x < 0) {
+ dest += -x * _bytesPerPixel;
+ w += x;
+ x = 0;
+ } else if (x + w > SCREEN_W) {
+ w = SCREEN_W - x;
+ }
+
+ if (w < 0 || h < 0)
+ return;
+
+ uint8 *pagePtr = getPagePtr(pageNum);
+
+ for (int i = y; i < y + h; ++i)
+ memcpy(dest + (i - y) * w * _bytesPerPixel, pagePtr + i * SCREEN_W * _bytesPerPixel + x * _bytesPerPixel, w * _bytesPerPixel);
+}
+
+void Screen::copyPage(uint8 srcPage, uint8 dstPage) {
+ uint8 *src = getPagePtr(srcPage);
+ uint8 *dst = getPagePtr(dstPage);
+ if (src != dst)
+ memcpy(dst, src, SCREEN_W * SCREEN_H * _bytesPerPixel);
+ copyOverlayRegion(0, 0, 0, 0, SCREEN_W, SCREEN_H, srcPage, dstPage);
+
+ if (dstPage == 0 || dstPage == 1)
+ _forceFullUpdate = true;
+}
+
+void Screen::copyBlockToPage(int pageNum, int x, int y, int w, int h, const uint8 *src) {
+ if (y < 0) {
+ src += (-y) * w * _bytesPerPixel;
+ h += y;
+ y = 0;
+ } else if (y + h > SCREEN_H) {
+ h = SCREEN_H - y;
+ }
+
+ if (x < 0) {
+ src += -x * _bytesPerPixel;
+ w += x;
+ x = 0;
+ } else if (x + w > SCREEN_W) {
+ w = SCREEN_W - x;
+ }
+
+ if (w < 0 || h < 0)
+ return;
+
+ uint8 *dst = getPagePtr(pageNum) + y * SCREEN_W * _bytesPerPixel + x * _bytesPerPixel;
+
+ if (pageNum == 0 || pageNum == 1)
+ addDirtyRect(x, y, w, h);
+
+ clearOverlayRect(pageNum, x, y, w, h);
+
+ while (h--) {
+ memcpy(dst, src, w * _bytesPerPixel);
+ dst += SCREEN_W * _bytesPerPixel;
+ src += w * _bytesPerPixel;
+ }
+}
+
+void Screen::shuffleScreen(int sx, int sy, int w, int h, int srcPage, int dstPage, int ticks, bool transparent) {
+ assert(sx >= 0 && w <= SCREEN_W);
+ int x;
+ uint16 x_offs[SCREEN_W];
+ for (x = 0; x < SCREEN_W; ++x)
+ x_offs[x] = x;
+
+ for (x = 0; x < w; ++x) {
+ int i = _vm->_rnd.getRandomNumber(w - 1);
+ SWAP(x_offs[x], x_offs[i]);
+ }
+
+ assert(sy >= 0 && h <= SCREEN_H);
+ int y;
+ uint8 y_offs[SCREEN_H];
+ for (y = 0; y < SCREEN_H; ++y)
+ y_offs[y] = y;
+
+ for (y = 0; y < h; ++y) {
+ int i = _vm->_rnd.getRandomNumber(h - 1);
+ SWAP(y_offs[y], y_offs[i]);
+ }
+
+ int32 start, now;
+ int wait;
+ for (y = 0; y < h && !_vm->shouldQuit(); ++y) {
+ start = (int32)_system->getMillis();
+ int y_cur = y;
+ for (x = 0; x < w; ++x) {
+ int i = sx + x_offs[x];
+ int j = sy + y_offs[y_cur];
+ ++y_cur;
+ if (y_cur >= h)
+ y_cur = 0;
+
+ uint8 color = getPagePixel(srcPage, i, j);
+ if (!transparent || color != 0)
+ setPagePixel(dstPage, i, j, color);
+ }
+ // forcing full update for now
+ _forceFullUpdate = true;
+ updateScreen();
+ now = (int32)_system->getMillis();
+ wait = ticks * _vm->tickLength() - (now - start);
+ if (wait > 0)
+ _vm->delay(wait);
+ }
+
+ copyOverlayRegion(sx, sy, sx, sy, w, h, srcPage, dstPage);
+
+ if (_vm->shouldQuit()) {
+ copyRegion(sx, sy, sx, sy, w, h, srcPage, dstPage);
+ _system->updateScreen();
+ }
+}
+
+void Screen::fillRect(int x1, int y1, int x2, int y2, uint8 color, int pageNum, bool xored) {
+ assert(x2 < SCREEN_W && y2 < SCREEN_H);
+ uint16 color16 = 0;
+ if (pageNum == -1)
+ pageNum = _curPage;
+
+ uint8 *dst = getPagePtr(pageNum) + y1 * SCREEN_W * _bytesPerPixel + x1 * _bytesPerPixel;
+
+ if (pageNum == 0 || pageNum == 1)
+ addDirtyRect(x1, y1, x2-x1+1, y2-y1+1);
+
+ clearOverlayRect(pageNum, x1, y1, x2-x1+1, y2-y1+1);
+
+ if (_use16ColorMode) {
+ color &= 0x0F;
+ color |= (color << 4);
+ } else if (_renderMode == Common::kRenderCGA) {
+ color &= 0x03;
+ } else if (_renderMode == Common::kRenderEGA && !_useHiResEGADithering) {
+ color &= 0x0F;
+ } else if (_bytesPerPixel == 2)
+ color16 = shade16bitColor(_16bitPalette[color]);
+
+ if (xored) {
+ // no 16 bit support for this (unneeded)
+ for (; y1 <= y2; ++y1) {
+ for (int x = x1; x <= x2; ++x)
+ dst[x] ^= color;
+ dst += SCREEN_W;
+ }
+ } else {
+ for (; y1 <= y2; ++y1) {
+ if (_bytesPerPixel == 2) {
+ uint16 *ptr = (uint16*)dst;
+ for (int i = 0; i < x2 - x1 + 1; i++)
+ *ptr++ = color16;
+ } else {
+ memset(dst, color, x2 - x1 + 1);
+ }
+ dst += SCREEN_W * _bytesPerPixel;
+ }
+ }
+}
+
+void Screen::drawBox(int x1, int y1, int x2, int y2, int color) {
+ drawClippedLine(x1, y1, x2, y1, color);
+ drawClippedLine(x1, y1, x1, y2, color);
+ drawClippedLine(x2, y1, x2, y2, color);
+ drawClippedLine(x1, y2, x2, y2, color);
+}
+
+void Screen::drawShadedBox(int x1, int y1, int x2, int y2, int color1, int color2) {
+ assert(x1 >= 0 && y1 >= 0);
+ fillRect(x1, y1, x2, y1 + 1, color1);
+ fillRect(x2 - 1, y1, x2, y2, color1);
+
+ drawClippedLine(x1, y1, x1, y2, color2);
+ drawClippedLine(x1 + 1, y1 + 1, x1 + 1, y2 - 1, color2);
+ drawClippedLine(x1, y2 - 1, x2 - 1, y2 - 1, color2);
+ drawClippedLine(x1, y2, x2, y2, color2);
+}
+
+void Screen::drawClippedLine(int x1, int y1, int x2, int y2, int color) {
+ if (x1 < 0)
+ x1 = 0;
+ else if (x1 > 319)
+ x1 = 319;
+
+ if (x2 < 0)
+ x2 = 0;
+ else if (x2 > 319)
+ x2 = 319;
+
+ if (y1 < 0)
+ y1 = 0;
+ else if (y1 > 199)
+ y1 = 199;
+
+ if (y2 < 0)
+ y2 = 0;
+ else if (y2 > 199)
+ y2 = 199;
+
+ if (x1 == x2)
+ if (y1 > y2)
+ drawLine(true, x1, y2, y1 - y2 + 1, color);
+ else
+ drawLine(true, x1, y1, y2 - y1 + 1, color);
+ else
+ if (x1 > x2)
+ drawLine(false, x2, y1, x1 - x2 + 1, color);
+ else
+ drawLine(false, x1, y1, x2 - x1 + 1, color);
+}
+
+void Screen::drawLine(bool vertical, int x, int y, int length, int color) {
+ uint8 *ptr = getPagePtr(_curPage) + y * SCREEN_W * _bytesPerPixel + x * _bytesPerPixel;
+
+ if (_use16ColorMode) {
+ color &= 0x0F;
+ color |= (color << 4);
+ } else if (_renderMode == Common::kRenderCGA) {
+ color &= 0x03;
+ } else if (_renderMode == Common::kRenderEGA && !_useHiResEGADithering) {
+ color &= 0x0F;
+ } else if (_bytesPerPixel == 2)
+ color = shade16bitColor(_16bitPalette[color]);
+
+ if (vertical) {
+ assert((y + length) <= SCREEN_H);
+ int currLine = 0;
+ while (currLine < length) {
+ if (_bytesPerPixel == 2)
+ *(uint16*)ptr = color;
+ else
+ *ptr = color;
+ ptr += SCREEN_W * _bytesPerPixel;
+ currLine++;
+ }
+ } else {
+ assert((x + length) <= SCREEN_W);
+ if (_bytesPerPixel == 2) {
+ for (int i = 0; i < length; i++) {
+ *(uint16*)ptr = color;
+ ptr += 2;
+ }
+ } else {
+ memset(ptr, color, length);
+ }
+ }
+
+ if (_curPage == 0 || _curPage == 1)
+ addDirtyRect(x, y, (vertical) ? 1 : length, (vertical) ? length : 1);
+
+ clearOverlayRect(_curPage, x, y, (vertical) ? 1 : length, (vertical) ? length : 1);
+}
+
+void Screen::setAnimBlockPtr(int size) {
+ delete[] _animBlockPtr;
+ _animBlockPtr = new uint8[size];
+ assert(_animBlockPtr);
+ memset(_animBlockPtr, 0, size);
+ _animBlockSize = size;
+}
+
+void Screen::setTextColor(const uint8 *cmap8, int a, int b) {
+ memcpy(&_textColorsMap[a], cmap8, (b - a + 1));
+ // We need to update the color tables of all fonts, we
+ // setup so far here.
+ for (int i = 0; i < FID_NUM; ++i) {
+ if (_fonts[i])
+ _fonts[i]->setColorMap(_textColorsMap);
+ }
+}
+
+void Screen::setTextColor16bit(const uint16 *cmap16) {
+ assert(cmap16);
+ _textColorsMap16bit[0] = cmap16[0];
+ _textColorsMap16bit[1] = cmap16[1];
+ // We need to update the color tables of all fonts, we
+ // setup so far here.
+ for (int i = 0; i < FID_NUM; ++i) {
+ if (_fonts[i])
+ _fonts[i]->set16bitColorMap(_textColorsMap16bit);
+ }
+}
+
+bool Screen::loadFont(FontId fontId, const char *filename) {
+ if (fontId == FID_SJIS_FNT) {
+ warning("Trying to replace system SJIS font");
+ return true;
+ }
+
+ Font *&fnt = _fonts[fontId];
+
+ if (!fnt) {
+ if (_isAmiga)
+ fnt = new AMIGAFont();
+#ifdef ENABLE_EOB
+ else if (_vm->game() == GI_EOB1 || _vm->game() == GI_EOB2)
+ // We use normal VGA rendering in EOB II, since we do the complete EGA dithering in updateScreen().
+ fnt = new OldDOSFont(_useHiResEGADithering ? Common::kRenderVGA : _renderMode);
+#endif // ENABLE_EOB
+ else
+ fnt = new DOSFont();
+
+ assert(fnt);
+ }
+
+ Common::SeekableReadStream *file = _vm->resource()->createReadStream(filename);
+ if (!file)
+ error("Font file '%s' is missing", filename);
+
+ bool ret = fnt->load(*file);
+ fnt->setColorMap(_textColorsMap);
+ delete file;
+ return ret;
+}
+
+Screen::FontId Screen::setFont(FontId fontId) {
+ FontId prev = _currentFont;
+ _currentFont = fontId;
+
+ assert(_fonts[_currentFont]);
+ return prev;
+}
+
+int Screen::getFontHeight() const {
+ return _fonts[_currentFont]->getHeight();
+}
+
+int Screen::getFontWidth() const {
+ return _fonts[_currentFont]->getWidth();
+}
+
+int Screen::getCharWidth(uint16 c) const {
+ const int width = _fonts[_currentFont]->getCharWidth(c);
+ return width + ((_currentFont != FID_SJIS_FNT && _currentFont != FID_SJIS_LARGE_FNT && _currentFont != FID_SJIS_SMALL_FNT) ? _charWidth : 0);
+}
+
+int Screen::getTextWidth(const char *str) {
+ int curLineLen = 0;
+ int maxLineLen = 0;
+
+ FontId curFont = _currentFont;
+
+ while (1) {
+ if (_sjisMixedFontMode && curFont != FID_SJIS_FNT && curFont != FID_SJIS_LARGE_FNT && curFont != FID_SJIS_SMALL_FNT)
+ setFont((*str & 0x80) ? ((_vm->game() == GI_EOB2 && curFont == FID_6_FNT) ? FID_SJIS_SMALL_FNT : FID_SJIS_FNT) : curFont);
+
+ uint c = fetchChar(str);
+
+ if (c == 0) {
+ break;
+ } else if (c == '\r') {
+ if (curLineLen > maxLineLen)
+ maxLineLen = curLineLen;
+ else
+ curLineLen = 0;
+ } else {
+ curLineLen += getCharWidth(c);
+ }
+ }
+
+ return MAX(curLineLen, maxLineLen);
+}
+
+void Screen::printText(const char *str, int x, int y, uint8 color1, uint8 color2) {
+ uint16 cmap16[2];
+ if (_16bitPalette) {
+ cmap16[0] = color2 ? shade16bitColor(_16bitPalette[color2]) : 0xFFFF;
+ cmap16[1] = _16bitPalette[color1];
+ setTextColor16bit(cmap16);
+ }
+
+ uint8 cmap8[2];
+ cmap8[0] = color2;
+ cmap8[1] = color1;
+ setTextColor(cmap8, 0, 1);
+
+ FontId curFont = _currentFont;
+
+ if (x < 0)
+ x = 0;
+ else if (x >= SCREEN_W)
+ return;
+
+ int x_start = x;
+ if (y < 0)
+ y = 0;
+ else if (y >= SCREEN_H)
+ return;
+
+ while (1) {
+ if (_sjisMixedFontMode && curFont != FID_SJIS_FNT && curFont != FID_SJIS_LARGE_FNT && curFont != FID_SJIS_SMALL_FNT)
+ setFont((*str & 0x80) ? ((_vm->game() == GI_EOB2 && curFont == FID_6_FNT) ? FID_SJIS_SMALL_FNT : FID_SJIS_FNT) : curFont);
+
+ uint8 charHeightFnt = getFontHeight();
+
+ uint c = fetchChar(str);
+
+ if (c == 0) {
+ break;
+ } else if (c == '\r') {
+ x = x_start;
+ y += (charHeightFnt + _charOffset);
+ } else {
+ int charWidth = getCharWidth(c);
+ if (x + charWidth > SCREEN_W) {
+ x = x_start;
+ y += (charHeightFnt + _charOffset);
+ if (y >= SCREEN_H)
+ break;
+ }
+
+ drawChar(c, x, y);
+ x += charWidth;
+ }
+ }
+}
+
+uint16 Screen::fetchChar(const char *&s) const {
+ if (_currentFont != FID_SJIS_FNT && _currentFont != FID_SJIS_LARGE_FNT && _currentFont != FID_SJIS_SMALL_FNT)
+ return (uint8)*s++;
+
+ uint16 ch = (uint8)*s++;
+
+ if (ch <= 0x7F || (ch >= 0xA1 && ch <= 0xDF))
+ return ch;
+
+ ch |= (uint8)(*s++) << 8;
+ return ch;
+}
+
+void Screen::drawChar(uint16 c, int x, int y) {
+ Font *fnt = _fonts[_currentFont];
+ assert(fnt);
+
+ const bool useOverlay = fnt->usesOverlay();
+ const int charWidth = fnt->getCharWidth(c);
+ const int charHeight = fnt->getHeight();
+
+ if (x < 0 || y < 0)
+ return;
+ if (x + charWidth > SCREEN_W || y + charHeight > SCREEN_H)
+ return;
+
+ if (useOverlay) {
+ uint8 *destPage = getOverlayPtr(_curPage);
+ if (!destPage) {
+ warning("trying to draw SJIS char on unsupported page %d", _curPage);
+ return;
+ }
+
+ int bpp = (_currentFont == Screen::FID_SJIS_FNT || _currentFont == Screen::FID_SJIS_SMALL_FNT) ? 1 : 2;
+ destPage += (y * 2) * 640 * bpp + (x * 2 * bpp);
+
+ fnt->drawChar(c, destPage, 640, bpp);
+ } else {
+ fnt->drawChar(c, getPagePtr(_curPage) + y * SCREEN_W * _bytesPerPixel + x * _bytesPerPixel, SCREEN_W, _bytesPerPixel);
+ }
+
+ if (_curPage == 0 || _curPage == 1)
+ addDirtyRect(x, y, charWidth, charHeight);
+}
+
+void Screen::drawShape(uint8 pageNum, const uint8 *shapeData, int x, int y, int sd, int flags, ...) {
+ if (!shapeData)
+ return;
+
+ if (_vm->gameFlags().useAltShapeHeader)
+ shapeData += 2;
+
+ if (*shapeData & 1)
+ flags |= 0x400;
+
+ va_list args;
+ va_start(args, flags);
+
+ static const int drawShapeVar2[] = {
+ 1, 3, 2, 5, 4, 3, 2, 1
+ };
+
+ _dsShapeFadingTable = 0;
+ _dsShapeFadingLevel = 0;
+ _dsColorTable = 0;
+ _dsTransparencyTable1 = 0;
+ _dsTransparencyTable2 = 0;
+ _dsBackgroundFadingTable = 0;
+ _dsDrawLayer = 0;
+
+ if (flags & DSF_CUSTOM_PALETTE) {
+ _dsColorTable = va_arg(args, uint8 *);
+ }
+
+ if (flags & DSF_SHAPE_FADING) {
+ _dsShapeFadingTable = va_arg(args, uint8 *);
+ _dsShapeFadingLevel = va_arg(args, int);
+ if (!_dsShapeFadingLevel)
+ flags &= ~DSF_SHAPE_FADING;
+ }
+
+ if (flags & DSF_TRANSPARENCY) {
+ _dsTransparencyTable1 = va_arg(args, uint8 *);
+ _dsTransparencyTable2 = va_arg(args, uint8 *);
+ }
+
+ if (flags & 0x200) {
+ _drawShapeVar1 = (_drawShapeVar1 + 1) & 0x7;
+ _drawShapeVar3 = drawShapeVar2[_drawShapeVar1];
+ _drawShapeVar4 = 0;
+ _drawShapeVar5 = 256;
+ }
+
+ if (flags & 0x4000)
+ _drawShapeVar5 = va_arg(args, int);
+
+ if (flags & 0x800)
+ _dsDrawLayer = va_arg(args, int);
+
+ if (flags & DSF_SCALE) {
+ _dsScaleW = va_arg(args, int);
+ _dsScaleH = va_arg(args, int);
+ } else {
+ _dsScaleW = 0x100;
+ _dsScaleH = 0x100;
+ }
+
+ if ((flags & DSF_BACKGROUND_FADING) && _vm->game() != GI_KYRA1)
+ _dsBackgroundFadingTable = va_arg(args, uint8 *);
+
+ va_end(args);
+
+ static const DsMarginSkipFunc dsMarginFunc[] = {
+ &Screen::drawShapeMarginNoScaleUpwind,
+ &Screen::drawShapeMarginNoScaleDownwind,
+ &Screen::drawShapeMarginNoScaleUpwind,
+ &Screen::drawShapeMarginNoScaleDownwind,
+ &Screen::drawShapeMarginScaleUpwind,
+ &Screen::drawShapeMarginScaleDownwind,
+ &Screen::drawShapeMarginScaleUpwind,
+ &Screen::drawShapeMarginScaleDownwind
+ };
+
+ static const DsMarginSkipFunc dsSkipFunc[] = {
+ &Screen::drawShapeMarginNoScaleUpwind,
+ &Screen::drawShapeMarginNoScaleDownwind,
+ &Screen::drawShapeMarginNoScaleUpwind,
+ &Screen::drawShapeMarginNoScaleDownwind,
+ &Screen::drawShapeSkipScaleUpwind,
+ &Screen::drawShapeSkipScaleDownwind,
+ &Screen::drawShapeSkipScaleUpwind,
+ &Screen::drawShapeSkipScaleDownwind
+ };
+
+ static const DsLineFunc dsLineFunc[] = {
+ &Screen::drawShapeProcessLineNoScaleUpwind,
+ &Screen::drawShapeProcessLineNoScaleDownwind,
+ &Screen::drawShapeProcessLineNoScaleUpwind,
+ &Screen::drawShapeProcessLineNoScaleDownwind,
+ &Screen::drawShapeProcessLineScaleUpwind,
+ &Screen::drawShapeProcessLineScaleDownwind,
+ &Screen::drawShapeProcessLineScaleUpwind,
+ &Screen::drawShapeProcessLineScaleDownwind
+ };
+
+ static const DsPlotFunc dsPlotFunc[] = {
+ &Screen::drawShapePlotType0, // used by Kyra 1 + 2
+ &Screen::drawShapePlotType1, // used by Kyra 3
+ 0,
+ &Screen::drawShapePlotType3_7, // used by Kyra 3 (shadow)
+ &Screen::drawShapePlotType4, // used by Kyra 1, 2 + 3
+ &Screen::drawShapePlotType5, // used by Kyra 1
+ &Screen::drawShapePlotType6, // used by Kyra 1 (invisibility)
+ &Screen::drawShapePlotType3_7, // used by Kyra 1 (invisibility)
+ &Screen::drawShapePlotType8, // used by Kyra 2
+ &Screen::drawShapePlotType9, // used by Kyra 1 + 3
+ 0,
+ &Screen::drawShapePlotType11_15, // used by Kyra 1 (invisibility) + Kyra 3 (shadow)
+ &Screen::drawShapePlotType12, // used by Kyra 2
+ &Screen::drawShapePlotType13, // used by Kyra 1
+ &Screen::drawShapePlotType14, // used by Kyra 1 (invisibility)
+ &Screen::drawShapePlotType11_15, // used by Kyra 1 (invisibility)
+ &Screen::drawShapePlotType16, // used by LoL PC-98/16 Colors (teleporters),
+ 0, 0, 0,
+ &Screen::drawShapePlotType20, // used by LoL (heal spell effect)
+ &Screen::drawShapePlotType21, // used by LoL (white tower spirits)
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0,
+ &Screen::drawShapePlotType33, // used by LoL (blood spots on the floor)
+ 0, 0, 0,
+ &Screen::drawShapePlotType37, // used by LoL (monsters)
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ &Screen::drawShapePlotType48, // used by LoL (slime spots on the floor)
+ 0, 0, 0,
+ &Screen::drawShapePlotType52, // used by LoL (projectiles)
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0
+ };
+
+ int scaleCounterV = 0;
+
+ const int drawFunc = flags & 0x0F;
+ _dsProcessMargin = dsMarginFunc[drawFunc];
+ _dsScaleSkip = dsSkipFunc[drawFunc];
+ _dsProcessLine = dsLineFunc[drawFunc];
+
+ const int ppc = (flags >> 8) & 0x3F;
+ _dsPlot = dsPlotFunc[ppc];
+ DsPlotFunc dsPlot2 = dsPlotFunc[ppc], dsPlot3 = dsPlotFunc[ppc];
+ if (flags & 0x800)
+ dsPlot3 = dsPlotFunc[((flags >> 8) & 0xF7) & 0x3F];
+
+ if (!_dsPlot || !dsPlot2 || !dsPlot3) {
+ if (!dsPlot2)
+ warning("Missing drawShape plotting method type %d", ppc);
+ if (dsPlot3 != dsPlot2 && !dsPlot3)
+ warning("Missing drawShape plotting method type %d", (((flags >> 8) & 0xF7) & 0x3F));
+ return;
+ }
+
+ int curY = y;
+ const uint8 *src = shapeData;
+ uint8 *dst = _dsDstPage = getPagePtr(pageNum);
+
+ const ScreenDim *dsDim = getScreenDim(sd);
+ dst += (dsDim->sx << 3);
+
+ if (!(flags & 0x10))
+ x -= (dsDim->sx << 3);
+
+ int x2 = (dsDim->w << 3);
+ int y1 = dsDim->sy;
+ if (flags & 0x10)
+ y += y1;
+
+ int y2 = y1 + dsDim->h;
+
+ uint16 shapeFlags = READ_LE_UINT16(src); src += 2;
+
+ int shapeHeight = *src++;
+ uint16 shapeWidth = READ_LE_UINT16(src); src += 2;
+
+ int shpWidthScaled1 = shapeWidth;
+ int shpWidthScaled2 = shapeWidth;
+
+ if (flags & DSF_SCALE) {
+ shapeHeight = (shapeHeight * _dsScaleH) >> 8;
+ shpWidthScaled1 = shpWidthScaled2 = (shapeWidth * _dsScaleW) >> 8;
+
+ if (!shapeHeight || !shpWidthScaled1)
+ return;
+ }
+
+ if (flags & DSF_CENTER) {
+ x -= (shpWidthScaled1 >> 1);
+ y -= (shapeHeight >> 1);
+ }
+
+ src += 3;
+
+ uint16 frameSize = READ_LE_UINT16(src); src += 2;
+
+ int colorTableColors = ((_vm->game() != GI_KYRA1) && (shapeFlags & 4)) ? *src++ : 16;
+
+ if (!(flags & 0x8000) && (shapeFlags & 1))
+ _dsColorTable = src;
+
+ if (flags & 0x400)
+ src += colorTableColors;
+
+ if (!(shapeFlags & 2)) {
+ decodeFrame4(src, _animBlockPtr, frameSize);
+ src = _animBlockPtr;
+ }
+
+ int t = (flags & 2) ? y2 - y - shapeHeight : y - y1;
+
+ if (t < 0) {
+ shapeHeight += t;
+ if (shapeHeight <= 0) {
+ return;
+ }
+
+ t *= -1;
+ const uint8 *srcBackUp = 0;
+
+ do {
+ _dsOffscreenScaleVal1 = 0;
+ srcBackUp = src;
+ _dsTmpWidth = shapeWidth;
+
+ int cnt = shapeWidth;
+ (this->*_dsScaleSkip)(dst, src, cnt);
+
+ scaleCounterV += _dsScaleH;
+
+ if (scaleCounterV & 0xFF00) {
+ uint8 r = scaleCounterV >> 8;
+ scaleCounterV &= 0xFF;
+ t -= r;
+ }
+ } while (!(scaleCounterV & 0xFF00) && (t > 0));
+
+ if (t < 0) {
+ src = srcBackUp;
+ scaleCounterV += (-t << 8);
+ }
+
+ if (!(flags & 2))
+ y = y1;
+ }
+
+ t = (flags & 2) ? y + shapeHeight - y1 : y2 - y;
+ if (t <= 0)
+ return;
+
+ if (t < shapeHeight) {
+ shapeHeight = t;
+ if (flags & 2)
+ y = y1;
+ }
+
+ _dsOffscreenLeft = 0;
+ if (x < 0) {
+ shpWidthScaled1 += x;
+ _dsOffscreenLeft = -x;
+ if (_dsOffscreenLeft >= shpWidthScaled2)
+ return;
+ x = 0;
+ }
+
+ _dsOffscreenRight = 0;
+ t = x2 - x;
+
+ if (t <= 0)
+ return;
+
+ if (t < shpWidthScaled1) {
+ shpWidthScaled1 = t;
+ _dsOffscreenRight = shpWidthScaled2 - _dsOffscreenLeft - shpWidthScaled1;
+ }
+
+ int dsPitch = 320;
+ int ty = y;
+
+ if (flags & 2) {
+ dsPitch *= -1;
+ ty = ty - 1 + shapeHeight;
+ }
+
+ if (flags & DSF_X_FLIPPED) {
+ SWAP(_dsOffscreenLeft, _dsOffscreenRight);
+ dst += (shpWidthScaled1 - 1);
+ }
+
+ dst += (320 * ty + x);
+
+ if (flags & DSF_SCALE) {
+ _dsOffscreenRight = 0;
+ _dsOffscreenScaleVal2 = _dsOffscreenLeft;
+ _dsOffscreenLeft <<= 8;
+ _dsOffscreenScaleVal1 = (_dsOffscreenLeft % _dsScaleW) * -1;
+ _dsOffscreenLeft /= _dsScaleW;
+ }
+
+ if (shapeHeight <= 0 || shpWidthScaled1 <= 0)
+ return;
+
+ if (pageNum == 0 || pageNum == 1)
+ addDirtyRect(x, y, shpWidthScaled1, shapeHeight);
+ clearOverlayRect(pageNum, x, y, shpWidthScaled1, shapeHeight);
+
+ uint8 *d = dst;
+
+ bool normalPlot = true;
+ while (true) {
+ while (!(scaleCounterV & 0xFF00)) {
+ scaleCounterV += _dsScaleH;
+ if (!(scaleCounterV & 0xFF00)) {
+ _dsTmpWidth = shapeWidth;
+ int cnt = shapeWidth;
+ (this->*_dsScaleSkip)(d, src, cnt);
+ }
+ }
+
+ const uint8 *b_src = src;
+
+ do {
+ src = b_src;
+ _dsTmpWidth = shapeWidth;
+ int cnt = _dsOffscreenLeft;
+ int scaleState = (this->*_dsProcessMargin)(d, src, cnt);
+
+ if (_dsTmpWidth) {
+ cnt += shpWidthScaled1;
+ if (cnt > 0) {
+ if (flags & 0x800)
+ normalPlot = (curY > _maskMinY && curY < _maskMaxY);
+ _dsPlot = normalPlot ? dsPlot2 : dsPlot3;
+ (this->*_dsProcessLine)(d, src, cnt, scaleState);
+ }
+ cnt += _dsOffscreenRight;
+ if (cnt)
+ (this->*_dsScaleSkip)(d, src, cnt);
+ }
+ dst += dsPitch;
+ d = dst;
+ ++curY;
+
+ if (!--shapeHeight)
+ return;
+
+ scaleCounterV -= 0x100;
+ } while (scaleCounterV & 0xFF00);
+ }
+}
+
+int Screen::drawShapeMarginNoScaleUpwind(uint8 *&dst, const uint8 *&src, int &cnt) {
+ while (cnt-- > 0) {
+ if (*src++)
+ continue;
+ cnt = cnt + 1 - (*src++);
+ }
+
+ cnt++;
+ dst -= cnt;
+ return 0;
+}
+
+int Screen::drawShapeMarginNoScaleDownwind(uint8 *&dst, const uint8 *&src, int &cnt) {
+ while (cnt-- > 0) {
+ if (*src++)
+ continue;
+ cnt = cnt + 1 - (*src++);
+ }
+
+ cnt++;
+ dst += cnt;
+ return 0;
+}
+
+int Screen::drawShapeMarginScaleUpwind(uint8 *&dst, const uint8 *&src, int &cnt) {
+ _dsTmpWidth -= cnt;
+
+ while (cnt > 0) {
+ --cnt;
+ if (*src++)
+ continue;
+
+ cnt = cnt + 1 - (*src++);
+ }
+
+ if (!cnt)
+ return _dsOffscreenScaleVal1;
+
+ _dsTmpWidth += cnt;
+
+ int i = (_dsOffscreenLeft - cnt) * _dsScaleW;
+ int res = i & 0xFF;
+ i >>= 8;
+ i -= _dsOffscreenScaleVal2;
+ dst += i;
+ cnt = -i;
+
+ return res;
+}
+
+int Screen::drawShapeMarginScaleDownwind(uint8 *&dst, const uint8 *&src, int &cnt) {
+ _dsTmpWidth -= cnt;
+
+ while (cnt > 0) {
+ --cnt;
+ if (*src++)
+ continue;
+
+ cnt = cnt + 1 - (*src++);
+ }
+
+ if (!cnt)
+ return _dsOffscreenScaleVal1;
+
+ _dsTmpWidth += cnt;
+
+ int i = (_dsOffscreenLeft - cnt) * _dsScaleW;
+ int res = i & 0xFF;
+ i >>= 8;
+ i -= _dsOffscreenScaleVal2;
+ dst -= i;
+ cnt = -i;
+
+ return res;
+}
+
+int Screen::drawShapeSkipScaleUpwind(uint8 *&dst, const uint8 *&src, int &cnt) {
+ cnt = _dsTmpWidth;
+
+ if (cnt <= 0)
+ return 0;
+
+ do {
+ --cnt;
+ if (*src++)
+ continue;
+ cnt = cnt + 1 - (*src++);
+ } while (cnt > 0);
+
+ return 0;
+}
+
+int Screen::drawShapeSkipScaleDownwind(uint8 *&dst, const uint8 *&src, int &cnt) {
+ cnt = _dsTmpWidth;
+ bool found = false;
+
+ if (cnt == 0)
+ return 0;
+
+ do {
+ --cnt;
+ if (*src++)
+ continue;
+ found = true;
+ cnt = cnt + 1 - (*src++);
+ } while (cnt > 0);
+
+ return found ? 0 : _dsOffscreenScaleVal1;
+}
+
+void Screen::drawShapeProcessLineNoScaleUpwind(uint8 *&dst, const uint8 *&src, int &cnt, int16) {
+ do {
+ uint8 c = *src++;
+ if (c) {
+ uint8 *d = dst++;
+ (this->*_dsPlot)(d, c);
+ cnt--;
+ } else {
+ c = *src++;
+ dst += c;
+ cnt -= c;
+ }
+ } while (cnt > 0);
+}
+
+void Screen::drawShapeProcessLineNoScaleDownwind(uint8 *&dst, const uint8 *&src, int &cnt, int16) {
+ do {
+ uint8 c = *src++;
+ if (c) {
+ uint8 *d = dst--;
+ (this->*_dsPlot)(d, c);
+ cnt--;
+ } else {
+ c = *src++;
+ dst -= c;
+ cnt -= c;
+ }
+ } while (cnt > 0);
+}
+
+void Screen::drawShapeProcessLineScaleUpwind(uint8 *&dst, const uint8 *&src, int &cnt, int16 scaleState) {
+ int c = 0;
+
+ do {
+ if ((scaleState & 0x8000) || !(scaleState & 0xFF00)) {
+ c = *src++;
+ _dsTmpWidth--;
+ if (c) {
+ scaleState += _dsScaleW;
+ } else {
+ _dsTmpWidth++;
+ c = *src++;
+ _dsTmpWidth -= c;
+ int r = c * _dsScaleW + scaleState;
+ dst += (r >> 8);
+ cnt -= (r >> 8);
+ scaleState = r & 0xFF;
+ }
+ } else if (scaleState) {
+ (this->*_dsPlot)(dst++, c);
+ scaleState -= 0x100;
+ cnt--;
+ }
+ } while (cnt > 0);
+
+ cnt = -1;
+}
+
+void Screen::drawShapeProcessLineScaleDownwind(uint8 *&dst, const uint8 *&src, int &cnt, int16 scaleState) {
+ int c = 0;
+
+ do {
+ if ((scaleState & 0x8000) || !(scaleState & 0xFF00)) {
+ c = *src++;
+ _dsTmpWidth--;
+ if (c) {
+ scaleState += _dsScaleW;
+ } else {
+ _dsTmpWidth++;
+ c = *src++;
+ _dsTmpWidth -= c;
+ int r = c * _dsScaleW + scaleState;
+ dst -= (r >> 8);
+ cnt -= (r >> 8);
+ scaleState = r & 0xFF;
+ }
+ } else {
+ (this->*_dsPlot)(dst--, c);
+ scaleState -= 0x100;
+ cnt--;
+ }
+ } while (cnt > 0);
+
+ cnt = -1;
+}
+
+void Screen::drawShapePlotType0(uint8 *dst, uint8 cmd) {
+ *dst = cmd;
+}
+
+void Screen::drawShapePlotType1(uint8 *dst, uint8 cmd) {
+ for (int i = 0; i < _dsShapeFadingLevel; ++i)
+ cmd = _dsShapeFadingTable[cmd];
+
+ if (cmd)
+ *dst = cmd;
+}
+
+void Screen::drawShapePlotType3_7(uint8 *dst, uint8 cmd) {
+ cmd = *dst;
+ for (int i = 0; i < _dsShapeFadingLevel; ++i)
+ cmd = _dsShapeFadingTable[cmd];
+
+ if (cmd)
+ *dst = cmd;
+}
+
+void Screen::drawShapePlotType4(uint8 *dst, uint8 cmd) {
+ *dst = _dsColorTable[cmd];
+}
+
+void Screen::drawShapePlotType5(uint8 *dst, uint8 cmd) {
+ cmd = _dsColorTable[cmd];
+ for (int i = 0; i < _dsShapeFadingLevel; ++i)
+ cmd = _dsShapeFadingTable[cmd];
+
+ if (cmd)
+ *dst = cmd;
+}
+
+void Screen::drawShapePlotType6(uint8 *dst, uint8 cmd) {
+ int t = _drawShapeVar4 + _drawShapeVar5;
+ if (t & 0xFF00) {
+ cmd = dst[_drawShapeVar3];
+ t &= 0xFF;
+ } else {
+ cmd = _dsColorTable[cmd];
+ }
+
+ _drawShapeVar4 = t;
+ *dst = cmd;
+}
+
+void Screen::drawShapePlotType8(uint8 *dst, uint8 cmd) {
+ uint32 relOffs = dst - _dsDstPage;
+ int t = (_shapePages[0][relOffs] & 0x7F) & 0x87;
+ if (_dsDrawLayer < t)
+ cmd = _shapePages[1][relOffs];
+
+ *dst = cmd;
+}
+
+void Screen::drawShapePlotType9(uint8 *dst, uint8 cmd) {
+ uint32 relOffs = dst - _dsDstPage;
+ int t = (_shapePages[0][relOffs] & 0x7F) & 0x87;
+ if (_dsDrawLayer < t) {
+ cmd = _shapePages[1][relOffs];
+ } else {
+ for (int i = 0; i < _dsShapeFadingLevel; ++i)
+ cmd = _dsShapeFadingTable[cmd];
+ }
+
+ if (cmd)
+ *dst = cmd;
+}
+
+void Screen::drawShapePlotType11_15(uint8 *dst, uint8 cmd) {
+ uint32 relOffs = dst - _dsDstPage;
+ int t = (_shapePages[0][relOffs] & 0x7F) & 0x87;
+
+ if (_dsDrawLayer < t) {
+ cmd = _shapePages[1][relOffs];
+ } else {
+ cmd = *dst;
+ for (int i = 0; i < _dsShapeFadingLevel; ++i)
+ cmd = _dsShapeFadingTable[cmd];
+ }
+
+ if (cmd)
+ *dst = cmd;
+}
+
+void Screen::drawShapePlotType12(uint8 *dst, uint8 cmd) {
+ uint32 relOffs = dst - _dsDstPage;
+ int t = (_shapePages[0][relOffs] & 0x7F) & 0x87;
+ if (_dsDrawLayer < t) {
+ cmd = _shapePages[1][relOffs];
+ } else {
+ cmd = _dsColorTable[cmd];
+ }
+
+ *dst = cmd;
+}
+
+void Screen::drawShapePlotType13(uint8 *dst, uint8 cmd) {
+ uint32 relOffs = dst - _dsDstPage;
+ int t = (_shapePages[0][relOffs] & 0x7F) & 0x87;
+ if (_dsDrawLayer < t) {
+ cmd = _shapePages[1][relOffs];
+ } else {
+ cmd = _dsColorTable[cmd];
+ for (int i = 0; i < _dsShapeFadingLevel; ++i)
+ cmd = _dsShapeFadingTable[cmd];
+ }
+
+ if (cmd)
+ *dst = cmd;
+}
+
+void Screen::drawShapePlotType14(uint8 *dst, uint8 cmd) {
+ uint32 relOffs = dst - _dsDstPage;
+ int t = (_shapePages[0][relOffs] & 0x7F) & 0x87;
+ if (_dsDrawLayer < t) {
+ cmd = _shapePages[1][relOffs];
+ } else {
+ t = _drawShapeVar4 + _drawShapeVar5;
+ if (t & 0xFF00) {
+ cmd = dst[_drawShapeVar3];
+ t &= 0xFF;
+ } else {
+ cmd = _dsColorTable[cmd];
+ }
+ }
+
+ _drawShapeVar4 = t;
+ *dst = cmd;
+}
+
+void Screen::drawShapePlotType16(uint8 *dst, uint8 cmd) {
+ uint8 tOffs = _dsTransparencyTable1[cmd];
+ if (!(tOffs & 0x80))
+ cmd = _dsTransparencyTable2[tOffs << 8 | *dst];
+ *dst = cmd;
+}
+
+void Screen::drawShapePlotType20(uint8 *dst, uint8 cmd) {
+ cmd = _dsColorTable[cmd];
+ uint8 tOffs = _dsTransparencyTable1[cmd];
+ if (!(tOffs & 0x80))
+ cmd = _dsTransparencyTable2[tOffs << 8 | *dst];
+
+ *dst = cmd;
+}
+
+void Screen::drawShapePlotType21(uint8 *dst, uint8 cmd) {
+ cmd = _dsColorTable[cmd];
+ uint8 tOffs = _dsTransparencyTable1[cmd];
+ if (!(tOffs & 0x80))
+ cmd = _dsTransparencyTable2[tOffs << 8 | *dst];
+
+ for (int i = 0; i < _dsShapeFadingLevel; ++i)
+ cmd = _dsShapeFadingTable[cmd];
+
+ if (cmd)
+ *dst = cmd;
+}
+
+void Screen::drawShapePlotType33(uint8 *dst, uint8 cmd) {
+ if (cmd == 255) {
+ *dst = _dsBackgroundFadingTable[*dst];
+ } else {
+ for (int i = 0; i < _dsShapeFadingLevel; ++i)
+ cmd = _dsShapeFadingTable[cmd];
+ if (cmd)
+ *dst = cmd;
+ }
+}
+
+void Screen::drawShapePlotType37(uint8 *dst, uint8 cmd) {
+ cmd = _dsColorTable[cmd];
+
+ if (cmd == 255) {
+ cmd = _dsBackgroundFadingTable[*dst];
+ } else {
+ for (int i = 0; i < _dsShapeFadingLevel; ++i)
+ cmd = _dsShapeFadingTable[cmd];
+ }
+
+ if (cmd)
+ *dst = cmd;
+}
+
+void Screen::drawShapePlotType48(uint8 *dst, uint8 cmd) {
+ uint8 offs = _dsTransparencyTable1[cmd];
+ if (!(offs & 0x80))
+ cmd = _dsTransparencyTable2[(offs << 8) | *dst];
+ *dst = cmd;
+}
+
+void Screen::drawShapePlotType52(uint8 *dst, uint8 cmd) {
+ cmd = _dsColorTable[cmd];
+ uint8 offs = _dsTransparencyTable1[cmd];
+
+ if (!(offs & 0x80))
+ cmd = _dsTransparencyTable2[(offs << 8) | *dst];
+
+ *dst = cmd;
+}
+
+void Screen::decodeFrame1(const uint8 *src, uint8 *dst, uint32 size) {
+ const uint8 *dstEnd = dst + size;
+
+ struct Pattern {
+ const uint8 *pos;
+ uint16 len;
+ };
+
+ Pattern *patterns = new Pattern[3840];
+ uint16 numPatterns = 0;
+ uint8 nib = 0;
+
+ uint16 code = decodeEGAGetCode(src, nib);
+ uint8 last = code & 0xFF;
+
+ uint8 *dstPrev = dst;
+ uint16 count = 1;
+ uint16 countPrev = 1;
+
+ *dst++ = last;
+
+ while (dst < dstEnd) {
+ code = decodeEGAGetCode(src, nib);
+ uint8 cmd = code >> 8;
+
+ if (cmd--) {
+ code = (cmd << 8) | (code & 0xFF);
+ uint8 *tmpDst = dst;
+
+ if (code < numPatterns) {
+ const uint8 *tmpSrc = patterns[code].pos;
+ countPrev = patterns[code].len;
+ last = *tmpSrc;
+ for (int i = 0; i < countPrev; i++)
+ *dst++ = *tmpSrc++;
+
+ } else {
+ const uint8 *tmpSrc = dstPrev;
+ count = countPrev;
+ for (int i = 0; i < countPrev; i++)
+ *dst++ = *tmpSrc++;
+ *dst++ = last;
+ countPrev++;
+ }
+
+ if (numPatterns < 3840) {
+ patterns[numPatterns].pos = dstPrev;
+ patterns[numPatterns++].len = ++count;
+ }
+
+ dstPrev = tmpDst;
+ count = countPrev;
+
+ } else {
+ *dst++ = last = (code & 0xFF);
+
+ if (numPatterns < 3840) {
+ patterns[numPatterns].pos = dstPrev;
+ patterns[numPatterns++].len = ++count;
+ }
+
+ dstPrev = dst - 1;
+ count = 1;
+ countPrev = 1;
+ }
+ }
+ delete[] patterns;
+}
+
+uint16 Screen::decodeEGAGetCode(const uint8 *&pos, uint8 &nib) {
+ uint16 res = READ_BE_UINT16(pos++);
+ if ((++nib) & 1) {
+ res >>= 4;
+ } else {
+ pos++;
+ res &= 0xFFF;
+ }
+ return res;
+}
+
+void Screen::decodeFrame3(const uint8 *src, uint8 *dst, uint32 size) {
+ const uint8 *dstEnd = dst + size;
+ while (dst < dstEnd) {
+ int8 code = *src++;
+ if (code == 0) {
+ uint16 sz = READ_BE_UINT16(src);
+ src += 2;
+ memset(dst, *src++, sz);
+ dst += sz;
+ } else if (code < 0) {
+ memset(dst, *src++, -code);
+ dst -= code;
+ } else {
+ memcpy(dst, src, code);
+ dst += code;
+ src += code;
+ }
+ }
+}
+
+uint Screen::decodeFrame4(const uint8 *src, uint8 *dst, uint32 dstSize) {
+ uint8 *dstOrig = dst;
+ uint8 *dstEnd = dst + dstSize;
+ while (1) {
+ int count = dstEnd - dst;
+ if (count == 0)
+ break;
+
+ uint8 code = *src++;
+ if (!(code & 0x80)) { // 8th bit isn't set
+ int len = MIN(count, (code >> 4) + 3); //upper half of code is the length
+ int offs = ((code & 0xF) << 8) | *src++; //lower half of code as byte 2 of offset.
+ const uint8 *dstOffs = dst - offs;
+ while (len--)
+ *dst++ = *dstOffs++;
+ } else if (code & 0x40) { // 7th bit is set
+ int len = (code & 0x3F) + 3;
+ if (code == 0xFE) {
+ len = READ_LE_UINT16(src); src += 2;
+ if (len > count)
+ len = count;
+
+ memset(dst, *src++, len); dst += len;
+ } else {
+ if (code == 0xFF) {
+ len = READ_LE_UINT16(src);
+ src += 2;
+ }
+
+ int offs = READ_LE_UINT16(src); src += 2;
+ if (len > count)
+ len = count;
+
+ const uint8 *dstOffs = dstOrig + offs;
+ while (len--)
+ *dst++ = *dstOffs++;
+ }
+ } else if (code != 0x80) { // not just the 8th bit set.
+ //Copy some bytes from source to dest.
+ int len = MIN(count, code & 0x3F);
+ while (len--)
+ *dst++ = *src++;
+ } else {
+ break;
+ }
+ }
+ return dst - dstOrig;
+}
+
+void Screen::decodeFrameDelta(uint8 *dst, const uint8 *src, bool noXor) {
+ if (noXor)
+ wrapped_decodeFrameDelta<true>(dst, src);
+ else
+ wrapped_decodeFrameDelta<false>(dst, src);
+}
+
+template<bool noXor>
+void Screen::wrapped_decodeFrameDelta(uint8 *dst, const uint8 *src) {
+ while (1) {
+ uint8 code = *src++;
+ if (code == 0) {
+ uint8 len = *src++;
+ code = *src++;
+ while (len--) {
+ if (noXor)
+ *dst++ = code;
+ else
+ *dst++ ^= code;
+ }
+ } else if (code & 0x80) {
+ code -= 0x80;
+ if (code != 0) {
+ dst += code;
+ } else {
+ uint16 subcode = READ_LE_UINT16(src); src += 2;
+ if (subcode == 0) {
+ break;
+ } else if (subcode & 0x8000) {
+ subcode -= 0x8000;
+ if (subcode & 0x4000) {
+ uint16 len = subcode - 0x4000;
+ code = *src++;
+ while (len--) {
+ if (noXor)
+ *dst++ = code;
+ else
+ *dst++ ^= code;
+ }
+ } else {
+ while (subcode--) {
+ if (noXor)
+ *dst++ = *src++;
+ else
+ *dst++ ^= *src++;
+ }
+ }
+ } else {
+ dst += subcode;
+ }
+ }
+ } else {
+ while (code--) {
+ if (noXor)
+ *dst++ = *src++;
+ else
+ *dst++ ^= *src++;
+ }
+ }
+ }
+}
+
+void Screen::decodeFrameDeltaPage(uint8 *dst, const uint8 *src, int pitch, bool noXor) {
+ if (noXor)
+ wrapped_decodeFrameDeltaPage<true>(dst, src, pitch);
+ else
+ wrapped_decodeFrameDeltaPage<false>(dst, src, pitch);
+}
+
+void Screen::convertAmigaGfx(uint8 *data, int w, int h, int depth, bool wsa, int bytesPerPlane) {
+ const int planeWidth = (bytesPerPlane == -1) ? (w + 7) / 8 : bytesPerPlane;
+ const int planeSize = planeWidth * h;
+ const uint imageSize = planeSize * depth;
+
+ // Our static buffer which holds the plane data. We need this
+ // because the "data" pointer is both source and destination pointer.
+ // The buffer has enough space to fit the AMIGA MSC files, which are
+ // the biggest graphics files found in the AMIGA version.
+ static uint8 temp[40320];
+ assert(imageSize <= sizeof(temp));
+
+ // WSA files store their graphics data in a little different format, than
+ // the usual AMIGA graphics format used in BitMaps. Thus we need to do
+ // some special handling for them here. Means we convert them into
+ // the usual format.
+ //
+ // TODO: We might think of moving this conversion into the WSAMovieAmiga
+ // class.
+ if (wsa) {
+ const byte *src = data;
+ for (int y = 0; y < h; ++y) {
+ for (int x = 0; x < planeWidth; ++x)
+ for (int i = 0; i < depth; ++i)
+ temp[y * planeWidth + x + planeSize * i] = *src++;
+ }
+ } else {
+ memcpy(temp, data, imageSize);
+ }
+
+ for (int y = 0; y < h; ++y) {
+ for (int x = 0; x < w; ++x) {
+ const int bytePos = x / 8 + y * planeWidth;
+ const int bitPos = 7 - (x & 7); // x & 7 == x % 8
+
+ byte col = 0;
+
+ for (int i = 0; i < depth; ++i)
+ col |= ((temp[bytePos + planeSize * i] >> bitPos) & 1) << i;
+
+ *data++ = col;
+ }
+ }
+}
+
+void Screen::convertAmigaMsc(uint8 *data) {
+ // MSC files are always 320x144, thus we can safely assume
+ // this to be correct. Also they contain 7 planes instead
+ // of the normal 5 planes, which is used in 32 color mode.
+ // The need for 7 planes can be explained, because the MSC
+ // files have 6 bits for the layer number (bits 1 to 6)
+ // and one bit for the "blocked" flag (bit 0), and every
+ // plane contains one bit per pixel.
+ convertAmigaGfx(data, 320, 144, 7);
+
+ // We need to do some post conversion, since
+ // the AMIGA MSC format is different from the DOS
+ // one we use internally for our code.That is even
+ // after converting it from the AMIGA plane based
+ // approach to one byte per pixel approach.
+ for (int i = 0; i < 320 * 144; ++i) {
+ // The lowest bit indicates, whether the position
+ // is walkable or not. If the bit is set, the
+ // position is walkable, elsewise it is blocked.
+ if (data[i] & 1)
+ data[i] &= 0xFE;
+ else
+ data[i] |= 0x80;
+
+ // The graphics layer for the pixel is saved
+ // in the following format:
+ // The highest bit set indicates the number of
+ // the graphics layer. We count the first
+ // bit as 0 here, thus we need to add one,
+ // to get the correct number.
+ //
+ // Funnily since the first bit (bit 0) is
+ // resevered for testing whether the position
+ // is walkable or not, there is no possibility
+ // for layer 1 to be present.
+ int layer = 0;
+ for (int k = 0; k < 7; ++k)
+ if (data[i] & (1 << k))
+ layer = k + 1;
+
+ data[i] &= 0x80;
+ data[i] |= layer;
+ }
+}
+
+template<bool noXor>
+void Screen::wrapped_decodeFrameDeltaPage(uint8 *dst, const uint8 *src, int pitch) {
+ int count = 0;
+ uint8 *dstNext = dst;
+ while (1) {
+ uint8 code = *src++;
+ if (code == 0) {
+ uint8 len = *src++;
+ code = *src++;
+ while (len--) {
+ if (noXor)
+ *dst++ = code;
+ else
+ *dst++ ^= code;
+
+ if (++count == pitch) {
+ count = 0;
+ dstNext += SCREEN_W;
+ dst = dstNext;
+ }
+ }
+ } else if (code & 0x80) {
+ code -= 0x80;
+ if (code != 0) {
+ dst += code;
+
+ count += code;
+ while (count >= pitch) {
+ count -= pitch;
+ dstNext += SCREEN_W;
+ dst = dstNext + count;
+ }
+ } else {
+ uint16 subcode = READ_LE_UINT16(src); src += 2;
+ if (subcode == 0) {
+ break;
+ } else if (subcode & 0x8000) {
+ subcode -= 0x8000;
+ if (subcode & 0x4000) {
+ uint16 len = subcode - 0x4000;
+ code = *src++;
+ while (len--) {
+ if (noXor)
+ *dst++ = code;
+ else
+ *dst++ ^= code;
+
+ if (++count == pitch) {
+ count = 0;
+ dstNext += SCREEN_W;
+ dst = dstNext;
+ }
+ }
+ } else {
+ while (subcode--) {
+ if (noXor)
+ *dst++ = *src++;
+ else
+ *dst++ ^= *src++;
+
+ if (++count == pitch) {
+ count = 0;
+ dstNext += SCREEN_W;
+ dst = dstNext;
+ }
+ }
+ }
+ } else {
+ dst += subcode;
+
+ count += subcode;
+ while (count >= pitch) {
+ count -= pitch;
+ dstNext += SCREEN_W;
+ dst = dstNext + count;
+ }
+
+ }
+ }
+ } else {
+ while (code--) {
+ if (noXor)
+ *dst++ = *src++;
+ else
+ *dst++ ^= *src++;
+
+ if (++count == pitch) {
+ count = 0;
+ dstNext += SCREEN_W;
+ dst = dstNext;
+ }
+ }
+ }
+ }
+}
+
+uint8 *Screen::encodeShape(int x, int y, int w, int h, int flags) {
+ uint8 *srcPtr = &_pagePtrs[_curPage][y * SCREEN_W + x];
+ int16 shapeSize = 0;
+ uint8 *tmp = srcPtr;
+ int xpos = w;
+
+ for (int i = h; i > 0; --i) {
+ uint8 *start = tmp;
+ shapeSize += w;
+ xpos = w;
+ while (xpos) {
+ uint8 value = *tmp++;
+ --xpos;
+
+ if (!value) {
+ shapeSize += 2;
+ int16 curX = xpos;
+ bool skip = false;
+
+ while (xpos) {
+ value = *tmp++;
+ --xpos;
+
+ if (value) {
+ skip = true;
+ break;
+ }
+ }
+
+ if (!skip)
+ ++curX;
+
+ curX -= xpos;
+ shapeSize -= curX;
+
+ while (curX > 0xFF) {
+ curX -= 0xFF;
+ shapeSize += 2;
+ }
+ }
+ }
+
+ tmp = start + SCREEN_W;
+ }
+
+ int16 shapeSize2 = shapeSize;
+ if (_vm->gameFlags().useAltShapeHeader)
+ shapeSize += 12;
+ else
+ shapeSize += 10;
+
+ if (flags & 1)
+ shapeSize += 16;
+
+ uint8 table[274];
+ int tableIndex = 0;
+
+ uint8 *newShape = 0;
+ newShape = new uint8[shapeSize+16];
+ assert(newShape);
+
+ byte *dst = newShape;
+
+ if (_vm->gameFlags().useAltShapeHeader)
+ dst += 2;
+
+ WRITE_LE_UINT16(dst, (flags & 3)); dst += 2;
+ *dst = h; dst += 1;
+ WRITE_LE_UINT16(dst, w); dst += 2;
+ *dst = h; dst += 1;
+ WRITE_LE_UINT16(dst, shapeSize); dst += 2;
+ WRITE_LE_UINT16(dst, shapeSize2); dst += 2;
+
+ byte *src = srcPtr;
+ if (flags & 1) {
+ dst += 16;
+ memset(table, 0, sizeof(table));
+ tableIndex = 1;
+ }
+
+ for (int ypos = h; ypos > 0; --ypos) {
+ uint8 *srcBackUp = src;
+ xpos = w;
+ while (xpos) {
+ uint8 value = *src++;
+ if (value) {
+ if (flags & 1) {
+ if (!table[value]) {
+ if (tableIndex == 16) {
+ value = 1;
+ } else {
+ table[0x100+tableIndex] = value;
+ table[value] = tableIndex;
+ ++tableIndex;
+ value = table[value];
+ }
+ } else {
+ value = table[value];
+ }
+ }
+ --xpos;
+ *dst++ = value;
+ } else {
+ int16 temp = 1;
+ --xpos;
+
+ while (xpos) {
+ if (*src)
+ break;
+ ++src;
+ ++temp;
+ --xpos;
+ }
+
+ while (temp > 0xFF) {
+ *dst++ = 0;
+ *dst++ = 0xFF;
+ temp -= 0xFF;
+ }
+
+ if (temp & 0xFF) {
+ *dst++ = 0;
+ *dst++ = temp & 0xFF;
+ }
+ }
+ }
+ src = srcBackUp + SCREEN_W;
+ }
+
+ if (!(flags & 2)) {
+ if (shapeSize > _animBlockSize) {
+ dst = newShape;
+ if (_vm->gameFlags().useAltShapeHeader)
+ dst += 2;
+
+ flags = READ_LE_UINT16(dst);
+ flags |= 2;
+ WRITE_LE_UINT16(dst, flags);
+ } else {
+ src = newShape;
+ if (_vm->gameFlags().useAltShapeHeader)
+ src += 2;
+ if (flags & 1)
+ src += 16;
+
+ src += 10;
+ uint8 *shapePtrBackUp = src;
+ dst = _animBlockPtr;
+ memcpy(dst, src, shapeSize2);
+
+ int16 size = encodeShapeAndCalculateSize(_animBlockPtr, shapePtrBackUp, shapeSize2);
+ if (size > shapeSize2) {
+ shapeSize -= shapeSize2 - size;
+ uint8 *newShape2 = new uint8[shapeSize];
+ assert(newShape2);
+ memcpy(newShape2, newShape, shapeSize);
+ delete[] newShape;
+ newShape = newShape2;
+ } else {
+ dst = shapePtrBackUp;
+ src = _animBlockPtr;
+ memcpy(dst, src, shapeSize2);
+ dst = newShape;
+ if (_vm->gameFlags().useAltShapeHeader)
+ dst += 2;
+ flags = READ_LE_UINT16(dst);
+ flags |= 2;
+ WRITE_LE_UINT16(dst, flags);
+ }
+ }
+ }
+
+ dst = newShape;
+ if (_vm->gameFlags().useAltShapeHeader)
+ dst += 2;
+ WRITE_LE_UINT16((dst + 6), shapeSize);
+
+ if (flags & 1) {
+ dst = newShape + 10;
+ if (_vm->gameFlags().useAltShapeHeader)
+ dst += 2;
+ src = &table[0x100];
+ memcpy(dst, src, sizeof(uint8)*16);
+ }
+
+ return newShape;
+}
+
+int16 Screen::encodeShapeAndCalculateSize(uint8 *from, uint8 *to, int size_to) {
+ byte *fromPtrEnd = from + size_to;
+ bool skipPixel = true;
+ byte *tempPtr = 0;
+ byte *toPtr = to;
+ byte *fromPtr = from;
+ byte *toPtr2 = to;
+
+ *to++ = 0x81;
+ *to++ = *from++;
+
+ while (from < fromPtrEnd) {
+ byte *curToPtr = to;
+ to = fromPtr;
+ int size = 1;
+
+ while (true) {
+ byte curPixel = *from;
+ if (curPixel == *(from+0x40)) {
+ byte *toBackUp = to;
+ to = from;
+
+ for (int i = 0; i < (fromPtrEnd - from); ++i) {
+ if (*to++ != curPixel)
+ break;
+ }
+ --to;
+ uint16 diffSize = (to - from);
+ if (diffSize >= 0x41) {
+ skipPixel = false;
+ from = to;
+ to = curToPtr;
+ *to++ = 0xFE;
+ WRITE_LE_UINT16(to, diffSize); to += 2;
+ *to++ = curPixel;
+ curToPtr = to;
+ to = toBackUp;
+ continue;
+ } else {
+ to = toBackUp;
+ }
+ }
+
+ bool breakLoop = false;
+ while (true) {
+ if ((from - to) == 0) {
+ breakLoop = true;
+ break;
+ }
+ for (int i = 0; i < (from - to); ++i) {
+ if (*to++ == curPixel)
+ break;
+ }
+ if (*(to-1) == curPixel) {
+ if (*(from+size-1) != *(to+size-2))
+ continue;
+
+ byte *fromBackUp = from;
+ byte *toBackUp = to;
+ --to;
+ const int checkSize = fromPtrEnd - from;
+ for (int i = 0; i < checkSize; ++i) {
+ if (*from++ != *to++)
+ break;
+ }
+ if (*(from - 1) == *(to - 1))
+ ++to;
+ from = fromBackUp;
+ int temp = to - toBackUp;
+ to = toBackUp;
+ if (temp >= size) {
+ size = temp;
+ tempPtr = toBackUp - 1;
+ }
+ break;
+ } else {
+ breakLoop = true;
+ break;
+ }
+ }
+
+ if (breakLoop)
+ break;
+ }
+
+ to = curToPtr;
+ if (size > 2) {
+ uint16 word = 0;
+ if (size <= 0x0A) {
+ uint16 diffSize = from - tempPtr;
+ if (diffSize <= 0x0FFF) {
+ byte highByte = ((diffSize & 0xFF00) >> 8) + (((size & 0xFF) - 3) << 4);
+ word = ((diffSize & 0xFF) << 8) | highByte;
+ WRITE_LE_UINT16(to, word); to += 2;
+ from += size;
+ skipPixel = false;
+ continue;
+ }
+ }
+
+ if (size > 0x40) {
+ *to++ = 0xFF;
+ WRITE_LE_UINT16(to, size); to += 2;
+ } else {
+ *to++ = ((size & 0xFF) - 3) | 0xC0;
+ }
+
+ word = tempPtr - fromPtr;
+ WRITE_LE_UINT16(to, word); to += 2;
+ from += size;
+ skipPixel = false;
+ } else {
+ if (!skipPixel) {
+ toPtr2 = to;
+ *to++ = 0x80;
+ }
+
+ if (*toPtr2 == 0xBF) {
+ toPtr2 = to;
+ *to++ = 0x80;
+ }
+
+ ++(*toPtr2);
+ *to++ = *from++;
+ skipPixel = true;
+ }
+ }
+ *to++ = 0x80;
+
+ return (to - toPtr);
+}
+
+uint16 Screen::shade16bitColor(uint16 col) {
+ uint8 r = (col & 0x1f);
+ uint8 g = (col & 0x3E0) >> 5;
+ uint8 b = (col & 0x7C00) >> 10;
+
+ r = (r > _16bitShadingLevel) ? r - _16bitShadingLevel : 0;
+ g = (g > _16bitShadingLevel) ? g - _16bitShadingLevel : 0;
+ b = (b > _16bitShadingLevel) ? b - _16bitShadingLevel : 0;
+
+ return (b << 10) | (g << 5) | r;
+}
+
+void Screen::hideMouse() {
+ ++_mouseLockCount;
+ CursorMan.showMouse(false);
+}
+
+void Screen::showMouse() {
+ if (_mouseLockCount == 1) {
+ CursorMan.showMouse(true);
+
+ // We need to call OSystem::updateScreen here, else the mouse cursor
+ // will only be visible on mouse movment.
+ _system->updateScreen();
+ }
+
+ if (_mouseLockCount > 0)
+ _mouseLockCount--;
+}
+
+
+bool Screen::isMouseVisible() const {
+ return _mouseLockCount == 0;
+}
+
+void Screen::setShapePages(int page1, int page2, int minY, int maxY) {
+ _shapePages[0] = _pagePtrs[page1];
+ _shapePages[1] = _pagePtrs[page2];
+ _maskMinY = minY;
+ _maskMaxY = maxY;
+}
+
+void Screen::setMouseCursor(int x, int y, const byte *shape) {
+ if (!shape)
+ return;
+
+ if (_vm->gameFlags().useAltShapeHeader)
+ shape += 2;
+
+ int mouseHeight = *(shape + 2);
+ int mouseWidth = (READ_LE_UINT16(shape + 3)) + 2;
+
+ if (_vm->gameFlags().useAltShapeHeader)
+ shape -= 2;
+
+ if (_vm->gameFlags().useHiRes) {
+ x <<= 1;
+ y <<= 1;
+ mouseWidth <<= 1;
+ mouseHeight <<= 1;
+ }
+
+ uint8 *cursor = new uint8[mouseHeight * mouseWidth];
+ fillRect(0, 0, mouseWidth, mouseHeight, _cursorColorKey, 8);
+ drawShape(8, shape, 0, 0, 0, 0);
+
+ int xOffset = 0;
+
+ if (_vm->gameFlags().useHiRes) {
+ xOffset = mouseWidth;
+ scale2x(getPagePtr(8) + mouseWidth, SCREEN_W, getPagePtr(8), SCREEN_W, mouseWidth, mouseHeight);
+ postProcessCursor(getPagePtr(8) + mouseWidth, mouseWidth, mouseHeight, SCREEN_W);
+ } else {
+ postProcessCursor(getPagePtr(8), mouseWidth, mouseHeight, SCREEN_W);
+ }
+
+ CursorMan.showMouse(false);
+ copyRegionToBuffer(8, xOffset, 0, mouseWidth, mouseHeight, cursor);
+ CursorMan.replaceCursor(cursor, mouseWidth, mouseHeight, x, y, _cursorColorKey);
+ if (isMouseVisible())
+ CursorMan.showMouse(true);
+ delete[] cursor;
+
+ // makes sure that the cursor is drawn
+ // we do not use Screen::updateScreen here
+ // so we can be sure that changes to page 0
+ // are NOT updated on the real screen here
+ _system->updateScreen();
+}
+
+Palette &Screen::getPalette(int num) {
+ assert(num >= 0 && (uint)num < _palettes.size());
+ return *_palettes[num];
+}
+
+void Screen::copyPalette(const int dst, const int src) {
+ getPalette(dst).copy(getPalette(src));
+}
+
+byte Screen::getShapeFlag1(int x, int y) {
+ uint8 color = _shapePages[0][y * SCREEN_W + x];
+ color &= 0x80;
+ color ^= 0x80;
+
+ if (color & 0x80)
+ return 1;
+ return 0;
+}
+
+byte Screen::getShapeFlag2(int x, int y) {
+ uint8 color = _shapePages[0][y * SCREEN_W + x];
+ color &= 0x7F;
+ color &= 0x87;
+ return color;
+}
+
+int Screen::getDrawLayer(int x, int y) {
+ int xpos = x - 8;
+ int ypos = y - 1;
+ int layer = 1;
+
+ for (int curX = xpos; curX < xpos + 16; ++curX) {
+ int tempLayer = getShapeFlag2(curX, ypos);
+
+ if (layer < tempLayer)
+ layer = tempLayer;
+
+ if (layer >= 7)
+ return 7;
+ }
+ return layer;
+}
+
+int Screen::getDrawLayer2(int x, int y, int height) {
+ int xpos = x - 8;
+ int ypos = y - 1;
+ int layer = 1;
+
+ for (int useX = xpos; useX < xpos + 16; ++useX) {
+ for (int useY = ypos - height; useY < ypos; ++useY) {
+ int tempLayer = getShapeFlag2(useX, useY);
+
+ if (tempLayer > layer)
+ layer = tempLayer;
+
+ if (tempLayer >= 7)
+ return 7;
+ }
+ }
+ return layer;
+}
+
+
+int Screen::setNewShapeHeight(uint8 *shape, int height) {
+ if (_vm->gameFlags().useAltShapeHeader)
+ shape += 2;
+
+ int oldHeight = shape[2];
+ shape[2] = height;
+ return oldHeight;
+}
+
+int Screen::resetShapeHeight(uint8 *shape) {
+ if (_vm->gameFlags().useAltShapeHeader)
+ shape += 2;
+
+ int oldHeight = shape[2];
+ shape[2] = shape[5];
+ return oldHeight;
+}
+
+void Screen::blockInRegion(int x, int y, int width, int height) {
+ assert(_shapePages[0]);
+ byte *toPtr = _shapePages[0] + (y * 320 + x);
+ for (int i = 0; i < height; ++i) {
+ byte *backUpTo = toPtr;
+ for (int i2 = 0; i2 < width; ++i2)
+ *toPtr++ &= 0x7F;
+ toPtr = (backUpTo + 320);
+ }
+}
+
+void Screen::blockOutRegion(int x, int y, int width, int height) {
+ assert(_shapePages[0]);
+ byte *toPtr = _shapePages[0] + (y * 320 + x);
+ for (int i = 0; i < height; ++i) {
+ byte *backUpTo = toPtr;
+ for (int i2 = 0; i2 < width; ++i2)
+ *toPtr++ |= 0x80;
+ toPtr = (backUpTo + 320);
+ }
+}
+
+void Screen::rectClip(int &x, int &y, int w, int h) {
+ if (x < 0)
+ x = 0;
+ else if (x + w >= 320)
+ x = 320 - w;
+
+ if (y < 0)
+ y = 0;
+ else if (y + h >= 200)
+ y = 200 - h;
+}
+
+void Screen::shakeScreen(int times) {
+ while (times--) {
+ // seems to be 1 line (320 pixels) offset in the original
+ // 4 looks more like dosbox though, maybe check this again
+ _system->setShakePos(4);
+ _system->updateScreen();
+ _system->setShakePos(0);
+ _system->updateScreen();
+ }
+}
+
+void Screen::loadBitmap(const char *filename, int tempPage, int dstPage, Palette *pal, bool skip) {
+ uint32 fileSize;
+ uint8 *srcData = _vm->resource()->fileData(filename, &fileSize);
+
+ if (!srcData) {
+ warning("couldn't load bitmap: '%s'", filename);
+ return;
+ }
+
+ if (skip)
+ srcData += 4;
+
+ const char *ext = filename + strlen(filename) - 3;
+ uint8 compType = srcData[2];
+ uint32 imgSize = (_vm->game() == GI_KYRA2 && !scumm_stricmp(ext, "CMP")) ? READ_LE_UINT16(srcData) : READ_LE_UINT32(srcData + 4);
+ uint16 palSize = READ_LE_UINT16(srcData + 8);
+
+ if (pal && palSize)
+ loadPalette(srcData + 10, *pal, palSize);
+
+ uint8 *srcPtr = srcData + 10 + palSize;
+ uint8 *dstData = getPagePtr(dstPage);
+ memset(dstData, 0, _screenPageSize);
+ if (dstPage == 0 || tempPage == 0)
+ _forceFullUpdate = true;
+
+ switch (compType) {
+ case 0:
+ memcpy(dstData, srcPtr, imgSize);
+ break;
+ case 1:
+ Screen::decodeFrame1(srcPtr, dstData, imgSize);
+ break;
+ case 3:
+ Screen::decodeFrame3(srcPtr, dstData, imgSize);
+ break;
+ case 4:
+ Screen::decodeFrame4(srcPtr, dstData, imgSize);
+ break;
+ default:
+ error("Unhandled bitmap compression %d", compType);
+ }
+
+ if (_isAmiga) {
+ if (!scumm_stricmp(ext, "MSC"))
+ Screen::convertAmigaMsc(dstData);
+ else
+ Screen::convertAmigaGfx(dstData, 320, 200);
+ }
+
+ if (skip)
+ srcData -= 4;
+
+ delete[] srcData;
+}
+
+bool Screen::loadPalette(const char *filename, Palette &pal) {
+ if (_renderMode == Common::kRenderCGA)
+ return true;
+
+ Common::SeekableReadStream *stream = _vm->resource()->createReadStream(filename);
+
+ if (!stream)
+ return false;
+
+ debugC(3, kDebugLevelScreen, "Screen::loadPalette('%s', %p)", filename, (const void *)&pal);
+
+ const int maxCols = _16bitPalette ? 256 : pal.getNumColors();
+ int numCols = 0;
+
+ if (_isAmiga) {
+ numCols = stream->size() / Palette::kAmigaBytesPerColor;
+ pal.loadAmigaPalette(*stream, 0, MIN(maxCols, numCols));
+ } else if (_vm->gameFlags().platform == Common::kPlatformPC98 && _use16ColorMode) {
+ numCols = stream->size() / Palette::kPC98BytesPerColor;
+ pal.loadPC98Palette(*stream, 0, MIN(maxCols, numCols));
+ } else if (_renderMode == Common::kRenderEGA) {
+ numCols = stream->size();
+ // There aren't any 16 color EGA palette files. So this shouldn't ever get triggered.
+ assert (numCols != 16);
+ numCols /= Palette::kVGABytesPerColor;
+ pal.loadVGAPalette(*stream, 0, numCols);
+ } else {
+ if (_bytesPerPixel == 2) {
+ numCols = stream->size() / 2;
+ pal.loadHiColorPalette(*stream, 0, numCols);
+ } else if (!_16bitPalette) {
+ numCols = stream->size() / Palette::kVGABytesPerColor;
+ pal.loadVGAPalette(*stream, 0, MIN(maxCols, numCols));
+ } else {
+ error("Screen::loadPalette(): Failed to load file '%s' with invalid size %d in HiColor mode", filename, stream->size());
+ }
+ }
+
+ if (numCols > maxCols)
+ warning("Palette file '%s' includes %d colors, but the target palette only support %d colors", filename, numCols, maxCols);
+
+ delete stream;
+ return true;
+}
+
+bool Screen::loadPaletteTable(const char *filename, int firstPalette) {
+ Common::SeekableReadStream *stream = _vm->resource()->createReadStream(filename);
+
+ if (!stream)
+ return false;
+
+ debugC(3, kDebugLevelScreen, "Screen::loadPaletteTable('%s', %d)", filename, firstPalette);
+
+ if (_isAmiga) {
+ const int numColors = getPalette(firstPalette).getNumColors();
+ const int palSize = getPalette(firstPalette).getNumColors() * Palette::kAmigaBytesPerColor;
+ const int numPals = stream->size() / palSize;
+
+ for (int i = 0; i < numPals; ++i)
+ getPalette(i + firstPalette).loadAmigaPalette(*stream, 0, numColors);
+ } else {
+ const int numColors = getPalette(firstPalette).getNumColors();
+ const int palSize = getPalette(firstPalette).getNumColors() * Palette::kVGABytesPerColor;
+ const int numPals = stream->size() / palSize;
+
+ for (int i = 0; i < numPals; ++i)
+ getPalette(i + firstPalette).loadVGAPalette(*stream, 0, numColors);
+ }
+
+ delete stream;
+ return true;
+}
+
+void Screen::loadPalette(const byte *data, Palette &pal, int bytes) {
+ Common::MemoryReadStream stream(data, bytes, DisposeAfterUse::NO);
+
+ if (_isAmiga)
+ pal.loadAmigaPalette(stream, 0, stream.size() / Palette::kAmigaBytesPerColor);
+ else if (_vm->gameFlags().platform == Common::kPlatformPC98 && _use16ColorMode)
+ pal.loadPC98Palette(stream, 0, stream.size() / Palette::kPC98BytesPerColor);
+ else if (_renderMode == Common::kRenderEGA) {
+ // EOB II checks the number of palette bytes to distinguish between real EGA palettes
+ // and normal palettes (which are used to generate a color map).
+ if (stream.size() == 16)
+ pal.loadEGAPalette(stream, 0, stream.size());
+ else
+ pal.loadVGAPalette(stream, 0, stream.size() / Palette::kVGABytesPerColor);
+ } else
+ pal.loadVGAPalette(stream, 0, stream.size() / Palette::kVGABytesPerColor);
+}
+
+// dirty rect handling
+
+void Screen::addDirtyRect(int x, int y, int w, int h) {
+ if (_dirtyRects.size() >= kMaxDirtyRects || _forceFullUpdate) {
+ _forceFullUpdate = true;
+ return;
+ }
+
+ Common::Rect r(x, y, x + w, y + h);
+
+ // Clip rectangle
+ r.clip(SCREEN_W, SCREEN_H);
+
+ // If it is empty after clipping, we are done
+ if (r.isEmpty())
+ return;
+
+ // Check if the new rectangle is contained within another in the list
+ Common::List<Common::Rect>::iterator it;
+ for (it = _dirtyRects.begin(); it != _dirtyRects.end(); ) {
+ // If we find a rectangle which fully contains the new one,
+ // we can abort the search.
+ if (it->contains(r))
+ return;
+
+ // Conversely, if we find rectangles which are contained in
+ // the new one, we can remove them
+ if (r.contains(*it))
+ it = _dirtyRects.erase(it);
+ else
+ ++it;
+ }
+
+ // If we got here, we can safely add r to the list of dirty rects.
+ _dirtyRects.push_back(r);
+}
+
+// overlay functions
+
+byte *Screen::getOverlayPtr(int page) {
+ if (page == 0 || page == 1)
+ return _sjisOverlayPtrs[1];
+ else if (page == 2 || page == 3)
+ return _sjisOverlayPtrs[2];
+
+ if (_vm->game() == GI_KYRA2) {
+ if (page == 12 || page == 13)
+ return _sjisOverlayPtrs[3];
+ } else if (_vm->game() == GI_LOL) {
+ if (page == 4 || page == 5)
+ return _sjisOverlayPtrs[3];
+ if (page == 6 || page == 7)
+ return _sjisOverlayPtrs[4];
+ if (page == 12 || page == 13)
+ return _sjisOverlayPtrs[5];
+ }
+
+ return 0;
+}
+
+void Screen::clearOverlayPage(int page) {
+ byte *dst = getOverlayPtr(page);
+ if (!dst)
+ return;
+ memset(dst, _sjisInvisibleColor, SCREEN_OVL_SJIS_SIZE);
+}
+
+void Screen::clearOverlayRect(int page, int x, int y, int w, int h) {
+ byte *dst = getOverlayPtr(page);
+
+ if (!dst || w < 0 || h < 0)
+ return;
+
+ x <<= 1; y <<= 1;
+ w <<= 1; h <<= 1;
+
+ dst += y * 640 + x;
+
+ if (w == 640 && h == 400) {
+ memset(dst, _sjisInvisibleColor, SCREEN_OVL_SJIS_SIZE);
+ } else {
+ while (h--) {
+ memset(dst, _sjisInvisibleColor, w);
+ dst += 640;
+ }
+ }
+}
+
+void Screen::copyOverlayRegion(int x, int y, int x2, int y2, int w, int h, int srcPage, int dstPage) {
+ byte *dst = getOverlayPtr(dstPage);
+ const byte *src = getOverlayPtr(srcPage);
+
+ if (!dst || !src)
+ return;
+
+ x <<= 1; x2 <<= 1;
+ y <<= 1; y2 <<= 1;
+ w <<= 1; h <<= 1;
+
+ if (w == 640 && h == 400) {
+ memcpy(dst, src, SCREEN_OVL_SJIS_SIZE);
+ } else {
+ dst += y2 * 640 + x2;
+ src += y * 640 + x;
+
+ while (h--) {
+ for (x = 0; x < w; ++x)
+ memmove(dst, src, w);
+ dst += 640;
+ src += 640;
+ }
+ }
+}
+
+void Screen::crossFadeRegion(int x1, int y1, int x2, int y2, int w, int h, int srcPage, int dstPage) {
+ if (srcPage > 13 || dstPage > 13)
+ error("Screen::crossFadeRegion(): attempting to use temp page as source or dest page.");
+
+ hideMouse();
+
+ uint16 *wB = (uint16 *)_pagePtrs[14];
+ uint8 *hB = _pagePtrs[14] + 640 * _bytesPerPixel;
+
+ for (int i = 0; i < w; i++)
+ wB[i] = i;
+
+ for (int i = 0; i < h; i++)
+ hB[i] = i;
+
+ for (int i = 0; i < w; i++)
+ SWAP(wB[_vm->_rnd.getRandomNumberRng(0, w - 1)], wB[i]);
+
+ for (int i = 0; i < h; i++)
+ SWAP(hB[_vm->_rnd.getRandomNumberRng(0, h - 1)], hB[i]);
+
+ uint8 *s = _pagePtrs[srcPage];
+ uint8 *d = _pagePtrs[dstPage];
+
+ for (int i = 0; i < h; i++) {
+ int iH = i;
+ uint32 end = _system->getMillis() + 3;
+ for (int ii = 0; ii < w; ii++) {
+ int sX = (x1 + wB[ii]);
+ int sY = (y1 + hB[iH]);
+ int dX = (x2 + wB[ii]);
+ int dY = (y2 + hB[iH]);
+
+ if (++iH >= h)
+ iH = 0;
+
+ if (_bytesPerPixel == 2)
+ ((uint16*)d)[dY * 320 + dX] = ((uint16*)s)[sY * 320 + sX];
+ else
+ d[dY * 320 + dX] = s[sY * 320 + sX];
+ addDirtyRect(dX, dY, 1, 1);
+ }
+
+ // This tries to speed things up, to get similiar speeds as in DOSBox etc.
+ // We can't write single pixels directly into the video memory like the original did.
+ // We also (unlike the original) want to aim at similiar speeds for all platforms.
+ if (!(i % 10))
+ updateScreen();
+
+ uint32 cur = _system->getMillis();
+ if (end > cur)
+ _system->delayMillis(end - cur);
+ }
+
+ updateScreen();
+ showMouse();
+}
+
+#pragma mark -
+
+DOSFont::DOSFont() {
+ _data = _widthTable = _heightTable = 0;
+ _colorMap = 0;
+ _width = _height = _numGlyphs = 0;
+ _bitmapOffsets = 0;
+}
+
+bool DOSFont::load(Common::SeekableReadStream &file) {
+ unload();
+
+ _data = new uint8[file.size()];
+ assert(_data);
+
+ file.read(_data, file.size());
+ if (file.err())
+ return false;
+
+ const uint16 fontSig = READ_LE_UINT16(_data + 2);
+
+ if (fontSig != 0x0500) {
+ warning("DOSFont: invalid font: %.04X)", fontSig);
+ return false;
+ }
+
+ const uint16 descOffset = READ_LE_UINT16(_data + 4);
+
+ _width = _data[descOffset + 5];
+ _height = _data[descOffset + 4];
+ _numGlyphs = _data[descOffset + 3] + 1;
+
+ _bitmapOffsets = (uint16 *)(_data + READ_LE_UINT16(_data + 6));
+ _widthTable = _data + READ_LE_UINT16(_data + 8);
+ _heightTable = _data + READ_LE_UINT16(_data + 12);
+
+ for (int i = 0; i < _numGlyphs; ++i)
+ _bitmapOffsets[i] = READ_LE_UINT16(&_bitmapOffsets[i]);
+
+ return true;
+}
+
+int DOSFont::getCharWidth(uint16 c) const {
+ if (c >= _numGlyphs)
+ return 0;
+ return _widthTable[c];
+}
+
+void DOSFont::drawChar(uint16 c, byte *dst, int pitch, int) const {
+ if (c >= _numGlyphs)
+ return;
+
+ if (!_bitmapOffsets[c])
+ return;
+
+ const uint8 *src = _data + _bitmapOffsets[c];
+ const uint8 charWidth = _widthTable[c];
+
+ if (!charWidth)
+ return;
+
+ pitch -= charWidth;
+
+ uint8 charH1 = _heightTable[c * 2 + 0];
+ uint8 charH2 = _heightTable[c * 2 + 1];
+ uint8 charH0 = _height - (charH1 + charH2);
+
+ while (charH1--) {
+ uint8 col = _colorMap[0];
+ for (int i = 0; i < charWidth; ++i) {
+ if (col != 0)
+ *dst = col;
+ ++dst;
+ }
+ dst += pitch;
+ }
+
+ while (charH2--) {
+ uint8 b = 0;
+ for (int i = 0; i < charWidth; ++i) {
+ uint8 col;
+ if (i & 1) {
+ col = _colorMap[b >> 4];
+ } else {
+ b = *src++;
+ col = _colorMap[b & 0xF];
+ }
+ if (col != 0) {
+ *dst = col;
+ }
+ ++dst;
+ }
+ dst += pitch;
+ }
+
+ while (charH0--) {
+ uint8 col = _colorMap[0];
+ for (int i = 0; i < charWidth; ++i) {
+ if (col != 0)
+ *dst = col;
+ ++dst;
+ }
+ dst += pitch;
+ }
+}
+
+void DOSFont::unload() {
+ delete[] _data;
+ _data = _widthTable = _heightTable = 0;
+ _colorMap = 0;
+ _width = _height = _numGlyphs = 0;
+ _bitmapOffsets = 0;
+}
+
+
+AMIGAFont::AMIGAFont() {
+ _width = _height = 0;
+ memset(_chars, 0, sizeof(_chars));
+}
+
+bool AMIGAFont::load(Common::SeekableReadStream &file) {
+ const uint16 dataSize = file.readUint16BE();
+ if (dataSize + 2 != file.size())
+ return false;
+
+ _width = file.readByte();
+ _height = file.readByte();
+
+ // Read the character definition offset table
+ uint16 offsets[ARRAYSIZE(_chars)];
+ for (int i = 0; i < ARRAYSIZE(_chars); ++i)
+ offsets[i] = file.readUint16BE() + 4;
+
+ if (file.err())
+ return false;
+
+ for (int i = 0; i < ARRAYSIZE(_chars); ++i) {
+ file.seek(offsets[i], SEEK_SET);
+
+ _chars[i].yOffset = file.readByte();
+ _chars[i].xOffset = file.readByte();
+ _chars[i].width = file.readByte();
+ file.readByte(); // unused
+
+ // If the y offset is 255, then the character
+ // does not have any bitmap representation
+ if (_chars[i].yOffset != 255) {
+ Character::Graphics &g = _chars[i].graphics;
+
+ g.width = file.readUint16BE();
+ g.height = file.readUint16BE();
+
+ int depth = file.readByte();
+ int specialWidth = file.readByte();
+ int flags = file.readByte();
+ int bytesPerPlane = file.readByte();
+
+ assert(depth != 0 && specialWidth == 0 && flags == 0 && bytesPerPlane != 0);
+
+ // Allocate a temporary buffer to store the plane data
+ const int planesSize = bytesPerPlane * g.height * depth;
+ uint8 *tempData = new uint8[MAX(g.width * g.height, planesSize)];
+ assert(tempData);
+
+ file.read(tempData, planesSize);
+
+ // Convert the plane based graphics to our graphic format
+ Screen::convertAmigaGfx(tempData, g.width, g.height, depth, false, bytesPerPlane);
+
+ // Create a buffer perfectly fitting the character
+ g.bitmap = new uint8[g.width * g.height];
+ assert(g.bitmap);
+
+ memcpy(g.bitmap, tempData, g.width * g.height);
+ delete[] tempData;
+ }
+
+ if (file.err())
+ return false;
+ }
+
+ return !file.err();
+}
+
+int AMIGAFont::getCharWidth(uint16 c) const {
+ if (c >= 255)
+ return 0;
+ return _chars[c].width;
+}
+
+void AMIGAFont::drawChar(uint16 c, byte *dst, int pitch, int) const {
+ if (c >= 255)
+ return;
+
+ if (_chars[c].yOffset == 255)
+ return;
+
+ dst += _chars[c].yOffset * pitch;
+ dst += _chars[c].xOffset;
+
+ pitch -= _chars[c].graphics.width;
+
+ const uint8 *src = _chars[c].graphics.bitmap;
+ assert(src);
+
+ for (int y = 0; y < _chars[c].graphics.height; ++y) {
+ for (int x = 0; x < _chars[c].graphics.width; ++x) {
+ if (*src)
+ *dst = *src;
+ ++src;
+ ++dst;
+ }
+
+ dst += pitch;
+ }
+}
+
+void AMIGAFont::unload() {
+ _width = _height = 0;
+ for (int i = 0; i < ARRAYSIZE(_chars); ++i)
+ delete[] _chars[i].graphics.bitmap;
+ memset(_chars, 0, sizeof(_chars));
+}
+
+SJISFont::SJISFont(Graphics::FontSJIS *font, const uint8 invisColor, bool is16Color, bool drawOutline, bool fatPrint, int extraSpacing)
+ : _colorMap(0), _font(font), _invisColor(invisColor), _is16Color(is16Color), _drawOutline(drawOutline), _sjisWidthOffset(extraSpacing) {
+ assert(_font);
+ _font->setDrawingMode(_drawOutline ? Graphics::FontSJIS::kOutlineMode : Graphics::FontSJIS::kDefaultMode);
+ _font->toggleFatPrint(fatPrint);
+ _sjisWidth = _font->getMaxFontWidth() >> 1;
+ _fontHeight = _font->getFontHeight() >> 1;
+ _asciiWidth = _font->getCharWidth('a') >> 1;
+}
+
+void SJISFont::unload() {
+ delete _font;
+ _font = 0;
+}
+
+int SJISFont::getHeight() const {
+ return _fontHeight;
+}
+
+int SJISFont::getWidth() const {
+ return _sjisWidth + _sjisWidthOffset;
+}
+
+int SJISFont::getCharWidth(uint16 c) const {
+ if (c <= 0x7F || (c >= 0xA1 && c <= 0xDF))
+ return _asciiWidth;
+ else
+ return _sjisWidth + _sjisWidthOffset;
+}
+
+void SJISFont::setColorMap(const uint8 *src) {
+ _colorMap = src;
+
+ if (!_is16Color) {
+ if (_colorMap[0] == _invisColor)
+ _font->setDrawingMode(Graphics::FontSJIS::kDefaultMode);
+ else
+ _font->setDrawingMode(_drawOutline ? Graphics::FontSJIS::kOutlineMode : Graphics::FontSJIS::kDefaultMode);
+ }
+}
+
+void SJISFont::drawChar(uint16 c, byte *dst, int pitch, int) const {
+ uint8 color1, color2;
+
+ if (_is16Color) {
+ // PC98 16 color games specify a color value which is for the
+ // PC98 text mode palette, thus we need to remap it.
+ color1 = ((_colorMap[1] >> 5) & 0x7) + 16;
+ color2 = ((_colorMap[0] >> 5) & 0x7) + 16;
+ } else {
+ color1 = _colorMap[1];
+ color2 = _colorMap[0];
+ }
+
+ _font->drawChar(dst, c, 640, 1, color1, color2, 640, 400);
+}
+
+#pragma mark -
+
+Palette::Palette(const int numColors) : _palData(0), _numColors(numColors) {
+ _palData = new uint8[numColors * 3];
+ assert(_palData);
+
+ memset(_palData, 0, numColors * 3);
+}
+
+Palette::~Palette() {
+ delete[] _palData;
+ _palData = 0;
+}
+
+void Palette::loadVGAPalette(Common::ReadStream &stream, int startIndex, int colors) {
+ assert(startIndex + colors <= _numColors);
+
+ uint8 *pos = _palData + startIndex * 3;
+ for (int i = 0 ; i < colors * 3; i++)
+ *pos++ = stream.readByte() & 0x3F;
+}
+
+void Palette::loadHiColorPalette(Common::ReadStream &stream, int startIndex, int colors) {
+ uint16 *pos = (uint16*)(_palData + startIndex * 2);
+
+ Graphics::PixelFormat currentFormat = g_system->getScreenFormat();
+ Graphics::PixelFormat originalFormat(2, 5, 5, 5, 0, 5, 10, 0, 0);
+
+ for (int i = 0; i < colors; i++) {
+ uint8 r, g, b;
+ originalFormat.colorToRGB(stream.readUint16LE(), r, g, b);
+ *pos++ = currentFormat.RGBToColor(r, g, b);
+ }
+}
+
+void Palette::loadEGAPalette(Common::ReadStream &stream, int startIndex, int colors) {
+ assert(startIndex + colors <= 16);
+
+ uint8 *dst = _palData + startIndex * 3;
+ for (int i = 0; i < colors; i++) {
+ uint8 index = stream.readByte();
+ assert(index < _egaNumColors);
+ memcpy(dst, &_egaColors[index * 3], 3);
+ dst += 3;
+ }
+}
+
+void Palette::setCGAPalette(int palIndex, CGAIntensity intensity) {
+ assert(_numColors >= _cgaNumColors);
+ assert(!(palIndex & ~1));
+ memcpy(_palData, _cgaColors[palIndex * 2 + intensity], _numColors * 3);
+}
+
+void Palette::loadAmigaPalette(Common::ReadStream &stream, int startIndex, int colors) {
+ assert(startIndex + colors <= _numColors);
+
+ for (int i = 0; i < colors; ++i) {
+ uint16 col = stream.readUint16BE();
+ _palData[(i + startIndex) * 3 + 2] = ((col & 0xF) * 0x3F) / 0xF; col >>= 4;
+ _palData[(i + startIndex) * 3 + 1] = ((col & 0xF) * 0x3F) / 0xF; col >>= 4;
+ _palData[(i + startIndex) * 3 + 0] = ((col & 0xF) * 0x3F) / 0xF; col >>= 4;
+ }
+}
+
+void Palette::loadPC98Palette(Common::ReadStream &stream, int startIndex, int colors) {
+ assert(startIndex + colors <= _numColors);
+
+ for (int i = 0; i < colors; ++i) {
+ const byte g = stream.readByte(), r = stream.readByte(), b = stream.readByte();
+
+ _palData[(i + startIndex) * 3 + 0] = ((r & 0xF) * 0x3F) / 0xF;
+ _palData[(i + startIndex) * 3 + 1] = ((g & 0xF) * 0x3F) / 0xF;
+ _palData[(i + startIndex) * 3 + 2] = ((b & 0xF) * 0x3F) / 0xF;
+ }
+}
+
+void Palette::clear() {
+ memset(_palData, 0, _numColors * 3);
+}
+
+void Palette::fill(int firstCol, int numCols, uint8 value) {
+ assert(firstCol >= 0 && firstCol + numCols <= _numColors);
+
+ memset(_palData + firstCol * 3, CLIP<int>(value, 0, 63), numCols * 3);
+}
+
+void Palette::copy(const Palette &source, int firstCol, int numCols, int dstStart) {
+ if (numCols == -1)
+ numCols = MIN(source.getNumColors(), _numColors) - firstCol;
+ if (dstStart == -1)
+ dstStart = firstCol;
+
+ assert(numCols >= 0 && numCols <= _numColors);
+ assert(firstCol >= 0 && firstCol <= source.getNumColors());
+ assert(dstStart >= 0 && dstStart + numCols <= _numColors);
+
+ memmove(_palData + dstStart * 3, source._palData + firstCol * 3, numCols * 3);
+}
+
+void Palette::copy(const uint8 *source, int firstCol, int numCols, int dstStart) {
+ if (dstStart == -1)
+ dstStart = firstCol;
+
+ assert(numCols >= 0 && numCols <= _numColors);
+ assert(firstCol >= 0);
+ assert(dstStart >= 0 && dstStart + numCols <= _numColors);
+
+ memmove(_palData + dstStart * 3, source + firstCol * 3, numCols * 3);
+}
+
+uint8 *Palette::fetchRealPalette() const {
+ uint8 *buffer = new uint8[_numColors * 3];
+ assert(buffer);
+
+ uint8 *dst = buffer;
+ const uint8 *palData = _palData;
+
+ for (int i = 0; i < _numColors; ++i) {
+ dst[0] = (palData[0] << 2) | (palData[0] & 3);
+ dst[1] = (palData[1] << 2) | (palData[1] & 3);
+ dst[2] = (palData[2] << 2) | (palData[2] & 3);
+
+ dst += 3;
+ palData += 3;
+ }
+
+ return buffer;
+}
+
+const uint8 Palette::_egaColors[] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0x00, 0xAA, 0x00, 0x00, 0xAA, 0xAA,
+ 0xAA, 0x00, 0x00, 0xAA, 0x00, 0xAA, 0xAA, 0x55, 0x00, 0xAA, 0xAA, 0xAA,
+ 0x55, 0x55, 0x55, 0x55, 0x55, 0xFF, 0x55, 0xFF, 0x55, 0x55, 0xFF, 0xFF,
+ 0xFF, 0x55, 0x55, 0xFF, 0x55, 0xFF, 0xFF, 0xFF, 0x55, 0xFF, 0xFF, 0xFF
+};
+
+const int Palette::_egaNumColors = ARRAYSIZE(_egaColors) / 3;
+
+const uint8 Palette::_cgaColors[4][12] = {
+ { 0x00, 0x00, 0x00, 0x00, 0x2A, 0x00, 0x2A, 0x00, 0x00, 0x2A, 0x15, 0x00 },
+ { 0x00, 0x00, 0x00, 0x15, 0x3F, 0x15, 0x3F, 0x15, 0x15, 0x3F, 0x3F, 0x15 },
+ { 0x00, 0x00, 0x00, 0x00, 0x2A, 0x2A, 0x2A, 0x00, 0x2A, 0x2A, 0x2A, 0x2A },
+ { 0x00, 0x00, 0x00, 0x15, 0x3F, 0x3F, 0x3F, 0x15, 0x3F, 0x3F, 0x3F, 0x3F }
+};
+
+const int Palette::_cgaNumColors = ARRAYSIZE(_cgaColors[0]) / 3;
+
+} // End of namespace Kyra
diff --git a/engines/kyra/graphics/screen.h b/engines/kyra/graphics/screen.h
new file mode 100644
index 0000000000..44113e4372
--- /dev/null
+++ b/engines/kyra/graphics/screen.h
@@ -0,0 +1,736 @@
+/* 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 2
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef KYRA_SCREEN_H
+#define KYRA_SCREEN_H
+
+#include "common/util.h"
+#include "common/func.h"
+#include "common/list.h"
+#include "common/array.h"
+#include "common/rect.h"
+#include "common/rendermode.h"
+#include "common/stream.h"
+
+class OSystem;
+
+namespace Graphics {
+class FontSJIS;
+} // End of namespace Graphics
+
+namespace Kyra {
+
+typedef Common::Functor0<void> UpdateFunctor;
+
+class KyraEngine_v1;
+class Screen;
+
+struct ScreenDim {
+ uint16 sx;
+ uint16 sy;
+ uint16 w;
+ uint16 h;
+ uint16 unk8;
+ uint16 unkA;
+ uint16 unkC;
+ uint16 unkE;
+};
+
+/**
+ * A class that handles KYRA fonts.
+ */
+class Font {
+public:
+ virtual ~Font() {}
+
+ /**
+ * Tries to load a file from the given stream
+ */
+ virtual bool load(Common::SeekableReadStream &file) = 0;
+
+ /**
+ * Whether the font draws on the overlay.
+ */
+ virtual bool usesOverlay() const { return false; }
+
+ /**
+ * The font height.
+ */
+ virtual int getHeight() const = 0;
+
+ /**
+ * The font width, this is the maximal character
+ * width.
+ */
+ virtual int getWidth() const = 0;
+
+ /**
+ * Gets the width of a specific character.
+ */
+ virtual int getCharWidth(uint16 c) const = 0;
+
+ /**
+ * Sets a text palette map. The map contains 16 entries.
+ */
+ virtual void setColorMap(const uint8 *src) = 0;
+
+ /**
+ * Sets a text 16bit palette map. Only used in in EOB II FM-Towns. The map contains 2 entries.
+ */
+ virtual void set16bitColorMap(const uint16 *src) {}
+
+ /**
+ * Draws a specific character.
+ *
+ * TODO/FIXME: Replace this with a nicer API. Currently
+ * the user has to assure that the character fits.
+ * We use this API, since it's hard to assure dirty rect
+ * handling from outside Screen.
+ */
+ virtual void drawChar(uint16 c, byte *dst, int pitch, int bpp) const = 0;
+};
+
+/**
+ * Implementation of the Font interface for DOS fonts.
+ *
+ * TODO: Clean up the implementation. For example we might be able
+ * to not to keep the whole font in memory.
+ */
+class DOSFont : public Font {
+public:
+ DOSFont();
+ ~DOSFont() { unload(); }
+
+ bool load(Common::SeekableReadStream &file);
+ int getHeight() const { return _height; }
+ int getWidth() const { return _width; }
+ int getCharWidth(uint16 c) const;
+ void setColorMap(const uint8 *src) { _colorMap = src; }
+ void drawChar(uint16 c, byte *dst, int pitch, int) const;
+
+private:
+ void unload();
+
+ const uint8 *_colorMap;
+
+ uint8 *_data;
+
+ int _width, _height;
+
+ int _numGlyphs;
+
+ uint8 *_widthTable;
+ uint8 *_heightTable;
+ uint16 *_bitmapOffsets;
+};
+
+#ifdef ENABLE_EOB
+/**
+* Implementation of the Font interface for old DOS fonts used
+* in EOB and EOB II.
+*
+*/
+class OldDOSFont : public Font {
+public:
+ OldDOSFont(Common::RenderMode mode);
+ ~OldDOSFont();
+
+ bool load(Common::SeekableReadStream &file);
+ int getHeight() const { return _height; }
+ int getWidth() const { return _width; }
+ int getCharWidth(uint16 c) const;
+ void setColorMap(const uint8 *src) { _colorMap8bit = src; }
+ void set16bitColorMap(const uint16 *src) { _colorMap16bit = src; }
+ void drawChar(uint16 c, byte *dst, int pitch, int bpp) const;
+
+private:
+ void unload();
+
+ uint8 *_data;
+ uint16 *_bitmapOffsets;
+
+ int _width, _height;
+ const uint8 *_colorMap8bit;
+ const uint16 *_colorMap16bit;
+
+ int _numGlyphs;
+
+ Common::RenderMode _renderMode;
+
+ static uint16 *_cgaDitheringTable;
+ static int _numRef;
+};
+#endif // ENABLE_EOB
+
+/**
+ * Implementation of the Font interface for AMIGA fonts.
+ */
+class AMIGAFont : public Font {
+public:
+ AMIGAFont();
+ ~AMIGAFont() { unload(); }
+
+ bool load(Common::SeekableReadStream &file);
+ int getHeight() const { return _height; }
+ int getWidth() const { return _width; }
+ int getCharWidth(uint16 c) const;
+ void setColorMap(const uint8 *src) {}
+ void drawChar(uint16 c, byte *dst, int pitch, int) const;
+
+private:
+ void unload();
+
+ int _width, _height;
+
+ struct Character {
+ uint8 yOffset, xOffset, width;
+
+ struct Graphics {
+ uint16 width, height;
+ uint8 *bitmap;
+ } graphics;
+ };
+
+ Character _chars[255];
+};
+
+/**
+ * Implementation of the Font interface for FM-Towns/PC98 fonts
+ */
+class SJISFont : public Font {
+public:
+ SJISFont(Graphics::FontSJIS *font, const uint8 invisColor, bool is16Color, bool drawOutline, bool fatPrint, int extraSpacing);
+ virtual ~SJISFont() { unload(); }
+
+ virtual bool usesOverlay() const { return true; }
+
+ bool load(Common::SeekableReadStream &) { return true; }
+ int getHeight() const;
+ int getWidth() const;
+ int getCharWidth(uint16 c) const;
+ void setColorMap(const uint8 *src);
+ virtual void drawChar(uint16 c, byte *dst, int pitch, int) const;
+
+protected:
+ void unload();
+
+ const uint8 *_colorMap;
+ Graphics::FontSJIS *_font;
+ int _sjisWidth, _asciiWidth;
+ int _fontHeight;
+ const bool _drawOutline;
+
+private:
+ const uint8 _invisColor;
+ const bool _is16Color;
+ // We use this for cases where the font width returned by getWidth() or getCharWidth() does not match the original.
+ // The original Japanese game versions use hard coded sjis font widths of 8 or 9. However, this does not necessarily
+ // depend on whether an outline is used or not (neither LOL/PC-9801 nor LOL/FM-TOWNS use an outline, but the first
+ // version uses a font width of 8 where the latter uses a font width of 9).
+ const int _sjisWidthOffset;
+};
+
+/**
+ * A class that manages KYRA palettes.
+ *
+ * This class stores the palette data as VGA RGB internally.
+ */
+class Palette {
+public:
+ Palette(const int numColors);
+ ~Palette();
+
+ enum {
+ kVGABytesPerColor = 3,
+ kPC98BytesPerColor = 3,
+ kAmigaBytesPerColor = 2
+ };
+
+ /**
+ * Load a VGA palette from the given stream.
+ */
+ void loadVGAPalette(Common::ReadStream &stream, int startIndex, int colors);
+
+ /**
+ * Load a HiColor palette from the given stream.
+ */
+ void loadHiColorPalette(Common::ReadStream &stream, int startIndex, int colors);
+
+ /**
+ * Load a EGA palette from the given stream.
+ */
+ void loadEGAPalette(Common::ReadStream &stream, int startIndex, int colors);
+
+ /**
+ * Set default CGA palette. We only need the cyan/magenta/grey mode.
+ */
+ enum CGAIntensity {
+ kIntensityLow = 0,
+ kIntensityHigh = 1
+ };
+
+ void setCGAPalette(int palIndex, CGAIntensity intensity);
+
+ /**
+ * Load a AMIGA palette from the given stream.
+ */
+ void loadAmigaPalette(Common::ReadStream &stream, int startIndex, int colors);
+
+ /**
+ * Load a PC98 16 color palette from the given stream.
+ */
+ void loadPC98Palette(Common::ReadStream &stream, int startIndex, int colors);
+
+ /**
+ * Return the number of colors this palette manages.
+ */
+ int getNumColors() const { return _numColors; }
+
+ /**
+ * Set all palette colors to black.
+ */
+ void clear();
+
+ /**
+ * Fill the given indexes with the given component value.
+ *
+ * @param firstCol the first color, which should be overwritten.
+ * @param numCols number of colors, which schould be overwritten.
+ * @param value color component value, which should be stored.
+ */
+ void fill(int firstCol, int numCols, uint8 value);
+
+ /**
+ * Copy data from another palette.
+ *
+ * @param source palette to copy data from.
+ * @param firstCol the first color of the source which should be copied.
+ * @param numCols number of colors, which should be copied. -1 all remaining colors.
+ * @param dstStart the first color, which should be ovewritten. If -1 firstCol will be used as start.
+ */
+ void copy(const Palette &source, int firstCol = 0, int numCols = -1, int dstStart = -1);
+
+ /**
+ * Copy data from a raw VGA palette.
+ *
+ * @param source source buffer
+ * @param firstCol the first color of the source which should be copied.
+ * @param numCols number of colors, which should be copied.
+ * @param dstStart the first color, which should be ovewritten. If -1 firstCol will be used as start.
+ */
+ void copy(const uint8 *source, int firstCol, int numCols, int dstStart = -1);
+
+ /**
+ * Fetch a RGB palette.
+ *
+ * @return a pointer to the RGB palette data, the client must delete[] it.
+ */
+ uint8 *fetchRealPalette() const;
+
+ //XXX
+ uint8 &operator[](const int index) {
+ assert(index >= 0 && index <= _numColors * 3);
+ return _palData[index];
+ }
+
+ const uint8 &operator[](const int index) const {
+ assert(index >= 0 && index <= _numColors * 3);
+ return _palData[index];
+ }
+
+ /**
+ * Gets raw access to the palette.
+ *
+ * TODO: Get rid of this.
+ */
+ uint8 *getData() { return _palData; }
+ const uint8 *getData() const { return _palData; }
+
+private:
+ uint8 *_palData;
+ const int _numColors;
+
+ static const uint8 _egaColors[];
+ static const int _egaNumColors;
+ static const uint8 _cgaColors[4][12];
+ static const int _cgaNumColors;
+};
+
+class Screen {
+public:
+ enum {
+ SCREEN_W = 320,
+ SCREEN_H = 200,
+ SCREEN_PAGE_SIZE = 320 * 200 + 1024,
+ SCREEN_OVL_SJIS_SIZE = 640 * 400,
+ SCREEN_PAGE_NUM = 16,
+ SCREEN_OVLS_NUM = 6
+ };
+
+ enum CopyRegionFlags {
+ CR_NO_P_CHECK = 0x01
+ };
+
+ enum DrawShapeFlags {
+ DSF_X_FLIPPED = 0x01,
+ DSF_Y_FLIPPED = 0x02,
+ DSF_SCALE = 0x04,
+ DSF_WND_COORDS = 0x10,
+ DSF_CENTER = 0x20,
+
+ DSF_SHAPE_FADING = 0x100,
+ DSF_TRANSPARENCY = 0x1000,
+ DSF_BACKGROUND_FADING = 0x2000,
+ DSF_CUSTOM_PALETTE = 0x8000
+ };
+
+ enum FontId {
+ FID_6_FNT = 0,
+ FID_8_FNT,
+ FID_9_FNT,
+ FID_CRED6_FNT,
+ FID_CRED8_FNT,
+ FID_BOOKFONT_FNT,
+ FID_GOLDFONT_FNT,
+ FID_INTRO_FNT,
+ FID_SJIS_FNT,
+ FID_SJIS_LARGE_FNT,
+ FID_SJIS_SMALL_FNT,
+ FID_NUM
+ };
+
+ Screen(KyraEngine_v1 *vm, OSystem *system, const ScreenDim *dimTable, const int dimTableSize);
+ virtual ~Screen();
+
+ // init
+ virtual bool init();
+ virtual void setResolution();
+ virtual void enableHiColorMode(bool enabled);
+
+ void updateScreen();
+
+ // debug functions
+ bool queryScreenDebug() const { return _debugEnabled; }
+ bool enableScreenDebug(bool enable);
+
+ // page cur. functions
+ int setCurPage(int pageNum);
+ void clearCurPage();
+
+ void copyWsaRect(int x, int y, int w, int h, int dimState, int plotFunc, const uint8 *src,
+ int unk1, const uint8 *unkPtr1, const uint8 *unkPtr2);
+
+ // page 0 functions
+ void copyToPage0(int y, int h, uint8 page, uint8 *seqBuf);
+ void shakeScreen(int times);
+
+ // page functions
+ void copyRegion(int x1, int y1, int x2, int y2, int w, int h, int srcPage, int dstPage, int flags=0);
+ void copyPage(uint8 srcPage, uint8 dstPage);
+
+ void copyRegionToBuffer(int pageNum, int x, int y, int w, int h, uint8 *dest);
+ void copyBlockToPage(int pageNum, int x, int y, int w, int h, const uint8 *src);
+
+ void shuffleScreen(int sx, int sy, int w, int h, int srcPage, int dstPage, int ticks, bool transparent);
+ void fillRect(int x1, int y1, int x2, int y2, uint8 color, int pageNum = -1, bool xored = false);
+
+ void clearPage(int pageNum);
+
+ int getPagePixel(int pageNum, int x, int y);
+ void setPagePixel(int pageNum, int x, int y, uint8 color);
+
+ const uint8 *getCPagePtr(int pageNum) const;
+ uint8 *getPageRect(int pageNum, int x, int y, int w, int h);
+
+ // palette handling
+ void fadeFromBlack(int delay=0x54, const UpdateFunctor *upFunc = 0);
+ void fadeToBlack(int delay=0x54, const UpdateFunctor *upFunc = 0);
+
+ virtual void fadePalette(const Palette &pal, int delay, const UpdateFunctor *upFunc = 0);
+ virtual void getFadeParams(const Palette &pal, int delay, int &delayInc, int &diff);
+ virtual int fadePalStep(const Palette &pal, int diff);
+
+ void setPaletteIndex(uint8 index, uint8 red, uint8 green, uint8 blue);
+ virtual void setScreenPalette(const Palette &pal);
+
+ // AMIGA version only
+ bool isInterfacePaletteEnabled() const { return _interfacePaletteEnabled; }
+ void enableInterfacePalette(bool e);
+ void setInterfacePalette(const Palette &pal, uint8 r, uint8 g, uint8 b);
+
+ virtual void getRealPalette(int num, uint8 *dst);
+ Palette &getPalette(int num);
+ void copyPalette(const int dst, const int src);
+
+ // gui specific (processing on _curPage)
+ void drawLine(bool vertical, int x, int y, int length, int color);
+ void drawClippedLine(int x1, int y1, int x2, int y2, int color);
+ virtual void drawShadedBox(int x1, int y1, int x2, int y2, int color1, int color2);
+ void drawBox(int x1, int y1, int x2, int y2, int color);
+
+ // font/text handling
+ virtual bool loadFont(FontId fontId, const char *filename);
+ FontId setFont(FontId fontId);
+
+ int getFontHeight() const;
+ int getFontWidth() const;
+
+ int getCharWidth(uint16 c) const;
+ int getTextWidth(const char *str);
+
+ void printText(const char *str, int x, int y, uint8 color1, uint8 color2);
+
+ virtual void setTextColorMap(const uint8 *cmap) = 0;
+ void setTextColor(const uint8 *cmap, int a, int b);
+ void setTextColor16bit(const uint16 *cmap16);
+
+ const ScreenDim *getScreenDim(int dim) const;
+ void modifyScreenDim(int dim, int x, int y, int w, int h);
+ int screenDimTableCount() const { return _dimTableCount; }
+
+ void setScreenDim(int dim);
+ int curDimIndex() const { return _curDimIndex; }
+
+ const ScreenDim *_curDim;
+
+ // shape handling
+ uint8 *encodeShape(int x, int y, int w, int h, int flags);
+
+ int setNewShapeHeight(uint8 *shape, int height);
+ int resetShapeHeight(uint8 *shape);
+
+ virtual void drawShape(uint8 pageNum, const uint8 *shapeData, int x, int y, int sd, int flags, ...);
+
+ // mouse handling
+ void hideMouse();
+ void showMouse();
+ bool isMouseVisible() const;
+ virtual void setMouseCursor(int x, int y, const byte *shape);
+
+ // rect handling
+ virtual int getRectSize(int w, int h) = 0;
+
+ void rectClip(int &x, int &y, int w, int h);
+
+ // misc
+ void loadBitmap(const char *filename, int tempPage, int dstPage, Palette *pal, bool skip=false);
+
+ virtual bool loadPalette(const char *filename, Palette &pal);
+ bool loadPaletteTable(const char *filename, int firstPalette);
+ virtual void loadPalette(const byte *data, Palette &pal, int bytes);
+
+ void setAnimBlockPtr(int size);
+
+ void setShapePages(int page1, int page2, int minY = -1, int maxY = 201);
+
+ virtual byte getShapeFlag1(int x, int y);
+ virtual byte getShapeFlag2(int x, int y);
+
+ virtual int getDrawLayer(int x, int y);
+ virtual int getDrawLayer2(int x, int y, int height);
+
+ void blockInRegion(int x, int y, int width, int height);
+ void blockOutRegion(int x, int y, int width, int height);
+
+ int _charWidth;
+ int _charOffset;
+ int _curPage;
+ uint8 *_shapePages[2];
+ int _maskMinY, _maskMaxY;
+ FontId _currentFont;
+
+ // decoding functions
+ static void decodeFrame1(const uint8 *src, uint8 *dst, uint32 size);
+ static uint16 decodeEGAGetCode(const uint8 *&pos, uint8 &nib);
+
+ static void decodeFrame3(const uint8 *src, uint8 *dst, uint32 size);
+ static uint decodeFrame4(const uint8 *src, uint8 *dst, uint32 dstSize);
+ static void decodeFrameDelta(uint8 *dst, const uint8 *src, bool noXor = false);
+ static void decodeFrameDeltaPage(uint8 *dst, const uint8 *src, const int pitch, bool noXor);
+
+ static void convertAmigaGfx(uint8 *data, int w, int h, int depth = 5, bool wsa = false, int bytesPerPlane = -1);
+ static void convertAmigaMsc(uint8 *data);
+
+ // RPG specific, this does not belong here
+ void crossFadeRegion(int x1, int y1, int x2, int y2, int w, int h, int srcPage, int dstPage);
+
+ uint16 *get16bitPalette() { return _16bitPalette; }
+ void set16bitShadingLevel(int lvl) { _16bitShadingLevel = lvl; }
+
+protected:
+ void resetPagePtrsAndBuffers(int pageSize);
+ uint8 *getPagePtr(int pageNum);
+ virtual void updateDirtyRects();
+ void updateDirtyRectsAmiga();
+ void updateDirtyRectsOvl();
+
+ void scale2x(byte *dst, int dstPitch, const byte *src, int srcPitch, int w, int h);
+ virtual void mergeOverlay(int x, int y, int w, int h);
+
+ // overlay specific
+ byte *getOverlayPtr(int pageNum);
+ void clearOverlayPage(int pageNum);
+ void clearOverlayRect(int pageNum, int x, int y, int w, int h);
+ void copyOverlayRegion(int x, int y, int x2, int y2, int w, int h, int srcPage, int dstPage);
+
+ // font/text specific
+ uint16 fetchChar(const char *&s) const;
+ void drawChar(uint16 c, int x, int y);
+
+ int16 encodeShapeAndCalculateSize(uint8 *from, uint8 *to, int size);
+
+ template<bool noXor> static void wrapped_decodeFrameDelta(uint8 *dst, const uint8 *src);
+ template<bool noXor> static void wrapped_decodeFrameDeltaPage(uint8 *dst, const uint8 *src, const int pitch);
+
+ uint8 *_pagePtrs[16];
+ uint8 *_sjisOverlayPtrs[SCREEN_OVLS_NUM];
+ uint8 _pageMapping[SCREEN_PAGE_NUM];
+
+ bool _useOverlays;
+ bool _useSJIS;
+ bool _use16ColorMode;
+ bool _useHiResEGADithering;
+ bool _useHiColorScreen;
+ bool _isAmiga;
+ Common::RenderMode _renderMode;
+ int _bytesPerPixel;
+ int _screenPageSize;
+
+ uint8 _sjisInvisibleColor;
+ bool _sjisMixedFontMode;
+
+ Palette *_screenPalette;
+ Common::Array<Palette *> _palettes;
+ Palette *_internFadePalette;
+
+ uint16 shade16bitColor(uint16 col);
+
+ uint16 *_16bitPalette;
+ uint16 *_16bitConversionPalette;
+ uint8 _16bitShadingLevel;
+
+ Font *_fonts[FID_NUM];
+ uint8 _textColorsMap[16];
+ uint16 _textColorsMap16bit[2];
+
+ uint8 *_decodeShapeBuffer;
+ int _decodeShapeBufferSize;
+
+ uint8 *_animBlockPtr;
+ int _animBlockSize;
+
+ // dimension handling
+ const ScreenDim *const _dimTable;
+ ScreenDim **_customDimTable;
+ const int _dimTableCount;
+ int _curDimIndex;
+
+ // mouse handling
+ int _mouseLockCount;
+ const uint8 _cursorColorKey;
+
+ virtual void postProcessCursor(uint8 *data, int w, int h, int pitch) {}
+
+ enum {
+ kMaxDirtyRects = 50
+ };
+
+ bool _forceFullUpdate;
+ bool _paletteChanged;
+ Common::List<Common::Rect> _dirtyRects;
+
+ void addDirtyRect(int x, int y, int w, int h);
+
+ OSystem *_system;
+ KyraEngine_v1 *_vm;
+
+ // shape
+ int drawShapeMarginNoScaleUpwind(uint8 *&dst, const uint8 *&src, int &cnt);
+ int drawShapeMarginNoScaleDownwind(uint8 *&dst, const uint8 *&src, int &cnt);
+ int drawShapeMarginScaleUpwind(uint8 *&dst, const uint8 *&src, int &cnt);
+ int drawShapeMarginScaleDownwind(uint8 *&dst, const uint8 *&src, int &cnt);
+ int drawShapeSkipScaleUpwind(uint8 *&dst, const uint8 *&src, int &cnt);
+ int drawShapeSkipScaleDownwind(uint8 *&dst, const uint8 *&src, int &cnt);
+ void drawShapeProcessLineNoScaleUpwind(uint8 *&dst, const uint8 *&src, int &cnt, int16 scaleState);
+ void drawShapeProcessLineNoScaleDownwind(uint8 *&dst, const uint8 *&src, int &cnt, int16 scaleState);
+ void drawShapeProcessLineScaleUpwind(uint8 *&dst, const uint8 *&src, int &cnt, int16 scaleState);
+ void drawShapeProcessLineScaleDownwind(uint8 *&dst, const uint8 *&src, int &cnt, int16 scaleState);
+
+ void drawShapePlotType0(uint8 *dst, uint8 cmd);
+ void drawShapePlotType1(uint8 *dst, uint8 cmd);
+ void drawShapePlotType3_7(uint8 *dst, uint8 cmd);
+ void drawShapePlotType4(uint8 *dst, uint8 cmd);
+ void drawShapePlotType5(uint8 *dst, uint8 cmd);
+ void drawShapePlotType6(uint8 *dst, uint8 cmd);
+ void drawShapePlotType8(uint8 *dst, uint8 cmd);
+ void drawShapePlotType9(uint8 *dst, uint8 cmd);
+ void drawShapePlotType11_15(uint8 *dst, uint8 cmd);
+ void drawShapePlotType12(uint8 *dst, uint8 cmd);
+ void drawShapePlotType13(uint8 *dst, uint8 cmd);
+ void drawShapePlotType14(uint8 *dst, uint8 cmd);
+ void drawShapePlotType16(uint8 *dst, uint8 cmd);
+ void drawShapePlotType20(uint8 *dst, uint8 cmd);
+ void drawShapePlotType21(uint8 *dst, uint8 cmd);
+ void drawShapePlotType33(uint8 *dst, uint8 cmd);
+ void drawShapePlotType37(uint8 *dst, uint8 cmd);
+ void drawShapePlotType48(uint8 *dst, uint8 cmd);
+ void drawShapePlotType52(uint8 *dst, uint8 cmd);
+
+ typedef int (Screen::*DsMarginSkipFunc)(uint8 *&dst, const uint8 *&src, int &cnt);
+ typedef void (Screen::*DsLineFunc)(uint8 *&dst, const uint8 *&src, int &cnt, int16 scaleState);
+ typedef void (Screen::*DsPlotFunc)(uint8 *dst, uint8 cmd);
+
+ DsMarginSkipFunc _dsProcessMargin;
+ DsMarginSkipFunc _dsScaleSkip;
+ DsLineFunc _dsProcessLine;
+ DsPlotFunc _dsPlot;
+
+ const uint8 *_dsShapeFadingTable;
+ int _dsShapeFadingLevel;
+ const uint8 *_dsColorTable;
+ const uint8 *_dsTransparencyTable1;
+ const uint8 *_dsTransparencyTable2;
+ const uint8 *_dsBackgroundFadingTable;
+ int _dsDrawLayer;
+ uint8 *_dsDstPage;
+ int _dsTmpWidth;
+ int _dsOffscreenLeft;
+ int _dsOffscreenRight;
+ int _dsScaleW;
+ int _dsScaleH;
+ int _dsOffscreenScaleVal1;
+ int _dsOffscreenScaleVal2;
+ int _drawShapeVar1;
+ int _drawShapeVar3;
+ int _drawShapeVar4;
+ int _drawShapeVar5;
+
+ // AMIGA version
+ bool _interfacePaletteEnabled;
+
+ // debug
+ bool _debugEnabled;
+};
+
+} // End of namespace Kyra
+
+#endif
diff --git a/engines/kyra/graphics/screen_eob.cpp b/engines/kyra/graphics/screen_eob.cpp
new file mode 100644
index 0000000000..3945c034fc
--- /dev/null
+++ b/engines/kyra/graphics/screen_eob.cpp
@@ -0,0 +1,1974 @@
+/* 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 2
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+
+#if !defined(ENABLE_EOB)
+#include "kyra/graphics/screen.h"
+#endif
+
+#ifdef ENABLE_EOB
+
+#include "kyra/engine/eobcommon.h"
+#include "kyra/resource/resource.h"
+
+#include "common/system.h"
+
+#include "graphics/cursorman.h"
+#include "graphics/palette.h"
+#include "graphics/sjis.h"
+
+namespace Kyra {
+
+Screen_EoB::Screen_EoB(EoBCoreEngine *vm, OSystem *system) : Screen(vm, system, _screenDimTable, _screenDimTableCount), _cursorColorKey16Bit(0x8000) {
+ _dsBackgroundFading = false;
+ _dsShapeFadingLevel = 0;
+ _dsBackgroundFadingXOffs = 0;
+ _dsShapeFadingTable = 0;
+ _dsX1 = _dsX2 = _dsY1 = _dsY2 = 0;
+ _gfxX = _gfxY = 0;
+ _gfxCol = 0;
+ _dsTempPage = 0;
+ _shpBuffer = _convertHiColorBuffer = 0;
+ _dsDiv = 0;
+ _dsRem = 0;
+ _dsScaleTrans = 0;
+ _cgaScaleTable = 0;
+ _gfxMaxY = 0;
+ _egaDitheringTable = 0;
+ _egaDitheringTempPage = 0;
+ _cgaMappingDefault = 0;
+ _cgaDitheringTables[0] = _cgaDitheringTables[1] = 0;
+ _useHiResEGADithering = false;
+}
+
+Screen_EoB::~Screen_EoB() {
+ delete[] _dsTempPage;
+ delete[] _shpBuffer;
+ delete[] _convertHiColorBuffer;
+ delete[] _cgaScaleTable;
+ delete[] _egaDitheringTable;
+ delete[] _egaDitheringTempPage;
+ delete[] _cgaDitheringTables[0];
+ delete[] _cgaDitheringTables[1];
+}
+
+bool Screen_EoB::init() {
+ if (Screen::init()) {
+ int temp;
+ _gfxMaxY = _vm->staticres()->loadRawData(kEoBBaseExpObjectY, temp);
+ _dsTempPage = new uint8[12000];
+
+ if (_vm->gameFlags().platform == Common::kPlatformFMTowns) {
+ _shpBuffer = new uint8[SCREEN_H * SCREEN_W];
+ _convertHiColorBuffer = new uint8[SCREEN_H * SCREEN_W];
+ enableHiColorMode(true);
+
+ Graphics::FontSJIS *font = Graphics::FontSJIS::createFont(Common::kPlatformFMTowns);
+ if (!font)
+ error("Could not load any SJIS font, neither the original nor ScummVM's 'SJIS.FNT'");
+ _fonts[FID_SJIS_LARGE_FNT] = new SJISFontLarge(font);
+
+ loadFont(FID_SJIS_SMALL_FNT, "FONT.DMP");
+ }
+
+ if (_vm->gameFlags().useHiRes && _renderMode == Common::kRenderEGA) {
+ _useHiResEGADithering = true;
+ _egaDitheringTable = new uint8[256];
+ _egaDitheringTempPage = new uint8[SCREEN_W * 2 * SCREEN_H * 2];
+ for (int i = 0; i < 256; i++)
+ _egaDitheringTable[i] = i & 0x0F;
+ } else if (_renderMode == Common::kRenderCGA) {
+ _cgaMappingDefault = _vm->staticres()->loadRawData(kEoB1CgaMappingDefault, temp);
+ _cgaDitheringTables[0] = new uint16[256];
+ memset(_cgaDitheringTables[0], 0, 256 * sizeof(uint16));
+ _cgaDitheringTables[1] = new uint16[256];
+ memset(_cgaDitheringTables[1], 0, 256 * sizeof(uint16));
+
+ _cgaScaleTable = new uint8[256];
+ memset(_cgaScaleTable, 0, 256 * sizeof(uint8));
+ for (int i = 0; i < 256; i++)
+ _cgaScaleTable[i] = ((i & 0xF0) >> 2) | (i & 0x03);
+ }
+
+ return true;
+ }
+ return false;
+}
+
+void Screen_EoB::setClearScreenDim(int dim) {
+ setScreenDim(dim);
+ clearCurDim();
+}
+
+void Screen_EoB::clearCurDim() {
+ fillRect(_curDim->sx << 3, _curDim->sy, ((_curDim->sx + _curDim->w) << 3) - 1, (_curDim->sy + _curDim->h) - 1, _curDim->unkA);
+}
+
+void Screen_EoB::setMouseCursor(int x, int y, const byte *shape) {
+ setMouseCursor(x, y, shape, 0);
+}
+
+void Screen_EoB::setMouseCursor(int x, int y, const byte *shape, const uint8 *ovl) {
+ if (!shape)
+ return;
+
+ int mouseW = (shape[2] << 3);
+ int mouseH = (shape[3]);
+ int colorKey = (_renderMode == Common::kRenderCGA) ? 0 : (_bytesPerPixel == 2 ? _cursorColorKey16Bit : _cursorColorKey);
+
+ int scaleFactor = _vm->gameFlags().useHiRes ? 2 : 1;
+ int bpp = _useHiColorScreen ? 2 : 1;
+
+ uint8 *cursor = new uint8[mouseW * scaleFactor * bpp * mouseH * scaleFactor];
+
+ if (_bytesPerPixel == 2) {
+ for (int s = mouseW * scaleFactor * bpp * mouseH * scaleFactor; s; s -= 2)
+ *(uint16*)(cursor + s - 2) = colorKey;
+ } else {
+ // We don't use fillRect here to make sure that the color key 0xFF doesn't get converted into EGA color
+ memset(cursor, colorKey, mouseW * scaleFactor * bpp * mouseH * scaleFactor);
+ }
+
+ copyBlockToPage(6, 0, 0, mouseW * scaleFactor, mouseH * scaleFactor, cursor);
+ drawShape(6, shape, 0, 0, 0, 2, ovl);
+ CursorMan.showMouse(false);
+
+ if (_useHiResEGADithering)
+ ditherRect(getCPagePtr(6), cursor, mouseW * scaleFactor, mouseW, mouseH, colorKey);
+ else if (_vm->gameFlags().useHiRes)
+ scale2x(cursor, mouseW * scaleFactor, getCPagePtr(6), SCREEN_W, mouseW, mouseH);
+ else
+ copyRegionToBuffer(6, 0, 0, mouseW, mouseH, cursor);
+
+ // Mouse cursor post processing for CGA mode. Unlike the original (which uses drawShape for the mouse cursor)
+ // the cursor manager cannot know whether a pixel value of 0 is supposed to be black or transparent. Thus, we
+ // go over the transparency mask again and turn the black pixels to color 4.
+ if (_renderMode == Common::kRenderCGA) {
+ const uint8 *maskTbl = shape + 4 + ((mouseW * mouseH) >> 2);
+ uint8 *dst = cursor;
+ uint8 trans = 0;
+ uint8 shift = 6;
+
+ uint16 mH = mouseH;
+ while (mH--) {
+ uint16 mW = mouseW;
+ while (mW--) {
+ if (shift == 6)
+ trans = *maskTbl++;
+ if (!*dst && !((trans >> shift) & 3))
+ *dst = 4;
+ dst++;
+ shift = (shift - 2) & 7;
+ }
+ }
+ }
+
+ // Convert color key to 16 bit after drawing the mouse cursor.
+ // The cursor has been converted to 16 bit in scale2x().
+ colorKey = _16bitConversionPalette ? _16bitConversionPalette[colorKey] : colorKey;
+ Graphics::PixelFormat pixelFormat = _system->getScreenFormat();
+
+ CursorMan.replaceCursor(cursor, mouseW * scaleFactor, mouseH * scaleFactor, x * scaleFactor, y * scaleFactor, colorKey, false, &pixelFormat);
+ if (isMouseVisible())
+ CursorMan.showMouse(true);
+ delete[] cursor;
+
+ // makes sure that the cursor is drawn
+ // we do not use Screen::updateScreen here
+ // so we can be sure that changes to page 0
+ // are NOT updated on the real screen here
+ _system->updateScreen();
+}
+
+void Screen_EoB::loadFileDataToPage(Common::SeekableReadStream *s, int pageNum, uint32 size) {
+ s->read(_pagePtrs[pageNum], size);
+}
+
+void Screen_EoB::printShadedText(const char *string, int x, int y, int col1, int col2) {
+ if (_vm->gameFlags().platform != Common::kPlatformFMTowns) {
+ printText(string, x - 1, y, 12, col2);
+ printText(string, x, y + 1, 12, 0);
+ printText(string, x - 1, y + 1, 12, 0);
+ } else if (col2) {
+ fillRect(x, y, x + getTextWidth(string) - 1, y + getFontHeight() - 1, col2);
+ }
+ printText(string, x, y, col1, 0);
+}
+
+void Screen_EoB::loadShapeSetBitmap(const char *file, int tempPage, int destPage) {
+ loadEoBBitmap(file, _cgaMappingDefault, tempPage, destPage, -1);
+ _curPage = 2;
+}
+
+void Screen_EoB::loadEoBBitmap(const char *file, const uint8 *cgaMapping, int tempPage, int destPage, int convertToPage) {
+ const char *filePattern = _vm->gameFlags().platform == Common::kPlatformFMTowns ? "%s.SHP" : ((_vm->game() == GI_EOB1 && (_renderMode == Common::kRenderEGA || _renderMode == Common::kRenderCGA)) ? "%s.EGA" : "%s.CPS");
+ Common::String tmp = Common::String::format(filePattern, file);
+ Common::SeekableReadStream *s = _vm->resource()->createReadStream(tmp);
+ bool loadAlternative = false;
+
+ if (_vm->gameFlags().platform == Common::kPlatformFMTowns) {
+ if (!s)
+ error("Screen_EoB::loadEoBBitmap(): Failed to load file '%s'", file);
+ s->read(_shpBuffer, s->size());
+ decodeSHP(_shpBuffer, destPage);
+ } else if (s) {
+ // This additional check is necessary since some localized versions of EOB II seem to contain invalid (size zero) cps files
+ if (s->size())
+ loadBitmap(tmp.c_str(), tempPage, destPage, 0);
+ else
+ loadAlternative = true;
+ } else {
+ loadAlternative = true;
+ }
+
+ delete s;
+
+ if (loadAlternative) {
+ if (_vm->game() == GI_EOB1) {
+ tmp.insertChar('1', tmp.size() - 4);
+ loadBitmap(tmp.c_str(), tempPage, destPage, 0);
+ } else {
+ tmp.setChar('X', 0);
+ s = _vm->resource()->createReadStream(tmp);
+ if (!s)
+ error("Screen_EoB::loadEoBBitmap(): Failed to load file '%s'", file);
+ s->seek(768);
+ loadFileDataToPage(s, destPage, 64000);
+ delete s;
+ }
+ }
+
+ if (convertToPage == -1)
+ return;
+
+ if (_16bitPalette)
+ convertToHiColor(destPage);
+
+ if (convertToPage == 2 && _renderMode == Common::kRenderCGA) {
+ convertPage(destPage, 4, cgaMapping);
+ copyRegion(0, 0, 0, 0, 320, 200, 4, 2, Screen::CR_NO_P_CHECK);
+ } else if (convertToPage == 0) {
+ convertPage(destPage, 2, cgaMapping);
+ copyRegion(0, 0, 0, 0, 320, 200, 2, 0, Screen::CR_NO_P_CHECK);
+ } else {
+ convertPage(destPage, convertToPage, cgaMapping);
+ }
+}
+
+void Screen_EoB::convertPage(int srcPage, int dstPage, const uint8 *cgaMapping) {
+ uint8 *src = getPagePtr(srcPage);
+ uint8 *dst = getPagePtr(dstPage);
+ if (src == dst)
+ return;
+
+ if (_renderMode == Common::kRenderCGA) {
+ if (cgaMapping)
+ generateCGADitheringTables(cgaMapping);
+
+ uint16 *d = (uint16 *)dst;
+ uint8 tblSwitch = 0;
+ for (int height = SCREEN_H; height; height--) {
+ const uint16 *table = _cgaDitheringTables[(tblSwitch++) & 1];
+ for (int width = SCREEN_W / 2; width; width--) {
+ WRITE_LE_UINT16(d++, table[((src[1] & 0x0F) << 4) | (src[0] & 0x0F)]);
+ src += 2;
+ }
+ }
+ } else if (_renderMode == Common::kRenderEGA && !_useHiResEGADithering) {
+ uint32 len = SCREEN_W * SCREEN_H;
+ while (len--)
+ *dst++ = *src++ & 0x0F;
+ } else {
+ copyPage(srcPage, dstPage);
+ }
+
+ if (dstPage == 0 || dstPage == 1)
+ _forceFullUpdate = true;
+}
+
+void Screen_EoB::setScreenPalette(const Palette &pal) {
+ if (_bytesPerPixel == 2) {
+ for (int i = 0; i < 4; i++)
+ createFadeTable16bit((const uint16*)(pal.getData()), &_16bitPalette[i * 256], 0, i * 85);
+ }else if (_useHiResEGADithering && pal.getNumColors() != 16) {
+ generateEGADitheringTable(pal);
+ } else if (_renderMode == Common::kRenderEGA && pal.getNumColors() == 16) {
+ _screenPalette->copy(pal);
+ _system->getPaletteManager()->setPalette(_screenPalette->getData(), 0, _screenPalette->getNumColors());
+ } else if (_renderMode != Common::kRenderCGA && _renderMode != Common::kRenderEGA) {
+ Screen::setScreenPalette(pal);
+ }
+}
+
+void Screen_EoB::getRealPalette(int num, uint8 *dst) {
+ if (_renderMode == Common::kRenderCGA || _renderMode == Common::kRenderEGA) {
+ const uint8 *pal = _screenPalette->getData();
+ for (int i = 0; i < 16; ++i) {
+ dst[0] = (pal[0] << 2) | (pal[0] & 3);
+ dst[1] = (pal[1] << 2) | (pal[1] & 3);
+ dst[2] = (pal[2] << 2) | (pal[2] & 3);
+ dst += 3;
+ pal += 3;
+ }
+ } else {
+ Screen::getRealPalette(num, dst);
+ }
+}
+
+uint8 *Screen_EoB::encodeShape(uint16 x, uint16 y, uint16 w, uint16 h, bool encode8bit, const uint8 *cgaMapping) {
+ uint8 *shp = 0;
+ uint16 shapesize = 0;
+
+ uint8 *srcLineStart = getPagePtr(_curPage | 1) + y * 320 + (x << 3);
+ uint8 *src = srcLineStart;
+
+ if (_renderMode == Common::kRenderEGA && !_useHiResEGADithering)
+ encode8bit = false;
+
+ if (_bytesPerPixel == 2 && encode8bit) {
+ shapesize = h * (w << 3) + 4;
+ shp = new uint8[shapesize];
+ memset(shp, 0, shapesize);
+ uint8 *dst = shp;
+
+ *dst++ = 0;
+ *dst++ = (h & 0xFF);
+ *dst++ = (w & 0xFF);
+ *dst++ = (h & 0xFF);
+
+ w <<= 3;
+
+ for (int i = 0; i < h; ++i) {
+ memcpy(dst, src, w);
+ srcLineStart += SCREEN_W;
+ src = srcLineStart;
+ dst += w;
+ }
+ } else if (_renderMode == Common::kRenderCGA) {
+ if (cgaMapping)
+ generateCGADitheringTables(cgaMapping);
+ shapesize = h * (w << 2) + 4;
+ shp = new uint8[shapesize];
+ memset(shp, 0, shapesize);
+ uint8 *dst = shp;
+
+ *dst++ = 4;
+ *dst++ = (h & 0xFF);
+ *dst++ = (w & 0xFF);
+ *dst++ = (h & 0xFF);
+
+ uint8 *dst2 = dst + (h * (w << 1));
+
+ uint8 tblSwitch = 0;
+ uint16 h1 = h;
+ while (h1--) {
+ uint16 w1 = w << 1;
+ const uint16 *table = _cgaDitheringTables[(tblSwitch++) & 1];
+
+ while (w1--) {
+ uint16 p0 = table[((src[1] & 0x0F) << 4) | (src[0] & 0x0F)];
+ uint16 p1 = table[((src[3] & 0x0F) << 4) | (src[2] & 0x0F)];
+
+ *dst++ = ((p0 & 0x0003) << 6) | ((p0 & 0x0300) >> 4) | ((p1 & 0x0003) << 2) | ((p1 & 0x0300) >> 8);
+
+ uint8 msk = 0;
+ for (int i = 0; i < 4; i++) {
+ if (!src[3 - i])
+ msk |= (3 << (i << 1));
+ }
+ *dst2++ = msk;
+ src += 4;
+ }
+ srcLineStart += SCREEN_W;
+ src = srcLineStart;
+ }
+
+ } else if (encode8bit) {
+ uint16 h1 = h;
+ while (h1--) {
+ uint8 *lineEnd = src + (w << 3);
+ do {
+ if (!*src++) {
+ shapesize++;
+ uint8 *startZeroPos = src;
+ while (src != lineEnd && *src == 0)
+ src++;
+
+ uint16 numZero = src - startZeroPos + 1;
+ if (numZero >> 8)
+ shapesize += 2;
+ }
+ shapesize++;
+ } while (src != lineEnd);
+
+ srcLineStart += SCREEN_W;
+ src = srcLineStart;
+ }
+
+ shapesize += 4;
+
+ shp = new uint8[shapesize];
+ memset(shp, 0, shapesize);
+ uint8 *dst = shp;
+
+ *dst++ = 1;
+ *dst++ = (h & 0xFF);
+ *dst++ = (w & 0xFF);
+ *dst++ = (h & 0xFF);
+
+ srcLineStart = getPagePtr(_curPage | 1) + y * 320 + (x << 3);
+ src = srcLineStart;
+
+ h1 = h;
+ while (h1--) {
+ uint8 *lineEnd = src + (w << 3);
+ do {
+ uint8 val = *src++;
+ if (!val) {
+ *dst++ = 0;
+ uint8 *startZeroPos = src;
+
+ while (src != lineEnd && *src == 0)
+ src++;
+
+ uint16 numZero = src - startZeroPos + 1;
+ if (numZero >> 8) {
+ *dst++ = 255;
+ *dst++ = 0;
+ numZero -= 255;
+ }
+ val = numZero & 0xFF;
+ }
+ *dst++ = val;
+ } while (src != lineEnd);
+
+ srcLineStart += SCREEN_W;
+ src = srcLineStart;
+ }
+
+ } else {
+ uint8 nib = 0, col = 0;
+ uint8 *colorMap = 0;
+
+ if (_renderMode != Common::kRenderEGA || _useHiResEGADithering) {
+ colorMap = new uint8[0x100];
+ memset(colorMap, 0xFF, 0x100);
+ }
+
+ shapesize = h * (w << 2) + 20;
+ shp = new uint8[shapesize];
+ memset(shp, 0, shapesize);
+ uint8 *dst = shp;
+
+ *dst++ = 2;
+ *dst++ = (h & 0xFF);
+ *dst++ = (w & 0xFF);
+ *dst++ = (h & 0xFF);
+
+ if (_renderMode != Common::kRenderEGA || _useHiResEGADithering) {
+ memset(dst, 0xFF, 0x10);
+ } else {
+ for (int i = 0; i < 16; i++)
+ dst[i] = i;
+ }
+
+ uint8 *pal = dst;
+ dst += 16;
+ nib = col = 0;
+
+ uint16 h1 = h;
+ while (h1--) {
+ uint16 w1 = w << 3;
+ while (w1--) {
+ uint8 s = *src++;
+ uint8 c = s & 0x0F;
+ if (colorMap) {
+ c = colorMap[s];
+ if (c == 0xFF) {
+ if (col < 0x10) {
+ *pal++ = s;
+ c = colorMap[s] = col++;
+ } else {
+ c = 0;
+ }
+ }
+ }
+
+ if (++nib & 1)
+ *dst = c << 4;
+ else
+ *dst++ |= c;
+ }
+ srcLineStart += SCREEN_W;
+ src = srcLineStart;
+ }
+ delete[] colorMap;
+ }
+
+ return shp;
+}
+
+void Screen_EoB::drawShape(uint8 pageNum, const uint8 *shapeData, int x, int y, int sd, int flags, ...) {
+ uint8 *dst = getPagePtr(pageNum);
+ const uint8 *src = shapeData;
+
+ if (!src)
+ return;
+
+ if (sd != -1) {
+ const ScreenDim *dm = getScreenDim(sd);
+ setShapeFrame(dm->sx, dm->sy, dm->sx + dm->w, dm->sy + dm->h);
+ x += (_dsX1 << 3);
+ y += _dsY1;
+ }
+
+ uint8 *ovl = 0;
+
+ va_list args;
+ va_start(args, flags);
+ if (flags & 2) {
+ ovl = va_arg(args, uint8 *);
+ _dsBackgroundFadingXOffs = x;
+ }
+ va_end(args);
+
+ dst += (_dsX1 << (2 + _bytesPerPixel));
+ int16 dX = x - (_dsX1 << 3);
+ int16 dY = y;
+ int16 dW = _dsX2 - _dsX1;
+
+ uint8 pixelsPerByte = *src++;
+ uint16 dH = *src++;
+ uint16 width = (*src++) << 3;
+ uint16 transOffset = (pixelsPerByte == 4) ? (dH * width) >> 2 : 0;
+ src++;
+
+ int rX = x;
+ int rY = y;
+ int rW = width + 8;
+ int rH = dH;
+
+ uint16 w2 = width;
+ int d = dY - _dsY1;
+
+ int pixelStep = (flags & 1) ? -1 : 1;
+
+ if (pixelsPerByte < 2) {
+ uint16 marginLeft = 0;
+ uint16 marginRight = 0;
+
+ if (d < 0) {
+ dH += d;
+ if (dH <= 0)
+ return;
+ d = -d;
+
+ for (int i = 0; i < d; i++) {
+ marginLeft = width;
+ for (int ii = 0; ii < marginLeft; ii++) {
+ if (!*src++)
+ marginLeft = marginLeft + 1 - *src++;
+ }
+ }
+ dY = _dsY1;
+ }
+
+ d = _dsY2 - dY;
+
+ if (d < 1)
+ return;
+
+ if (d < dH)
+ dH = d;
+
+ marginLeft = 0;
+
+ if (dX < 0) {
+ width += dX;
+ marginLeft = -dX;
+
+ if (marginLeft >= w2)
+ return;
+
+ dX = 0;
+ }
+
+ marginRight = 0;
+ d = (dW << 3) - dX;
+
+ if (d < 1)
+ return;
+
+ if (d < width) {
+ width = d;
+ marginRight = w2 - marginLeft - width;
+ }
+
+ dst += (dY * SCREEN_W * _bytesPerPixel + dX * _bytesPerPixel);
+ uint8 *dstL = dst;
+
+ if (pageNum == 0 || pageNum == 1)
+ addDirtyRect(rX, rY, rW, rH);
+
+ while (dH--) {
+ int16 xpos = (int16) marginLeft;
+
+ if (flags & 1) {
+ if (pixelsPerByte == 1) {
+ for (int i = 0; i < w2; i++) {
+ if (*src++ == 0) {
+ i += (*src - 1);
+ src += (*src - 1);
+ }
+ }
+ } else {
+ src += w2;
+ }
+ src--;
+ }
+ const uint8 *src2 = src;
+
+ if (xpos) {
+ if (pixelsPerByte == 1) {
+ do {
+ uint8 val = (flags & 1) ? *(src - 1) : *src;
+ while (val && xpos) {
+ src += pixelStep;
+ xpos--;
+ val = (flags & 1) ? *(src - 1) : *src;
+ }
+
+ val = (flags & 1) ? *(src - 1) : *src;
+ if (!val) {
+ src += pixelStep;
+ uint8 bt = (flags & 1) ? src[1] : src[0];
+ src += pixelStep;
+ xpos = xpos - bt;
+ }
+ } while (xpos > 0);
+ } else {
+ src += (xpos * pixelStep);
+ xpos = 0;
+ }
+ }
+
+ dst -= xpos * _bytesPerPixel;
+ xpos += width;
+
+ while (xpos > 0) {
+ uint8 c = *src;
+ uint8 m = (flags & 1) ? *(src - 1) : c;
+ src += pixelStep;
+
+ if (m) {
+ drawShapeSetPixel(dst, c);
+ dst += _bytesPerPixel;
+ xpos--;
+ } else if (pixelsPerByte) {
+ uint8 len = (flags & 1) ? src[1] : src[0];
+ dst += len * _bytesPerPixel;
+ xpos -= len;
+ src += pixelStep;
+ } else {
+ dst += _bytesPerPixel;
+ xpos--;
+ }
+ }
+ xpos += marginRight;
+
+ if (xpos) {
+ do {
+ if (pixelsPerByte == 1) {
+ uint8 val = (flags & 1) ? *(src - 1) : *src;
+ while (val && xpos) {
+ src += pixelStep;
+ xpos--;
+ val = (flags & 1) ? *(src - 1) : *src;
+ }
+
+ val = (flags & 1) ? *(src - 1) : *src;
+ if (!val) {
+ src += pixelStep;
+ uint8 bt = (flags & 1) ? src[1] : src[0];
+ src += pixelStep;
+ xpos = xpos - bt;
+ }
+ } else {
+ src += (xpos * pixelStep);
+ xpos = 0;
+ }
+ } while (xpos > 0);
+ }
+
+ dstL += SCREEN_W * _bytesPerPixel;
+ dst = dstL;
+ if (flags & 1)
+ src = src2 + 1;
+ }
+ } else {
+ const uint8 *pal = 0;
+ uint8 cgaPal[4];
+ memset(cgaPal, 0, 4);
+
+ if (pixelsPerByte == 2) {
+ pal = ovl ? ovl : src;
+ src += 16;
+ } else {
+ static const uint8 cgaDefOvl[] = { 0x00, 0x55, 0xAA, 0xFF };
+ pal = ovl ? ovl : cgaDefOvl;
+ for (int i = 0; i < 4; i++)
+ cgaPal[i] = pal[i] & 3;
+ pal = cgaPal;
+ }
+
+ if (d < 0) {
+ d = -d;
+ if (d >= dH)
+ return;
+ src += (d * (width / pixelsPerByte));
+ d = dY + dH - _dsY1;
+ if (d >= 0) {
+ dH = d;
+ dY = _dsY1;
+ d = _dsY2 - dY;
+ }
+ } else {
+ d = _dsY2 - dY;
+ }
+
+ if (d < 1)
+ return;
+
+ if (d < dH)
+ dH = d;
+
+ bool trimL = false;
+ uint8 dXbitAlign = dX & (pixelsPerByte - 1);
+
+ if (dX < 0) {
+ width += dX;
+ d = -dX;
+ if (flags & 1)
+ src -= (d / pixelsPerByte);
+ else
+ src += (d / pixelsPerByte);
+
+ if (d >= w2)
+ return;
+
+ dX = 0;
+ trimL = true;
+ }
+
+ d = (dW << 3) - dX;
+
+ if (d < 1)
+ return;
+
+ if (d < width)
+ width = d;
+
+ dst += (dY * SCREEN_W * _bytesPerPixel + dX * _bytesPerPixel);
+
+ if (pageNum == 0 || pageNum == 1)
+ addDirtyRect(rX, rY, rW, rH);
+
+ int pitch = SCREEN_W - width;
+ int16 lineSrcStep = (w2 - width) / pixelsPerByte;
+ uint8 lineSrcStepRemainder = (w2 - width) % pixelsPerByte;
+
+ w2 /= pixelsPerByte;
+ if (flags & 1)
+ src += (w2 - 1);
+
+ uint8 pixelPacking = 8 / pixelsPerByte;
+ uint8 pixelPackingMask = 0;
+
+ for (int i = 0; i < pixelPacking; i++)
+ pixelPackingMask |= (1 << i);
+
+ if (trimL && (dXbitAlign > lineSrcStepRemainder))
+ lineSrcStep--;
+
+ uint8 bitShDef = 8 - pixelPacking;
+ if (flags & 1) {
+ lineSrcStep = (w2 << 1) - lineSrcStep;
+ bitShDef = 0;
+ }
+
+ uint8 bitShLineStart = bitShDef;
+ if (trimL)
+ bitShLineStart -= (dXbitAlign * pixelStep * pixelPacking);
+
+ while (dH--) {
+ int16 wd = width;
+ uint8 in = 0;
+ uint8 trans = 0;
+ uint8 shift = bitShLineStart;
+ uint8 shSwtch = bitShLineStart;
+
+ while (wd--) {
+ if (shift == shSwtch) {
+ in = *src;
+ trans = src[transOffset];
+ src += pixelStep;
+ shSwtch = bitShDef;
+ }
+ uint8 col = (pixelsPerByte == 2) ? pal[(in >> shift) & pixelPackingMask] : (*dst & ((trans >> shift) & (pixelPackingMask))) | pal[(in >> shift) & pixelPackingMask];
+ if (col || pixelsPerByte == 4)
+ drawShapeSetPixel(dst, col);
+ dst += _bytesPerPixel;
+ shift = ((shift - (pixelStep * pixelPacking)) & 7);
+ }
+ src += lineSrcStep;
+ dst += (pitch * _bytesPerPixel);
+ }
+ }
+}
+
+const uint8 *Screen_EoB::scaleShape(const uint8 *shapeData, int steps) {
+ setShapeFadingLevel(steps);
+
+ while (shapeData && steps--)
+ shapeData = scaleShapeStep(shapeData);
+
+ return shapeData;
+}
+
+const uint8 *Screen_EoB::scaleShapeStep(const uint8 *shp) {
+ uint8 *dst = (shp != _dsTempPage) ? _dsTempPage : _dsTempPage + 6000;
+ uint8 *d = dst;
+ uint8 pixelsPerByte = *d++ = *shp++;
+ assert(pixelsPerByte > 1);
+
+ uint16 h = shp[0] + 1;
+ d[0] = d[2] = (h << 1) / 3;
+
+ uint16 w = shp[1];
+ uint16 w2 = (w << 3) / pixelsPerByte;
+ uint16 t = ((w << 1) % 3) ? 1 : 0;
+ d[1] = ((w << 1) / 3) + t;
+
+ uint32 transOffsetSrc = (pixelsPerByte == 4) ? (shp[0] * shp[1]) << 1 : 0;
+ uint32 transOffsetDst = (pixelsPerByte == 4) ? (d[0] * d[1]) << 1 : 0;
+ shp += 3;
+ d += 3;
+
+ if (pixelsPerByte == 2) {
+ int i = 0;
+ while (i < 16) {
+ if (!shp[i]) {
+ i = -i;
+ break;
+ }
+ i++;
+ }
+
+ if (i >= 0)
+ i = 0;
+ else
+ i = -i;
+
+ _dsScaleTrans = (i << 4) | (i & 0x0F);
+ for (int ii = 0; ii < 16; ii++)
+ *d++ = *shp++;
+ }
+
+ _dsDiv = w2 / 3;
+ _dsRem = w2 % 3;
+
+ while (--h) {
+ if (pixelsPerByte == 2)
+ scaleShapeProcessLine4Bit(d, shp);
+ else
+ scaleShapeProcessLine2Bit(d, shp, transOffsetDst, transOffsetSrc);
+ if (!--h)
+ break;
+ if (pixelsPerByte == 2)
+ scaleShapeProcessLine4Bit(d, shp);
+ else
+ scaleShapeProcessLine2Bit(d, shp, transOffsetDst, transOffsetSrc);
+ if (!--h)
+ break;
+ shp += w2;
+ }
+
+ return (const uint8 *)dst;
+}
+
+const uint8 *Screen_EoB::generateShapeOverlay(const uint8 *shp, const uint8 *fadingTable) {
+ if (*shp != 2)
+ return 0;
+
+ if (_bytesPerPixel == 2) {
+ setFadeTable(fadingTable);
+ setShapeFadingLevel(1);
+ return 0;
+ }
+
+ shp += 4;
+ for (int i = 0; i < 16; i++)
+ _shapeOverlay[i] = fadingTable[shp[i]];
+ return _shapeOverlay;
+}
+
+void Screen_EoB::setShapeFrame(int x1, int y1, int x2, int y2) {
+ _dsX1 = x1;
+ _dsY1 = y1;
+ _dsX2 = x2;
+ _dsY2 = y2;
+}
+
+void Screen_EoB::enableShapeBackgroundFading(bool enable) {
+ _dsBackgroundFading = enable;
+}
+
+void Screen_EoB::setShapeFadingLevel(int level) {
+ _dsShapeFadingLevel = level;
+}
+
+void Screen_EoB::setGfxParameters(int x, int y, int col) {
+ _gfxX = x;
+ _gfxY = y;
+ _gfxCol = col;
+}
+
+void Screen_EoB::drawExplosion(int scale, int radius, int numElements, int stepSize, int aspectRatio, const uint8 *colorTable, int colorTableSize) {
+ int ymin = 0;
+ int ymax = _gfxMaxY[scale];
+ int xmin = -100;
+ int xmax = 276;
+
+ if (scale)
+ --scale;
+
+ hideMouse();
+
+ const ScreenDim *dm = getScreenDim(5);
+ int rX1 = dm->sx << 3;
+ int rY1 = dm->sy;
+ int rX2 = rX1 + (dm->w << 3);
+ int rY2 = rY1 + dm->h - 1;
+
+ int16 gx2 = _gfxX;
+ int16 gy2 = _gfxY;
+
+ int16 *ptr2 = (int16 *)_dsTempPage;
+ int16 *ptr3 = (int16 *)&_dsTempPage[300];
+ int16 *ptr4 = (int16 *)&_dsTempPage[600];
+ int16 *ptr5 = (int16 *)&_dsTempPage[900];
+ int16 *ptr6 = (int16 *)&_dsTempPage[1200];
+ int16 *ptr7 = (int16 *)&_dsTempPage[1500];
+ int16 *ptr8 = (int16 *)&_dsTempPage[1800];
+
+ if (numElements > 150)
+ numElements = 150;
+
+ for (int i = 0; i < numElements; i++) {
+ ptr2[i] = ptr3[i] = 0;
+ ptr4[i] = _vm->_rnd.getRandomNumberRng(0, radius) - (radius >> 1);
+ ptr5[i] = _vm->_rnd.getRandomNumberRng(0, radius) - (radius >> 1) - (radius >> (8 - aspectRatio));
+ ptr7[i] = _vm->_rnd.getRandomNumberRng(1024 / stepSize, 2048 / stepSize);
+ ptr8[i] = scale << 8;
+ }
+
+ for (int l = 2; l;) {
+ if (l != 2) {
+ for (int i = numElements - 1; i >= 0; i--) {
+ int16 px = ((ptr2[i] >> 6) >> scale) + gx2;
+ int16 py = ((ptr3[i] >> 6) >> scale) + gy2;
+ if (py > ymax)
+ py = ymax;
+ if (posWithinRect(px, py, rX1, rY1, rX2, rY2)) {
+ if (_bytesPerPixel == 2)
+ setPagePixel16bit(0, px, py, ptr6[i]);
+ else
+ setPagePixel(0, px, py, ptr6[i]);
+ }
+ }
+ }
+
+ l = 0;
+
+ for (int i = 0; i < numElements; i++) {
+ uint32 end = _system->getMillis() + 1;
+ if (ptr4[i] <= 0)
+ ptr4[i]++;
+ else
+ ptr4[i]--;
+ ptr2[i] += ptr4[i];
+ ptr5[i] += 5;
+ ptr3[i] += ptr5[i];
+ ptr8[i] += ptr7[i];
+
+ int16 px = ((ptr2[i] >> 6) >> scale) + gx2;
+ int16 py = ((ptr3[i] >> 6) >> scale) + gy2;
+ if (py >= ymax || py < ymin)
+ ptr5[i] = -(ptr5[i] >> 1);
+ if (px >= xmax || px < xmin)
+ ptr4[i] = -(ptr4[i] >> 1);
+
+ if (py > ymax)
+ py = ymax;
+
+ int pxVal1 = 0;
+ if (posWithinRect(px, py, 0, 0, 319, 199)) {
+ pxVal1 = getPagePixel(2, px, py);
+ ptr6[i] = getPagePixel(0, px, py);
+ }
+
+ assert((ptr8[i] >> 8) < colorTableSize);
+ int pxVal2 = colorTable[ptr8[i] >> 8];
+ if (pxVal2) {
+ l = 1;
+ if (pxVal1 == _gfxCol && posWithinRect(px, py, rX1, rY1, rX2, rY2)) {
+ setPagePixel(0, px, py, pxVal2);
+ if (i % 5 == 0) {
+ updateScreen();
+ uint32 cur = _system->getMillis();
+ if (end > cur)
+ _system->delayMillis(end - cur);
+ }
+ }
+ } else {
+ ptr7[i] = 0;
+ }
+ }
+ }
+
+ showMouse();
+}
+
+void Screen_EoB::drawVortex(int numElements, int radius, int stepSize, int, int disorder, const uint8 *colorTable, int colorTableSize) {
+ int16 *xCoords = (int16 *)_dsTempPage;
+ int16 *yCoords = (int16 *)&_dsTempPage[300];
+ int16 *xMod = (int16 *)&_dsTempPage[600];
+ int16 *yMod = (int16 *)&_dsTempPage[900];
+ int16 *pixBackup = (int16 *)&_dsTempPage[1200];
+ int16 *colTableStep = (int16 *)&_dsTempPage[1500];
+ int16 *colTableIndex = (int16 *)&_dsTempPage[1800];
+ int16 *pixDelay = (int16 *)&_dsTempPage[2100];
+
+ hideMouse();
+ int cp = _curPage;
+
+ if (numElements > 150)
+ numElements = 150;
+
+ int cx = 88;
+ int cy = 48;
+ radius <<= 6;
+
+ for (int i = 0; i < numElements; i++) {
+ int16 v38 = _vm->_rnd.getRandomNumberRng(radius >> 2, radius);
+ int16 stepSum = 0;
+ int16 sqsum = 0;
+ while (sqsum < v38) {
+ stepSum += stepSize;
+ sqsum += stepSum;
+ }
+
+ switch (_vm->_rnd.getRandomNumber(255) & 3) {
+ case 0:
+ xCoords[i] = 32;
+ yCoords[i] = sqsum;
+ xMod[i] = stepSum;
+ yMod[i] = 0;
+ break;
+
+ case 1:
+ xCoords[i] = sqsum;
+ yCoords[i] = 32;
+ xMod[i] = 0;
+ yMod[i] = stepSum;
+ break;
+
+ case 2:
+ xCoords[i] = 32;
+ yCoords[i] = -sqsum;
+ xMod[i] = stepSum;
+ yMod[i] = 0;
+ break;
+
+ default:
+ xCoords[i] = -sqsum;
+ yCoords[i] = 32;
+ xMod[i] = 0;
+ yMod[i] = stepSum;
+ break;
+ }
+
+ if (_vm->_rnd.getRandomBit()) {
+ xMod[i] *= -1;
+ yMod[i] *= -1;
+ }
+
+ colTableStep[i] = _vm->_rnd.getRandomNumberRng(1024 / disorder, 2048 / disorder);
+ colTableIndex[i] = 0;
+ pixDelay[i] = _vm->_rnd.getRandomNumberRng(0, disorder >> 2);
+ }
+
+ int d = 0;
+ for (int i = 2; i;) {
+ if (i != 2) {
+ for (int ii = numElements - 1; ii >= 0; ii--) {
+ int16 px = CLIP((xCoords[ii] >> 6) + cx, 0, SCREEN_W - 1);
+ int16 py = CLIP((yCoords[ii] >> 6) + cy, 0, SCREEN_H - 1);
+ if (_bytesPerPixel == 2)
+ setPagePixel16bit(0, px, py, pixBackup[ii]);
+ else
+ setPagePixel(0, px, py, pixBackup[ii]);
+ }
+ }
+
+ i = 0;
+ int r = (stepSize >> 1) + (stepSize >> 2) + (stepSize >> 3);
+ uint32 nextDelay = _system->getMillis() + 1;
+
+ for (int ii = 0; ii < numElements; ii++) {
+ if (pixDelay[ii] == 0) {
+ if (xCoords[ii] > 0) {
+ xMod[ii] -= ((xMod[ii] > 0) ? stepSize : r);
+ } else {
+ xMod[ii] += ((xMod[ii] < 0) ? stepSize : r);
+ }
+
+ if (yCoords[ii] > 0) {
+ yMod[ii] -= ((yMod[ii] > 0) ? stepSize : r);
+ } else {
+ yMod[ii] += ((yMod[ii] < 0) ? stepSize : r);
+ }
+
+ xCoords[ii] += xMod[ii];
+ yCoords[ii] += yMod[ii];
+ colTableIndex[ii] += colTableStep[ii];
+
+ } else {
+ pixDelay[ii]--;
+ }
+
+ int16 px = CLIP((xCoords[ii] >> 6) + cx, 0, SCREEN_W - 1);
+ int16 py = CLIP((yCoords[ii] >> 6) + cy, 0, SCREEN_H - 1);
+
+ uint8 tc1 = ((disorder >> 2) <= d) ? getPagePixel(2, px, py) : 0;
+ pixBackup[ii] = getPagePixel(0, px, py);
+ uint8 tblIndex = CLIP(colTableIndex[ii] >> 8, 0, colorTableSize - 1);
+ uint8 tc2 = colorTable[tblIndex];
+
+ if (tc2) {
+ i = 1;
+ if (tc1 == _gfxCol && !pixDelay[ii]) {
+ setPagePixel(0, px, py, tc2);
+ if (ii % 15 == 0) {
+ updateScreen();
+ uint32 cur = _system->getMillis();
+ if (nextDelay > cur)
+ _system->delayMillis(nextDelay - cur);
+ nextDelay += 1;
+ }
+ }
+ } else {
+ colTableStep[ii] = 0;
+ }
+ }
+ d++;
+ }
+
+ _curPage = cp;
+ showMouse();
+}
+
+void Screen_EoB::fadeTextColor(Palette *pal, int color1, int rate) {
+ uint8 *col = pal->getData();
+
+ for (bool loop = true; loop;) {
+ loop = true;
+ uint32 end = _system->getMillis() + _vm->tickLength();
+
+ loop = false;
+ for (int ii = 0; ii < 3; ii++) {
+ uint8 c = col[color1 * 3 + ii];
+ if (c > rate) {
+ col[color1 * 3 + ii] -= rate;
+ loop = true;
+ } else if (c) {
+ col[color1 * 3 + ii] = 0;
+ loop = true;
+ }
+ }
+
+ if (loop) {
+ setScreenPalette(*pal);
+ updateScreen();
+ uint32 cur = _system->getMillis();
+ if (end > cur)
+ _system->delayMillis(end - cur);
+ }
+ }
+}
+
+bool Screen_EoB::delayedFadePalStep(Palette *fadePal, Palette *destPal, int rate) {
+ bool res = false;
+
+ uint8 *s = fadePal->getData();
+ uint8 *d = destPal->getData();
+
+ for (int i = 0; i < 765; i++) {
+ int fadeVal = *s++;
+ int dstCur = *d;
+ int diff = ABS(fadeVal - dstCur);
+
+ if (diff == 0) {
+ d++;
+ continue;
+ }
+
+ res = true;
+ diff = MIN(diff, rate);
+
+ if (dstCur < fadeVal)
+ *d += diff;
+ else
+ *d -= diff;
+ d++;
+ }
+
+ return res;
+}
+
+int Screen_EoB::getRectSize(int w, int h) {
+ return w * h;
+}
+
+void Screen_EoB::setFadeTable(const uint8 *table) {
+ _dsShapeFadingTable = table;
+ if (_bytesPerPixel == 2)
+ memcpy(&_16bitPalette[0x100], table, 512);
+}
+
+void Screen_EoB::createFadeTable(const uint8 *palData, uint8 *dst, uint8 rootColor, uint8 weight) {
+ if (!palData)
+ return;
+
+ const uint8 *src = palData + 3 * rootColor;
+ uint8 r = *src++;
+ uint8 g = *src++;
+ uint8 b = *src;
+ uint8 tr, tg, tb;
+ src = palData + 3;
+
+ *dst++ = 0;
+ weight >>= 1;
+
+ for (uint8 i = 1; i; i++) {
+ uint16 tmp = (uint16)((*src - r) * weight) << 1;
+ tr = *src++ - ((tmp >> 8) & 0xFF);
+ tmp = (uint16)((*src - g) * weight) << 1;
+ tg = *src++ - ((tmp >> 8) & 0xFF);
+ tmp = (uint16)((*src - b) * weight) << 1;
+ tb = *src++ - ((tmp >> 8) & 0xFF);
+
+ const uint8 *d = palData + 3;
+ uint16 v = 0xFFFF;
+ uint8 col = rootColor;
+
+ for (uint8 ii = 1; ii; ii++) {
+ int a = *d++ - tr;
+ int t = a * a;
+ a = *d++ - tg;
+ t += (a * a);
+ a = *d++ - tb;
+ t += (a * a);
+
+ if (t <= v && (ii == rootColor || ii != i)) {
+ v = t;
+ col = ii;
+ }
+ }
+ *dst++ = col;
+ }
+}
+
+void Screen_EoB::createFadeTable16bit(const uint16 *palData, uint16 *dst, uint16 rootColor, uint8 weight) {
+ rootColor = palData[rootColor];
+ uint8 r8 = (rootColor & 0x1f);
+ uint8 g8 = (rootColor & 0x3E0) >> 5;
+ uint8 b8 = (rootColor & 0x7C00) >> 10;
+
+ int root_r = r8 << 4;
+ int root_g = g8 << 4;
+ int root_b = b8 << 4;
+
+ *dst++ = palData[0];
+
+ for (uint8 i = 1; i; i++) {
+ r8 = (palData[i] & 0x1f);
+ g8 = (palData[i] & 0x3E0) >> 5;
+ b8 = (palData[i] & 0x7C00) >> 10;
+
+ int red = r8 << 4;
+ int green = g8 << 4;
+ int blue = b8 << 4;
+
+ if (red > root_r) {
+ red -= weight;
+ if (root_r > red)
+ red = root_r;
+ } else {
+ red += weight;
+ if (root_r < red)
+ red = root_r;
+ }
+
+ if (green > root_g) {
+ green -= weight;
+ if (root_g > green)
+ green = root_g;
+ } else {
+ green += weight;
+ if (root_g < green)
+ green = root_g;
+ }
+
+ if (blue > root_b) {
+ blue -= weight;
+ if (root_b > blue)
+ blue = root_b;
+ } else {
+ blue += weight;
+ if (root_b < blue)
+ blue = root_b;
+ }
+
+ r8 = red >> 4;
+ g8 = green >> 4;
+ b8 = blue >> 4;
+
+ *dst++ = (b8 << 10) | (g8 << 5) | r8;
+ }
+}
+
+const uint16 *Screen_EoB::getCGADitheringTable(int index) {
+ return !(index & ~1) ? _cgaDitheringTables[index] : 0;
+}
+
+const uint8 *Screen_EoB::getEGADitheringTable() {
+ return _egaDitheringTable;
+}
+
+bool Screen_EoB::loadFont(FontId fontId, const char *filename) {
+ if (scumm_stricmp(filename, "FONT.DMP"))
+ return Screen::loadFont(fontId, filename);
+
+ Font *&fnt = _fonts[fontId];
+ int temp;
+
+ const uint16 *tbl = _vm->staticres()->loadRawDataBe16(kEoB2FontDmpSearchTbl, temp);
+ assert(tbl);
+
+ if (!fnt) {
+ fnt = new SJISFont12x12(tbl);
+ assert(fnt);
+ }
+
+ Common::SeekableReadStream *file = _vm->resource()->createReadStream(filename);
+ if (!file)
+ error("Font file '%s' is missing", filename);
+
+ bool ret = fnt->load(*file);
+ fnt->setColorMap(_textColorsMap);
+ delete file;
+ return ret;
+}
+
+void Screen_EoB::decodeSHP(const uint8 *data, int dstPage) {
+ int32 bytesLeft = READ_LE_UINT32(data);
+ const uint8 *src = data + 4;
+ uint8 *dst = getPagePtr(dstPage);
+
+ if (bytesLeft < 0) {
+ memcpy(dst, data, 64000);
+ return;
+ }
+
+ while (bytesLeft > 0) {
+ uint8 code = *src++;
+ bytesLeft--;
+
+ for (int i = 8; i; i--) {
+ if (code & 0x80) {
+ uint16 copyOffs = (src[0] << 4) | (src[1] >> 4);
+ uint8 count = (src[1] & 0x0F) + 3;
+ src += 2;
+ bytesLeft -= 2;
+ const uint8 *copySrc = dst - 1 - copyOffs;
+ while (count--)
+ *dst++ = *copySrc++;
+ } else if (bytesLeft) {
+ *dst++ = *src++;
+ bytesLeft--;
+ } else {
+ break;
+ }
+ code <<= 1;
+ }
+ }
+}
+
+void Screen_EoB::convertToHiColor(int page) {
+ if (!_16bitPalette)
+ return;
+ uint16 *dst = (uint16 *)getPagePtr(page);
+ memcpy(_convertHiColorBuffer, dst, SCREEN_H * SCREEN_W);
+ uint8 *src = _convertHiColorBuffer;
+ for (int s = SCREEN_H * SCREEN_W; s; --s)
+ *dst++ = _16bitPalette[*src++];
+}
+
+void Screen_EoB::shadeRect(int x1, int y1, int x2, int y2, int shadingLevel) {
+ if (!_16bitPalette)
+ return;
+
+ int l = _16bitShadingLevel;
+ _16bitShadingLevel = shadingLevel;
+
+ if (_curPage == 0 || _curPage == 1)
+ addDirtyRect(x1, y1, x2 - x1 + 1, y2 - y1 + 1);
+
+ uint16 *dst = (uint16*)(getPagePtr(_curPage) + y1 * SCREEN_W * _bytesPerPixel + x1 * _bytesPerPixel);
+
+ for (; y1 < y2; ++y1) {
+ uint16 *ptr = dst;
+ for (int i = 0; i < x2 - x1; i++) {
+ *ptr = shade16bitColor(*ptr);
+ ptr++;
+ }
+ dst += SCREEN_W;
+ }
+
+ _16bitShadingLevel = l;
+}
+
+void Screen_EoB::updateDirtyRects() {
+ if (!_useHiResEGADithering) {
+ Screen::updateDirtyRects();
+ return;
+ }
+
+ if (_forceFullUpdate) {
+ ditherRect(getCPagePtr(0), _egaDitheringTempPage, SCREEN_W * 2, SCREEN_W, SCREEN_H);
+ _system->copyRectToScreen(_egaDitheringTempPage, SCREEN_W * 2, 0, 0, SCREEN_W * 2, SCREEN_H * 2);
+ } else {
+ const byte *page0 = getCPagePtr(0);
+ Common::List<Common::Rect>::iterator it;
+ for (it = _dirtyRects.begin(); it != _dirtyRects.end(); ++it) {
+ ditherRect(page0 + it->top * SCREEN_W + it->left, _egaDitheringTempPage, SCREEN_W * 2, it->width(), it->height());
+ _system->copyRectToScreen(_egaDitheringTempPage, SCREEN_W * 2, it->left * 2, it->top * 2, it->width() * 2, it->height() * 2);
+ }
+ }
+ _forceFullUpdate = false;
+ _dirtyRects.clear();
+}
+
+void Screen_EoB::ditherRect(const uint8 *src, uint8 *dst, int dstPitch, int srcW, int srcH, int colorKey) {
+ while (srcH--) {
+ uint8 *dst2 = dst + dstPitch;
+ for (int i = 0; i < srcW; i++) {
+ int in = *src++;
+ if (in != colorKey) {
+ in = _egaDitheringTable[in];
+ *dst++ = *dst2++ = in >> 4;
+ *dst++ = *dst2++ = in & 0x0F;
+ } else {
+ dst[0] = dst[1] = dst2[0] = dst2[1] = colorKey;
+ dst += 2;
+ dst2 += 2;
+ }
+ }
+ src += (SCREEN_W - srcW);
+ dst += ((dstPitch - srcW) * 2);
+ }
+}
+
+void Screen_EoB::drawShapeSetPixel(uint8 *dst, uint8 col) {
+ if (_bytesPerPixel == 2) {
+ *(uint16*)dst = _16bitPalette[(_dsShapeFadingLevel << 8) + col];
+ return;
+ } else if ((_renderMode != Common::kRenderCGA && _renderMode != Common::kRenderEGA) || _useHiResEGADithering) {
+ if (_dsBackgroundFading) {
+ if (_dsShapeFadingLevel) {
+ col = *dst;
+ } else {
+ _dsBackgroundFadingXOffs &= 7;
+ col = *(dst + _dsBackgroundFadingXOffs++);
+ }
+ }
+
+ if (_dsShapeFadingLevel) {
+ uint8 cnt = _dsShapeFadingLevel;
+ while (cnt--)
+ col = _dsShapeFadingTable[col];
+ }
+ }
+
+ *dst = col;
+}
+
+void Screen_EoB::scaleShapeProcessLine2Bit(uint8 *&shpDst, const uint8 *&shpSrc, uint32 transOffsetDst, uint32 transOffsetSrc) {
+ for (int i = 0; i < _dsDiv; i++) {
+ shpDst[0] = (_cgaScaleTable[shpSrc[0]] << 2) | (shpSrc[1] >> 6);
+ shpDst[1] = ((shpSrc[1] & 0x0F) << 4) | ((shpSrc[2] >> 2) & 0x0F);
+ shpDst[transOffsetDst] = (_cgaScaleTable[shpSrc[transOffsetSrc]] << 2) | (shpSrc[transOffsetSrc + 1] >> 6);
+ shpDst[transOffsetDst + 1] = ((shpSrc[transOffsetSrc + 1] & 0x0F) << 4) | ((shpSrc[transOffsetSrc + 2] >> 2) & 0x0F);
+ shpSrc += 3;
+ shpDst += 2;
+ }
+
+ if (_dsRem == 1) {
+ shpDst[0] = _cgaScaleTable[shpSrc[0]] << 2;
+ shpDst[1] = 0;
+ shpDst[transOffsetDst] = (_cgaScaleTable[shpSrc[transOffsetSrc]] << 2) | 3;
+ shpDst[transOffsetDst + 1] = 0xFF;
+ shpSrc++;
+ shpDst += 2;
+
+ } else if (_dsRem == 2) {
+ shpDst[0] = (_cgaScaleTable[shpSrc[0]] << 2) | (shpSrc[1] >> 6);
+ shpDst[1] = (shpSrc[1] & 0x3F) << 2;
+ shpDst[transOffsetDst] = (_cgaScaleTable[shpSrc[transOffsetSrc]] << 2) | (shpSrc[transOffsetSrc + 1] >> 6);
+ shpDst[transOffsetDst + 1] = ((shpSrc[transOffsetSrc + 1] & 0x3F) << 2) | 3;
+ shpSrc += 2;
+ shpDst += 2;
+ }
+}
+
+void Screen_EoB::scaleShapeProcessLine4Bit(uint8 *&dst, const uint8 *&src) {
+ for (int i = 0; i < _dsDiv; i++) {
+ *dst++ = *src++;
+ *dst++ = (READ_BE_UINT16(src) >> 4) & 0xFF;
+ src += 2;
+ }
+
+ if (_dsRem == 1) {
+ *dst++ = *src++;
+ *dst++ = _dsScaleTrans;
+ } else if (_dsRem == 2) {
+ *dst++ = (src[0] & 0xF0) | (src[1] >> 4);
+ src += 2;
+ *dst++ = _dsScaleTrans;
+ *dst++ = _dsScaleTrans;
+ *dst++ = _dsScaleTrans;
+ }
+}
+
+bool Screen_EoB::posWithinRect(int posX, int posY, int x1, int y1, int x2, int y2) {
+ if (posX < x1 || posX > x2 || posY < y1 || posY > y2)
+ return false;
+ return true;
+}
+
+void Screen_EoB::setPagePixel16bit(int pageNum, int x, int y, uint16 color) {
+ assert(pageNum < SCREEN_PAGE_NUM);
+ assert(x >= 0 && x < SCREEN_W && y >= 0 && y < SCREEN_H);
+ assert(_bytesPerPixel == 2);
+
+ if (pageNum == 0 || pageNum == 1)
+ addDirtyRect(x, y, 1, 1);
+
+ ((uint16*)_pagePtrs[pageNum])[y * SCREEN_W + x] = color;
+}
+
+void Screen_EoB::generateEGADitheringTable(const Palette &pal) {
+ assert(_egaDitheringTable);
+ const uint8 *src = pal.getData();
+ uint8 *dst = _egaDitheringTable;
+
+ for (int i = 256; i; i--) {
+ int r = *src++;
+ int g = *src++;
+ int b = *src++;
+
+ uint8 col = 0;
+ uint16 min = 0x2E83;
+
+ for (int ii = 256; ii; ii--) {
+ const uint8 *palEntry = _egaMatchTable + (ii - 1) * 3;
+ if (*palEntry == 0xFF)
+ continue;
+
+ int e_r = palEntry[0] - r;
+ int e_g = palEntry[1] - g;
+ int e_b = palEntry[2] - b;
+
+ uint16 s = (e_r * e_r) + (e_g * e_g) + (e_b * e_b);
+
+ if (s <= min) {
+ min = s;
+ col = ii - 1;
+ }
+ }
+ *dst++ = col;
+ }
+}
+
+void Screen_EoB::generateCGADitheringTables(const uint8 *mappingData) {
+ for (int i = 0; i < 256; i++) {
+ _cgaDitheringTables[0][i] = (mappingData[(i >> 4) + 16] << 8) | mappingData[i & 0x0F];
+ _cgaDitheringTables[1][i] = (mappingData[i >> 4] << 8) | mappingData[(i & 0x0F) + 16];
+ }
+}
+
+const uint8 Screen_EoB::_egaMatchTable[] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x1E, 0x00, 0x00, 0x1E, 0x1E, 0x1E, 0x00, 0x00, 0x1E,
+ 0x00, 0x1E, 0x1E, 0x0F, 0x00, 0x1E, 0x1E, 0x1E, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x2D, 0x0F, 0x2D,
+ 0x0F, 0x0F, 0x2D, 0x2D, 0x2D, 0x0F, 0x0F, 0x2D, 0x0F, 0x2D, 0x2D, 0x2D, 0x0F, 0x2D, 0x2D, 0x2D,
+ 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x2A, 0x00, 0x1E, 0x1E, 0x00, 0x1E, 0x2A, 0x1E, 0x00, 0x1E, 0x1E,
+ 0x00, 0x2A, 0x1E, 0x0F, 0x1E, 0x1E, 0x1E, 0x2A, 0x0F, 0x0F, 0x21, 0x0F, 0x0F, 0x36, 0x0F, 0x2D,
+ 0x21, 0x0F, 0x2D, 0x36, 0x2D, 0x0F, 0x21, 0x2D, 0x0F, 0x36, 0x2D, 0x2D, 0x21, 0x2D, 0x2D, 0x36,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x2A, 0x00, 0x00, 0x2A, 0x1E, 0x1E, 0x1E, 0x00, 0x1E,
+ 0x1E, 0x1E, 0x1E, 0x21, 0x00, 0x1E, 0x2A, 0x1E, 0x0F, 0x21, 0x0F, 0x0F, 0x21, 0x2D, 0x0F, 0x36,
+ 0x0F, 0x0F, 0x36, 0x2D, 0x2D, 0x21, 0x0F, 0x2D, 0x21, 0x2D, 0x2D, 0x36, 0x0F, 0x2D, 0x36, 0x2D,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x2A, 0x2A, 0x1E, 0x1E, 0x1E, 0x1E,
+ 0x1E, 0x2A, 0x1E, 0x21, 0x1E, 0x1E, 0x2A, 0x2A, 0x0F, 0x21, 0x21, 0x0F, 0x21, 0x36, 0x0F, 0x36,
+ 0x21, 0x0F, 0x36, 0x36, 0x2D, 0x21, 0x21, 0x2D, 0x21, 0x36, 0x2D, 0x36, 0x21, 0x2D, 0x36, 0x36,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x2A, 0x00, 0x00, 0x2A,
+ 0x00, 0x1E, 0x2A, 0x0F, 0x00, 0x2A, 0x1E, 0x1E, 0x21, 0x0F, 0x0F, 0x21, 0x0F, 0x2D, 0x21, 0x2D,
+ 0x0F, 0x21, 0x2D, 0x2D, 0x36, 0x0F, 0x0F, 0x36, 0x0F, 0x2D, 0x36, 0x2D, 0x0F, 0x36, 0x2D, 0x2D,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x2A,
+ 0x00, 0x2A, 0x2A, 0x0F, 0x1E, 0x2A, 0x1E, 0x2A, 0x21, 0x0F, 0x21, 0x21, 0x0F, 0x36, 0x21, 0x2D,
+ 0x21, 0x21, 0x2D, 0x36, 0x36, 0x0F, 0x21, 0x36, 0x0F, 0x36, 0x36, 0x2D, 0x21, 0x36, 0x2D, 0x36,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0x2A, 0x15, 0x00, 0x2A, 0x21, 0x1E, 0x21, 0x15, 0x0F, 0x21, 0x15, 0x2D, 0x21, 0x2F,
+ 0x0F, 0x21, 0x2F, 0x2D, 0x36, 0x15, 0x0F, 0x36, 0x15, 0x2D, 0x36, 0x2F, 0x0F, 0x36, 0x2F, 0x2D,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x2A, 0x2A, 0x2A, 0x21, 0x21, 0x21, 0x21, 0x21, 0x36, 0x21, 0x36,
+ 0x21, 0x21, 0x36, 0x36, 0x36, 0x21, 0x21, 0x36, 0x21, 0x36, 0x36, 0x36, 0x21, 0x36, 0x36, 0x36,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x15, 0x15, 0x15, 0x15, 0x15, 0x2F, 0x15, 0x2F,
+ 0x15, 0x15, 0x2F, 0x2F, 0x2F, 0x15, 0x15, 0x2F, 0x15, 0x2F, 0x2F, 0x2F, 0x15, 0x2F, 0x2F, 0x2F,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x15, 0x15, 0x3F, 0x15, 0x2F,
+ 0x2F, 0x15, 0x2F, 0x3F, 0x2F, 0x15, 0x2F, 0x2F, 0x15, 0x3F, 0x2F, 0x2F, 0x2F, 0x2F, 0x2F, 0x3F,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x15, 0x3F,
+ 0x15, 0x15, 0x3F, 0x2F, 0x2F, 0x2F, 0x15, 0x2F, 0x2F, 0x2F, 0x2F, 0x3F, 0x15, 0x2F, 0x3F, 0x2F,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0x15, 0x3F, 0x3F, 0x2F, 0x2F, 0x2F, 0x2F, 0x2F, 0x3F, 0x2F, 0x3F, 0x2F, 0x2F, 0x3F, 0x3F,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0x3F, 0x15, 0x15, 0x3F, 0x15, 0x2F, 0x3F, 0x2F, 0x15, 0x3F, 0x2F, 0x2F,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x3F, 0x15, 0x3F, 0x3F, 0x2F, 0x2F, 0x3F, 0x2F, 0x3F,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x3F, 0x3F, 0x15, 0x3F, 0x3F, 0x2F,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x3F, 0x3F, 0x3F
+};
+
+uint16 *OldDOSFont::_cgaDitheringTable = 0;
+int OldDOSFont::_numRef = 0;
+
+OldDOSFont::OldDOSFont(Common::RenderMode mode) : _renderMode(mode) {
+ _data = 0;
+ _width = _height = _numGlyphs = 0;
+ _bitmapOffsets = 0;
+
+ _numRef++;
+ if (!_cgaDitheringTable && _numRef == 1) {
+ _cgaDitheringTable = new uint16[256];
+ memset(_cgaDitheringTable, 0, 256 * sizeof(uint16));
+ static const uint bits[] = { 0, 3, 12, 15 };
+ for (int i = 0; i < 256; i++)
+ _cgaDitheringTable[i] = (bits[i & 3] << 8) | (bits[(i >> 2) & 3] << 12) | (bits[(i >> 4) & 3] << 0) | (bits[(i >> 6) & 3] << 4);
+ }
+}
+
+OldDOSFont::~OldDOSFont() {
+ unload();
+
+ if (_numRef)
+ --_numRef;
+
+ if (_cgaDitheringTable && !_numRef) {
+ delete[] _cgaDitheringTable;
+ _cgaDitheringTable = 0;
+ }
+}
+
+bool OldDOSFont::load(Common::SeekableReadStream &file) {
+ unload();
+
+ _data = new uint8[file.size()];
+ assert(_data);
+
+ file.read(_data, file.size());
+ if (file.err())
+ return false;
+
+ if (file.size() - 2 != READ_LE_UINT16(_data))
+ return false;
+
+ _width = _data[0x103];
+ _height = _data[0x102];
+ _numGlyphs = 255;
+
+ _bitmapOffsets = (uint16 *)(_data + 2);
+
+ for (int i = 0; i < _numGlyphs; ++i)
+ _bitmapOffsets[i] = READ_LE_UINT16(&_bitmapOffsets[i]);
+
+ return true;
+}
+
+int OldDOSFont::getCharWidth(uint16 c) const {
+ if (c >= _numGlyphs)
+ return 0;
+ return _width;
+}
+
+void OldDOSFont::drawChar(uint16 c, byte *dst, int pitch, int bpp) const {
+ static const uint8 renderMaskTable6[] = { 0xFC, 0x00, 0x7E, 0x00, 0x3F, 0x00, 0x1F, 0x80, 0x0F, 0xC0, 0x07, 0xE0, 0x03, 0xF0, 0x01, 0xF8 };
+ static const uint8 renderMaskTable8[] = { 0xFF, 0x00, 0x7F, 0x80, 0x3F, 0xC0, 0x1F, 0xE0, 0x0F, 0xF0, 0x07, 0xF8, 0x03, 0xFC, 0x01, 0xFE };
+
+ if (_width != 8 && _width != 6)
+ error("EOB font rendering not implemented for other font widths than 6 and 8.");
+
+ if (_width == 6) {
+ switch (c) {
+ case 0x81:
+ case 0x9A:
+ c = 0x5D;
+ break;
+ case 0x84:
+ case 0x8E:
+ c = 0x5B;
+ break;
+ case 0x94:
+ case 0x99:
+ c = 0x40;
+ case 0xE1:
+ // TODO: recheck this: no conversion for 'ß' ?
+ break;
+ }
+ } else if (_width == 8) {
+ switch (c) {
+ case 0x81:
+ case 0x9A:
+ case 0x5D:
+ c = 0x1D;
+ break;
+ case 0x84:
+ case 0x5B:
+ c = 0x1E;
+ break;
+ case 0x94:
+ case 0x40:
+ c = 0x1F;
+ break;
+ case 0x8E:
+ c = 0x1B;
+ break;
+ case 0x99:
+ c = 0x1C;
+ break;
+ case 0xE1:
+ c = 0x19;
+ break;
+ }
+ }
+
+ pitch *= bpp;
+ const uint8 *src = &_data[_bitmapOffsets[c]];
+ uint8 *dst2 = dst + pitch;
+
+ int w = (_width - 1) >> 3;
+ pitch -= _width * bpp;
+
+ uint16 color1 = _colorMap8bit[1];
+ uint16 color2 = _colorMap8bit[0];
+
+ if (bpp == 2) {
+ color1 = _colorMap16bit[1];
+ color2 = _colorMap16bit[0];
+ } else if (_renderMode == Common::kRenderCGA || _renderMode == Common::kRenderEGA) {
+ color1 &= 0x0F;
+ color2 &= 0x0F;
+ }
+
+ static const uint16 cgaColorMask[] = { 0, 0x5555, 0xAAAA, 0xFFFF };
+ uint16 cgaMask1 = cgaColorMask[color1 & 3];
+ uint16 cgaMask2 = cgaColorMask[color2 & 3];
+
+ int cH = _height;
+ while (cH--) {
+ int cW = w;
+ uint8 last = 0;
+ const uint8 *mtbl = _width == 8 ? renderMaskTable8 : renderMaskTable6;
+
+ if (_renderMode == Common::kRenderCGA) {
+ uint8 s = *src++;
+ uint8 m = *mtbl++;
+
+ uint8 in = s | last;
+ uint16 cmp1 = 0;
+ uint16 cmp2 = 0;
+
+ if (color1) {
+ in &= m;
+ cmp1 = _cgaDitheringTable[in];
+ }
+
+ if (color2) {
+ in = ~in & m;
+ cmp2 = _cgaDitheringTable[in];
+ }
+
+ uint16 cDst = 0;
+ uint8 sh = 6;
+ for (int i = 0; i < _width; i++) {
+ cDst |= ((dst[i] & 3) << sh);
+ sh = (sh - 2) & 0x0F;
+ }
+
+ uint16 out = (~(cmp1 | cmp2) & cDst) | (cmp1 & cgaMask1) | (cmp2 & cgaMask2);
+
+ sh = 6;
+ for (int i = 0; i < _width; i++) {
+ *dst++ = (out >> sh) & 3;
+ sh = (sh - 2) & 0x0F;
+ }
+
+ last = s;
+
+ } else {
+ for (bool runWidthLoop = true; runWidthLoop;) {
+ uint8 s = *src++;
+ uint8 m = *mtbl++;
+
+ for (uint8 i = 0x80; i; i >>= 1) {
+ if (!(m & i)) {
+ runWidthLoop = false;
+ break;
+ }
+
+ if (s & i) {
+ if (bpp == 2)
+ *(uint16*)dst = color1;
+ else if (color1)
+ *dst = color1;
+ } else {
+ if (bpp == 2) {
+ if (color2 != 0xFFFF)
+ *(uint16*)dst = color2;
+ } else if (color2) {
+ *dst = color2;
+ }
+ }
+ dst += bpp;
+ }
+
+ if (cW)
+ cW--;
+ else
+ runWidthLoop = false;
+ }
+ }
+
+ dst += pitch;
+ dst2 += pitch;
+ }
+}
+
+void OldDOSFont::unload() {
+ delete[] _data;
+ _data = 0;
+ _width = _height = _numGlyphs = 0;
+ _bitmapOffsets = 0;
+}
+
+SJISFontLarge::SJISFontLarge(Graphics::FontSJIS *font) : SJISFont(font, 0, false, false, false, 0) {
+ _sjisWidth = _font->getMaxFontWidth();
+ _fontHeight = _font->getFontHeight();
+ _asciiWidth = _font->getCharWidth('a');
+}
+
+void SJISFontLarge::drawChar(uint16 c, byte *dst, int pitch, int) const {
+ _font->drawChar(dst, c, 320, 1, _colorMap[1], _colorMap[0], 320, 200);
+}
+
+SJISFont12x12::SJISFont12x12(const uint16 *searchTable) : _height(6), _width(6), _data(0) {
+ assert(searchTable);
+ for (int i = 0; i < 148; i++)
+ _searchTable[searchTable[i]] = i + 1;
+}
+
+bool SJISFont12x12::load(Common::SeekableReadStream &file) {
+ delete[] _data;
+ int size = 148 * 24;
+ if (file.size() < size)
+ return false;
+
+ _data = new uint8[size];
+ file.read(_data, size);
+
+ return true;
+}
+
+void SJISFont12x12::unload() {
+ delete[] _data;
+ _data = 0;
+ _searchTable.clear();
+}
+
+void SJISFont12x12::drawChar(uint16 c, byte *dst, int pitch, int) const {
+ int offs = _searchTable[c];
+ if (!offs)
+ return;
+
+ const uint8 *src = _data + (offs - 1) * 24;
+ uint8 color1 = _colorMap[1];
+
+ int bt = 0;
+ uint16 chr = 0;
+
+ for (int i = 0; i < 192; ++i) {
+ if (!bt) {
+ chr = *src++;
+ bt = 8;
+ }
+ if (chr & 0x80)
+ *dst = color1;
+ dst++;
+ if (--bt)
+ chr <<= 1;
+ else if (i & 8)
+ dst += (pitch - 16);
+ }
+}
+
+} // End of namespace Kyra
+
+#endif // ENABLE_EOB
diff --git a/engines/kyra/graphics/screen_eob.h b/engines/kyra/graphics/screen_eob.h
new file mode 100644
index 0000000000..401a229045
--- /dev/null
+++ b/engines/kyra/graphics/screen_eob.h
@@ -0,0 +1,177 @@
+/* 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 2
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef KYRA_SCREEN_EOB_H
+#define KYRA_SCREEN_EOB_H
+
+#ifdef ENABLE_EOB
+
+#include "kyra/graphics/screen.h"
+
+namespace Kyra {
+
+class EoBCoreEngine;
+class Screen_EoB : public Screen {
+public:
+ Screen_EoB(EoBCoreEngine *vm, OSystem *system);
+ virtual ~Screen_EoB();
+
+ bool init();
+
+ void setClearScreenDim(int dim);
+ void clearCurDim();
+
+ void setMouseCursor(int x, int y, const byte *shape);
+ void setMouseCursor(int x, int y, const byte *shape, const uint8 *ovl);
+
+ void loadFileDataToPage(Common::SeekableReadStream *s, int pageNum, uint32 size);
+
+ void printShadedText(const char *string, int x, int y, int col1, int col2);
+
+ void loadEoBBitmap(const char *file, const uint8 *cgaMapping, int tempPage, int destPage, int convertToPage);
+ void loadShapeSetBitmap(const char *file, int tempPage, int destPage);
+
+ void convertPage(int srcPage, int dstPage, const uint8 *cgaMapping);
+
+ void setScreenPalette(const Palette &pal);
+ void getRealPalette(int num, uint8 *dst);
+
+ uint8 *encodeShape(uint16 x, uint16 y, uint16 w, uint16 h, bool encode8bit = false, const uint8 *cgaMapping = 0);
+ void drawShape(uint8 pageNum, const uint8 *shapeData, int x, int y, int sd = -1, int flags = 0, ...);
+ const uint8 *scaleShape(const uint8 *shapeData, int blockDistance);
+ const uint8 *scaleShapeStep(const uint8 *shp);
+ const uint8 *generateShapeOverlay(const uint8 *shp, const uint8 *fadingTable);
+
+ void setShapeFrame(int x1, int y1, int x2, int y2);
+ void enableShapeBackgroundFading(bool enable);
+ void setShapeFadingLevel(int val);
+
+ void setGfxParameters(int x, int y, int col);
+ void drawExplosion(int scale, int radius, int numElements, int stepSize, int aspectRatio, const uint8 *colorTable, int colorTableSize);
+ void drawVortex(int numElements, int radius, int stepSize, int, int disorder, const uint8 *colorTable, int colorTableSize);
+
+ void fadeTextColor(Palette *pal, int color1, int fadeTextColor);
+ bool delayedFadePalStep(Palette *fadePal, Palette *destPal, int rate);
+
+ void setTextColorMap(const uint8 *cmap) {}
+ int getRectSize(int w, int h);
+
+ void setFadeTable(const uint8 *table);
+ void createFadeTable(const uint8 *palData, uint8 *dst, uint8 rootColor, uint8 weight);
+ void createFadeTable16bit(const uint16 *palData, uint16 *dst, uint16 rootColor, uint8 weight);
+
+ const uint16 *getCGADitheringTable(int index);
+ const uint8 *getEGADitheringTable();
+
+ bool loadFont(FontId fontId, const char *filename);
+
+ // FM-Towns specific
+ void decodeSHP(const uint8 *data, int dstPage);
+ void convertToHiColor(int page);
+ void shadeRect(int x1, int y1, int x2, int y2, int shadingLevel);
+
+private:
+ void updateDirtyRects();
+ void ditherRect(const uint8 *src, uint8 *dst, int dstPitch, int srcW, int srcH, int colorKey = -1);
+
+ void drawShapeSetPixel(uint8 *dst, uint8 col);
+ void scaleShapeProcessLine2Bit(uint8 *&shpDst, const uint8 *&shpSrc, uint32 transOffsetDst, uint32 transOffsetSrc);
+ void scaleShapeProcessLine4Bit(uint8 *&dst, const uint8 *&src);
+ bool posWithinRect(int posX, int posY, int x1, int y1, int x2, int y2);
+
+ void setPagePixel16bit(int pageNum, int x, int y, uint16 color);
+
+ void generateEGADitheringTable(const Palette &pal);
+ void generateCGADitheringTables(const uint8 *mappingData);
+
+ int _dsDiv, _dsRem, _dsScaleTrans;
+ uint8 *_cgaScaleTable;
+ int16 _gfxX, _gfxY;
+ uint16 _gfxCol;
+ const uint8 *_gfxMaxY;
+
+ int16 _dsX1, _dsX2, _dsY1, _dsY2;
+
+ bool _dsBackgroundFading;
+ int16 _dsBackgroundFadingXOffs;
+ uint8 _shapeOverlay[16];
+
+ uint8 *_dsTempPage;
+ uint8 *_shpBuffer;
+ uint8 *_convertHiColorBuffer;
+
+ uint16 *_cgaDitheringTables[2];
+ const uint8 *_cgaMappingDefault;
+
+ uint8 *_egaDitheringTable;
+ uint8 *_egaDitheringTempPage;
+
+ const uint16 _cursorColorKey16Bit;
+
+ static const uint8 _egaMatchTable[];
+ static const ScreenDim _screenDimTable[];
+ static const int _screenDimTableCount;
+};
+
+/**
+* SJIS Font variant used in the intro and outro of EOB II FM-Towns. It appears twice as large, since it is not rendered on the hires overlay pages
+*/
+class SJISFontLarge : public SJISFont {
+public:
+ SJISFontLarge(Graphics::FontSJIS *font);
+ virtual ~SJISFontLarge() { unload(); }
+
+ virtual bool usesOverlay() const { return false; }
+ virtual void drawChar(uint16 c, byte *dst, int pitch, int) const;
+};
+
+/**
+* 12 x 12 SJIS font for EOB II FM-Towns. The data for this font comes from a file, not from the font rom.
+*/
+class SJISFont12x12 : public Font {
+public:
+ SJISFont12x12(const uint16 *searchTable);
+ virtual ~SJISFont12x12() { unload(); }
+
+ virtual bool load(Common::SeekableReadStream &file);
+ virtual bool usesOverlay() const { return true; }
+ virtual int getHeight() const { return _height; }
+ virtual int getWidth() const { return _width; }
+ virtual int getCharWidth(uint16 c) const { return _width; }
+ virtual void setColorMap(const uint8 *src) { _colorMap = src; }
+ virtual void drawChar(uint16 c, byte *dst, int pitch, int) const;
+
+private:
+ void unload();
+
+ uint8 *_data;
+ Common::HashMap<uint16, uint8> _searchTable;
+
+ const uint8 *_colorMap;
+ const int _height, _width;
+};
+
+} // End of namespace Kyra
+
+#endif // ENABLE_EOB
+
+#endif
diff --git a/engines/kyra/graphics/screen_hof.cpp b/engines/kyra/graphics/screen_hof.cpp
new file mode 100644
index 0000000000..a3ab811cdb
--- /dev/null
+++ b/engines/kyra/graphics/screen_hof.cpp
@@ -0,0 +1,96 @@
+/* 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 2
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "kyra/graphics/screen_hof.h"
+#include "kyra/engine/kyra_hof.h"
+
+namespace Kyra {
+
+Screen_HoF::Screen_HoF(KyraEngine_HoF *vm, OSystem *system)
+ : Screen_v2(vm, system, _screenDimTable, _screenDimTableCount), _vm(vm) {
+}
+
+void Screen_HoF::generateGrayOverlay(const Palette &srcPal, uint8 *grayOverlay, int factor, int addR, int addG, int addB, int lastColor, bool flag) {
+ Palette tmpPal(lastColor);
+
+ for (int i = 0; i != lastColor; i++) {
+ if (flag) {
+ int v = ((((srcPal[3 * i] & 0x3F) + (srcPal[3 * i + 1] & 0x3F)
+ + (srcPal[3 * i + 2] & 0x3F)) / 3) * factor) / 0x40;
+ tmpPal[3 * i] = tmpPal[3 * i + 1] = tmpPal[3 * i + 2] = v & 0xFF;
+ } else {
+ int v = (((srcPal[3 * i] & 0x3F) * factor) / 0x40) + addR;
+ tmpPal[3 * i] = (v > 0x3F) ? 0x3F : v & 0xFF;
+ v = (((srcPal[3 * i + 1] & 0x3F) * factor) / 0x40) + addG;
+ tmpPal[3 * i + 1] = (v > 0x3F) ? 0x3F : v & 0xFF;
+ v = (((srcPal[3 * i + 2] & 0x3F) * factor) / 0x40) + addB;
+ tmpPal[3 * i + 2] = (v > 0x3F) ? 0x3F : v & 0xFF;
+ }
+ }
+
+ for (int i = 0; i < lastColor; i++)
+ grayOverlay[i] = findLeastDifferentColor(tmpPal.getData() + 3 * i, srcPal, 0, lastColor);
+}
+
+void Screen_HoF::cmpFadeFrameStep(int srcPage, int srcW, int srcH, int srcX, int srcY, int dstPage, int dstW,
+ int dstH, int dstX, int dstY, int cmpW, int cmpH, int cmpPage) {
+
+ if (!cmpW || !cmpH)
+ return;
+
+ int r1, r2, r3, r4, r5, r6;
+
+ int X1 = srcX;
+ int Y1 = srcY;
+ int W1 = cmpW;
+ int H1 = cmpH;
+
+ if (!calcBounds(srcW, srcH, X1, Y1, W1, H1, r1, r2, r3))
+ return;
+
+ int X2 = dstX;
+ int Y2 = dstY;
+ int W2 = W1;
+ int H2 = H1;
+
+ if (!calcBounds(dstW, dstH, X2, Y2, W2, H2, r4, r5, r6))
+ return;
+
+ const uint8 *src = getPagePtr(srcPage) + srcW * (Y1 + r5);
+ uint8 *dst = getPagePtr(dstPage) + dstW * (Y2 + r2);
+ const uint8 *cmp = getPagePtr(cmpPage);
+
+ while (H2--) {
+ const uint8 *s = src + r4 + X1;
+ uint8 *d = dst + r1 + X2;
+
+ for (int i = 0; i < W2; i++) {
+ int ix = (*s++ << 8) + *d;
+ *d++ = cmp[ix];
+ }
+
+ src += W1;
+ dst += W2;
+ }
+}
+
+} // End of namespace Kyra
diff --git a/engines/kyra/graphics/screen_hof.h b/engines/kyra/graphics/screen_hof.h
new file mode 100644
index 0000000000..5cb189e0ef
--- /dev/null
+++ b/engines/kyra/graphics/screen_hof.h
@@ -0,0 +1,50 @@
+/* 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 2
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef KYRA_SCREEN_HOF_H
+#define KYRA_SCREEN_HOF_H
+
+#include "kyra/graphics/screen_v2.h"
+
+namespace Kyra {
+
+class KyraEngine_HoF;
+
+class Screen_HoF : public Screen_v2 {
+friend class Debugger_v2;
+public:
+ Screen_HoF(KyraEngine_HoF *vm, OSystem *system);
+
+ // sequence player
+ void generateGrayOverlay(const Palette &pal, uint8 *grayOverlay, int factor, int addR, int addG, int addB, int lastColor, bool flag);
+ void cmpFadeFrameStep(int srcPage, int srcW, int srcH, int srcX, int srcY, int dstPage, int dstW, int dstH, int dstX, int dstY, int cmpW, int cmpH, int cmpPage);
+
+private:
+ KyraEngine_HoF *_vm;
+
+ static const ScreenDim _screenDimTable[];
+ static const int _screenDimTableCount;
+};
+
+} // End of namespace Kyra
+
+#endif
diff --git a/engines/kyra/graphics/screen_lok.cpp b/engines/kyra/graphics/screen_lok.cpp
new file mode 100644
index 0000000000..114382b487
--- /dev/null
+++ b/engines/kyra/graphics/screen_lok.cpp
@@ -0,0 +1,447 @@
+/* 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 2
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "kyra/graphics/screen_lok.h"
+#include "kyra/engine/kyra_lok.h"
+
+#include "common/system.h"
+
+#include "graphics/palette.h"
+
+namespace Kyra {
+
+Screen_LoK::Screen_LoK(KyraEngine_LoK *vm, OSystem *system)
+ : Screen(vm, system, _screenDimTable, _screenDimTableCount) {
+ _vm = vm;
+ _unkPtr1 = _unkPtr2 = 0;
+ _bitBlitNum = 0;
+}
+
+Screen_LoK::~Screen_LoK() {
+ for (int i = 0; i < ARRAYSIZE(_saveLoadPage); ++i) {
+ delete[] _saveLoadPage[i];
+ _saveLoadPage[i] = 0;
+ }
+
+ for (int i = 0; i < ARRAYSIZE(_saveLoadPageOvl); ++i) {
+ delete[] _saveLoadPageOvl[i];
+ _saveLoadPageOvl[i] = 0;
+ }
+
+ delete[] _unkPtr1;
+ delete[] _unkPtr2;
+}
+
+bool Screen_LoK::init() {
+ if (!Screen::init())
+ return false;
+
+ memset(_bitBlitRects, 0, sizeof(_bitBlitRects));
+ _bitBlitNum = 0;
+ memset(_saveLoadPage, 0, sizeof(_saveLoadPage));
+ memset(_saveLoadPageOvl, 0, sizeof(_saveLoadPageOvl));
+
+ _unkPtr1 = new uint8[getRectSize(1, 144)];
+ assert(_unkPtr1);
+ memset(_unkPtr1, 0, getRectSize(1, 144));
+ _unkPtr2 = new uint8[getRectSize(1, 144)];
+ assert(_unkPtr2);
+ memset(_unkPtr2, 0, getRectSize(1, 144));
+
+ return true;
+}
+
+void Screen_LoK::fadeSpecialPalette(int palIndex, int startIndex, int size, int fadeTime) {
+ if (_vm->gameFlags().platform == Common::kPlatformAmiga)
+ return;
+
+ assert(_vm->palTable1()[palIndex]);
+
+ Palette tempPal(getPalette(0).getNumColors());
+ tempPal.copy(getPalette(0));
+ tempPal.copy(_vm->palTable1()[palIndex], 0, size, startIndex);
+
+ fadePalette(tempPal, fadeTime * 18);
+
+ getPalette(0).copy(tempPal, startIndex, size);
+ setScreenPalette(getPalette(0));
+ _system->updateScreen();
+}
+
+void Screen_LoK::addBitBlitRect(int x, int y, int w, int h) {
+ if (_bitBlitNum >= kNumBitBlitRects)
+ error("too many bit blit rects");
+
+ _bitBlitRects[_bitBlitNum].left = x;
+ _bitBlitRects[_bitBlitNum].top = y;
+ _bitBlitRects[_bitBlitNum].right = x + w;
+ _bitBlitRects[_bitBlitNum].bottom = y + h;
+ ++_bitBlitNum;
+}
+
+void Screen_LoK::bitBlitRects() {
+ Common::Rect *cur = _bitBlitRects;
+ while (_bitBlitNum) {
+ _bitBlitNum--;
+ copyRegion(cur->left, cur->top, cur->left, cur->top, cur->width(), cur->height(), 2, 0);
+ ++cur;
+ }
+}
+
+void Screen_LoK::savePageToDisk(const char *file, int page) {
+ if (!_saveLoadPage[page / 2]) {
+ _saveLoadPage[page / 2] = new uint8[SCREEN_W * SCREEN_H];
+ assert(_saveLoadPage[page / 2]);
+ }
+ memcpy(_saveLoadPage[page / 2], getPagePtr(page), SCREEN_W * SCREEN_H);
+
+ if (_useOverlays) {
+ if (!_saveLoadPageOvl[page / 2]) {
+ _saveLoadPageOvl[page / 2] = new uint8[SCREEN_OVL_SJIS_SIZE];
+ assert(_saveLoadPageOvl[page / 2]);
+ }
+
+ uint8 *srcPage = getOverlayPtr(page);
+ if (!srcPage) {
+ warning("trying to save unsupported overlay page %d", page);
+ return;
+ }
+
+ memcpy(_saveLoadPageOvl[page / 2], srcPage, SCREEN_OVL_SJIS_SIZE);
+ }
+}
+
+void Screen_LoK::loadPageFromDisk(const char *file, int page) {
+ if (!_saveLoadPage[page / 2]) {
+ warning("trying to restore page %d, but no backup found", page);
+ return;
+ }
+
+ copyBlockToPage(page, 0, 0, SCREEN_W, SCREEN_H, _saveLoadPage[page / 2]);
+ delete[] _saveLoadPage[page / 2];
+ _saveLoadPage[page / 2] = 0;
+
+ if (_saveLoadPageOvl[page / 2]) {
+ uint8 *dstPage = getOverlayPtr(page);
+ if (!dstPage) {
+ warning("trying to restore unsupported overlay page %d", page);
+ return;
+ }
+
+ memcpy(dstPage, _saveLoadPageOvl[page / 2], SCREEN_OVL_SJIS_SIZE);
+ delete[] _saveLoadPageOvl[page / 2];
+ _saveLoadPageOvl[page / 2] = 0;
+ }
+}
+
+void Screen_LoK::queryPageFromDisk(const char *file, int page, uint8 *buffer) {
+ if (!_saveLoadPage[page / 2]) {
+ warning("trying to query page %d, but no backup found", page);
+ return;
+ }
+
+ memcpy(buffer, _saveLoadPage[page / 2], SCREEN_W * SCREEN_H);
+}
+
+void Screen_LoK::deletePageFromDisk(int page) {
+ delete[] _saveLoadPage[page / 2];
+ _saveLoadPage[page / 2] = 0;
+
+ if (_saveLoadPageOvl[page / 2]) {
+ delete[] _saveLoadPageOvl[page / 2];
+ _saveLoadPageOvl[page / 2] = 0;
+ }
+}
+
+void Screen_LoK::copyBackgroundBlock(int x, int page, int flag) {
+ if (x < 1)
+ return;
+
+ int height = 128;
+ if (flag)
+ height += 8;
+ if (!(x & 1))
+ ++x;
+ if (x == 19)
+ x = 17;
+
+ uint8 *ptr1 = _unkPtr1;
+ uint8 *ptr2 = _unkPtr2;
+ int oldVideoPage = _curPage;
+ _curPage = page;
+
+ int curX = x;
+ copyRegionToBuffer(_curPage, 8, 8, 8, height, ptr2);
+ for (int i = 0; i < 19; ++i) {
+ int tempX = curX + 1;
+ copyRegionToBuffer(_curPage, tempX << 3, 8, 8, height, ptr1);
+ copyBlockToPage(_curPage, tempX << 3, 8, 8, height, ptr2);
+ int newXPos = curX + x;
+ if (newXPos > 37)
+ newXPos = newXPos % 38;
+
+ tempX = newXPos + 1;
+ copyRegionToBuffer(_curPage, tempX << 3, 8, 8, height, ptr2);
+ copyBlockToPage(_curPage, tempX << 3, 8, 8, height, ptr1);
+ curX += x * 2;
+ if (curX > 37) {
+ curX = curX % 38;
+ }
+ }
+ _curPage = oldVideoPage;
+}
+
+void Screen_LoK::copyBackgroundBlock2(int x) {
+ copyBackgroundBlock(x, 4, 1);
+}
+
+void Screen_LoK::setTextColorMap(const uint8 *cmap) {
+ setTextColor(cmap, 0, 11);
+}
+
+int Screen_LoK::getRectSize(int x, int y) {
+ if (x < 1)
+ x = 1;
+ else if (x > 40)
+ x = 40;
+
+ if (y < 1)
+ y = 1;
+ else if (y > 200)
+ y = 200;
+
+ return ((x * y) << 3);
+}
+
+void Screen_LoK::postProcessCursor(uint8 *data, int width, int height, int pitch) {
+ if (_vm->gameFlags().platform == Common::kPlatformAmiga && _interfacePaletteEnabled) {
+ pitch -= width;
+
+ for (int y = 0; y < height; ++y) {
+ for (int x = 0; x < width; ++x) {
+ if (*data != _cursorColorKey)
+ *data += 32;
+ ++data;
+ }
+
+ data += pitch;
+ }
+ }
+}
+
+#pragma mark -
+
+Screen_LoK_16::Screen_LoK_16(KyraEngine_LoK *vm, OSystem *system) : Screen_LoK(vm, system) {
+ memset(_paletteDither, 0, sizeof(_paletteDither));
+}
+
+void Screen_LoK_16::setScreenPalette(const Palette &pal) {
+ _screenPalette->copy(pal);
+
+ for (int i = 0; i < 256; ++i)
+ paletteMap(i, pal[i * 3 + 0] << 2, pal[i * 3 + 1] << 2, pal[i * 3 + 2] << 2);
+
+ set16ColorPalette(_palette16);
+ _forceFullUpdate = true;
+}
+
+void Screen_LoK_16::fadePalette(const Palette &pal, int delay, const UpdateFunctor *upFunc) {
+ uint8 notBlackFlag = 0;
+ for (int i = 0; i < 768; ++i) {
+ if ((*_screenPalette)[i])
+ notBlackFlag |= 1;
+ if (pal[i])
+ notBlackFlag |= 2;
+ }
+
+ if (notBlackFlag == 1 || notBlackFlag == 2) {
+ bool upFade = false;
+
+ for (int i = 0; i < 768; ++i) {
+ if ((*_screenPalette)[i] < pal[i]) {
+ upFade = true;
+ break;
+ }
+ }
+
+ if (upFade) {
+ for (int i = 0; i < 256; ++i)
+ paletteMap(i, pal[i * 3 + 0] << 2, pal[i * 3 + 1] << 2, pal[i * 3 + 2] << 2);
+ _forceFullUpdate = true;
+ }
+
+ uint8 color16Palette[16 * 3];
+
+ if (upFade)
+ memset(color16Palette, 0, sizeof(color16Palette));
+ else
+ memcpy(color16Palette, _palette16, sizeof(color16Palette));
+
+ set16ColorPalette(color16Palette);
+ updateScreen();
+
+ for (int i = 0; i < 16; ++i) {
+ set16ColorPalette(color16Palette);
+
+ for (int k = 0; k < 48; ++k) {
+ if (upFade) {
+ if (color16Palette[k] < _palette16[k])
+ ++color16Palette[k];
+ } else {
+ if (color16Palette[k] > 0)
+ --color16Palette[k];
+ }
+ }
+
+ if (upFunc && upFunc->isValid())
+ (*upFunc)();
+ else
+ _system->updateScreen();
+
+ _vm->delay((delay >> 5) * _vm->tickLength());
+ }
+ }
+
+ setScreenPalette(pal);
+}
+
+void Screen_LoK_16::getFadeParams(const Palette &pal, int delay, int &delayInc, int &diff) {
+ error("Screen_LoK_16::getFadeParams called");
+}
+
+int Screen_LoK_16::fadePalStep(const Palette &pal, int diff) {
+ error("Screen_LoK_16::fadePalStep called");
+ return 0; // for compilers that don't support NORETURN
+}
+
+void Screen_LoK_16::paletteMap(uint8 idx, int r, int g, int b) {
+ const int red = r;
+ const int green = g;
+ const int blue = b;
+
+ uint16 rgbDiff = 1000;
+ int rDiff = 0, gDiff = 0, bDiff = 0;
+
+ int index1 = -1;
+
+ for (int i = 0; i < 16; ++i) {
+ const int realR = _palette16[i * 3 + 0] << 4;
+ const int realG = _palette16[i * 3 + 1] << 4;
+ const int realB = _palette16[i * 3 + 2] << 4;
+
+ uint16 diff = ABS(r - realR) + ABS(g - realG) + ABS(b - realB);
+
+ if (diff < rgbDiff) {
+ rgbDiff = diff;
+ index1 = i;
+
+ rDiff = r - realR;
+ gDiff = g - realG;
+ bDiff = b - realB;
+ }
+ }
+
+ r = rDiff / 4 + red;
+ g = gDiff / 4 + green;
+ b = bDiff / 4 + blue;
+
+ rgbDiff = 1000;
+ int index2 = -1;
+
+ for (int i = 0; i < 16; ++i) {
+ const int realR = _palette16[i * 3 + 0] << 4;
+ const int realG = _palette16[i * 3 + 1] << 4;
+ const int realB = _palette16[i * 3 + 2] << 4;
+
+ uint16 diff = ABS(r - realR) + ABS(g - realG) + ABS(b - realB);
+
+ if (diff < rgbDiff) {
+ rgbDiff = diff;
+ index2 = i;
+ }
+ }
+
+ _paletteDither[idx].bestMatch = index1;
+ _paletteDither[idx].invertMatch = index2;
+}
+
+void Screen_LoK_16::convertTo16Colors(uint8 *page, int w, int h, int pitch, int keyColor) {
+ const int rowAdd = pitch * 2 - w;
+
+ uint8 *row1 = page;
+ uint8 *row2 = page + pitch;
+
+ for (int i = 0; i < h; i += 2) {
+ for (int k = 0; k < w; k += 2) {
+ if (keyColor == -1 || keyColor != *row1) {
+ const PaletteDither &dither = _paletteDither[*row1];
+
+ *row1++ = dither.bestMatch;
+ *row1++ = dither.invertMatch;
+ *row2++ = dither.invertMatch;
+ *row2++ = dither.bestMatch;
+ } else {
+ row1 += 2;
+ row2 += 2;
+ }
+ }
+
+ row1 += rowAdd;
+ row2 += rowAdd;
+ }
+}
+
+void Screen_LoK_16::mergeOverlay(int x, int y, int w, int h) {
+ byte *dst = _sjisOverlayPtrs[0] + y * 640 + x;
+
+ // We do a game screen rect to 16 color dithering here. It is
+ // important that we do not dither the overlay, since else the
+ // japanese fonts will look wrong.
+ convertTo16Colors(dst, w, h, 640);
+
+ const byte *src = _sjisOverlayPtrs[1] + y * 640 + x;
+
+ int add = 640 - w;
+
+ while (h--) {
+ for (x = 0; x < w; ++x, ++dst) {
+ byte col = *src++;
+ if (col != _sjisInvisibleColor)
+ *dst = _paletteDither[col].bestMatch;
+ }
+ dst += add;
+ src += add;
+ }
+}
+
+void Screen_LoK_16::set16ColorPalette(const uint8 *pal) {
+ uint8 palette[16 * 3];
+ for (int i = 0; i < 16; ++i) {
+ palette[i * 3 + 0] = (pal[i * 3 + 0] * 0xFF) / 0x0F;
+ palette[i * 3 + 1] = (pal[i * 3 + 1] * 0xFF) / 0x0F;
+ palette[i * 3 + 2] = (pal[i * 3 + 2] * 0xFF) / 0x0F;
+ }
+
+ _system->getPaletteManager()->setPalette(palette, 0, 16);
+}
+
+} // End of namespace Kyra
diff --git a/engines/kyra/graphics/screen_lok.h b/engines/kyra/graphics/screen_lok.h
new file mode 100644
index 0000000000..3a4ebfd6a4
--- /dev/null
+++ b/engines/kyra/graphics/screen_lok.h
@@ -0,0 +1,111 @@
+/* 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 2
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef KYRA_SCREEN_LOK_H
+#define KYRA_SCREEN_LOK_H
+
+#include "kyra/graphics/screen.h"
+
+namespace Kyra {
+
+class KyraEngine_LoK;
+
+class Screen_LoK : public Screen {
+public:
+ Screen_LoK(KyraEngine_LoK *vm, OSystem *system);
+ virtual ~Screen_LoK();
+
+ bool init();
+
+ int getRectSize(int w, int h);
+
+ void setTextColorMap(const uint8 *cmap);
+
+ void fadeSpecialPalette(int palIndex, int startIndex, int size, int fadeTime);
+
+ void savePageToDisk(const char *file, int page);
+ void loadPageFromDisk(const char *file, int page);
+ void queryPageFromDisk(const char *file, int page, uint8 *buffer);
+ void deletePageFromDisk(int page);
+
+ void copyBackgroundBlock(int x, int page, int flag);
+ void copyBackgroundBlock2(int x);
+
+ void addBitBlitRect(int x, int y, int w, int h);
+ void bitBlitRects();
+
+ // AMIGA specific
+ virtual void postProcessCursor(uint8 *data, int width, int height, int pitch);
+
+protected:
+ enum {
+ kNumBitBlitRects = 10
+ };
+
+ KyraEngine_LoK *_vm;
+
+ static const ScreenDim _screenDimTable[];
+ static const int _screenDimTableCount;
+
+ Common::Rect _bitBlitRects[kNumBitBlitRects];
+ int _bitBlitNum;
+ uint8 *_unkPtr1, *_unkPtr2;
+
+ uint8 *_saveLoadPage[8];
+ uint8 *_saveLoadPageOvl[8];
+};
+
+class Screen_LoK_16 : public Screen_LoK {
+public:
+ Screen_LoK_16(KyraEngine_LoK *vm, OSystem *system);
+
+ void setScreenPalette(const Palette &pal);
+
+ void fadePalette(const Palette &pal, int delay, const UpdateFunctor *upFunc = 0);
+ void getFadeParams(const Palette &pal, int delay, int &delayInc, int &diff);
+ int fadePalStep(const Palette &pal, int diff);
+private:
+ void updateDirtyRectsOvl();
+
+ void convertTo16Colors(uint8 *page, int w, int h, int pitch, int keyColor = -1);
+ void postProcessCursor(uint8 *data, int width, int height, int pitch) {
+ convertTo16Colors(data, width, height, pitch, _cursorColorKey);
+ }
+ void mergeOverlay(int x, int y, int w, int h);
+
+ void set16ColorPalette(const uint8 *pal);
+
+ void paletteMap(uint8 idx, int r, int g, int b);
+
+ struct PaletteDither {
+ uint8 bestMatch;
+ uint8 invertMatch;
+ };
+
+ PaletteDither _paletteDither[256];
+
+ static const uint8 _palette16[48];
+};
+
+} // End of namespace Kyra
+
+#endif
diff --git a/engines/kyra/graphics/screen_lol.cpp b/engines/kyra/graphics/screen_lol.cpp
new file mode 100644
index 0000000000..b42565fc9d
--- /dev/null
+++ b/engines/kyra/graphics/screen_lol.cpp
@@ -0,0 +1,893 @@
+/* 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 2
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifdef ENABLE_LOL
+
+#include "kyra/graphics/screen_lol.h"
+#include "kyra/engine/lol.h"
+
+#include "common/system.h"
+
+#include "graphics/palette.h"
+
+namespace Kyra {
+
+Screen_LoL::Screen_LoL(LoLEngine *vm, OSystem *system) : Screen_v2(vm, system, vm->gameFlags().use16ColorMode ? _screenDimTable16C : _screenDimTable256C, _screenDimTableCount) {
+ _paletteOverlay1 = new uint8[0x100];
+ _paletteOverlay2 = new uint8[0x100];
+ _grayOverlay = new uint8[0x100];
+ memset(_paletteOverlay1, 0, 0x100);
+ memset(_paletteOverlay2, 0, 0x100);
+ memset(_grayOverlay, 0, 0x100);
+
+ for (int i = 0; i < 8; i++)
+ _levelOverlays[i] = new uint8[256];
+
+ _fadeFlag = 2;
+}
+
+Screen_LoL::~Screen_LoL() {
+ for (int i = 0; i < 8; i++)
+ delete[] _levelOverlays[i];
+
+ delete[] _paletteOverlay1;
+ delete[] _paletteOverlay2;
+ delete[] _grayOverlay;
+}
+
+void Screen_LoL::fprintString(const char *format, int x, int y, uint8 col1, uint8 col2, uint flags, ...) {
+ if (!format)
+ return;
+
+ char string[240];
+ va_list vaList;
+ va_start(vaList, flags);
+ vsnprintf(string, sizeof(string), format, vaList);
+ va_end(vaList);
+
+ if (flags & 1)
+ x -= (getTextWidth(string) >> 1);
+
+ if (flags & 2)
+ x -= getTextWidth(string);
+
+ if (_use16ColorMode) {
+ if (flags & 12) {
+ printText(string, x - 1, y, 0x44, col2);
+ printText(string, x, y + 1, 0x44, col2);
+ }
+ } else {
+ if (flags & 4) {
+ printText(string, x - 1, y, 1, col2);
+ printText(string, x, y + 1, 1, col2);
+ }
+
+ if (flags & 8) {
+ printText(string, x - 1, y, 227, col2);
+ printText(string, x, y + 1, 227, col2);
+ }
+ }
+
+ printText(string, x, y, col1, col2);
+}
+
+void Screen_LoL::fprintStringIntro(const char *format, int x, int y, uint8 c1, uint8 c2, uint8 c3, uint flags, ...) {
+ char buffer[400];
+
+ va_list args;
+ va_start(args, flags);
+ vsnprintf(buffer, sizeof(buffer), format, args);
+ va_end(args);
+
+ if ((flags & 0x0F00) == 0x100)
+ x -= getTextWidth(buffer) >> 1;
+ if ((flags & 0x0F00) == 0x200)
+ x -= getTextWidth(buffer);
+
+ if ((flags & 0x00F0) == 0x20) {
+ printText(buffer, x - 1, y, c3, c2);
+ printText(buffer, x, y + 1, c3, c2);
+ }
+
+ printText(buffer, x, y, c1, c2);
+}
+
+void Screen_LoL::drawShadedBox(int x1, int y1, int x2, int y2, int color1, int color2) {
+ assert(x1 >= 0 && y1 >= 0);
+ hideMouse();
+
+ fillRect(x1, y1, x2, y1 + 1, color1);
+ fillRect(x1, y1, x1 + 1, y2, color1);
+
+ drawClippedLine(x2, y1, x2, y2, color2);
+ drawClippedLine(x2 - 1, y1 + 1, x2 - 1, y2 - 1, color2);
+ drawClippedLine(x1 + 1, y2 - 1, x2, y2 - 1, color2);
+ drawClippedLine(x1, y2, x2, y2, color2);
+
+ if (_use16ColorMode && color1 > color2)
+ drawBox(x1, y1, x2, y2, 0x44);
+
+ showMouse();
+}
+
+void Screen_LoL::generateGrayOverlay(const Palette &srcPal, uint8 *grayOverlay, int factor, int addR, int addG, int addB, int lastColor, bool skipSpecialColors) {
+ Palette tmpPal(lastColor);
+
+ for (int i = 0; i != lastColor; i++) {
+ int v = (((srcPal[3 * i] & 0x3F) * factor) / 0x40) + addR;
+ tmpPal[3 * i] = (v > 0x3F) ? 0x3F : v & 0xFF;
+ v = (((srcPal[3 * i + 1] & 0x3F) * factor) / 0x40) + addG;
+ tmpPal[3 * i + 1] = (v > 0x3F) ? 0x3F : v & 0xFF;
+ v = (((srcPal[3 * i + 2] & 0x3F) * factor) / 0x40) + addB;
+ tmpPal[3 * i + 2] = (v > 0x3F) ? 0x3F : v & 0xFF;
+ }
+
+ for (int i = 0; i < lastColor; i++)
+ grayOverlay[i] = findLeastDifferentColor(tmpPal.getData() + 3 * i, srcPal, 0, lastColor, skipSpecialColors);
+}
+
+void Screen_LoL::createTransparencyTablesIntern(const uint8 *ovl, int a, const uint8 *fxPal1, const uint8 *fxPal2, uint8 *outTable1, uint8 *outTable2, int b) {
+ Palette screenPal(256);
+ screenPal.copy(fxPal2, 0, 256);
+
+ memset(outTable1, 0xFF, 256);
+
+ for (int i = 0; i < a; i++)
+ outTable1[ovl[i]] = i;
+
+ for (int i = 0; i < a; i++) {
+ if (ovl[i]) {
+ uint8 tcol[3];
+ uint16 fcol[3];
+ uint16 scol[3];
+
+ uint16 t1 = (b << 6) / 100;
+ uint16 t2 = 64 - t1;
+
+ uint8 c = ovl[i];
+ fcol[0] = fxPal1[3 * c];
+ fcol[1] = fxPal1[3 * c + 1];
+ fcol[2] = fxPal1[3 * c + 2];
+
+ uint8 *o = &outTable2[i << 8];
+
+ for (int ii = 0; ii < 256; ii++) {
+ scol[0] = screenPal[3 * ii];
+ scol[1] = screenPal[3 * ii + 1];
+ scol[2] = screenPal[3 * ii + 2];
+
+ tcol[0] = CLIP(((fcol[0] * t2) >> 6) + ((scol[0] * t1) >> 6), 0, 63);
+ tcol[1] = CLIP(((fcol[1] * t2) >> 6) + ((scol[1] * t1) >> 6), 0, 63);
+ tcol[2] = CLIP(((fcol[2] * t2) >> 6) + ((scol[2] * t1) >> 6), 0, 63);
+
+ o[ii] = findLeastDifferentColor(tcol, screenPal, 0, 255);
+ }
+
+ } else {
+ memset(&outTable2[i << 8], 0, 256);
+ }
+ }
+}
+
+void Screen_LoL::drawGridBox(int x, int y, int w, int h, int col) {
+ if (w <= 0 || x >= 320 || h <= 0 || y >= 200)
+ return;
+
+ if (x < 0) {
+ x += w;
+ if (x <= 0)
+ return;
+ w = x;
+ x = 0;
+ }
+
+ int tmp = x + w;
+ if (tmp >= 320) {
+ w = 320 - x;
+ }
+
+ int pitch = 320 - w;
+
+ if (y < 0) {
+ y += h;
+ if (y <= 0)
+ return;
+ h = y;
+ y = 0;
+ }
+
+ tmp = y + h;
+ if (tmp >= 200) {
+ h = 200 - y;
+ }
+
+ tmp = (y + x) & 1;
+ uint8 *p = getPagePtr(_curPage) + y * 320 + x;
+ uint8 s = (tmp >> 8) & 1;
+
+ w >>= 1;
+ int w2 = w;
+
+ while (h--) {
+ if (w) {
+ while (w--) {
+ *(p + tmp) = col;
+ p += 2;
+ }
+ }
+
+ if (s == 1) {
+ if (tmp == 0)
+ *p = col;
+ p++;
+ }
+ tmp ^= 1;
+ p += pitch;
+ w = w2;
+ }
+}
+
+void Screen_LoL::fadeClearSceneWindow(int delay) {
+ if (_fadeFlag == 1)
+ return;
+
+ if (_use16ColorMode) {
+ fadeToBlack(delay);
+ fillRect(112, 0, 288, 120, 0x44);
+ } else {
+ Palette tpal(getPalette(0).getNumColors());
+ tpal.copy(getPalette(0), 128);
+
+ loadSpecialColors(tpal);
+ fadePalette(tpal, delay);
+
+ fillRect(112, 0, 288, 120, 0);
+ }
+
+ _fadeFlag = 1;
+}
+
+void Screen_LoL::backupSceneWindow(int srcPageNum, int dstPageNum) {
+ uint8 *src = getPagePtr(srcPageNum) + 112;
+ uint8 *dst = getPagePtr(dstPageNum) + 0xA500;
+
+ for (int h = 0; h < 120; h++) {
+ for (int w = 0; w < 176; w++)
+ *dst++ = *src++;
+ src += 144;
+ }
+}
+
+void Screen_LoL::restoreSceneWindow(int srcPageNum, int dstPageNum) {
+ uint8 *src = getPagePtr(srcPageNum) + 0xA500;
+ uint8 *dst = getPagePtr(dstPageNum) + 112;
+
+ for (int h = 0; h < 120; h++) {
+ memcpy(dst, src, 176);
+ src += 176;
+ dst += 320;
+ }
+
+ if (!dstPageNum)
+ addDirtyRect(112, 0, 176, 120);
+}
+
+void Screen_LoL::clearGuiShapeMemory(int pageNum) {
+ uint8 *dst = getPagePtr(pageNum) + 0x79B0;
+ for (int i = 0; i < 23; i++) {
+ memset(dst, 0, 176);
+ dst += 320;
+ }
+}
+
+void Screen_LoL::copyGuiShapeFromSceneBackupBuffer(int srcPageNum, int dstPageNum) {
+ uint8 *src = getPagePtr(srcPageNum) + 0x79C3;
+ uint8 *dst = getPagePtr(dstPageNum);
+
+ for (int i = 0; i < 23; i++) {
+ uint8 len = 0;
+ uint8 v = 0;
+
+ do {
+ v = *src++;
+ len++;
+ } while (!v);
+
+ *dst++ = len;
+
+ len = 69 - len;
+ memcpy(dst, src, len);
+ src += (len + 251);
+ dst += len;
+ }
+}
+
+void Screen_LoL::copyGuiShapeToSurface(int srcPageNum, int dstPageNum) {
+ uint8 *src = getPagePtr(srcPageNum);
+ uint8 *dst = getPagePtr(dstPageNum) + 0xE7C3;
+
+ for (int i = 0; i < 23; i++) {
+ uint8 v = *src++;
+ uint8 len = 69 - v;
+ dst += v;
+ memcpy(dst, src, len);
+ src += (len - 1);
+ dst += len;
+
+ for (int ii = 0; ii < len; ii++)
+ *dst++ = *src--;
+
+ src += (len + 1);
+ dst += (v + 38);
+ }
+}
+
+void Screen_LoL::smoothScrollZoomStepTop(int srcPageNum, int dstPageNum, int x, int y) {
+ uint8 *src = getPagePtr(srcPageNum) + 0xA500 + y * 176 + x;
+ uint8 *dst = getPagePtr(dstPageNum) + 0xA500;
+
+ x <<= 1;
+ uint16 width = 176 - x;
+ uint16 scaleX = (((x + 1) << 8) / width + 0x100);
+ uint16 cntW = scaleX >> 8;
+ scaleX <<= 8;
+ width--;
+ uint16 widthCnt = width;
+
+ uint16 height = 46 - y;
+ uint16 scaleY = (((y + 1) << 8) / height + 0x100);
+ scaleY <<= 8;
+
+ uint32 scaleYc = 0;
+ while (height) {
+ uint32 scaleXc = 0;
+ do {
+ scaleXc += scaleX;
+ int numbytes = cntW + (scaleXc >> 16);
+ scaleXc &= 0xFFFF;
+ memset(dst, *src++, numbytes);
+ dst += numbytes;
+ } while (--widthCnt);
+
+ *dst++ = *src++;
+ widthCnt = width;
+
+ src += x;
+ scaleYc += scaleY;
+
+ if (scaleYc >> 16) {
+ scaleYc = 0;
+ src -= 176;
+ continue;
+ }
+
+ height--;
+ }
+}
+
+void Screen_LoL::smoothScrollZoomStepBottom(int srcPageNum, int dstPageNum, int x, int y) {
+ uint8 *src = getPagePtr(srcPageNum) + 0xC4A0 + x;
+ uint8 *dst = getPagePtr(dstPageNum) + 0xC4A0;
+
+ x <<= 1;
+ uint16 width = 176 - x;
+ uint16 scaleX = (((x + 1) << 8) / width + 0x100);
+ uint16 cntW = scaleX >> 8;
+ scaleX <<= 8;
+ width--;
+ uint16 widthCnt = width;
+
+ uint16 height = 74 - y;
+ uint16 scaleY = (((y + 1) << 8) / height + 0x100);
+ scaleY <<= 8;
+
+ uint32 scaleYc = 0;
+ while (height) {
+ uint32 scaleXc = 0;
+ do {
+ scaleXc += scaleX;
+ int numbytes = cntW + (scaleXc >> 16);
+ scaleXc &= 0xFFFF;
+ memset(dst, *src++, numbytes);
+ dst += numbytes;
+ } while (--widthCnt);
+
+ *dst++ = *src++;
+ widthCnt = width;
+
+ src += x;
+ scaleYc += scaleY;
+
+ if (scaleYc >> 16) {
+ scaleYc = 0;
+ src -= 176;
+ continue;
+ }
+
+ height--;
+ }
+}
+
+void Screen_LoL::smoothScrollHorizontalStep(int pageNum, int srcX, int dstX, int w) {
+ uint8 *d = getPagePtr(pageNum);
+ uint8 *s = d + 112 + srcX;
+
+ int w2 = srcX + w - dstX;
+ int pitchS = 320 + w2 - (w << 1);
+
+ int pitchD = 320 - w;
+ int h = 120;
+
+ while (h--) {
+ for (int i = 0; i < w; i++)
+ *d++ = *s++;
+ d -= w;
+ s -= w2;
+
+ for (int i = 0; i < w; i++)
+ *s++ = *d++;
+
+ s += pitchS;
+ d += pitchD;
+ }
+}
+
+void Screen_LoL::smoothScrollTurnStep1(int srcPage1Num, int srcPage2Num, int dstPageNum) {
+ uint8 *s = getPagePtr(srcPage1Num) + 273;
+ uint8 *d = getPagePtr(dstPageNum) + 0xA500;
+
+ for (int i = 0; i < 120; i++) {
+ uint8 a = *s++;
+ *d++ = a;
+ *d++ = a;
+
+ for (int ii = 0; ii < 14; ii++) {
+ a = *s++;
+ *d++ = a;
+ *d++ = a;
+ *d++ = a;
+ }
+
+ s += 305;
+ d += 132;
+ }
+
+ s = getPagePtr(srcPage2Num) + 112;
+ d = getPagePtr(dstPageNum) + 0xA52C;
+
+ for (int i = 0; i < 120; i++) {
+ for (int ii = 0; ii < 33; ii++) {
+ *d++ = *s++;
+ *d++ = *s++;
+ uint8 a = *s++;
+ *d++ = a;
+ *d++ = a;
+ }
+
+ s += 221;
+ d += 44;
+ }
+}
+
+void Screen_LoL::smoothScrollTurnStep2(int srcPage1Num, int srcPage2Num, int dstPageNum) {
+ uint8 *s = getPagePtr(srcPage1Num) + 244;
+ uint8 *d = getPagePtr(dstPageNum) + 0xA500;
+
+ for (int k = 0; k < 2; k++) {
+ for (int i = 0; i < 120; i++) {
+ for (int ii = 0; ii < 44; ii++) {
+ uint8 a = *s++;
+ *d++ = a;
+ *d++ = a;
+ }
+
+ s += 276;
+ d += 88;
+ }
+
+ s = getPagePtr(srcPage2Num) + 112;
+ d = getPagePtr(dstPageNum) + 0xA558;
+ }
+}
+
+void Screen_LoL::smoothScrollTurnStep3(int srcPage1Num, int srcPage2Num, int dstPageNum) {
+ uint8 *s = getPagePtr(srcPage1Num) + 189;
+ uint8 *d = getPagePtr(dstPageNum) + 0xA500;
+
+ for (int i = 0; i < 120; i++) {
+ for (int ii = 0; ii < 33; ii++) {
+ *d++ = *s++;
+ *d++ = *s++;
+ uint8 a = *s++;
+ *d++ = a;
+ *d++ = a;
+ }
+
+ s += 221;
+ d += 44;
+ }
+
+ s = getPagePtr(srcPage2Num) + 112;
+ d = getPagePtr(dstPageNum) + 0xA584;
+
+ for (int i = 0; i < 120; i++) {
+ for (int ii = 0; ii < 14; ii++) {
+ uint8 a = *s++;
+ *d++ = a;
+ *d++ = a;
+ *d++ = a;
+ }
+
+ uint8 a = *s++;
+ *d++ = a;
+ *d++ = a;
+
+ s += 305;
+ d += 132;
+ }
+}
+
+void Screen_LoL::copyRegionSpecial(int page1, int w1, int h1, int x1, int y1, int page2, int w2, int h2, int x2, int y2, int w3, int h3, int mode, ...) {
+ if (!w3 || !h3)
+ return;
+
+ uint8 *table1 = 0;
+ uint8 *table2 = 0;
+
+ if (mode == 2) {
+ va_list args;
+ va_start(args, mode);
+ table1 = va_arg(args, uint8 *);
+ table2 = va_arg(args, uint8 *);
+ va_end(args);
+ }
+
+ int na = 0, nb = 0, nc = w3;
+ if (!calcBounds(w1, h1, x1, y1, w3, h3, na, nb, nc))
+ return;
+
+ int iu5_1 = na;
+ int iu6_1 = nb;
+ int ibw_1 = w3;
+ int dx_1 = x1;
+ int dy_1 = y1;
+
+ if (!calcBounds(w2, h2, x2, y2, w3, h3, na, nb, nc))
+ return;
+
+ int iu5_2 = na;
+ int iu6_2 = nb;
+ int ibw_2 = w3;
+ int ibh_2 = h3;
+ int dx_2 = x2;
+ int dy_2 = y2;
+
+ uint8 *src = getPagePtr(page1) + (dy_1 + iu6_2) * w1;
+ uint8 *dst = getPagePtr(page2) + (dy_2 + iu6_1) * w2;
+
+ for (int i = 0; i < ibh_2; i++) {
+ uint8 *s = src + iu5_2 + dx_1;
+ uint8 *d = dst + iu5_1 + dx_2;
+
+ if (mode == 0) {
+ memcpy(d, s, ibw_2);
+
+ } else if (mode == 1) {
+ if (!(i & 1)) {
+ s++;
+ d++;
+ }
+
+ for (int ii = (i & 1) ^ 1; ii < ibw_2; ii += 2) {
+ *d = *s;
+ d += 2;
+ s += 2;
+ }
+
+ } else if (mode == 2) {
+ for (int ii = 0; ii < ibw_2; ii++) {
+ uint8 cmd = *s++;
+ uint8 offs = table1[cmd];
+ if (!(offs & 0x80))
+ cmd = table2[(offs << 8) | *d];
+ *d++ = cmd;
+ }
+
+ } else if (mode == 3) {
+ s = s - iu5_2 + ibw_1;
+ s = s - iu5_2 - 1;
+ for (int ii = 0; ii < ibw_2; ii++)
+ *d++ = *s--;
+ }
+
+ dst += w2;
+ src += w1;
+ }
+
+ if (!page2)
+ addDirtyRect(x2, y2, w2, h2);
+}
+
+void Screen_LoL::copyBlockAndApplyOverlay(int page1, int x1, int y1, int page2, int x2, int y2, int w, int h, int dim, uint8 *ovl) {
+ if (!w || !h || !ovl)
+ return;
+
+ const ScreenDim *cdim = getScreenDim(dim);
+ int ix = cdim->sx << 3;
+ int iy = cdim->sy;
+ int iw = cdim->w << 3;
+ int ih = cdim->h;
+
+ int na = 0, nb = 0, nc = w;
+ if (!calcBounds(iw, ih, x2, y2, w, h, na, nb, nc))
+ return;
+
+ uint8 *src = getPagePtr(page1) + y1 * 320 + x1;
+ uint8 *dst = getPagePtr(page2) + (y2 + iy) * 320;
+
+ for (int i = 0; i < h; i++) {
+ uint8 *s = src + na;
+ uint8 *d = dst + (x2 + ix);
+
+ for (int ii = 0; ii < w; ii++) {
+ uint8 p = ovl[*s++];
+ if (p)
+ *d = p;
+ d++;
+ }
+
+ dst += 320;
+ src += 320;
+ }
+
+ if (!page2)
+ addDirtyRect(x2 + ix, y2 + iy, w, h);
+}
+
+void Screen_LoL::applyOverlaySpecial(int page1, int x1, int y1, int page2, int x2, int y2, int w, int h, int dim, int flag, uint8 *ovl) {
+ if (!w || !h || !ovl)
+ return;
+
+ const ScreenDim *cdim = getScreenDim(dim);
+ int ix = cdim->sx << 3;
+ int iy = cdim->sy;
+ int iw = cdim->w << 3;
+ int ih = cdim->h;
+
+ int na = 0, nb = 0, nc = w;
+ if (!calcBounds(iw, ih, x2, y2, w, h, na, nb, nc))
+ return;
+
+ uint8 *src = getPagePtr(page1) + y1 * 320 + x1;
+ uint8 *dst = getPagePtr(page2) + (y2 + iy) * 320;
+
+ for (int i = 0; i < h; i++) {
+ uint8 *s = src + na;
+ uint8 *d = dst + (x2 + ix);
+
+ if (flag)
+ d += (i >> 1);
+
+ for (int ii = 0; ii < w; ii++) {
+ if (*s++)
+ *d = ovl[*d];
+ d++;
+ }
+
+ dst += 320;
+ src += 320;
+ }
+
+ if (!page2)
+ addDirtyRect(x2 + ix, y2 + iy, w, h);
+}
+
+void Screen_LoL::copyBlockAndApplyOverlayOutro(int srcPage, int dstPage, const uint8 *ovl) {
+ if (!ovl)
+ return;
+
+ const byte *src = getCPagePtr(srcPage);
+ byte *dst = getPagePtr(dstPage);
+
+ for (int y = 0; y < 200; ++y) {
+ for (int x = 0; x < 80; ++x) {
+ uint32 srcData = READ_LE_UINT32(src); src += 4;
+ uint32 dstData = READ_LE_UINT32(dst);
+ uint16 offset = 0;
+
+ offset = ((srcData & 0xFF) << 8) + (dstData & 0xFF);
+ *dst++ = ovl[offset];
+
+ offset = (srcData & 0xFF00) + ((dstData & 0xFF00) >> 8);
+ *dst++ = ovl[offset];
+
+ srcData >>= 16;
+ dstData >>= 16;
+
+ offset = ((srcData & 0xFF) << 8) + (dstData & 0xFF);
+ *dst++ = ovl[offset];
+
+ offset = (srcData & 0xFF00) + ((dstData & 0xFF00) >> 8);
+ *dst++ = ovl[offset];
+ }
+ }
+}
+
+void Screen_LoL::fadeToBlack(int delay, const UpdateFunctor *upFunc) {
+ Screen::fadeToBlack(delay, upFunc);
+ _fadeFlag = 2;
+}
+
+void Screen_LoL::fadeToPalette1(int delay) {
+ loadSpecialColors(getPalette(1));
+ fadePalette(getPalette(1), delay);
+ _fadeFlag = 0;
+}
+
+void Screen_LoL::loadSpecialColors(Palette &dst) {
+ if (_use16ColorMode)
+ return;
+
+ dst.copy(*_screenPalette, 192, 4);
+}
+
+void Screen_LoL::copyColor(int dstColorIndex, int srcColorIndex) {
+ uint8 *s = _screenPalette->getData() + srcColorIndex * 3;
+ uint8 *d = _screenPalette->getData() + dstColorIndex * 3;
+ memcpy(d, s, 3);
+
+ uint8 ci[3];
+ ci[0] = (d[0] << 2) | (d[0] & 3);
+ ci[1] = (d[1] << 2) | (d[1] & 3);
+ ci[2] = (d[2] << 2) | (d[2] & 3);
+
+ _system->getPaletteManager()->setPalette(ci, dstColorIndex, 1);
+}
+
+bool Screen_LoL::fadeColor(int dstColorIndex, int srcColorIndex, uint32 elapsedTicks, uint32 totalTicks) {
+ if (_use16ColorMode)
+ return false;
+
+ const uint8 *dst = _screenPalette->getData() + 3 * dstColorIndex;
+ const uint8 *src = _screenPalette->getData() + 3 * srcColorIndex;
+ uint8 *p = getPalette(1).getData() + 3 * dstColorIndex;
+
+ bool res = false;
+
+ int16 srcV = 0;
+ int16 dstV = 0;
+ int32 outV = 0;
+
+ uint8 tmpPalEntry[3];
+
+ for (int i = 0; i < 3; i++) {
+ if (elapsedTicks < totalTicks) {
+ srcV = *src & 0x3F;
+ dstV = *dst & 0x3F;
+
+ outV = srcV - dstV;
+ if (outV)
+ res = true;
+
+ outV = dstV + ((((outV << 8) / (int32)totalTicks) * (int32)elapsedTicks) >> 8);
+ } else {
+ *p = outV = *src;
+ res = false;
+ }
+
+ tmpPalEntry[i] = outV & 0xFF;
+ src++;
+ dst++;
+ p++;
+ }
+
+ _internFadePalette->copy(*_screenPalette);
+ _internFadePalette->copy(tmpPalEntry, 0, 1, dstColorIndex);
+ setScreenPalette(*_internFadePalette);
+ updateScreen();
+
+ return res;
+}
+
+Palette **Screen_LoL::generateFadeTable(Palette **dst, Palette *src1, Palette *src2, int numTabs) {
+ int len = _use16ColorMode ? 48 : 768;
+ if (!src1)
+ src1 = _screenPalette;
+
+ uint8 *p1 = (*dst++)->getData();
+ uint8 *p2 = src1->getData();
+ uint8 *p3 = src2->getData();
+ uint8 *p4 = p1;
+ uint8 *p5 = p2;
+
+ for (int i = 0; i < len; i++) {
+ int8 val = (int8)*p3++ - (int8)*p2++;
+ *p4++ = (uint8)val;
+ }
+
+ int16 t = 0;
+ int16 d = 256 / numTabs;
+
+ for (int i = 1; i < numTabs - 1; i++) {
+ p2 = p5;
+ p3 = p1;
+ t += d;
+ p4 = (*dst++)->getData();
+
+ for (int ii = 0; ii < len; ii++) {
+ int16 val = (((int8)*p3++ * t) >> 8) + (int8)*p2++;
+ *p4++ = (uint8)val;
+ }
+ }
+
+ memcpy(p1, p5, len);
+ (*dst)->copy(*src2);
+
+ return ++dst;
+}
+
+uint8 Screen_LoL::getShapePaletteSize(const uint8 *shp) {
+ return shp[10];
+}
+
+void Screen_LoL::mergeOverlay(int x, int y, int w, int h) {
+ // For now we convert to 16 colors on overlay merging. If that gives
+ // any problems, like Screen functionallity not prepared for the
+ // format PC98 16 color uses, we'll need to think of a better way.
+ //
+ // We must do this before merging the overlay, else the font colors
+ // will be wrong.
+ if (_use16ColorMode)
+ convertPC98Gfx(_sjisOverlayPtrs[0] + y * 640 + x, w, h, 640);
+
+ Screen_v2::mergeOverlay(x, y, w, h);
+}
+
+void Screen_LoL::convertPC98Gfx(uint8 *data, int w, int h, int pitch) {
+ while (h--) {
+ for (int i = 0; i < w; ++i) {
+ *data = (*data >> 4) & (*data & 0x0F);
+ ++data;
+ }
+
+ data += pitch - w;
+ }
+}
+
+void Screen_LoL::postProcessCursor(uint8 *data, int w, int h, int pitch) {
+ if (!_use16ColorMode)
+ return;
+
+ while (h--) {
+ for (int i = 0; i < w; ++i) {
+ if (*data != _cursorColorKey)
+ *data = (*data >> 4) & (*data & 0x0F);
+ ++data;
+ }
+
+ data += pitch - w;
+ }
+}
+
+} // End of namespace Kyra
+
+#endif // ENABLE_LOL
diff --git a/engines/kyra/graphics/screen_lol.h b/engines/kyra/graphics/screen_lol.h
new file mode 100644
index 0000000000..ef14c52463
--- /dev/null
+++ b/engines/kyra/graphics/screen_lol.h
@@ -0,0 +1,105 @@
+/* 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 2
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifdef ENABLE_LOL
+
+#ifndef KYRA_SCREEN_LOL_H
+#define KYRA_SCREEN_LOL_H
+
+#include "kyra/graphics/screen_v2.h"
+
+namespace Kyra {
+
+class LoLEngine;
+
+class Screen_LoL : public Screen_v2 {
+public:
+ Screen_LoL(LoLEngine *vm, OSystem *system);
+ ~Screen_LoL();
+
+ void fprintString(const char *format, int x, int y, uint8 col1, uint8 col2, uint flags, ...) GCC_PRINTF(2, 8);
+ void fprintStringIntro(const char *format, int x, int y, uint8 c1, uint8 c2, uint8 c3, uint flags, ...) GCC_PRINTF(2, 9);
+
+ void drawShadedBox(int x1, int y1, int x2, int y2, int color1, int color2);
+
+ void drawGridBox(int x, int y, int w, int h, int col);
+ void fadeClearSceneWindow(int delay);
+
+ // smooth scrolling
+ void backupSceneWindow(int srcPageNum, int dstPageNum);
+ void restoreSceneWindow(int srcPageNum, int dstPageNum);
+ void clearGuiShapeMemory(int pageNum);
+ void copyGuiShapeFromSceneBackupBuffer(int srcPageNum, int dstPageNum);
+ void copyGuiShapeToSurface(int srcPageNum, int dstPageNum);
+ void smoothScrollZoomStepTop(int srcPageNum, int dstPageNum, int x, int y);
+ void smoothScrollZoomStepBottom(int srcPageNum, int dstPageNum, int x, int y);
+ void smoothScrollHorizontalStep(int pageNum, int x, int u2, int w);
+ void smoothScrollTurnStep1(int srcPage1Num, int srcPage2Num, int dstPageNum);
+ void smoothScrollTurnStep2(int srcPage1Num, int srcPage2Num, int dstPageNum);
+ void smoothScrollTurnStep3(int srcPage1Num, int srcPage2Num, int dstPageNum);
+
+ void copyRegionSpecial(int page1, int w1, int h1, int x1, int y1, int page2, int w2, int h2, int x2, int y2, int w3, int h3, int mode, ...);
+
+ // palette stuff
+ void fadeToBlack(int delay=0x54, const UpdateFunctor *upFunc = 0);
+ void fadeToPalette1(int delay);
+ void loadSpecialColors(Palette &dst);
+ void copyColor(int dstColorIndex, int srcColorIndex);
+ bool fadeColor(int dstColorIndex, int srcColorIndex, uint32 elapsedTicks, uint32 totalTicks);
+ Palette **generateFadeTable(Palette **dst, Palette *src1, Palette *src2, int numTabs);
+
+ void generateGrayOverlay(const Palette &Pal, uint8 *grayOverlay, int factor, int addR, int addG, int addB, int lastColor, bool skipSpecialColors);
+ uint8 *getLevelOverlay(int index) { return _levelOverlays[index]; }
+
+ void createTransparencyTablesIntern(const uint8 *ovl, int a, const uint8 *fxPal1, const uint8 *fxPal2, uint8 *outTable1, uint8 *outTable2, int b);
+
+ void copyBlockAndApplyOverlay(int page1, int x1, int y1, int page2, int x2, int y2, int w, int h, int dim, uint8 *ovl);
+ void applyOverlaySpecial(int page1, int x1, int y1, int page2, int x2, int y2, int w, int h, int dim, int flag, uint8 *ovl);
+
+ void copyBlockAndApplyOverlayOutro(int srcPage, int dstPage, const uint8 *ovl);
+
+ uint8 getShapePaletteSize(const uint8 *shp);
+
+ uint8 *_paletteOverlay1;
+ uint8 *_paletteOverlay2;
+ uint8 *_grayOverlay;
+ int _fadeFlag;
+
+ // PC98 specific
+ static void convertPC98Gfx(uint8 *data, int w, int h, int pitch);
+
+private:
+ static const ScreenDim _screenDimTable256C[];
+ static const ScreenDim _screenDimTable16C[];
+ static const int _screenDimTableCount;
+
+ uint8 *_levelOverlays[8];
+
+ void mergeOverlay(int x, int y, int w, int h);
+ void postProcessCursor(uint8 *data, int width, int height, int pitch);
+};
+
+} // End of namespace Kyra
+
+#endif
+
+#endif // ENABLE_LOL
diff --git a/engines/kyra/graphics/screen_mr.cpp b/engines/kyra/graphics/screen_mr.cpp
new file mode 100644
index 0000000000..acb99fe0b0
--- /dev/null
+++ b/engines/kyra/graphics/screen_mr.cpp
@@ -0,0 +1,130 @@
+/* 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 2
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "kyra/graphics/screen_mr.h"
+#include "kyra/engine/kyra_mr.h"
+
+namespace Kyra {
+
+Screen_MR::Screen_MR(KyraEngine_MR *vm, OSystem *system)
+ : Screen_v2(vm, system, _screenDimTable, _screenDimTableCount) {
+}
+
+Screen_MR::~Screen_MR() {
+}
+
+int Screen_MR::getLayer(int x, int y) {
+ if (x < 0)
+ x = 0;
+ else if (x >= 320)
+ x = 319;
+ if (y < 0)
+ y = 0;
+ else if (y >= 188)
+ y = 187;
+
+ if (y < _maskMinY || y > _maskMaxY)
+ return 15;
+
+ uint8 pixel = *(getCPagePtr(5) + y * 320 + x);
+ pixel &= 0x7F;
+ pixel >>= 3;
+
+ if (pixel < 1)
+ pixel = 1;
+ else if (pixel > 15)
+ pixel = 15;
+ return pixel;
+}
+
+byte Screen_MR::getShapeFlag1(int x, int y) {
+ if (y < _maskMinY || y > _maskMaxY)
+ return 0;
+
+ uint8 color = _shapePages[0][y * SCREEN_W + x];
+ color &= 0x80;
+ color ^= 0x80;
+
+ if (color & 0x80)
+ return 1;
+ return 0;
+}
+
+byte Screen_MR::getShapeFlag2(int x, int y) {
+ if (y < _maskMinY || y > _maskMaxY)
+ return 0;
+
+ uint8 color = _shapePages[0][y * SCREEN_W + x];
+ color &= 0x7F;
+ color &= 0x87;
+ return color;
+}
+
+int Screen_MR::getDrawLayer(int x, int y) {
+ int xpos = x - 8;
+ int ypos = y;
+ int layer = 1;
+
+ for (int curX = xpos; curX < xpos + 24; ++curX) {
+ int tempLayer = getShapeFlag2(curX, ypos);
+
+ if (layer < tempLayer)
+ layer = tempLayer;
+
+ if (layer >= 7)
+ return 7;
+ }
+ return layer;
+}
+
+int Screen_MR::getDrawLayer2(int x, int y, int height) {
+ int xpos = x - 8;
+ int ypos = y;
+ int layer = 1;
+
+ for (int useX = xpos; useX < xpos + 24; ++useX) {
+ for (int useY = ypos - height; useY < ypos; ++useY) {
+ int tempLayer = getShapeFlag2(useX, useY);
+
+ if (tempLayer > layer)
+ layer = tempLayer;
+
+ if (tempLayer >= 7)
+ return 7;
+ }
+ }
+ return layer;
+}
+
+void Screen_MR::drawFilledBox(int x1, int y1, int x2, int y2, uint8 c1, uint8 c2, uint8 c3) {
+ fillRect(x1, y1, x2, y2, c1);
+
+ fillRect(x1, y1, x2, y1+1, c2);
+ fillRect(x2-1, y1, x2, y2, c2);
+
+ drawClippedLine(x1, y1, x1, y2, c3);
+ drawClippedLine(x1+1, y1+1, x1+1, y2-2, c3);
+ drawClippedLine(x1, y2, x2, y2, c3);
+ drawClippedLine(x1, y2-1, x2-1, y2-1, c3);
+}
+
+} // End of namespace Kyra
diff --git a/engines/kyra/graphics/screen_mr.h b/engines/kyra/graphics/screen_mr.h
new file mode 100644
index 0000000000..fcbef380c7
--- /dev/null
+++ b/engines/kyra/graphics/screen_mr.h
@@ -0,0 +1,53 @@
+/* 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 2
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef KYRA_SCREEN_MR_H
+#define KYRA_SCREEN_MR_H
+
+#include "kyra/graphics/screen_v2.h"
+
+namespace Kyra {
+
+class KyraEngine_MR;
+
+class Screen_MR : public Screen_v2 {
+public:
+ Screen_MR(KyraEngine_MR *vm, OSystem *system);
+ ~Screen_MR();
+
+ int getLayer(int x, int y);
+
+ byte getShapeFlag1(int x, int y);
+ byte getShapeFlag2(int x, int y);
+
+ int getDrawLayer(int x, int y);
+ int getDrawLayer2(int x, int y, int height);
+
+ void drawFilledBox(int x1, int y1, int x2, int y2, uint8 c1, uint8 c2, uint8 c3);
+private:
+ static const ScreenDim _screenDimTable[];
+ static const int _screenDimTableCount;
+};
+
+} // End of namespace Kyra
+
+#endif
diff --git a/engines/kyra/graphics/screen_v2.cpp b/engines/kyra/graphics/screen_v2.cpp
new file mode 100644
index 0000000000..cb9fbca1b7
--- /dev/null
+++ b/engines/kyra/graphics/screen_v2.cpp
@@ -0,0 +1,461 @@
+/* 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 2
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "kyra/graphics/screen_v2.h"
+
+#include "common/endian.h"
+
+namespace Kyra {
+
+Screen_v2::Screen_v2(KyraEngine_v1 *vm, OSystem *system, const ScreenDim *dimTable, const int dimTableSize)
+ : Screen(vm, system, dimTable, dimTableSize), _wsaFrameAnimBuffer(0) {
+ _wsaFrameAnimBuffer = new uint8[1024];
+ assert(_wsaFrameAnimBuffer);
+}
+
+Screen_v2::~Screen_v2() {
+ delete[] _wsaFrameAnimBuffer;
+}
+
+uint8 *Screen_v2::generateOverlay(const Palette &pal, uint8 *buffer, int opColor, uint weight, int maxColor) {
+ if (!buffer)
+ return buffer;
+
+ weight = MIN<uint>(weight, 255) >> 1;
+
+ const byte opR = pal[opColor * 3 + 0];
+ const byte opG = pal[opColor * 3 + 1];
+ const byte opB = pal[opColor * 3 + 2];
+
+ uint8 *dst = buffer;
+ *dst++ = 0;
+
+ int maxIndex = maxColor;
+ if (maxIndex == -1) {
+ if (_vm->game() == GI_LOL) {
+ if (_use16ColorMode)
+ maxIndex = 255;
+ else
+ maxIndex = 127;
+ } else {
+ maxIndex = 255;
+ }
+ }
+
+ for (int i = 1; i != 256; ++i) {
+ const byte curR = pal[i * 3 + 0] - (((pal[i * 3 + 0] - opR) * weight) >> 7);
+ const byte curG = pal[i * 3 + 1] - (((pal[i * 3 + 1] - opG) * weight) >> 7);
+ const byte curB = pal[i * 3 + 2] - (((pal[i * 3 + 2] - opB) * weight) >> 7);
+
+ uint16 idxSum = _use16ColorMode ? 0xFFFF : 0x7FFF;
+ byte index = opColor;
+
+ for (int curIdx = 1; curIdx <= maxIndex; ++curIdx) {
+ if (!_use16ColorMode && i == curIdx)
+ continue;
+
+ int diff = 0;
+ uint16 sum = 0;
+
+ diff = pal[curIdx * 3 + 0] - curR;
+ sum += diff * diff;
+ diff = pal[curIdx * 3 + 1] - curG;
+ sum += diff * diff;
+ diff = pal[curIdx * 3 + 2] - curB;
+ sum += diff * diff;
+
+ if (!sum) {
+ index = curIdx;
+ break;
+ }
+
+ if (sum <= idxSum) {
+ if (!_use16ColorMode || (curIdx == opColor || curIdx != i)) {
+ idxSum = sum;
+ index = curIdx;
+ }
+ }
+ }
+
+ *dst++ = index;
+ }
+
+ return buffer;
+}
+
+void Screen_v2::applyOverlay(int x, int y, int w, int h, int pageNum, const uint8 *overlay) {
+ if (pageNum == 0 || pageNum == 1)
+ addDirtyRect(x, y, w, h);
+
+ uint8 *dst = getPagePtr(pageNum) + y * 320 + x;
+ while (h--) {
+ for (int wi = 0; wi < w; ++wi) {
+ uint8 index = *dst;
+ *dst++ = overlay[index];
+ }
+ dst += 320 - w;
+ }
+}
+
+int Screen_v2::findLeastDifferentColor(const uint8 *paletteEntry, const Palette &pal, uint8 firstColor, uint16 numColors, bool skipSpecialColors) {
+ int m = 0x7FFF;
+ int r = 0x101;
+
+ for (int i = 0; i < numColors; i++) {
+ if (skipSpecialColors && i >= 0xC0 && i <= 0xC3)
+ continue;
+
+ int v = paletteEntry[0] - pal[(i + firstColor) * 3 + 0];
+ int c = v * v;
+ v = paletteEntry[1] - pal[(i + firstColor) * 3 + 1];
+ c += (v * v);
+ v = paletteEntry[2] - pal[(i + firstColor) * 3 + 2];
+ c += (v * v);
+
+ if (c <= m) {
+ m = c;
+ r = i;
+ }
+ }
+
+ return r;
+}
+
+void Screen_v2::getFadeParams(const Palette &pal, int delay, int &delayInc, int &diff) {
+ int maxDiff = 0;
+ diff = 0;
+ for (int i = 0; i < pal.getNumColors() * 3; ++i) {
+ diff = ABS(pal[i] - (*_screenPalette)[i]);
+ maxDiff = MAX(maxDiff, diff);
+ }
+
+ delayInc = delay << 8;
+ if (maxDiff != 0) {
+ delayInc /= maxDiff;
+ delayInc = MIN(delayInc, 0x7FFF);
+ }
+
+ delay = delayInc;
+ for (diff = 1; diff <= maxDiff; ++diff) {
+ if (delayInc >= 256)
+ break;
+ delayInc += delay;
+ }
+}
+
+bool Screen_v2::timedPaletteFadeStep(uint8 *pal1, uint8 *pal2, uint32 elapsedTime, uint32 totalTime) {
+ Palette &p1 = getPalette(1);
+
+ bool res = false;
+ for (int i = 0; i < p1.getNumColors() * 3; i++) {
+ uint8 out = 0;
+
+ if (elapsedTime < totalTime) {
+ int32 d = ((pal2[i] & 0x3F) - (pal1[i] & 0x3F));
+ if (d)
+ res = true;
+
+ int32 val = ((((d << 8) / (int32)totalTime) * (int32)elapsedTime) >> 8);
+ out = ((pal1[i] & 0x3F) + (int8)val);
+ } else {
+ out = p1[i] = (pal2[i] & 0x3F);
+ res = false;
+ }
+
+ (*_internFadePalette)[i] = out;
+ }
+
+ setScreenPalette(*_internFadePalette);
+ updateScreen();
+
+ return res;
+}
+
+const uint8 *Screen_v2::getPtrToShape(const uint8 *shpFile, int shape) {
+ uint16 shapes = READ_LE_UINT16(shpFile);
+
+ if (shapes <= shape)
+ return 0;
+
+ uint32 offset = READ_LE_UINT32(shpFile + (shape << 2) + 2);
+
+ return shpFile + offset + 2;
+}
+
+uint8 *Screen_v2::getPtrToShape(uint8 *shpFile, int shape) {
+ uint16 shapes = READ_LE_UINT16(shpFile);
+
+ if (shapes <= shape)
+ return 0;
+
+ uint32 offset = READ_LE_UINT32(shpFile + (shape << 2) + 2);
+
+ return shpFile + offset + 2;
+}
+
+int Screen_v2::getShapeScaledWidth(const uint8 *shpFile, int scale) {
+ if (!shpFile)
+ return 0;
+ int width = READ_LE_UINT16(shpFile + 3);
+ return (width * scale) >> 8;
+}
+
+int Screen_v2::getShapeScaledHeight(const uint8 *shpFile, int scale) {
+ if (!shpFile)
+ return 0;
+ int height = shpFile[2];
+ return (height * scale) >> 8;
+}
+
+uint16 Screen_v2::getShapeSize(const uint8 *shp) {
+ if (!shp)
+ return 0;
+ return READ_LE_UINT16(shp + 6);
+}
+
+uint8 *Screen_v2::makeShapeCopy(const uint8 *src, int index) {
+ const uint8 *shape = getPtrToShape(src, index);
+ if (!shape)
+ return 0;
+
+ int size = getShapeSize(shape);
+
+ uint8 *copy = new uint8[size];
+ assert(copy);
+ memcpy(copy, shape, size);
+
+ return copy;
+}
+
+int Screen_v2::getLayer(int x, int y) {
+ if (x < 0)
+ x = 0;
+ else if (x >= 320)
+ x = 319;
+ if (y < 0)
+ y = 0;
+ else if (y >= 144)
+ y = 143;
+
+ uint8 pixel = *(getCPagePtr(5) + y * 320 + x);
+ pixel &= 0x7F;
+ pixel >>= 3;
+
+ if (pixel < 1)
+ pixel = 1;
+ else if (pixel > 15)
+ pixel = 15;
+ return pixel;
+}
+
+int Screen_v2::getRectSize(int w, int h) {
+ if (w > 320 || h > 200)
+ return 0;
+ return w * h;
+}
+
+void Screen_v2::setTextColorMap(const uint8 *cmap) {
+ setTextColor(cmap, 0, 15);
+}
+
+void Screen_v2::wsaFrameAnimationStep(int x1, int y1, int x2, int y2,
+ int w1, int h1, int w2, int h2, int srcPage, int dstPage, int dim) {
+
+ if (!w1 || !h1 || !w2 || !h2)
+ return;
+
+ ScreenDim cdm = *getScreenDim(dim);
+ cdm.sx <<= 3;
+ cdm.w <<= 3;
+
+ int na = 0, nb = 0, nc = w2;
+
+ if (!calcBounds(cdm.w, cdm.h, x2, y2, w2, h2, na, nb, nc))
+ return;
+
+ const uint8 *src = getPagePtr(srcPage) + y1 * 320;
+ uint8 *dst = getPagePtr(dstPage) + (y2 + cdm.sy) * 320;
+
+ int u = -1;
+
+ do {
+ int t = (nb * h1) / h2;
+ if (t != u) {
+ u = t;
+ const uint8 *s = src + x1 + t * 320;
+ uint8 *dt = (uint8 *)_wsaFrameAnimBuffer;
+
+ t = w2 - w1;
+ if (!t) {
+ memcpy(dt, s, w2);
+ } else if (t > 0) {
+ if (w1 == 1) {
+ memset(dt, *s, w2);
+ } else {
+ t = ((((((w2 - w1 + 1) & 0xFFFF) << 8) / w1) + 0x100) & 0xFFFF) << 8;
+ int bp = 0;
+ for (int i = 0; i < w1; i++) {
+ int cnt = (t >> 16);
+ bp += (t & 0xFFFF);
+ if (bp > 0xFFFF) {
+ bp -= 0xFFFF;
+ cnt++;
+ }
+ memset(dt, *s++, cnt);
+ dt += cnt;
+ }
+ }
+ } else {
+ if (w2 == 1) {
+ *dt = *s;
+ } else {
+ t = (((((w1 - w2) & 0xFFFF) << 8) / w2) & 0xFFFF) << 8;
+ int bp = 0;
+ for (int i = 0; i < w2; i++) {
+ *dt++ = *s++;
+ bp += (t & 0xFFFF);
+ if (bp > 0xFFFF) {
+ bp -= 0xFFFF;
+ s++;
+ }
+ s += (t >> 16);
+ }
+ }
+ }
+ }
+ memcpy(dst + x2 + cdm.sx, _wsaFrameAnimBuffer + na, w2);
+ dst += 320;
+ } while (++nb < h2);
+
+ if (!dstPage)
+ addDirtyRect(x2, y2, w2, h2);
+}
+
+void Screen_v2::copyPageMemory(int srcPage, int srcPos, int dstPage, int dstPos, int numBytes) {
+ const uint8 *src = getPagePtr(srcPage) + srcPos;
+ uint8 *dst = getPagePtr(dstPage) + dstPos;
+ memcpy(dst, src, numBytes);
+}
+
+void Screen_v2::copyRegionEx(int srcPage, int srcW, int srcH, int dstPage, int dstX, int dstY, int dstW, int dstH, const ScreenDim *dim, bool flag) {
+ int x0 = dim->sx << 3;
+ int y0 = dim->sy;
+ int w0 = dim->w << 3;
+ int h0 = dim->h;
+
+ int x1 = dstX;
+ int y1 = dstY;
+ int w1 = dstW;
+ int h1 = dstH;
+
+ int x2, y2, w2;
+
+ calcBounds(w0, h0, x1, y1, w1, h1, x2, y2, w2);
+
+ const uint8 *src = getPagePtr(srcPage) + (320 * srcH) + srcW;
+ uint8 *dst = getPagePtr(dstPage) + 320 * (y0 + y1);
+
+ for (int y = 0; y < h1; y++) {
+ const uint8 *s = src + x2;
+ uint8 *d = dst + x0 + x1;
+
+ if (flag)
+ d += (h1 >> 1);
+
+ for (int x = 0; x < w1; x++) {
+ if (*s)
+ *d = *s;
+ s++;
+ d++;
+ }
+ dst += 320;
+ src += 320;
+ }
+}
+
+bool Screen_v2::calcBounds(int w0, int h0, int &x1, int &y1, int &w1, int &h1, int &x2, int &y2, int &w2) {
+ x2 = 0;
+ y2 = 0;
+ w2 = w1;
+
+ int t = x1 + w1;
+ if (t < 1) {
+ w1 = h1 = -1;
+ } else {
+ if (t <= x1) {
+ x2 = w1 - t;
+ w1 = t;
+ x1 = 0;
+ }
+ t = w0 - x1;
+ if (t < 1) {
+ w1 = h1 = -1;
+ } else {
+ if (t <= w1) {
+ w1 = t;
+ }
+ w2 -= w1;
+ t = h1 + y1;
+ if (t < 1) {
+ w1 = h1 = -1;
+ } else {
+ if (t <= y1) {
+ y2 = h1 - t;
+ h1 = t;
+ y1 = 0;
+ }
+ t = h0 - y1;
+ if (t < 1) {
+ w1 = h1 = -1;
+ } else {
+ if (t <= h1) {
+ h1 = t;
+ }
+ }
+ }
+ }
+ }
+
+ return w1 != -1;
+}
+
+void Screen_v2::checkedPageUpdate(int srcPage, int dstPage) {
+ const uint32 *src = (const uint32 *)getPagePtr(srcPage);
+ uint32 *dst = (uint32 *)getPagePtr(dstPage);
+ uint32 *page0 = (uint32 *)getPagePtr(0);
+
+ bool updated = false;
+
+ for (int y = 0; y < 200; ++y) {
+ for (int x = 0; x < 80; ++x, ++src, ++dst, ++page0) {
+ if (*src != *dst) {
+ updated = true;
+ *dst = *page0 = *src;
+ }
+ }
+ }
+
+ if (updated)
+ addDirtyRect(0, 0, 320, 200);
+}
+
+} // End of namespace Kyra
diff --git a/engines/kyra/graphics/screen_v2.h b/engines/kyra/graphics/screen_v2.h
new file mode 100644
index 0000000000..9c8aa12563
--- /dev/null
+++ b/engines/kyra/graphics/screen_v2.h
@@ -0,0 +1,81 @@
+/* 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 2
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef KYRA_SCREEN_V2_H
+#define KYRA_SCREEN_V2_H
+
+#include "kyra/graphics/screen.h"
+#include "kyra/engine/kyra_v2.h"
+
+namespace Kyra {
+
+class Screen_v2 : public Screen {
+public:
+ Screen_v2(KyraEngine_v1 *vm, OSystem *system, const ScreenDim *dimTable, const int dimTableSize);
+ ~Screen_v2();
+
+ // screen page handling
+ void checkedPageUpdate(int srcPage, int dstPage);
+
+ // palette handling
+ uint8 *generateOverlay(const Palette &pal, uint8 *buffer, int color, uint weight, int maxColor = -1);
+ void applyOverlay(int x, int y, int w, int h, int pageNum, const uint8 *overlay);
+ int findLeastDifferentColor(const uint8 *paletteEntry, const Palette &pal, uint8 firstColor, uint16 numColors, bool skipSpecialColors = false);
+
+ virtual void getFadeParams(const Palette &pal, int delay, int &delayInc, int &diff);
+
+ bool timedPaletteFadeStep(uint8 *pal1, uint8 *pal2, uint32 elapsedTime, uint32 totalTime);
+
+ // shape handling
+ uint8 *getPtrToShape(uint8 *shpFile, int shape);
+ const uint8 *getPtrToShape(const uint8 *shpFile, int shape);
+
+ int getShapeScaledWidth(const uint8 *shpFile, int scale);
+ int getShapeScaledHeight(const uint8 *shpFile, int scale);
+
+ uint16 getShapeSize(const uint8 *shp);
+
+ uint8 *makeShapeCopy(const uint8 *src, int index);
+
+ // rect handling
+ int getRectSize(int w, int h);
+ bool calcBounds(int w0, int h0, int &x1, int &y1, int &w1, int &h1, int &x2, int &y2, int &w2);
+
+ // text display
+ void setTextColorMap(const uint8 *cmap);
+
+ // layer handling
+ virtual int getLayer(int x, int y);
+
+ // special WSA handling
+ void wsaFrameAnimationStep(int x1, int y1, int x2, int y2, int w1, int h1, int w2, int h2, int srcPage, int dstPage, int dim);
+
+ // used in non-interactive HoF/LoL demos
+ void copyPageMemory(int srcPage, int srcPos, int dstPage, int dstPos, int numBytes);
+ void copyRegionEx(int srcPage, int srcW, int srcH, int dstPage, int dstX,int dstY, int dstW, int dstH, const ScreenDim *d, bool flag = false);
+protected:
+ uint8 *_wsaFrameAnimBuffer;
+};
+
+} // End of namespace Kyra
+
+#endif
diff --git a/engines/kyra/graphics/vqa.cpp b/engines/kyra/graphics/vqa.cpp
new file mode 100644
index 0000000000..5a4e250b42
--- /dev/null
+++ b/engines/kyra/graphics/vqa.cpp
@@ -0,0 +1,667 @@
+/* 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 2
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+// Player for Kyrandia 3 VQA movies, based on the information found at
+// http://multimedia.cx/VQA_INFO.TXT
+//
+// The benchl.vqa movie (or whatever it is) is not supported. It does not have
+// a FINF chunk.
+//
+// The jung2.vqa movie does work, but only thanks to a grotesque hack.
+
+#include "kyra/kyra_v1.h"
+#include "kyra/graphics/vqa.h"
+#include "kyra/graphics/screen.h"
+
+#include "audio/audiostream.h"
+#include "audio/decoders/raw.h"
+
+#include "common/system.h"
+#include "common/events.h"
+
+#include "graphics/palette.h"
+#include "graphics/surface.h"
+
+namespace Kyra {
+
+static uint32 readTag(Common::SeekableReadStream *stream) {
+ // Some tags have to be on an even offset, so they are padded with a
+ // zero byte. Skip that.
+
+ uint32 tag = stream->readUint32BE();
+
+ if (stream->eos())
+ return 0;
+
+ if (!(tag & 0xFF000000)) {
+ tag = (tag << 8) | stream->readByte();
+ }
+
+ return tag;
+}
+
+VQADecoder::VQADecoder() {
+ memset(&_header, 0, sizeof(_header));
+}
+
+VQADecoder::~VQADecoder() {
+ close();
+ delete[] _frameInfo;
+}
+
+bool VQADecoder::loadStream(Common::SeekableReadStream *stream) {
+ close();
+ _fileStream = stream;
+
+ if (_fileStream->readUint32BE() != MKTAG('F','O','R','M')) {
+ warning("VQADecoder::loadStream(): Cannot find `FORM' tag");
+ return false;
+ }
+
+ // Ignore the size of the FORM chunk. We're only interested in its
+ // children.
+ _fileStream->readUint32BE();
+
+ if (_fileStream->readUint32BE() != MKTAG('W','V','Q','A')) {
+ warning("VQADecoder::loadStream(): Cannot find `WVQA' tag");
+ return false;
+ }
+
+ // We want to find both a VQHD chunk containing the header, and a FINF
+ // chunk containing the frame offsets.
+
+ bool foundVQHD = false;
+ bool foundFINF = false;
+
+ VQAAudioTrack *audioTrack = NULL;
+
+ // The information we need is stored in two chunks: VQHD and FINF. We
+ // need both of them before we can begin decoding the movie.
+
+ while (!foundVQHD || !foundFINF) {
+ uint32 tag = readTag(stream);
+ uint32 size = _fileStream->readUint32BE();
+
+ switch (tag) {
+ case MKTAG('V','Q','H','D'):
+ handleVQHD(_fileStream);
+ if (_header.flags & 1) {
+ audioTrack = new VQAAudioTrack(&_header, getSoundType());
+ addTrack(audioTrack);
+ }
+ foundVQHD = true;
+ break;
+ case MKTAG('F','I','N','F'):
+ if (!foundVQHD) {
+ warning("VQADecoder::loadStream(): Found `FINF' before `VQHD'");
+ return false;
+ }
+ if (size != 4 * getFrameCount()) {
+ warning("VQADecoder::loadStream(): Expected size %d for `FINF' chunk, but got %u", 4 * getFrameCount(), size);
+ return false;
+ }
+ handleFINF(_fileStream);
+ foundFINF = true;
+ break;
+ default:
+ warning("VQADecoder::loadStream(): Unknown tag `%s'", tag2str(tag));
+ _fileStream->seek(size, SEEK_CUR);
+ break;
+ }
+ }
+
+ return true;
+}
+
+void VQADecoder::handleVQHD(Common::SeekableReadStream *stream) {
+ _header.version = stream->readUint16LE();
+ _header.flags = stream->readUint16LE();
+ _header.numFrames = stream->readUint16LE();
+ _header.width = stream->readUint16LE();
+ _header.height = stream->readUint16LE();
+ _header.blockW = stream->readByte();
+ _header.blockH = stream->readByte();
+ _header.frameRate = stream->readByte();
+ _header.cbParts = stream->readByte();
+ _header.colors = stream->readUint16LE();
+ _header.maxBlocks = stream->readUint16LE();
+ _header.unk1 = stream->readUint32LE();
+ _header.unk2 = stream->readUint16LE();
+ _header.freq = stream->readUint16LE();
+ _header.channels = stream->readByte();
+ _header.bits = stream->readByte();
+ _header.unk3 = stream->readUint32LE();
+ _header.unk4 = stream->readUint16LE();
+ _header.maxCBFZSize = stream->readUint32LE();
+ _header.unk5 = stream->readUint32LE();
+
+ _frameInfo = new uint32[_header.numFrames + 1];
+
+ VQAVideoTrack *videoTrack = new VQAVideoTrack(&_header);
+ addTrack(videoTrack);
+
+ // Kyrandia 3 uses version 1 VQA files, and is the only known game to
+ // do so. This version of the format has some implicit default values.
+
+ if (_header.version == 1) {
+ if (_header.freq == 0)
+ _header.freq = 22050;
+ if (_header.channels == 0)
+ _header.channels = 1;
+ if (_header.bits == 0)
+ _header.bits = 8;
+ }
+
+ if (_header.flags & 1) {
+ // Kyrandia 3 uses 8-bit sound, and so far testing indicates
+ // that it's all mono.
+ //
+ // This is good, because it means we won't have to worry about
+ // the confusing parts of the VQA spec, where 8- and 16-bit
+ // data have different signedness and stereo sample layout
+ // varies between different games.
+
+ assert(_header.bits == 8);
+ assert(_header.channels == 1);
+ }
+}
+
+void VQADecoder::handleFINF(Common::SeekableReadStream *stream) {
+ for (int i = 0; i < _header.numFrames; i++) {
+ _frameInfo[i] = 2 * stream->readUint32LE();
+ }
+
+ // HACK: This flag is set in jung2.vqa, and its purpose - if it has
+ // one - is currently unknown. It can't be a general purpose flag,
+ // because in large movies the frame offset can be large enough to
+ // set this flag, though of course never for the first frame.
+ //
+ // At least in my copy of Kyrandia 3, _frameInfo[0] is 0x81000098, and
+ // the desired index is 0x4716. So the value should be 0x80004716, but
+ // I don't want to hard-code it. Instead, scan the file for the offset
+ // to the first VQFR chunk.
+
+ if (_frameInfo[0] & 0x01000000) {
+ uint32 oldPos = stream->pos();
+
+ while (1) {
+ uint32 scanTag = readTag(stream);
+ uint32 scanSize = stream->readUint32BE();
+
+ if (stream->eos())
+ break;
+
+ if (scanTag == MKTAG('V','Q','F','R')) {
+ _frameInfo[0] = (stream->pos() - 8) | 0x80000000;
+ break;
+ }
+
+ stream->seek(scanSize, SEEK_CUR);
+ }
+
+ stream->seek(oldPos);
+ }
+
+ _frameInfo[_header.numFrames] = 0x7FFFFFFF;
+}
+
+void VQADecoder::readNextPacket() {
+ VQAVideoTrack *videoTrack = (VQAVideoTrack *)getTrack(0);
+ VQAAudioTrack *audioTrack = (VQAAudioTrack *)getTrack(1);
+
+ assert(videoTrack);
+
+ int curFrame = videoTrack->getCurFrame();
+
+ // Stop if reading the tag is enough to put us ahead of the next frame
+ int32 end = (_frameInfo[curFrame + 1] & 0x7FFFFFFF) - 7;
+
+ // At this point, we probably only need to adjust for the offset in the
+ // stream to be even. But we may as well do this to really make sure
+ // we have the correct offset.
+ if (curFrame >= 0) {
+ _fileStream->seek(_frameInfo[curFrame] & 0x7FFFFFFF);
+ if (_frameInfo[curFrame] & 0x80000000) {
+ videoTrack->setHasDirtyPalette();
+ }
+ }
+
+ while (!_fileStream->eos() && _fileStream->pos() < end) {
+ uint32 tag = readTag(_fileStream);
+ uint32 size;
+
+ switch (tag) {
+ case MKTAG('S','N','D','0'): // Uncompressed sound
+ assert(audioTrack);
+ audioTrack->handleSND0(_fileStream);
+ break;
+ case MKTAG('S','N','D','1'): // Compressed sound, almost like AUD
+ assert(audioTrack);
+ audioTrack->handleSND1(_fileStream);
+ break;
+ case MKTAG('S','N','D','2'): // Compressed sound
+ assert(audioTrack);
+ audioTrack->handleSND2(_fileStream);
+ break;
+ case MKTAG('V','Q','F','R'):
+ videoTrack->handleVQFR(_fileStream);
+ break;
+ case MKTAG('C','M','D','S'):
+ // The purpose of this is unknown, but it's known to
+ // exist so don't warn about it.
+ size = _fileStream->readUint32BE();
+ _fileStream->seek(size, SEEK_CUR);
+ break;
+ default:
+ warning("VQADecoder::readNextPacket(): Unknown tag `%s'", tag2str(tag));
+ size = _fileStream->readUint32BE();
+ _fileStream->seek(size, SEEK_CUR);
+ break;
+ }
+ }
+}
+
+// -----------------------------------------------------------------------
+
+VQADecoder::VQAAudioTrack::VQAAudioTrack(const VQAHeader *header, Audio::Mixer::SoundType soundType) :
+ AudioTrack(soundType) {
+ _audioStream = Audio::makeQueuingAudioStream(header->freq, false);
+}
+
+VQADecoder::VQAAudioTrack::~VQAAudioTrack() {
+ delete _audioStream;
+}
+
+Audio::AudioStream *VQADecoder::VQAAudioTrack::getAudioStream() const {
+ return _audioStream;
+}
+
+void VQADecoder::VQAAudioTrack::handleSND0(Common::SeekableReadStream *stream) {
+ uint32 size = stream->readUint32BE();
+ byte *buf = (byte *)malloc(size);
+ stream->read(buf, size);
+ _audioStream->queueBuffer(buf, size, DisposeAfterUse::YES, Audio::FLAG_UNSIGNED);
+}
+
+void VQADecoder::VQAAudioTrack::handleSND1(Common::SeekableReadStream *stream) {
+ stream->readUint32BE();
+ uint16 outsize = stream->readUint16LE();
+ uint16 insize = stream->readUint16LE();
+ byte *inbuf = (byte *)malloc(insize);
+
+ stream->read(inbuf, insize);
+
+ if (insize == outsize) {
+ _audioStream->queueBuffer(inbuf, insize, DisposeAfterUse::YES, Audio::FLAG_UNSIGNED);
+ } else {
+ const int8 WSTable2Bit[] = { -2, -1, 0, 1 };
+ const int8 WSTable4Bit[] = {
+ -9, -8, -6, -5, -4, -3, -2, -1,
+ 0, 1, 2, 3, 4, 5, 6, 8
+ };
+
+ byte *outbuf = (byte *)malloc(outsize);
+ byte *in = inbuf;
+ byte *out = outbuf;
+ int16 curSample = 0x80;
+ uint16 bytesLeft = outsize;
+
+ while (bytesLeft > 0) {
+ uint16 input = *in++ << 2;
+ byte code = (input >> 8) & 0xFF;
+ int8 count = (input & 0xFF) >> 2;
+ int i;
+
+ switch (code) {
+ case 2:
+ if (count & 0x20) {
+ /* NOTE: count is signed! */
+ count <<= 3;
+ curSample += (count >> 3);
+ *out++ = curSample;
+ bytesLeft--;
+ } else {
+ for (; count >= 0; count--) {
+ *out++ = *in++;
+ bytesLeft--;
+ }
+ curSample = *(out - 1);
+ }
+ break;
+ case 1:
+ for (; count >= 0; count--) {
+ code = *in++;
+
+ for (i = 0; i < 2; i++) {
+ curSample += WSTable4Bit[code & 0x0F];
+ curSample = CLIP<int16>(curSample, 0, 255);
+ code >>= 4;
+ *out++ = curSample;
+ }
+
+ bytesLeft -= 2;
+ }
+ break;
+ case 0:
+ for (; count >= 0; count--) {
+ code = *in++;
+
+ for (i = 0; i < 4; i++) {
+ curSample += WSTable2Bit[code & 0x03];
+ curSample = CLIP<int16>(curSample, 0, 255);
+ code >>= 2;
+ *out++ = curSample;
+ }
+
+ bytesLeft -= 4;
+ }
+ break;
+ default:
+ for (; count >= 0; count--) {
+ *out++ = curSample;
+ bytesLeft--;
+ }
+ break;
+ }
+ }
+ _audioStream->queueBuffer(outbuf, outsize, DisposeAfterUse::YES, Audio::FLAG_UNSIGNED);
+ free(inbuf);
+ }
+}
+
+void VQADecoder::VQAAudioTrack::handleSND2(Common::SeekableReadStream *stream) {
+ uint32 size = stream->readUint32BE();
+ warning("VQADecoder::VQAAudioTrack::handleSND2(): `SND2' is not implemented");
+ stream->seek(size, SEEK_CUR);
+}
+
+// -----------------------------------------------------------------------
+
+VQADecoder::VQAVideoTrack::VQAVideoTrack(const VQAHeader *header) {
+ memset(_palette, 0, sizeof(_palette));
+ _dirtyPalette = false;
+
+ _width = header->width;
+ _height = header->height;
+ _blockW = header->blockW;
+ _blockH = header->blockH;
+ _cbParts = header->cbParts;
+
+ _newFrame = false;
+
+ _curFrame = -1;
+ _frameCount = header->numFrames;
+ _frameRate = header->frameRate;
+
+ _codeBookSize = 0xF00 * header->blockW * header->blockH;
+ _compressedCodeBook = false;
+ _codeBook = new byte[_codeBookSize];
+ _partialCodeBookSize = 0;
+ _numPartialCodeBooks = 0;
+ _partialCodeBook = new byte[_codeBookSize];
+ _numVectorPointers = (header->width / header->blockW) * (header->height * header->blockH);
+ _vectorPointers = new uint16[_numVectorPointers];
+
+ memset(_codeBook, 0, _codeBookSize);
+ memset(_partialCodeBook, 0, _codeBookSize);
+ memset(_vectorPointers, 0, _numVectorPointers);
+
+ _surface = new Graphics::Surface();
+ _surface->create(header->width, header->height, Graphics::PixelFormat::createFormatCLUT8());
+}
+
+VQADecoder::VQAVideoTrack::~VQAVideoTrack() {
+ _surface->free();
+ delete _surface;
+ delete[] _codeBook;
+ delete[] _partialCodeBook;
+ delete[] _vectorPointers;
+}
+
+uint16 VQADecoder::VQAVideoTrack::getWidth() const {
+ return _width;
+}
+
+uint16 VQADecoder::VQAVideoTrack::getHeight() const {
+ return _height;
+}
+
+Graphics::PixelFormat VQADecoder::VQAVideoTrack::getPixelFormat() const {
+ return _surface->format;
+}
+
+int VQADecoder::VQAVideoTrack::getCurFrame() const {
+ return _curFrame;
+}
+
+int VQADecoder::VQAVideoTrack::getFrameCount() const {
+ return _frameCount;
+}
+
+Common::Rational VQADecoder::VQAVideoTrack::getFrameRate() const {
+ return _frameRate;
+}
+
+void VQADecoder::VQAVideoTrack::setHasDirtyPalette() {
+ _dirtyPalette = true;
+}
+
+bool VQADecoder::VQAVideoTrack::hasDirtyPalette() const {
+ return _dirtyPalette;
+}
+
+const byte *VQADecoder::VQAVideoTrack::getPalette() const {
+ _dirtyPalette = false;
+ return _palette;
+}
+
+const Graphics::Surface *VQADecoder::VQAVideoTrack::decodeNextFrame() {
+ if (_newFrame) {
+ _newFrame = false;
+
+ int blockPitch = _width / _blockW;
+
+ for (int by = 0; by < _height / _blockH; by++) {
+ for (int bx = 0; bx < blockPitch; bx++) {
+ byte *dst = (byte *)_surface->getBasePtr(bx * _blockW, by * _blockH);
+ int val = _vectorPointers[by * blockPitch + bx];
+ int i;
+
+ if ((val & 0xFF00) == 0xFF00) {
+ // Solid color
+ for (i = 0; i < _blockH; i++) {
+ memset(dst, 255 - (val & 0xFF), _blockW);
+ dst += _width;
+ }
+ } else {
+ // Copy data from _vectorPointers. I'm not sure
+ // why we don't use the three least significant
+ // bits of 'val'.
+ byte *src = &_codeBook[(val >> 3) * _blockW * _blockH];
+
+ for (i = 0; i < _blockH; i++) {
+ memcpy(dst, src, _blockW);
+ src += _blockW;
+ dst += _width;
+ }
+ }
+ }
+ }
+
+ if (_numPartialCodeBooks == _cbParts) {
+ if (_compressedCodeBook) {
+ Screen::decodeFrame4(_partialCodeBook, _codeBook, _codeBookSize);
+ } else {
+ memcpy(_codeBook, _partialCodeBook, _partialCodeBookSize);
+ }
+ _numPartialCodeBooks = 0;
+ _partialCodeBookSize = 0;
+ }
+ }
+
+ _curFrame++;
+ return _surface;
+}
+
+void VQADecoder::VQAVideoTrack::handleVQFR(Common::SeekableReadStream *stream) {
+ uint32 size = stream->readUint32BE();
+ int32 end = stream->pos() + size - 8;
+ byte *inbuf;
+
+ _newFrame = true;
+
+ while (stream->pos() < end) {
+ uint32 tag = readTag(stream);
+ uint32 i;
+ size = stream->readUint32BE();
+
+ switch (tag) {
+ case MKTAG('C','B','F','0'): // Full codebook
+ stream->read(_codeBook, size);
+ break;
+ case MKTAG('C','B','F','Z'): // Full codebook
+ inbuf = (byte *)malloc(size);
+ stream->read(inbuf, size);
+ Screen::decodeFrame4(inbuf, _codeBook, _codeBookSize);
+ free(inbuf);
+ break;
+ case MKTAG('C','B','P','0'): // Partial codebook
+ _compressedCodeBook = false;
+ stream->read(_partialCodeBook + _partialCodeBookSize, size);
+ _partialCodeBookSize += size;
+ _numPartialCodeBooks++;
+ break;
+ case MKTAG('C','B','P','Z'): // Partial codebook
+ _compressedCodeBook = true;
+ stream->read(_partialCodeBook + _partialCodeBookSize, size);
+ _partialCodeBookSize += size;
+ _numPartialCodeBooks++;
+ break;
+ case MKTAG('C','P','L','0'): // Palette
+ assert(size <= 3 * 256);
+ stream->read(_palette, size);
+ break;
+ case MKTAG('C','P','L','Z'): // Palette
+ inbuf = (byte *)malloc(size);
+ stream->read(inbuf, size);
+ Screen::decodeFrame4(inbuf, _palette, 3 * 256);
+ free(inbuf);
+ break;
+ case MKTAG('V','P','T','0'): // Frame data
+ assert(size / 2 <= _numVectorPointers);
+ for (i = 0; i < size / 2; i++)
+ _vectorPointers[i] = stream->readUint16LE();
+ break;
+ case MKTAG('V','P','T','Z'): // Frame data
+ inbuf = (byte *)malloc(size);
+ stream->read(inbuf, size);
+ size = Screen::decodeFrame4(inbuf, (uint8 *)_vectorPointers, 2 * _numVectorPointers);
+ for (i = 0; i < size / 2; i++)
+ _vectorPointers[i] = TO_LE_16(_vectorPointers[i]);
+ free(inbuf);
+ break;
+ default:
+ warning("VQADecoder::VQAVideoTrack::handleVQFR(): Unknown `VQFR' sub-tag `%s'", tag2str(tag));
+ stream->seek(size, SEEK_CUR);
+ break;
+ }
+ }
+}
+
+// -----------------------------------------------------------------------
+
+VQAMovie::VQAMovie(KyraEngine_v1 *vm, OSystem *system) {
+ _system = system;
+ _vm = vm;
+ _screen = _vm->screen();
+ _decoder = new VQADecoder();
+}
+
+VQAMovie::~VQAMovie() {
+ close();
+ delete _decoder;
+}
+
+bool VQAMovie::open(const char *filename) {
+ if (_file.open(filename)) {
+ return true;
+ }
+ return false;
+}
+
+void VQAMovie::close() {
+ if (_file.isOpen()) {
+ _file.close();
+ }
+}
+
+void VQAMovie::play() {
+ if (_decoder->loadStream(&_file)) {
+ Common::EventManager *eventMan = _vm->getEventManager();
+ int width = _decoder->getWidth();
+ int height = _decoder->getHeight();
+ int x = (Screen::SCREEN_W - width) / 2;
+ int y = (Screen::SCREEN_H - height) / 2;
+
+ _decoder->start();
+
+ // Note that decoding starts at frame -1. That's because there
+ // is usually sound data before the first frame, probably to
+ // avoid sound underflow.
+
+ while (!_decoder->endOfVideo()) {
+ Common::Event event;
+ while (eventMan->pollEvent(event)) {
+ switch (event.type) {
+ case Common::EVENT_KEYDOWN:
+ if (event.kbd.keycode == Common::KEYCODE_ESCAPE)
+ return;
+ break;
+ case Common::EVENT_RTL:
+ case Common::EVENT_QUIT:
+ return;
+ default:
+ break;
+ }
+ }
+
+ if (_decoder->needsUpdate()) {
+ const Graphics::Surface *surface = _decoder->decodeNextFrame();
+ if (_decoder->hasDirtyPalette()) {
+ const byte *decoderPalette = _decoder->getPalette();
+ byte systemPalette[256 * 3];
+ for (int i = 0; i < ARRAYSIZE(systemPalette); i++) {
+ systemPalette[i] = (decoderPalette[i] * 0xFF) / 0x3F;
+ }
+ _system->getPaletteManager()->setPalette(systemPalette, 0, 256);
+ }
+
+ _system->copyRectToScreen((const byte *)surface->getBasePtr(0, 0), surface->pitch, x, y, width, height);
+ }
+
+ _system->updateScreen();
+ _system->delayMillis(10);
+ }
+ }
+}
+
+} // End of namespace Kyra
diff --git a/engines/kyra/graphics/vqa.h b/engines/kyra/graphics/vqa.h
new file mode 100644
index 0000000000..df51a81988
--- /dev/null
+++ b/engines/kyra/graphics/vqa.h
@@ -0,0 +1,161 @@
+/* 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 2
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef KYRA_VQA_H
+#define KYRA_VQA_H
+
+#include "video/video_decoder.h"
+#include "common/file.h"
+#include "common/rational.h"
+
+class OSystem;
+
+namespace Audio {
+class QueuingAudioStream;
+} // End of namespace Audio
+
+namespace Kyra {
+
+class KyraEngine_v1;
+class Screen;
+
+class VQADecoder : public Video::VideoDecoder {
+public:
+ VQADecoder();
+ virtual ~VQADecoder();
+
+ bool loadStream(Common::SeekableReadStream *stream);
+ void readNextPacket();
+
+private:
+ Common::SeekableReadStream *_fileStream;
+
+ void handleVQHD(Common::SeekableReadStream *stream);
+ void handleFINF(Common::SeekableReadStream *stream);
+
+ struct VQAHeader {
+ uint16 version;
+ uint16 flags;
+ uint16 numFrames;
+ uint16 width;
+ uint16 height;
+ uint8 blockW;
+ uint8 blockH;
+ uint8 frameRate;
+ uint8 cbParts;
+ uint16 colors;
+ uint16 maxBlocks;
+ uint32 unk1;
+ uint16 unk2;
+ uint16 freq;
+ uint8 channels;
+ uint8 bits;
+ uint32 unk3;
+ uint16 unk4;
+ uint32 maxCBFZSize;
+ uint32 unk5;
+ };
+
+ VQAHeader _header;
+ uint32 *_frameInfo;
+
+ class VQAAudioTrack : public AudioTrack {
+ public:
+ VQAAudioTrack(const VQAHeader *header, Audio::Mixer::SoundType soundType);
+ ~VQAAudioTrack();
+
+ void handleSND0(Common::SeekableReadStream *stream);
+ void handleSND1(Common::SeekableReadStream *stream);
+ void handleSND2(Common::SeekableReadStream *stream);
+
+ protected:
+ Audio::AudioStream *getAudioStream() const;
+
+ private:
+ Audio::QueuingAudioStream *_audioStream;
+ };
+
+ class VQAVideoTrack : public FixedRateVideoTrack {
+ public:
+ VQAVideoTrack(const VQAHeader *header);
+ ~VQAVideoTrack();
+
+ uint16 getWidth() const;
+ uint16 getHeight() const;
+ Graphics::PixelFormat getPixelFormat() const;
+ int getCurFrame() const;
+ int getFrameCount() const;
+ const Graphics::Surface *decodeNextFrame();
+
+ void setHasDirtyPalette();
+ bool hasDirtyPalette() const;
+ const byte *getPalette() const;
+
+ void handleVQFR(Common::SeekableReadStream *stream);
+
+ protected:
+ Common::Rational getFrameRate() const;
+
+ private:
+ Graphics::Surface *_surface;
+ byte _palette[3 * 256];
+ mutable bool _dirtyPalette;
+
+ bool _newFrame;
+
+ uint16 _width, _height;
+ uint8 _blockW, _blockH;
+ uint8 _cbParts;
+ int _frameCount;
+ int _curFrame;
+ byte _frameRate;
+
+ uint32 _codeBookSize;
+ bool _compressedCodeBook;
+ byte *_codeBook;
+ int _partialCodeBookSize;
+ int _numPartialCodeBooks;
+ byte *_partialCodeBook;
+ uint32 _numVectorPointers;
+ uint16 *_vectorPointers;
+ };
+};
+
+class VQAMovie {
+public:
+ VQAMovie(KyraEngine_v1 *vm, OSystem *system);
+ ~VQAMovie();
+
+ bool open(const char *filename);
+ void close();
+ void play();
+private:
+ OSystem *_system;
+ KyraEngine_v1 *_vm;
+ Screen *_screen;
+ VQADecoder *_decoder;
+ Common::File _file;
+};
+
+} // End of namespace Kyra
+
+#endif
diff --git a/engines/kyra/graphics/wsamovie.cpp b/engines/kyra/graphics/wsamovie.cpp
new file mode 100644
index 0000000000..6cbda7b6c9
--- /dev/null
+++ b/engines/kyra/graphics/wsamovie.cpp
@@ -0,0 +1,463 @@
+/* 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 2
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "kyra/graphics/wsamovie.h"
+#include "kyra/resource/resource.h"
+
+#include "common/endian.h"
+
+namespace Kyra {
+
+WSAMovie_v1::WSAMovie_v1(KyraEngine_v1 *vm)
+ : Movie(vm), _frameData(0), _frameOffsTable(0), _offscreenBuffer(0), _deltaBuffer(0) {
+}
+
+WSAMovie_v1::~WSAMovie_v1() {
+ close();
+}
+
+int WSAMovie_v1::open(const char *filename, int offscreenDecode, Palette *palBuf) {
+ close();
+
+ uint32 flags = 0;
+ uint32 fileSize;
+ uint8 *p = _vm->resource()->fileData(filename, &fileSize);
+ if (!p)
+ return 0;
+
+ const uint8 *wsaData = p;
+ _numFrames = READ_LE_UINT16(wsaData); wsaData += 2;
+ _width = READ_LE_UINT16(wsaData); wsaData += 2;
+ _height = READ_LE_UINT16(wsaData); wsaData += 2;
+ _deltaBufferSize = READ_LE_UINT16(wsaData); wsaData += 2;
+ _offscreenBuffer = NULL;
+ _flags = 0;
+ if (_vm->gameFlags().useAltShapeHeader) {
+ flags = READ_LE_UINT16(wsaData);
+ wsaData += 2;
+ }
+
+ uint32 offsPal = 0;
+ if (flags & 1) {
+ offsPal = 0x300;
+ _flags |= WF_HAS_PALETTE;
+ if (palBuf)
+ _screen->loadPalette(wsaData + 8 + ((_numFrames << 2) & 0xFFFF), *palBuf, 0x300);
+ }
+
+ if (offscreenDecode) {
+ _flags |= WF_OFFSCREEN_DECODE;
+ const int offscreenBufferSize = _width * _height;
+ _offscreenBuffer = new uint8[offscreenBufferSize];
+ memset(_offscreenBuffer, 0, offscreenBufferSize);
+ }
+
+ if (_numFrames & 0x8000) {
+ // This is used in the Amiga version.
+ if (_vm->gameFlags().platform != Common::kPlatformAmiga)
+ warning("Unhandled wsa flags 0x8000");
+ _flags |= WF_FLIPPED;
+ _numFrames &= 0x7FFF;
+ }
+ _currentFrame = _numFrames;
+
+ _deltaBuffer = new uint8[_deltaBufferSize];
+ memset(_deltaBuffer, 0, _deltaBufferSize);
+
+ // read frame offsets
+ _frameOffsTable = new uint32[_numFrames + 2];
+ _frameOffsTable[0] = 0;
+ uint32 frameDataOffs = READ_LE_UINT32(wsaData); wsaData += 4;
+ bool firstFrame = true;
+
+ if (frameDataOffs == 0) {
+ firstFrame = false;
+ frameDataOffs = READ_LE_UINT32(wsaData);
+ _flags |= WF_NO_FIRST_FRAME;
+ }
+
+ for (int i = 1; i < _numFrames + 2; ++i) {
+ _frameOffsTable[i] = READ_LE_UINT32(wsaData);
+ if (_frameOffsTable[i])
+ _frameOffsTable[i] -= frameDataOffs;
+ wsaData += 4;
+ }
+
+ if (!_frameOffsTable[_numFrames + 1])
+ _flags |= WF_NO_LAST_FRAME;
+
+ // skip palette
+ wsaData += offsPal;
+
+ // read frame data
+ const int frameDataSize = p + fileSize - wsaData;
+ _frameData = new uint8[frameDataSize];
+ memcpy(_frameData, wsaData, frameDataSize);
+
+ // decode first frame
+ if (firstFrame)
+ Screen::decodeFrame4(_frameData, _deltaBuffer, _deltaBufferSize);
+
+ delete[] p;
+ _opened = true;
+
+ return _numFrames;
+}
+
+void WSAMovie_v1::close() {
+ if (_opened) {
+ delete[] _deltaBuffer;
+ delete[] _offscreenBuffer;
+ delete[] _frameOffsTable;
+ delete[] _frameData;
+ _opened = false;
+ }
+}
+
+void WSAMovie_v1::displayFrame(int frameNum, int pageNum, int x, int y, uint16 flags, const uint8 *table1, const uint8 *table2) {
+ if (frameNum >= _numFrames || frameNum < 0 || !_opened)
+ return;
+
+ _x = x;
+ _y = y;
+ _drawPage = pageNum;
+
+ uint8 *dst = 0;
+ if (_flags & WF_OFFSCREEN_DECODE)
+ dst = _offscreenBuffer;
+ else
+ dst = _screen->getPageRect(_drawPage, _x, _y, _width, _height);
+
+ if (_currentFrame == _numFrames) {
+ if (!(_flags & WF_NO_FIRST_FRAME)) {
+ if (_flags & WF_OFFSCREEN_DECODE)
+ Screen::decodeFrameDelta(dst, _deltaBuffer);
+ else
+ Screen::decodeFrameDeltaPage(dst, _deltaBuffer, _width, (_flags & WF_XOR) == 0);
+ }
+ _currentFrame = 0;
+ }
+
+ // try to reduce the number of needed frame operations
+ int diffCount = ABS(_currentFrame - frameNum);
+ int frameStep = 1;
+ int frameCount;
+ if (_currentFrame < frameNum) {
+ frameCount = _numFrames - frameNum + _currentFrame;
+ if (diffCount > frameCount && !(_flags & WF_NO_LAST_FRAME))
+ frameStep = -1;
+ else
+ frameCount = diffCount;
+ } else {
+ frameCount = _numFrames - _currentFrame + frameNum;
+ if (frameCount >= diffCount || (_flags & WF_NO_LAST_FRAME)) {
+ frameStep = -1;
+ frameCount = diffCount;
+ }
+ }
+
+ // process
+ if (frameStep > 0) {
+ uint16 cf = _currentFrame;
+ while (frameCount--) {
+ cf += frameStep;
+ processFrame(cf, dst);
+ if (cf == _numFrames)
+ cf = 0;
+ }
+ } else {
+ uint16 cf = _currentFrame;
+ while (frameCount--) {
+ if (cf == 0)
+ cf = _numFrames;
+ processFrame(cf, dst);
+ cf += frameStep;
+ }
+ }
+
+ // display
+ _currentFrame = frameNum;
+ if (_flags & WF_OFFSCREEN_DECODE) {
+ int pageBackUp = _screen->setCurPage(_drawPage);
+
+ int plotFunc = (flags & 0xFF00) >> 12;
+ int unk1 = flags & 0xFF;
+
+ _screen->copyWsaRect(_x, _y, _width, _height, 0, plotFunc, _offscreenBuffer, unk1, table1, table2);
+
+ _screen->_curPage = pageBackUp;
+ }
+}
+
+void WSAMovie_v1::processFrame(int frameNum, uint8 *dst) {
+ if (!_opened)
+ return;
+ assert(frameNum <= _numFrames);
+ const uint8 *src = _frameData + _frameOffsTable[frameNum];
+ Screen::decodeFrame4(src, _deltaBuffer, _deltaBufferSize);
+ if (_flags & WF_OFFSCREEN_DECODE)
+ Screen::decodeFrameDelta(dst, _deltaBuffer);
+ else
+ Screen::decodeFrameDeltaPage(dst, _deltaBuffer, _width, false);
+}
+
+#pragma mark -
+
+WSAMovieAmiga::WSAMovieAmiga(KyraEngine_v1 *vm) : WSAMovie_v1(vm), _buffer(0) {}
+
+int WSAMovieAmiga::open(const char *filename, int offscreenDecode, Palette *palBuf) {
+ int res = WSAMovie_v1::open(filename, offscreenDecode, palBuf);
+
+ if (!res)
+ return 0;
+
+ _buffer = new uint8[_width * _height];
+ assert(_buffer);
+ return res;
+}
+
+void WSAMovieAmiga::close() {
+ if (_opened) {
+ delete[] _buffer;
+ _buffer = 0;
+ }
+ WSAMovie_v1::close();
+}
+
+void WSAMovieAmiga::displayFrame(int frameNum, int pageNum, int x, int y, uint16 flags, const uint8 *table1, const uint8 *table2) {
+ if (frameNum >= _numFrames || frameNum < 0 || !_opened)
+ return;
+
+ _x = x;
+ _y = y;
+ _drawPage = pageNum;
+
+ uint8 *dst;
+ dst = _buffer;
+ memset(_buffer, 0, _width * _height);
+
+ if (_currentFrame == _numFrames) {
+ if (!(_flags & WF_NO_FIRST_FRAME)) {
+ Screen::decodeFrameDelta(dst, _deltaBuffer, true);
+ Screen::convertAmigaGfx(dst, _width, _height, 5, (_flags & WF_FLIPPED) != 0);
+
+ if (_flags & WF_OFFSCREEN_DECODE) {
+ dst = _offscreenBuffer;
+ const uint8 *src = _buffer;
+ int size = _width * _height;
+
+ for (int i = 0; i < size; ++i)
+ *dst++ ^= *src++;
+
+ dst = _buffer;
+ } else {
+ _screen->copyBlockToPage(_drawPage, _x, _y, _width, _height, _buffer);
+ }
+ }
+ _currentFrame = 0;
+ }
+
+ // try to reduce the number of needed frame operations
+ int diffCount = ABS(_currentFrame - frameNum);
+ int frameStep = 1;
+ int frameCount;
+ if (_currentFrame < frameNum) {
+ frameCount = _numFrames - frameNum + _currentFrame;
+ if (diffCount > frameCount && !(_flags & WF_NO_LAST_FRAME))
+ frameStep = -1;
+ else
+ frameCount = diffCount;
+ } else {
+ frameCount = _numFrames - _currentFrame + frameNum;
+ if (frameCount >= diffCount || (_flags & WF_NO_LAST_FRAME)) {
+ frameStep = -1;
+ frameCount = diffCount;
+ }
+ }
+
+ // process
+ if (frameStep > 0) {
+ uint16 cf = _currentFrame;
+ while (frameCount--) {
+ cf += frameStep;
+ processFrame(cf, dst);
+ if (cf == _numFrames)
+ cf = 0;
+ }
+ } else {
+ uint16 cf = _currentFrame;
+ while (frameCount--) {
+ if (cf == 0)
+ cf = _numFrames;
+ processFrame(cf, dst);
+ cf += frameStep;
+ }
+ }
+
+ // display
+ _currentFrame = frameNum;
+ if (_flags & WF_OFFSCREEN_DECODE) {
+ int pageBackUp = _screen->setCurPage(_drawPage);
+
+ int plotFunc = (flags & 0xFF00) >> 12;
+ int unk1 = flags & 0xFF;
+
+ _screen->copyWsaRect(_x, _y, _width, _height, 0, plotFunc, _offscreenBuffer, unk1, table1, table2);
+
+ _screen->_curPage = pageBackUp;
+ }
+}
+
+void WSAMovieAmiga::processFrame(int frameNum, uint8 *dst) {
+ if (!_opened)
+ return;
+ assert(frameNum <= _numFrames);
+
+ memset(dst, 0, _width * _height);
+
+ const uint8 *src = _frameData + _frameOffsTable[frameNum];
+ Screen::decodeFrame4(src, _deltaBuffer, _deltaBufferSize);
+ Screen::decodeFrameDelta(dst, _deltaBuffer, true);
+ Screen::convertAmigaGfx(dst, _width, _height, 5, (_flags & WF_FLIPPED) != 0);
+
+ src = dst;
+ dst = 0;
+ int dstPitch = 0;
+ if (_flags & WF_OFFSCREEN_DECODE) {
+ dst = _offscreenBuffer;
+ dstPitch = _width;
+ } else {
+ dst = _screen->getPageRect(_drawPage, _x, _y, _width, _height);
+ dstPitch = Screen::SCREEN_W;
+ }
+
+ for (int y = 0; y < _height; ++y) {
+ for (int x = 0; x < _width; ++x)
+ *dst++ ^= *src++;
+ dst += dstPitch - _width;
+ }
+}
+
+#pragma mark -
+
+WSAMovie_v2::WSAMovie_v2(KyraEngine_v1 *vm) : WSAMovie_v1(vm), _xAdd(0), _yAdd(0) {}
+
+int WSAMovie_v2::open(const char *filename, int unk1, Palette *palBuf) {
+ close();
+
+ uint32 flags = 0;
+ uint32 fileSize;
+ uint8 *p = _vm->resource()->fileData(filename, &fileSize);
+ if (!p) {
+ warning("couldn't load wsa file: '%s'", filename);
+ return 0;
+ }
+
+ const uint8 *wsaData = p;
+ _numFrames = READ_LE_UINT16(wsaData); wsaData += 2;
+ _xAdd = (int16)(READ_LE_UINT16(wsaData)); wsaData += 2;
+ _yAdd = (int16)(READ_LE_UINT16(wsaData)); wsaData += 2;
+ _width = READ_LE_UINT16(wsaData); wsaData += 2;
+ _height = READ_LE_UINT16(wsaData); wsaData += 2;
+ _deltaBufferSize = READ_LE_UINT16(wsaData); wsaData += 2;
+ _offscreenBuffer = NULL;
+ _flags = 0;
+ flags = READ_LE_UINT16(wsaData); wsaData += 2;
+
+ uint32 offsPal = 0;
+ if (flags & 1) {
+ offsPal = 0x300;
+ _flags |= WF_HAS_PALETTE;
+ if (palBuf)
+ _screen->loadPalette(wsaData + 8 + ((_numFrames << 2) & 0xFFFF), *palBuf, 0x300);
+ }
+
+ if (flags & 2) {
+ if (_vm->gameFlags().use16ColorMode) {
+ offsPal = 0x30;
+ _flags |= WF_HAS_PALETTE;
+ if (palBuf)
+ _screen->loadPalette(wsaData + 8 + ((_numFrames << 2) & 0xFFFF), *palBuf, 0x30);
+ }
+
+ _flags |= WF_XOR;
+ }
+
+
+ if (!(unk1 & 2)) {
+ _flags |= WF_OFFSCREEN_DECODE;
+ const int offscreenBufferSize = _width * _height;
+ _offscreenBuffer = new uint8[offscreenBufferSize];
+ memset(_offscreenBuffer, 0, offscreenBufferSize);
+ }
+
+ if (_numFrames & 0x8000) {
+ warning("Unhandled wsa flags 0x80");
+ _flags |= 0x80;
+ _numFrames &= 0x7FFF;
+ }
+ _currentFrame = _numFrames;
+
+ _deltaBuffer = new uint8[_deltaBufferSize];
+ memset(_deltaBuffer, 0, _deltaBufferSize);
+
+ // read frame offsets
+ _frameOffsTable = new uint32[_numFrames + 2];
+ _frameOffsTable[0] = 0;
+ uint32 frameDataOffs = READ_LE_UINT32(wsaData); wsaData += 4;
+ bool firstFrame = true;
+ if (frameDataOffs == 0) {
+ firstFrame = false;
+ frameDataOffs = READ_LE_UINT32(wsaData);
+ _flags |= WF_NO_FIRST_FRAME;
+ }
+
+ for (int i = 1; i < _numFrames + 2; ++i) {
+ _frameOffsTable[i] = READ_LE_UINT32(wsaData);
+ if (_frameOffsTable[i])
+ _frameOffsTable[i] -= frameDataOffs;
+ wsaData += 4;
+ }
+
+ if (!_frameOffsTable[_numFrames + 1])
+ _flags |= WF_NO_LAST_FRAME;
+
+ // skip palette
+ wsaData += offsPal;
+
+ // read frame data
+ const int frameDataSize = p + fileSize - wsaData;
+
+ _frameData = new uint8[frameDataSize];
+ memcpy(_frameData, wsaData, frameDataSize);
+
+ // decode first frame
+ if (firstFrame)
+ Screen::decodeFrame4(_frameData, _deltaBuffer, _deltaBufferSize);
+
+ delete[] p;
+ _opened = true;
+
+ return _numFrames;
+}
+
+} // End of namespace Kyra
diff --git a/engines/kyra/graphics/wsamovie.h b/engines/kyra/graphics/wsamovie.h
new file mode 100644
index 0000000000..d00aa89af1
--- /dev/null
+++ b/engines/kyra/graphics/wsamovie.h
@@ -0,0 +1,134 @@
+/* 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 2
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef KYRA_WSAMOVIE_H
+#define KYRA_WSAMOVIE_H
+
+#include "kyra/kyra_v1.h"
+
+namespace Kyra {
+
+class Palette;
+
+class Movie {
+public:
+ Movie(KyraEngine_v1 *vm) : _vm(vm), _screen(vm->screen()), _opened(false), _x(-1), _y(-1), _drawPage(-1) {}
+ virtual ~Movie() {}
+
+ virtual bool opened() { return _opened; }
+
+ virtual int xAdd() const { return 0; }
+ virtual int yAdd() const { return 0; }
+
+ virtual int width() const = 0;
+ virtual int height() const = 0;
+
+ virtual int open(const char *filename, int offscreen, Palette *palette) = 0;
+ virtual void close() = 0;
+
+ virtual int frames() = 0;
+
+ virtual void displayFrame(int frameNum, int pageNum, int x, int y, uint16 flags, const uint8 *table1, const uint8 *table2) = 0;
+
+protected:
+ KyraEngine_v1 *_vm;
+ Screen *_screen;
+ bool _opened;
+
+ int _x, _y;
+ int _drawPage;
+};
+
+class WSAMovie_v1 : public Movie {
+public:
+ WSAMovie_v1(KyraEngine_v1 *vm);
+ virtual ~WSAMovie_v1();
+
+ int width() const { return _width; }
+ int height() const { return _height; }
+
+ virtual int open(const char *filename, int offscreen, Palette *palette);
+ virtual void close();
+
+ virtual int frames() { return _opened ? _numFrames : -1; }
+
+ virtual void displayFrame(int frameNum, int pageNum, int x, int y, uint16 flags, const uint8 *table1, const uint8 *table2);
+
+ enum WSAFlags {
+ WF_OFFSCREEN_DECODE = 0x10,
+ WF_NO_LAST_FRAME = 0x20,
+ WF_NO_FIRST_FRAME = 0x40,
+ WF_FLIPPED = 0x80,
+ WF_HAS_PALETTE = 0x100,
+ WF_XOR = 0x200
+ };
+
+protected:
+ virtual void processFrame(int frameNum, uint8 *dst);
+
+ uint16 _currentFrame;
+ uint16 _numFrames;
+ uint16 _width;
+ uint16 _height;
+ uint16 _flags;
+ uint8 *_deltaBuffer;
+ uint32 _deltaBufferSize;
+ uint8 *_offscreenBuffer;
+ uint32 *_frameOffsTable;
+ uint8 *_frameData;
+};
+
+class WSAMovieAmiga : public WSAMovie_v1 {
+public:
+ WSAMovieAmiga(KyraEngine_v1 *vm);
+ int open(const char *filename, int offscreen, Palette *palette);
+ void close();
+
+ void displayFrame(int frameNum, int pageNum, int x, int y, uint16 flags, const uint8 *table1, const uint8 *table2);
+private:
+ void processFrame(int frameNum, uint8 *dst);
+
+ uint8 *_buffer;
+};
+
+class WSAMovie_v2 : public WSAMovie_v1 {
+public:
+ WSAMovie_v2(KyraEngine_v1 *vm);
+
+ int open(const char *filename, int unk1, Palette *palette);
+ virtual void displayFrame(int frameNum, int pageNum, int x, int y, uint16 flags, const uint8 *table1, const uint8 *table2) {
+ WSAMovie_v1::displayFrame(frameNum, pageNum, x + _xAdd, y + _yAdd, flags, table1, table2);
+ }
+
+ int xAdd() const { return _xAdd; }
+ int yAdd() const { return _yAdd; }
+
+ void setWidth(int w) { _width = w; }
+ void setHeight(int h) { _height = h; }
+protected:
+ int16 _xAdd;
+ int16 _yAdd;
+};
+
+} // End of namespace Kyra
+
+#endif