Initial commit

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

View File

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

View File

@@ -0,0 +1,646 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "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 = nullptr;
_noDrawShapesFlag = 0;
_brandonDrawFrame = 0;
_brandonScaleX = _brandonScaleY = _brandonAnimSeqSizeWidth = _brandonAnimSeqSizeHeight = 0;
_actorBkgBackUp[0] = new uint8[_screen->getRectSize(8, 69)]();
_actorBkgBackUp[1] = new uint8[_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);
_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 = nullptr;
}
}
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 invisibilityDrawFlag = 0, blurDrawFlag = 0, fadeDrawFlag = 0;
if (_noDrawShapesFlag)
return;
if (_vm->_brandonStatusBit & 0x20)
invisibilityDrawFlag = Screen::kDRAWSHP_PREDATOR;
if (_vm->_brandonStatusBit & 0x40)
blurDrawFlag = Screen::kDRAWSHP_MORPH;
while (curObject) {
if (curObject->active) {
int xpos = curObject->x1;
int ypos = curObject->y1;
int drawLayer = 0;
if (!(curObject->flags & Screen::kDRAWSHP_PRIORITY))
drawLayer = 7;
else if (curObject->disable)
drawLayer = 0;
else
drawLayer = _vm->_sprites->getDrawLayer(curObject->drawY);
// Talking head functionallity
// DOS Floppy, Amiga and Mac work differently than DOS Talkie. PC-98 and FM-Towns are similar to DOS Talkie, but also slightly different.
bool fmTownsOrPC98 = (_vm->gameFlags().platform == Common::kPlatformFMTowns || _vm->gameFlags().platform == Common::kPlatformPC98);
if (_vm->_talkingCharNum != -1 && (fmTownsOrPC98 || (_vm->gameFlags().isTalkie ?
(_vm->_currentCharacter->currentAnimFrame != 88 || curObject->index != 0) : (_brandonScaleX == 0x100 || !_vm->_scaleMode)))) {
const int16 baseAnimFrameTable1[] = { 0x11, 0x35, 0x59, 0x00, 0x00 };
const int16 baseAnimFrameTable2[] = { 0x15, 0x39, 0x5D, 0x00, 0x00 };
const int8 xOffsetTable1[] = { 2, 4, 0, 5, 2, };
const int8 xOffsetTable2[] = { 6, 4, 8, 3, 6, };
const int8 yOffsetTable[] = { 0, 8, 1, 1, 0, };
if (curObject->index <= 4) {
int shapesIndex = 0;
if (curObject->index == _vm->_talkHeadAnimCharNum)
shapesIndex = _vm->_currHeadShape + baseAnimFrameTable1[curObject->index];
else if (curObject->index != 2 || _vm->_characterList[2].sceneId == 77 || _vm->_characterList[2].sceneId == 86)
shapesIndex = baseAnimFrameTable2[curObject->index];
else
shapesIndex = -1;
xpos = curObject->x1;
ypos = curObject->y1;
int tempX = 0, tempY = 0;
if (curObject->flags & Screen::kDRAWSHP_XFLIP) {
tempX = xOffsetTable1[curObject->index];
tempY = yOffsetTable[curObject->index];
} else {
tempX = xOffsetTable2[curObject->index];
tempY = yOffsetTable[curObject->index];
}
if (_vm->gameFlags().isTalkie || fmTownsOrPC98) {
tempX = (tempX * _brandonScaleX) >> 8;
tempY = (tempY * _brandonScaleY) >> 8;
if (_vm->_scaleMode && _brandonScaleX != 0x100)
++tempX;
}
xpos += tempX;
ypos += tempY;
if (curObject->index == 0 && shapesIndex != -1) {
if (!(_vm->_brandonStatusBit & 2)) {
fadeDrawFlag = Screen::kDRAWSHP_FADE;
if ((invisibilityDrawFlag & Screen::kDRAWSHP_PREDATOR) || (blurDrawFlag & Screen::kDRAWSHP_MORPH))
fadeDrawFlag = 0;
int tempFlags = 0;
if (fadeDrawFlag & Screen::kDRAWSHP_FADE) {
tempFlags = curObject->flags & Screen::kDRAWSHP_XFLIP;
tempFlags |= (Screen::kDRAWSHP_PRIORITY | invisibilityDrawFlag | Screen::kDRAWSHP_FADE);
}
if (!(fadeDrawFlag & Screen::kDRAWSHP_FADE) && (blurDrawFlag & Screen::kDRAWSHP_MORPH)) {
tempFlags = curObject->flags & Screen::kDRAWSHP_XFLIP;
tempFlags |= (Screen::kDRAWSHP_PRIORITY | Screen::kDRAWSHP_FADE | invisibilityDrawFlag | Screen::kDRAWSHP_MORPH);
_screen->drawShape(drawPage, _vm->_shapes[shapesIndex], xpos, ypos, 2, tempFlags | Screen::kDRAWSHP_SCALE, _vm->_brandonPoisonFlagsGFX, int(1), int(_vm->_brandonInvFlag), drawLayer, _brandonScaleX, _brandonScaleY);
} else {
if (!(blurDrawFlag & Screen::kDRAWSHP_MORPH)) {
tempFlags = curObject->flags & Screen::kDRAWSHP_XFLIP;
tempFlags |= (Screen::kDRAWSHP_PRIORITY | Screen::kDRAWSHP_FADE | invisibilityDrawFlag);
}
_screen->drawShape(drawPage, _vm->_shapes[shapesIndex], xpos, ypos, 2, tempFlags | Screen::kDRAWSHP_SCALE, _vm->_brandonPoisonFlagsGFX, int(1), drawLayer, _brandonScaleX, _brandonScaleY);
}
}
} else {
if (shapesIndex != -1) {
int tempFlags = 0;
if (curObject->flags & Screen::kDRAWSHP_XFLIP)
tempFlags = Screen::kDRAWSHP_XFLIP;
_screen->drawShape(drawPage, _vm->_shapes[shapesIndex], xpos, ypos, 2, tempFlags | Screen::kDRAWSHP_PRIORITY, drawLayer);
}
}
}
}
xpos = curObject->x1;
ypos = curObject->y1;
curObject->flags |= Screen::kDRAWSHP_PRIORITY;
if (curObject->index == 0) {
fadeDrawFlag = Screen::kDRAWSHP_FADE;
if (invisibilityDrawFlag & Screen::kDRAWSHP_PREDATOR || blurDrawFlag & Screen::kDRAWSHP_MORPH)
fadeDrawFlag = 0;
if (_vm->_brandonStatusBit & 2)
curObject->flags &= ~Screen::kDRAWSHP_XFLIP;
if (!_vm->_scaleMode) {
if (fadeDrawFlag & Screen::kDRAWSHP_FADE)
_screen->drawShape(drawPage, curObject->sceneAnimPtr, xpos, ypos, 2, curObject->flags | invisibilityDrawFlag | Screen::kDRAWSHP_FADE, (uint8 *)_vm->_brandonPoisonFlagsGFX, int(1), drawLayer);
else if (blurDrawFlag & Screen::kDRAWSHP_MORPH)
_screen->drawShape(drawPage, curObject->sceneAnimPtr, xpos, ypos, 2, curObject->flags | invisibilityDrawFlag | Screen::kDRAWSHP_MORPH, int(_vm->_brandonInvFlag), drawLayer);
else
_screen->drawShape(drawPage, curObject->sceneAnimPtr, xpos, ypos, 2, curObject->flags | invisibilityDrawFlag, drawLayer);
} else {
if (fadeDrawFlag & Screen::kDRAWSHP_FADE)
_screen->drawShape(drawPage, curObject->sceneAnimPtr, xpos, ypos, 2, curObject->flags | invisibilityDrawFlag | Screen::kDRAWSHP_FADE | Screen::kDRAWSHP_SCALE, (uint8 *)_vm->_brandonPoisonFlagsGFX, int(1), drawLayer, _brandonScaleX, _brandonScaleY);
else if (blurDrawFlag & Screen::kDRAWSHP_MORPH)
_screen->drawShape(drawPage, curObject->sceneAnimPtr, xpos, ypos, 2, curObject->flags | invisibilityDrawFlag | Screen::kDRAWSHP_MORPH | Screen::kDRAWSHP_SCALE, int(_vm->_brandonInvFlag), drawLayer, _brandonScaleX, _brandonScaleY);
else
_screen->drawShape(drawPage, curObject->sceneAnimPtr, xpos, ypos, 2, curObject->flags | invisibilityDrawFlag | Screen::kDRAWSHP_SCALE, drawLayer, _brandonScaleX, _brandonScaleY);
}
} else {
if (curObject->index >= 16 && curObject->index <= 27)
_screen->drawShape(drawPage, curObject->sceneAnimPtr, xpos, ypos, 2, curObject->flags | Screen::kDRAWSHP_SCALE, 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, bool refreshScreen) {
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;
}
}
}
if (refreshScreen)
_screen->updateScreen();
}
void Animator_LoK::updateAllObjectShapes(bool refreshScreen) {
restoreAllObjectBackgrounds();
preserveAnyChangedBackgrounds();
prepDrawAllObjects();
copyChangedObjectsForward(0, refreshScreen);
}
void Animator_LoK::animRemoveGameItem(int index) {
restoreAllObjectBackgrounds();
AnimObject *animObj = &_items[index];
animObj->sceneAnimPtr = nullptr;
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 nullptr;
return cur->nextAnimObject;
}
if (!cur->nextAnimObject) {
if (cur == rem) {
if (!prev)
return nullptr;
else
prev->nextAnimObject = nullptr;
}
} 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 = nullptr;
}
return queue;
}
void Animator_LoK::addObjectToQueue(AnimObject *object) {
if (!_objectQueue)
_objectQueue = objectAddHead(nullptr, 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(nullptr, 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
// UPDATE: From DOS floppy disasm: _brandonDrawFrame > 122 --> Test where this actually occurs
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

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,184 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "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);
_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 = nullptr;
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 = nullptr;
}
return list;
}
KyraEngine_v2::AnimObj *KyraEngine_v2::deleteAnimListEntry(AnimObj *list, AnimObj *entry) {
if (!list)
return nullptr;
AnimObj *old = nullptr;
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 nullptr;
cur = cur->nextObject;
return cur;
}
if (!cur->nextObject) {
if (!old)
return nullptr;
old->nextObject = nullptr;
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 = nullptr;
animObj->shapeIndex1 = 0xFFFF;
animObj->shapeIndex2 = 0xFFFF;
animObj->needRefresh = 1;
refreshAnimObjectsIfNeed();
animObj->enabled = 0;
_animList = deleteAnimListEntry(_animList, animObj);
}
} // End of namespace Kyra

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,967 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef 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"
#include "common/ptr.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 col1;
uint16 col2;
uint16 line;
uint16 column;
};
/**
* A class that handles KYRA fonts.
*/
class Font {
public:
/* Font types
* Currently, we actually only care about oneByte and twoByte, but
* naming it like this makes it easier to extend if the need arises.
*/
enum Type {
kASCII = 0,
kJIS_X0201,
kSJIS,
kBIG5,
kJohab
};
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; }
/**
* Whether the font is Ascii or Sjis.
*/
virtual Type getType() const = 0;
/**
* 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;
/**
* Gets the height of a specific character. The only font that needs this
* is the SegaCD one. For all other fonts this is a fixed value.
*/
virtual int getCharHeight(uint16 c) const { return getHeight(); }
/**
* 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) {}
enum FontStyle {
kStyleNone = 0,
kStyleLeftShadow = 1 << 0,
kStyleBorder = 1 << 1,
kStyleFat = 1 << 2,
kStyleNarrow1 = 1 << 3,
kStyleNarrow2 = 1 << 4,
kStyleFullWidth = 1 << 5,
kStyleForceOneByte = 1 << 6
};
/**
* Sets a drawing style. Only rudimentary implementation based on what is needed.
*/
virtual void setStyles(int styles) {}
/**
* 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;
virtual void drawChar(uint16 c, byte *dst, int pitch, int xOffs, int yOffs) const {}
};
/**
* 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() override { unload(); }
bool load(Common::SeekableReadStream &file) override;
Type getType() const override { return kASCII; }
int getHeight() const override { return _height; }
int getWidth() const override { return _width; }
int getCharWidth(uint16 c) const override;
void setColorMap(const uint8 *src) override { _colorMap = src; }
void drawChar(uint16 c, byte *dst, int pitch, int) const override;
private:
void unload();
const uint8 *_colorMap;
uint8 *_data;
int _width, _height;
int _numGlyphs;
uint8 *_widthTable;
uint8 *_heightTable;
uint16 *_bitmapOffsets;
};
/**
* Implementation of the Font interface for Kyra 1 style (non-native AmigaDOS) AMIGA fonts.
*/
class AMIGAFont : public Font {
public:
AMIGAFont();
~AMIGAFont() override { unload(); }
bool load(Common::SeekableReadStream &file) override;
Type getType() const override { return kASCII; }
int getHeight() const override { return _height; }
int getWidth() const override { return _width; }
int getCharWidth(uint16 c) const override;
void setColorMap(const uint8 *src) override {}
void drawChar(uint16 c, byte *dst, int pitch, int) const override;
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(Common::SharedPtr<Graphics::FontSJIS> &font, const uint8 invisColor, bool is16Color, bool drawOutline, int extraSpacing);
~SJISFont() override {}
bool usesOverlay() const override { return true; }
Type getType() const override { return kSJIS; }
bool load(Common::SeekableReadStream &) override { return true; }
int getHeight() const override;
int getWidth() const override;
int getCharWidth(uint16 c) const override;
void setColorMap(const uint8 *src) override;
void setStyles(int style) override { _style = style; }
void drawChar(uint16 c, byte *dst, int pitch, int) const override;
protected:
const uint8 *_colorMap;
Common::SharedPtr<Graphics::FontSJIS> _font;
const bool _drawOutline;
int _style;
private:
const uint8 _invisColor;
const bool _isTextMode;
// 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;
};
class ChineseFont : public Font {
public:
ChineseFont(int pitch, int renderWidth, int renderHeight, int spacingWidth, int spacingHeight, int extraSpacingWidth, int extraSpacingHeight);
~ChineseFont() override;
virtual Type getType() const override { return kBIG5; }
bool load(Common::SeekableReadStream &data) override;
void setStyles(int styles) override { _border = (styles & kStyleBorder); }
int getHeight() const override { return _spacingHeight + (_border ? _borderExtraSpacingHeight : 0); }
int getWidth() const override { return _spacingWidth + (_border ? _borderExtraSpacingWidth : 0); }
int getCharWidth(uint16 c) const override { return hasGlyphForCharacter(c) ? getWidth() : -1; }
int getCharHeight(uint16 c) const override { return hasGlyphForCharacter(c) ? _renderHeight + (_border ? _borderExtraSpacingHeight : 0) : -1; }
void setColorMap(const uint8 *src) override;
void drawChar(uint16 c, byte *dst, int pitch, int) const override;
protected:
uint32 getGlyphDataSize() const { return _glyphDataSize; }
uint16 _textColor[2];
bool _pixelColorShading;
const uint8 *_colorMap;
bool _border;
private:
virtual bool hasGlyphForCharacter(uint16 c) const = 0;
virtual uint32 getFontOffset(uint16 c) const = 0;
virtual void processColorMap() = 0;
const int _spacingWidth, _spacingHeight;
const int _borderExtraSpacingWidth, _borderExtraSpacingHeight;
const int _renderWidth, _renderHeight;
const uint8 *_glyphData;
uint32 _glyphDataSize;
const uint16 _pitch;
};
class JohabFontLoK final : public Font {
public:
JohabFontLoK(Font *&font8fat, const uint16 *lookupTable, uint32 lookupTableSize);
~JohabFontLoK() override;
enum {
kNumJongseong = 191,
kNumJungseong = 85,
kNumChoseong = 109
};
bool load(Common::SeekableReadStream &data) override;
Type getType() const override { return kJohab; }
int getHeight() const override { return _height; }
int getWidth() const override { return _width; }
int getCharWidth(uint16 c) const override;
int getCharHeight(uint16 c) const override;
void setColorMap(const uint8 *src) override;
void drawChar(uint16 c, byte *dst, int pitch, int) const override;
private:
const uint8 *createGlyph(uint16 chr) const;
void processColorMap();
void renderGlyph(byte *dst, const uint8 *glyph, uint8 col, int pitch) const;
int _width, _height;
const uint8 *_colorMap;
Font *&_font8fat;
const uint8 *_fileData;
const uint8 *_glyphData[3];
const uint16 *_2byteTables[7];
uint8 *_glyphTemp;
};
class ChineseOneByteFontLoK final : public ChineseFont {
public:
ChineseOneByteFontLoK(int pitch);
private:
bool hasGlyphForCharacter(uint16 c) const override { return !(c & 0x80); }
uint32 getFontOffset(uint16 c) const override { return (c & 0x7F) * 14; }
void processColorMap() override;
};
class ChineseTwoByteFontLoK final : public ChineseFont {
public:
ChineseTwoByteFontLoK(int pitch, const uint16 *lookupTable, uint32 lookupTableSize);
private:
bool hasGlyphForCharacter(uint16 c) const override;
uint32 getFontOffset(uint16 c) const override;
void processColorMap() override;
const uint16 *_lookupTable;
uint32 _lookupTableSize;
};
class ChineseOneByteFontMR final : public ChineseFont {
public:
ChineseOneByteFontMR(int pitch) : ChineseFont(pitch, 7, 14, 9, 14, 0, 2) {}
private:
bool hasGlyphForCharacter(uint16 c) const override { return (c == 0x6187) || !(c & 0x80); }
uint32 getFontOffset(uint16 c) const override { return ((c == 0x6187) ? 128 : (c & 0x7F)) * 14; }
void processColorMap() override;
};
class ChineseTwoByteFontMR final : public ChineseFont {
public:
ChineseTwoByteFontMR(int pitch) : ChineseFont(pitch, 15, 14, 18, 14, 0, 2) {}
private:
bool hasGlyphForCharacter(uint16 c) const override { return (c != 0x6187) && (c & 0x80); }
uint32 getFontOffset(uint16 c) const override;
void processColorMap() override;
};
#ifdef ENABLE_LOL
class ChineseOneByteFontLoL final : public ChineseFont {
public:
ChineseOneByteFontLoL(int pitch) : ChineseFont(pitch, 8, 14, 8, 16, 0, 0) { _pixelColorShading = false; }
void setStyles(int styles) override {}
private:
bool hasGlyphForCharacter(uint16 c) const override { return !(c & 0x80); }
uint32 getFontOffset(uint16 c) const override { return (c & 0x7F) * 14; }
void processColorMap() override;
};
class ChineseTwoByteFontLoL final : public ChineseFont {
public:
ChineseTwoByteFontLoL(int pitch) : ChineseFont(pitch, 16, 14, 16, 16, 0, 0) { _pixelColorShading = false; }
void setStyles(int styles) override {}
private:
bool hasGlyphForCharacter(uint16 c) const override { return (c & 0x80) && getFontOffset(c) < getGlyphDataSize(); }
uint32 getFontOffset(uint16 c) const override;
void processColorMap() override;
};
#endif
class ChineseOneByteFontHOF final : public ChineseFont {
public:
ChineseOneByteFontHOF(int pitch) : ChineseFont(pitch, 8, 14, 9, 15, 0, 0) {}
private:
bool hasGlyphForCharacter(uint16 c) const override { return !(c & 0x80); }
uint32 getFontOffset(uint16 c) const override { return (c & 0x7F) * 14; }
void processColorMap() override;
};
class ChineseTwoByteFontHOF final : public ChineseFont {
public:
ChineseTwoByteFontHOF(int pitch) : ChineseFont(pitch, 16, 14, 18, 15, 0, 0) {}
private:
bool hasGlyphForCharacter(uint16 c) const override { return (c & 0x80); }
uint32 getFontOffset(uint16 c) const override;
void processColorMap() override;
};
class MultiSubsetFont final : public Font {
public:
MultiSubsetFont(Common::Array<Font*> *subsets) : Font(), _subsets(subsets) {}
~MultiSubsetFont() override;
Type getType() const override { return kBIG5; }
// Caveat: This method will try to load a font into the first subset slot it finds.
// It expects the load method of the subset font to return false if the slot has
// already been filled. It will then try the next slot. So, unlike other fonts the
// subset fonts cannot be allowed to call the load method as often as they want
// (which we never did anyway - we only ever load each font exactly one time).
// But this also means that different
bool load(Common::SeekableReadStream &data) override;
void setStyles(int styles) override;
int getHeight() const override;
int getWidth() const override;
int getCharWidth(uint16 c) const override;
int getCharHeight(uint16 c) const override;
void setColorMap(const uint8 *src) override;
void drawChar(uint16 c, byte *dst, int pitch, int) const override;
private:
Common::Array<Font*> *_subsets;
};
/**
* 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_H_SEGA_NTSC = 224,
SCREEN_PAGE_SIZE = 320 * 200 + 1024,
SCREEN_OVL_SJIS_SIZE = 640 * 400,
SCREEN_PAGE_NUM = 16,
SCREEN_OVLS_NUM = 6,
SCREEN_IDLEREFRESH_RESTART_MSEC = 250,
SCREEN_IDLEREFRESH_RATE_MSEC = 16
};
enum CopyRegionFlags {
CR_NO_P_CHECK = 0x01
};
enum DrawShapeFlags {
kDRAWSHP_XFLIP = 0x01,
kDRAWSHP_YFLIP = 0x02,
kDRAWSHP_SCALE = 0x04,
kDRAWSHP_WINREL = 0x10,
kDRAWSHP_CENTER = 0x20,
kDRAWSHP_FADE = 0x100,
kDRAWSHP_PREDATOR = 0x200,
kDRAWSHP_COMPACT = 0x400,
kDRAWSHP_PRIORITY = 0x800,
kDRAWSHP_TRANSPARENT = 0x1000,
kDRAWSHP_BCKGRNDFADE = 0x2000,
kDRAWSHP_MORPH = 0x4000,
kDRAWSHP_COLOR = 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_TEXTMODE_FNT,
FID_SJIS_LARGE_FNT,
FID_SJIS_SMALL_FNT,
FID_CHINESE_FNT,
FID_KOREAN_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);
// refresh
int updateScreen();
void updateBackendScreen(bool force);
uint32 _idleUpdateTimer;
// 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);
// SegaCD version
// This is a somewhat hacky but probably least invasive way to
// move the whole ingame screen output down a couple of lines.
void transposeScreenOutputY(int yAdd);
// AMIGA version only
bool isInterfacePaletteEnabled() const { return _dualPaletteModeSplitY; }
void enableDualPaletteMode(int splitY);
void disableDualPaletteMode();
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 getCharHeight(uint16 c) const;
int getTextWidth(const char *str, bool nextWordOnly = false);
int getNumberOfCharacters(const char *str);
void printText(const char *str, int x, int y, uint8 color1, uint8 color2, int pitch = -1);
virtual void setTextColorMap(const uint8 *cmap) = 0;
void setTextColor(const uint8 *cmap, int a, int b);
void setTextColor16bit(const uint16 *cmap16);
int setFontStyles(FontId fontId, int styles);
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; }
void setTextMarginRight(int x) { _textMarginRight = x; }
uint16 _textMarginRight;
bool _overdrawMargin;
const ScreenDim *_curDim;
Common::String _lineBreakChars;
// 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
virtual 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 _charSpacing;
int _lineSpacing;
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, bool isAmiga);
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);
// This seems to be a variant of shuffleScreen (which is used in LoK). At the time of coding (and long after that) the
// fact that this is a double implementation went unnoticed. My - admittedly limited - efforts to get rid of one of these
// implementations were unsatisfactory, though. Each method seems to be optimized to produce accurate results for its
// specifc purpose (LoK for shuffleScreen, EoB/LoL for crossFadeRegion). Merging these methods has no priority, since we
// can well afford the 20 lines of extra code.
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();
template<typename srcType, typename scaleToType> void scale2x(uint8 *dst, int dstPitch, const uint8 *src, int srcPitch, int w, int h);
template<typename pixelType> void mergeOverlayImpl(int x, int y, int w, int h);
virtual void mergeOverlay(int x, int y, int w, int h) {
if (_useHiColorScreen)
mergeOverlayImpl<uint16>(x, y, w, h);
else
mergeOverlayImpl<uint8>(x, y, w, 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, int pitch = -1);
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];
const uint8 *_pagePtrsBuff;
uint8 *_sjisOverlayPtrs[SCREEN_OVLS_NUM];
uint8 _pageMapping[SCREEN_PAGE_NUM];
bool _useOverlays;
bool _useSJIS;
int _fontStyles;
Font *_fonts[FID_NUM];
uint8 _textColorsMap[16];
uint16 _textColorsMap16bit[2];
uint8 *_textRenderBuffer;
int _textRenderBufferSize;
Common::SharedPtr<Graphics::FontSJIS> _sjisFontShared;
uint8 _sjisInvisibleColor;
bool _sjisMixedFontMode;
// colors/palette specific
bool _use16ColorMode;
bool _useShapeShading;
bool _4bitPixelPacking;
bool _useHiResEGADithering;
bool _useHiColorScreen;
bool _isAmiga;
bool _useAmigaExtraColors;
bool _isSegaCD;
Common::RenderMode _renderMode;
int _bytesPerPixel;
int _screenPageSize;
const int _screenHeight;
int _yTransOffs;
Palette *_screenPalette;
Common::Array<Palette *> _palettes;
Palette *_internFadePalette;
uint16 shade16bitColor(uint16 col);
uint16 *_16bitPalette;
uint16 *_16bitConversionPalette;
uint8 _16bitShadingLevel;
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
typedef void (Screen::*DsPlotFunc)(uint8*, uint8);
typedef int (Screen::*DsMarginSkipFunc)(uint8*&, const uint8*&, int&);
typedef void (Screen::*DsLineFunc)(uint8*&, const uint8*&, const DsPlotFunc, int&, int16);
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, const DsPlotFunc plot, int &cnt, int16);
void drawShapeProcessLineNoScaleDownwind(uint8 *&dst, const uint8 *&src, const DsPlotFunc plot, int &cnt, int16);
void drawShapeProcessLineScaleUpwind(uint8 *&dst, const uint8 *&src, const DsPlotFunc plot, int &cnt, int16 scaleState);
void drawShapeProcessLineScaleDownwind(uint8 *&dst, const uint8 *&src, const DsPlotFunc plot, 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);
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
int _dualPaletteModeSplitY;
// debug
bool _debugEnabled;
};
} // End of namespace Kyra
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,536 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef KYRA_SCREEN_EOB_H
#define KYRA_SCREEN_EOB_H
#ifdef ENABLE_EOB
#include "graphics/big5.h"
#include "kyra/graphics/screen.h"
namespace Kyra {
class EoBCoreEngine;
class SegaRenderer;
class SegaAnimator;
class Screen_EoB : public Screen {
friend class SegaRenderer;
public:
// The purpose of this enum is to keep better track of which page is used
// when and for which purpose. We use the pages for more backup operations
// than the original and also have to deal with the different ports which
// all do their own things. This is supposed to help avoid using pages that
// are already in use for something else. It also allows for quick fixes
// if necessary.
enum {
kSegaInitShapesPage = 7,
kSegaRenderPage = 8,
kDefeatMsgBackupPage = 10,
kCheckPwBackupPage = 10,
kSpellbookBackupPage = 10,
kEoB2ScriptHelperPage = 12,
kCampMenuBackupPage = 12
};
public:
Screen_EoB(EoBCoreEngine *vm, OSystem *system);
~Screen_EoB() override;
bool init() override;
void setClearScreenDim(int dim);
void clearCurDim();
void clearCurDimOvl(int pageNum);
void setMouseCursor(int x, int y, const byte *shape) override;
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, int shadowCol, int pitch = -1);
static void eob2ChineseLZUncompress(byte *dest, uint32 destSize, Common::SeekableReadStream *src);
void loadChineseEOB2LZBitmap(Common::SeekableReadStream *s, int pageNum, uint32 size);
void loadBitmap(const char *filename, int tempPage, int dstPage, Palette *pal, bool skip = false) override;
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) override;
void getRealPalette(int num, uint8 *dst) override;
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, ...) override;
void drawT1Shape(uint8 pageNum, const byte *t1data, int x, int y, int sd);
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 color, int rate);
bool delayedFadePalStep(Palette *fadePal, Palette *destPal, int rate);
void setTextColorMap(const uint8 *cmap) override {}
int getRectSize(int w, int h) override;
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) override;
// 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);
// PC-98 specific
void selectPC98Palette(int palID, Palette &dest, int brightness = 0, bool set = false);
void decodeBIN(const uint8 *src, uint8 *dst, uint16 inSize);
void decodePC98PlanarBitmap(uint8 *srcDstBuffer, uint8 *tmpBuffer, uint16 size = 64000);
struct PalCycleData {
const int8 *data;
uint8 delay;
};
void initPC98PaletteCycle(int palID, PalCycleData *data);
void updatePC98PaletteCycle(int brightness);
PalCycleData *_activePalCycle;
uint8 *_cyclePalette;
// Amiga specific
void loadSpecialAmigaCPS(const char *fileName, int destPage, bool isGraphics);
// This is a simple way of emulating the Amiga copper list palette magic for more than 32 colors.
// I use colors 32 to 63 for these extra colors (which the Amiga copper sends to the color
// registers on the fly at vertical beam position 120).
void setDualPalettes(Palette &top, Palette &bottom);
// SegaCD specific
void sega_initGraphics();
void sega_selectPalette(int srcPalID, int dstPalID, bool set = false);
void sega_loadCustomPaletteData(Common::ReadStream *in);
void sega_updatePaletteFaders(int palID);
void sega_fadePalette(int delay, int16 brEnd, int dstPalID = -1, bool waitForCompletion = true, bool noUpdate = false);
void sega_fadeToBlack(int delay) { sega_fadePalette(delay, -7); }
void sega_fadeToWhite(int delay) { sega_fadePalette(delay, 7); }
void sega_fadeToNeutral(int delay) { sega_fadePalette(delay, 0); }
void sega_paletteOps(int16 opPal, int16 par1, int16 par2);
void sega_setTextBuffer(uint8 *buffer, uint32 bufferSize);
void sega_clearTextBuffer(uint8 col);
void sega_loadTextBackground(const uint8 *src, uint16 size);
void sega_drawTextBox(int pW, int pH, int x, int y, int w, int h, uint8 color1, uint8 color2);
void sega_loadTextBufferToVRAM(uint16 srcOffset, uint16 addr, int size);
void sega_gfxScale(uint8 *out, uint16 w, uint16 h, uint16 pitch, const uint8 *in, const uint16 *stampMap, const uint16 *traceVectors);
void sega_drawClippedLine(int pW, int pH, int x, int y, int w, int h, uint8 color);
uint8 *sega_convertShape(const uint8 *src, int w, int h, int pal, int hOffs = 0);
void sega_encodeShapesFromSprites(const uint8 **dst, const uint8 *src, int numShapes, int w, int h, int pal, bool removeSprites = true);
SegaRenderer *sega_getRenderer() const { return _segaRenderer; }
SegaAnimator *sega_getAnimator() const { return _segaAnimator; }
private:
void updateDirtyRects() override;
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;
Common::String _cpsFilePattern;
const uint16 _cursorColorKey16Bit;
static const uint8 _egaMatchTable[];
static const ScreenDim _screenDimTableIntl[];
static const ScreenDim _screenDimTableZH[];
static const int _screenDimTableCount;
// SegaCD specific
struct PaletteFader {
PaletteFader() : _brCur(0), _brDest(0), _fadeIncr(0), _fadeDelay(0), _fadeTimer(0), _needRefresh(false) {}
int16 _brCur;
int16 _brDest;
int16 _fadeIncr;
int16 _fadeDelay;
int16 _fadeTimer;
bool _needRefresh;
};
PaletteFader *_palFaders;
bool _specialColorReplace;
SegaRenderer *_segaRenderer;
SegaAnimator *_segaAnimator;
uint16 _segaCurPalette[64];
uint16 *_segaCustomPalettes;
uint8 *_defaultRenderBuffer;
int _defaultRenderBufferSize;
Common::SharedPtr<Graphics::Big5Font> _big5;
};
class ChineseTwoByteFontEoB final : public Font {
public:
ChineseTwoByteFontEoB(Common::SharedPtr<Graphics::Big5Font> big5, Font *singleByte) : _big5(big5), _singleByte(singleByte), _border(false), _colorMap(nullptr) {}
virtual Type getType() const override { return kBIG5; }
bool load(Common::SeekableReadStream &data) override {
return _singleByte->load(data);
}
void setStyles(int styles) override { _border = (styles & kStyleBorder); _singleByte->setStyles(styles); }
int getHeight() const override { return MAX(_big5->getFontHeight(), _singleByte->getHeight()); }
int getWidth() const override { return MAX(_big5->kChineseTraditionalWidth + 2, _singleByte->getWidth()); }
void setColorMap(const uint8 *src) override { _colorMap = src; _singleByte->setColorMap(src); }
int getCharWidth(uint16 c) const override;
int getCharHeight(uint16 c) const override;
void drawChar(uint16 c, byte *dst, int pitch, int bpp) const override;
private:
uint16 translateBig5(uint16 in) const;
Common::SharedPtr<Graphics::Big5Font> _big5;
Common::ScopedPtr<Font> _singleByte;
bool _border;
const uint8 *_colorMap;
};
/**
* Implementation of the Font interface for old DOS fonts used
* in EOB and EOB II.
*
*/
class OldDOSFont : public Font {
public:
OldDOSFont(Common::RenderMode mode, uint8 shadowColor, bool remapCharacters = true);
~OldDOSFont() override;
bool load(Common::SeekableReadStream &file) override;
bool loadPCBIOSTall();
Type getType() const override { return kASCII; }
int getHeight() const override { return _height; }
int getWidth() const override { return _width; }
bool usesOverlay() const override { return _useOverlay; }
int getCharWidth(uint16 c) const override;
void setColorMap(const uint8 *src) override;
void set16bitColorMap(const uint16 *src) override { _colorMap16bit = src; }
void setStyles(int styles) override { _style = styles; }
void drawChar(uint16 c, byte *dst, int pitch, int bpp) const override;
protected:
void unload();
int _style;
const uint8 *_colorMap8bit;
uint8 *_data;
uint16 *_bitmapOffsets;
int _width, _height;
int _numGlyphs;
uint8 _shadowColor;
uint16 _numGlyphsMax;
bool _useOverlay;
int _scaleV;
private:
void drawCharIntern(uint16 c, byte *dst, int pitch, int bpp, int col1, int col2) const;
virtual uint16 convert(uint16 c) const;
Common::RenderMode _renderMode;
const uint16 *_colorMap16bit;
bool _remapCharacters;
static uint16 *_cgaDitheringTable;
static int _numRef;
};
/**
* Implementation of the Font interface for native AmigaDOS system fonts (normally to be loaded via diskfont.library)
*/
class Resource;
class AmigaDOSFont : public Font {
public:
AmigaDOSFont(Resource *res, bool needsLocalizedFont = false);
~AmigaDOSFont() override { unload(); }
bool load(Common::SeekableReadStream &file) override;
Type getType() const override { return kASCII; }
int getHeight() const override { return _height; }
int getWidth() const override { return _width; }
int getCharWidth(uint16 c) const override;
void setColorMap(const uint8 *src) override { _colorMap = src; }
void drawChar(uint16 c, byte *dst, int pitch, int) const override;
static void errorDialog(int index);
private:
void unload();
struct TextFont {
TextFont() : data(0), bitmap(0), location(0), spacing(0), kerning(0), height(0), width(0), baseLine(0), firstChar(0), lastChar(0), modulo(0) {}
~TextFont() {
delete[] data;
}
uint16 height;
uint16 width;
uint16 baseLine;
uint8 firstChar;
uint8 lastChar;
uint16 modulo;
const uint8 *data;
const uint8 *bitmap;
const uint16 *location;
const int16 *spacing;
const int16 *kerning;
};
TextFont *loadContentFile(const Common::Path &fileName);
void selectMode(int mode);
struct FontContent {
FontContent() : height(0), style(0), flags(0) {}
~FontContent() {
data.reset();
}
Common::String contentFile;
Common::SharedPtr<TextFont> data;
uint16 height;
uint8 style;
uint8 flags;
};
int _width, _height;
uint8 _first, _last;
FontContent *_content;
uint16 _numElements;
uint16 _selectedElement;
const uint8 *_colorMap;
const uint16 _maxPathLen;
const bool _needsLocalizedFont;
static uint8 _errorDialogDisplayed;
Resource *_res;
};
/**
* SJIS Font variant used in EOB I PC-98. It converts 1-byte characters into 2-byte characters and has a specific shadowing to the left.
*/
class SJISFontEoB1PC98 : public SJISFont {
public:
SJISFontEoB1PC98(Common::SharedPtr<Graphics::FontSJIS> &font, /*uint8 shadowColor,*/ const uint16 *convTable1, const uint16 *convTable2);
~SJISFontEoB1PC98() override {}
int getCharWidth(uint16 c) const override;
void drawChar(uint16 c, byte *dst, int pitch, int) const override;
private:
uint16 convert(uint16 c) const;
const uint16 *_convTable1, *_convTable2;
bool _defaultConv;
};
/**
* SJIS Font variant used in EOB II PC-98. It converts 1-byte characters into 2-byte characters.
*/
class SJISFontEoB2PC98 : public SJISFont {
public:
SJISFontEoB2PC98(Common::SharedPtr<Graphics::FontSJIS> &font, /*uint8 shadowColor,*/ const char *convTable1, const char *convTable2);
~SJISFontEoB2PC98() override {}
int getCharWidth(uint16 c) const override;
void drawChar(uint16 c, byte *dst, int pitch, int) const override;
private:
uint16 convert(uint16 c) const;
const char *_convTable1, *_convTable2;
//bool _defaultConv;
};
/**
* OldDOSFont variant used in EOB I PC-98. It uses the same drawing routine, but has a different loader. It contains
* ASCII and Katakana characters in JIS X 0201 and requires several conversion tables to display these. It gets drawn on the hires overlay.
*/
class Font12x12PC98 : public OldDOSFont{
public:
Font12x12PC98(uint8 shadowColor, const uint16 *convTable1, const uint16 *convTable2, const uint8 *lookupTable);
~Font12x12PC98() override;
bool usesOverlay() const override { return true; }
Type getType() const override { return kJIS_X0201; }
int getHeight() const override { return _height >> 1; }
int getWidth() const override { return _width >> 1; }
int getCharWidth(uint16 c) const override { return _width >> 1; };
bool load(Common::SeekableReadStream &file) override;
private:
uint16 convert(uint16 c) const override;
const uint16 *_convTable1, *_convTable2;
uint16 *_bmpOffs;
};
/**
* OldDOSFont variant used in EOB II PC-98 which supports twice the number of characters. Some font files may include kana characters. The font supports
* weird vertical scaling and can be drawn on the hires overlay.
*/
class PC98Font : public OldDOSFont {
public:
PC98Font(uint8 shadowColor, bool useOverlay, int scaleV, const uint8 *convTable1 = 0, const char *convTable2 = 0, const char *convTable3 = 0);
~PC98Font() override {}
bool load(Common::SeekableReadStream &file) override;
int getHeight() const override { return _outputHeight; }
int getWidth() const override { return _outputWidth; }
int getCharWidth(uint16 c) const override { return _outputWidth; };
Type getType() const override { return _type; }
private:
uint16 convert(uint16 c) const override;
uint16 makeTwoByte(uint16 c) const;
const uint8 *_convTable1;
const char *_convTable2, *_convTable3;
int _outputHeight;
int _outputWidth;
const Type _type;
};
/**
* 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(Common::SharedPtr<Graphics::FontSJIS> &font);
~SJISFontLarge() override {}
int getHeight() const override;
int getWidth() const override;
int getCharWidth(uint16 c) const override;
bool usesOverlay() const override { return false; }
void drawChar(uint16 c, byte *dst, int pitch, int) const override;
};
/**
* 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);
~SJISFont12x12() override { unload(); }
bool load(Common::SeekableReadStream &file) override;
Type getType() const override { return kSJIS; }
bool usesOverlay() const override { return true; }
int getHeight() const override { return _height; }
int getWidth() const override { return _width; }
int getCharWidth(uint16 c) const override { return _width; }
void setColorMap(const uint8 *src) override { _colorMap = src; }
void drawChar(uint16 c, byte *dst, int pitch, int) const override;
private:
void unload();
uint8 *_data;
Common::HashMap<uint16, uint8> _searchTable;
const uint8 *_colorMap;
const int _height, _width;
};
class SegaCDFont : public Font {
public:
SegaCDFont(Common::Language lang, const uint16 *convTable1, const uint16 *convTable2, const uint8 *widthTable1, const uint8 *widthTable2, const uint8 *widthTable3);
~SegaCDFont() override;
bool load(Common::SeekableReadStream &file) override;
Type getType() const override { return _forceOneByte ? kASCII : kSJIS; }
int getHeight() const override { return _height; }
int getWidth() const override { return _width; }
int getCharWidth(uint16 c) const override;
int getCharHeight(uint16 c) const override;
void setStyles(int styles) override;
void setColorMap(const uint8 *src) override { _colorMap = src; }
void drawChar(uint16 c, byte *dst, int pitch, int bpp) const override { drawChar(c, dst, pitch, 0, 0); }
void drawChar(uint16 c, byte *dst, int pitch, int xOffs, int yOffs) const override;
private:
const uint8 *getGlyphData(uint16 c, uint8 &charWidth, uint8 &charHeight, uint8 &pitch) const;
const uint8 *_data;
const uint8 *_buffer;
bool _forceTwoByte;
bool _forceOneByte;
Common::Language _lang;
uint8 _style;
const uint8 *_colorMap;
const int _height, _width;
const uint16 *_convTable1, *_convTable2;
const uint8 *_widthTable1, *_widthTable2, *_widthTable3;
};
} // End of namespace Kyra
#endif // ENABLE_EOB
#endif

View File

@@ -0,0 +1,411 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifdef ENABLE_EOB
#include "kyra/resource/resource.h"
#include "common/memstream.h"
#include "common/translation.h"
#include "gui/error.h"
namespace Kyra {
static uint32 _decodeFrameAmiga_x = 0;
bool decodeFrameAmiga_readNextBit(const uint8 *&data, uint32 &code, uint32 &chk) {
_decodeFrameAmiga_x = code & 1;
code >>= 1;
if (code)
return _decodeFrameAmiga_x;
data -= 4;
code = READ_BE_UINT32(data);
chk ^= code;
_decodeFrameAmiga_x = code & 1;
code = (code >> 1) | (1 << 31);
return _decodeFrameAmiga_x;
}
uint32 decodeFrameAmiga_readBits(const uint8 *&data, uint32 &code, uint32 &chk, int count) {
uint32 res = 0;
while (count--) {
decodeFrameAmiga_readNextBit(data, code, chk);
uint32 bt1 = _decodeFrameAmiga_x;
_decodeFrameAmiga_x = res >> 31;
res = (res << 1) | bt1;
}
return res;
}
void Screen_EoB::loadSpecialAmigaCPS(const char *fileName, int destPage, bool isGraphics) {
uint32 fileSize = 0;
const uint8 *file = _vm->resource()->fileData(fileName, &fileSize);
if (!file)
error("Screen_EoB::loadSpecialAmigaCPS(): Failed to load file '%s'", fileName);
uint32 inSize = READ_BE_UINT32(file);
const uint8 *pos = file;
// Check whether the file starts with the actual compression header.
// If this is not the case, there should a palette before the header.
// Unlike normal CPS files these files never have more than one palette.
if (((inSize + 15) & ~3) != ((fileSize + 3) & ~3)) {
Common::MemoryReadStream in(pos, 64);
_palettes[0]->loadAmigaPalette(in, 0, 32);
pos += 64;
}
inSize = READ_BE_UINT32(pos);
uint32 outSize = READ_BE_UINT32(pos + 4);
uint32 chk = READ_BE_UINT32(pos + 8);
pos = pos + 8 + inSize;
uint8 *dstStart = _pagePtrs[destPage];
uint8 *dst = dstStart + outSize;
uint32 val = READ_BE_UINT32(pos);
_decodeFrameAmiga_x = 0;
chk ^= val;
while (dst > dstStart) {
int para = -1;
int para2 = 0;
if (decodeFrameAmiga_readNextBit(pos, val, chk)) {
uint32 code = decodeFrameAmiga_readBits(pos, val, chk, 2);
if (code == 3) {
para = para2 = 8;
} else {
int cnt = 0;
if (code < 2) {
cnt = 3 + code;
para2 = 9 + code;
} else {
cnt = decodeFrameAmiga_readBits(pos, val, chk, 8) + 1;
para2 = 12;
}
code = decodeFrameAmiga_readBits(pos, val, chk, para2);
while (cnt--) {
dst--;
*dst = dst[code & 0xFFFF];
}
}
} else {
if (decodeFrameAmiga_readNextBit(pos, val, chk)) {
uint32 code = decodeFrameAmiga_readBits(pos, val, chk, 8);
dst--;
*dst = dst[code & 0xFFFF];
dst--;
*dst = dst[code & 0xFFFF];
} else {
para = 3;
}
}
if (para > 0) {
uint32 code = decodeFrameAmiga_readBits(pos, val, chk, para);
uint32 cnt = (code & 0xFFFF) + para2 + 1;
while (cnt--) {
for (int i = 0; i < 8; ++i) {
decodeFrameAmiga_readNextBit(pos, val, chk);
uint32 bt1 = _decodeFrameAmiga_x;
_decodeFrameAmiga_x = code >> 31;
code = (code << 1) | bt1;
}
*(--dst) = code & 0xFF;
}
}
}
delete[] file;
if (chk)
error("Screen_EoB::loadSpecialAmigaCPS(): Checksum error");
if (isGraphics)
convertAmigaGfx(_pagePtrs[destPage], 320, 200);
}
void Screen_EoB::setDualPalettes(Palette &top, Palette &bottom) {
// The original supports simultaneous fading of both palettes, but doesn't make any use of that
// feature. The fade rate is always set to 0. So I see no need to implement that.
_palettes[0]->copy(top, 0, 32, 0);
_palettes[0]->copy(bottom, 0, 32, 32);
setScreenPalette(*_palettes[0]);
enableDualPaletteMode(120);
}
AmigaDOSFont::AmigaDOSFont(Resource *res, bool needsLocalizedFont) : _res(res), _needsLocalizedFont(needsLocalizedFont), _width(0), _height(0), _first(0), _last(0), _content(0), _numElements(0), _selectedElement(0), _maxPathLen(256), _colorMap(nullptr) {
assert(_res);
}
bool AmigaDOSFont::load(Common::SeekableReadStream &file) {
unload();
uint16 id = file.readUint16BE();
// We only support type 0x0f00, since this is the only type used for EOB
if (id != 0x0f00)
return false;
_numElements = file.readUint16BE();
_content = new FontContent[_numElements];
char *cfile = new char[_maxPathLen];
for (int i = 0; i < _numElements; ++i) {
file.read(cfile, _maxPathLen);
_content[i].height = file.readUint16BE();
_content[i].style = file.readByte();
_content[i].flags = file.readByte();
_content[i].contentFile = cfile;
for (int ii = 0; ii < i; ++ii) {
if (_content[ii].contentFile == _content[i].contentFile && _content[ii].data.get())
_content[i].data = _content[ii].data;
}
if (!_content[i].data.get()) {
TextFont *contentData = loadContentFile(cfile);
if (contentData) {
_content[i].data = Common::SharedPtr<TextFont>(contentData);
} else {
unload();
return false;
}
}
if (!(_content[i].flags & 0x40) && (_content[i].height != _content[i].data->height)) {
warning("Amiga DOS Font construction / scaling not implemented.");
}
}
delete[] cfile;
selectMode(0);
return true;
}
int AmigaDOSFont::getCharWidth(uint16 c) const {
if (c < _first || c > _last)
return 0;
c -= _first;
int width = _content[_selectedElement].data->spacing ? _content[_selectedElement].data->spacing[c] : _content[_selectedElement].data->width;
/*if (_content[_selectedElement].data->kerning)
width += _content[_selectedElement].data->kerning[c];*/
return width;
}
void AmigaDOSFont::drawChar(uint16 c, byte *dst, int pitch, int) const {
if (c < _first || c > _last || !dst)
return;
static const uint16 table[] = {
0x8000, 0xc000, 0xe000, 0xf000, 0xf800, 0xfc00, 0xfe00, 0xff00,
0xff80, 0xffc0, 0xffe0, 0xfff0, 0xfff8, 0xfffc, 0xfffe, 0xffff
};
c -= _first;
int w = _content[_selectedElement].data->spacing ? _content[_selectedElement].data->spacing[c] : _content[_selectedElement].data->width;
int xbits = _content[_selectedElement].data->location[c * 2 + 1];
int h = _content[_selectedElement].data->height;
uint16 bitPos = _content[_selectedElement].data->location[c * 2] & 0x0F;
uint16 mod = _content[_selectedElement].data->modulo;
const uint8 *data = _content[_selectedElement].data->bitmap + ((_content[_selectedElement].data->location[c * 2] >> 3) & ~1);
uint32 xbt_mask = xbits ? table[(xbits - 1) & 0x0F] << 16 : 0;
for (int y = 0; y < h; ++y) {
uint32 mask = 0x80000000;
uint32 bits = (READ_BE_UINT32(data) << bitPos) & xbt_mask;
data += mod;
for (int x = 0; x < w; ++x) {
if (bits & mask) {
if (_colorMap[1])
*dst = _colorMap[1];
} else {
if (_colorMap[0])
*dst = _colorMap[0];
}
mask >>= 1;
dst++;
}
dst += (pitch - w);
}
}
uint8 AmigaDOSFont::_errorDialogDisplayed = 0;
void AmigaDOSFont::errorDialog(int index) {
if (_errorDialogDisplayed & (1 << index))
return;
_errorDialogDisplayed |= (1 << index);
// I've made rather elaborate dialogs here, since the Amiga font file handling is quite prone to cause problems for users.
// This will hopefully prevent unnecessary forum posts and bug reports.
if (index == 0) {
::GUI::displayErrorDialog(_(
"This AMIGA version requires the following font files:\n\nEOBF6.FONT\nEOBF6/6\nEOBF8.FONT\nEOBF8/8\n\n"
"If you used the original installer for the installation these files\nshould be located in the AmigaDOS system 'Fonts/' folder.\n"
"Please copy them into the EOB game data directory.\n"
));
error("Failed to load font files.");
} else if (index == 1) {
::GUI::displayErrorDialog(_(
"This AMIGA version requires the following font files:\n\nEOBF6.FONT\nEOBF6/6\nEOBF8.FONT\nEOBF8/8\n\n"
"This is a localized (non-English) version of EOB II which uses language specific characters\n"
"contained only in the specific font files that came with your game. You cannot use the font\n"
"files from the English version or from any EOB I game which seems to be what you are doing.\n\n"
"The game will continue, but the language specific characters will not be displayed.\n"
"Please copy the correct font files into your EOB II game data directory.\n\n"
));
}
}
void AmigaDOSFont::unload() {
delete[] _content;
}
AmigaDOSFont::TextFont *AmigaDOSFont::loadContentFile(const Common::Path &fileName) {
Common::SeekableReadStreamEndian *str = _res->createEndianAwareReadStream(fileName);
if (!str && !fileName.getParent().empty()) {
// These content files are usually located in sub directories (i. e. the eobf8.font
// has a sub dir named 'eobf8' with a file '8' in it). In case someone put the content
// files directly in the game directory we still try to open it.
Common::Path fileNameAlt(fileName.baseName(), Common::Path::kNoSeparator);
str = _res->createEndianAwareReadStream(fileNameAlt);
if (!str) {
// Someone might even have copied the floppy disks to the game directory with the
// full sub directory structure. So we also try that...
fileNameAlt = Common::Path("fonts");
fileNameAlt.joinInPlace(fileName);
str = _res->createEndianAwareReadStream(fileNameAlt);
}
if (!str)
errorDialog(0);
}
assert(str);
uint32 hunkId = str->readUint32();
// Except for some sanity checks we skip all of the Amiga hunk file magic
if (hunkId != 0x03f3)
return 0;
str->seek(20, SEEK_CUR);
uint32 hunkType = str->readUint32();
if (hunkType != 0x3E9)
return 0;
uint32 dataSize = str->readUint32() * 4;
int32 hunkStartPos = str->pos();
str->seek(34, SEEK_CUR);
TextFont *fnt = new TextFont();
int32 fntStartPos = str->pos();
str->seek(44, SEEK_CUR);
fnt->height = str->readUint16();
str->seek(2, SEEK_CUR);
fnt->width = str->readUint16();
fnt->baseLine = str->readUint16();
str->seek(4, SEEK_CUR);
fnt->firstChar = str->readByte();
fnt->lastChar = str->readByte();
if (_needsLocalizedFont && fnt->lastChar <= 127)
errorDialog(1);
str->seek(18, SEEK_CUR);
int32 curPos = str->pos();
uint32 bufferSize = dataSize - (curPos - fntStartPos);
uint8 *buffer = new uint8[bufferSize];
str->read(buffer, bufferSize);
str->seek(curPos - 18, SEEK_SET);
uint32 offset = str->readUint32();
fnt->bitmap = offset ? buffer + offset - (curPos - hunkStartPos) : nullptr;
assert(fnt->bitmap);
fnt->modulo = str->readUint16();
offset = str->readUint32();
uint16 *loc = (uint16*)(offset ? buffer + offset - (curPos - hunkStartPos) : nullptr);
assert(loc);
for (int i = 0; i <= (fnt->lastChar - fnt->firstChar) * 2 + 1; ++i)
loc[i] = READ_BE_UINT16(&loc[i]);
fnt->location = loc;
offset = str->readUint32();
int16 *idat = offset ? (int16*)(buffer + offset - (curPos - hunkStartPos)) : nullptr;
if (idat) {
for (int i = 0; i <= (fnt->lastChar - fnt->firstChar) * 2 + 1; ++i)
idat[i] = (int16)READ_BE_UINT16(&idat[i]);
}
fnt->spacing = idat;
offset = str->readUint32();
// This warning will only show up if someone tries to use this code elsewhere. It cannot happen with EOB fonts.
if (offset)
warning("Trying to load an AmigaDOS font with kerning data. This is not implemented. Font Rendering will not be accurate.");
idat = offset ? (int16*)(buffer + offset - (curPos - hunkStartPos)) : nullptr;
if (idat) {
for (int i = 0; i <= (fnt->lastChar - fnt->firstChar) * 2 + 1; ++i)
idat[i] = (int16)READ_BE_UINT16(&idat[i]);
}
fnt->kerning = idat;
fnt->data = buffer;
delete str;
return fnt;
}
void AmigaDOSFont::selectMode(int mode) {
if (mode < 0 || mode > _numElements - 1)
return;
_selectedElement = mode;
_width = _content[mode].data->width;
_height = _content[mode].data->height;
_first = _content[mode].data->firstChar;
_last = _content[mode].data->lastChar;
}
} // End of namespace Kyra
#endif // ENABLE_EOB

View File

@@ -0,0 +1,399 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifdef ENABLE_EOB
#include "kyra/resource/resource.h"
#include "graphics/sjis.h"
namespace Kyra {
void Screen_EoB::selectPC98Palette(int palID, Palette &dest, int brightness, bool set) {
if (palID < 0 || palID > 9)
return;
if (!_use16ColorMode)
return;
int temp = 0;
const uint8 *pal16c = _vm->staticres()->loadRawData(kEoB1Palettes16c, temp);
if (!pal16c)
return;
uint8 pal[48];
for (int i = 0; i < 48; ++i)
pal[i] = CLIP<int>(pal16c[palID * 48 + i] + brightness, 0, 15);
loadPalette(pal, dest, 48);
if (set)
setScreenPalette(dest);
}
void Screen_EoB::decodeBIN(const uint8 *src, uint8 *dst, uint16 inSize) {
const uint8 *end = src + inSize;
memset(_dsTempPage, 0, 2048);
int tmpDstOffs = 0;
while (src < end) {
uint8 code = *src++;
if (!(code & 0x80)) {
int offs = code << 4;
code = *src++;
offs |= (code >> 4);
int len = (code & 0x0F) + 2;
int tmpSrcOffs = (tmpDstOffs - offs) & 0x7FF;
const uint8 *tmpSrc2 = dst;
for (int len2 = len; len2; len2--) {
*dst++ = _dsTempPage[tmpSrcOffs++];
tmpSrcOffs &= 0x7FF;
}
while (len--) {
_dsTempPage[tmpDstOffs++] = *tmpSrc2++;
tmpDstOffs &= 0x7FF;
}
} else if (code & 0x40) {
int len = code & 7;
if (code & 0x20)
len = (len << 8) | *src++;
len += 2;
int planes = ((code >> 3) & 3) + 1;
while (len--) {
for (int i = 0; i < planes; ++i) {
*dst++ = _dsTempPage[tmpDstOffs++] = src[i];
tmpDstOffs &= 0x7FF;
}
}
src += planes;
} else {
for (int len = (code & 0x3F) + 1; len; len--) {
*dst++ = _dsTempPage[tmpDstOffs++] = *src++;
tmpDstOffs &= 0x7FF;
}
}
}
}
void Screen_EoB::decodePC98PlanarBitmap(uint8 *srcDstBuffer, uint8 *tmpBuffer, uint16 size) {
assert(tmpBuffer != srcDstBuffer);
memcpy(tmpBuffer, srcDstBuffer, size);
const uint8 *src = tmpBuffer;
uint8 *dst1 = srcDstBuffer;
uint8 *dst2 = srcDstBuffer + 4;
size >>= 3;
while (size--) {
for (int i = 0; i < 4; ++i) {
uint8 col1 = 0;
uint8 col2 = 0;
for (int ii = 0; ii < 4; ++ii) {
col1 |= ((src[ii] >> (7 - i)) & 1) << ii;
col2 |= ((src[ii] >> (3 - i)) & 1) << ii;
}
*dst1++ = col1;
*dst2++ = col2;
}
src += 4;
dst1 += 4;
dst2 += 4;
}
}
void Screen_EoB::initPC98PaletteCycle(int palID, PalCycleData *data) {
if (!_use16ColorMode || !_cyclePalette)
return;
if (palID < 0 || palID > 9)
return;
_activePalCycle = data;
int temp = 0;
const uint8 *pal16c = _vm->staticres()->loadRawData(kEoB1Palettes16c, temp);
if (!pal16c)
return;
if (data)
memcpy(_cyclePalette, &pal16c[palID * 48], 48);
else
memset(_cyclePalette, 0, 48);
}
void Screen_EoB::updatePC98PaletteCycle(int brightness) {
if (_activePalCycle) {
for (int i = 0; i < 48; ++i) {
if (--_activePalCycle[i].delay)
continue;
for (int8 in = 32; in == 32; ) {
in = *_activePalCycle[i].data++;
if (in < 16 && in > -16) {
_cyclePalette[i] += in;
_activePalCycle[i].delay = *_activePalCycle[i].data++;
} else if (in < 32) {
_cyclePalette[i] = in - 16;
_activePalCycle[i].delay = *_activePalCycle[i].data++;
} else if (in == 32)
_activePalCycle[i].data += READ_BE_INT16(_activePalCycle[i].data);
}
}
}
uint8 pal[48];
for (int i = 0; i < 48; ++i)
pal[i] = CLIP<int>(_cyclePalette[i] + brightness, 0, 15);
loadPalette(pal, *_palettes[0], 48);
setScreenPalette(*_palettes[0]);
}
SJISFontEoB1PC98::SJISFontEoB1PC98(Common::SharedPtr<Graphics::FontSJIS> &font, /*uint8 shadowColor,*/ const uint16 *convTable1, const uint16 *convTable2) : SJISFont(font, 0, false, false, 0),
/*_shadowColor(shadowColor),*/ _convTable1(convTable1), _convTable2(convTable2), _defaultConv(true) {
assert(_convTable1);
assert(_convTable2);
}
int SJISFontEoB1PC98::getCharWidth(uint16 c) const {
return SJISFont::getCharWidth(convert(c));
}
void SJISFontEoB1PC98::drawChar(uint16 c, byte *dst, int pitch, int) const {
c = convert(c);
_font->setDrawingMode(_style == kStyleLeftShadow ? Graphics::FontSJIS::kShadowLeftMode : Graphics::FontSJIS::kDefaultMode);
_font->toggleFatPrint(false);
_font->drawChar(dst, c, 640, 1, _colorMap[1], _colorMap[0], 640, 400);
}
uint16 SJISFontEoB1PC98::convert(uint16 c) const {
uint8 l = c & 0xFF;
uint8 h = c >> 8;
if (c < 128) {
assert(l > 31);
c = _convTable2[l - 32];
} else if (l > 160 && l < 225) {
bool done = false;
if (_defaultConv) {
if (h == 0xDE) {
if ((l >= 182 && l <= 196) || (l >= 202 && l <= 206)) {
c = _convTable1[l - 182];
done = true;
}
} else if (h == 0xDF) {
if (l >= 202 && l <= 206) {
c = _convTable1[l - 177];
done = true;
}
}
}
if (!done)
c = _convTable2[l - 64];
}
return c;
}
SJISFontEoB2PC98::SJISFontEoB2PC98(Common::SharedPtr<Graphics::FontSJIS> &font, /*uint8 shadowColor,*/ const char *convTable1, const char *convTable2) : SJISFont(font, 0, false, false, 0),
/*_shadowColor(shadowColor),*/ _convTable1(convTable1), _convTable2(convTable2)/*, _defaultConv(true)*/ {
assert(_convTable1);
assert(_convTable2);
}
int SJISFontEoB2PC98::getCharWidth(uint16 c) const {
return SJISFont::getCharWidth(convert(c));
}
void SJISFontEoB2PC98::drawChar(uint16 c, byte *dst, int pitch, int) const {
SJISFont::drawChar(convert(c), dst, pitch, 0);
}
uint16 SJISFontEoB2PC98::convert(uint16 c) const {
uint8 l = c & 0xFF;
uint8 h = c >> 8;
if (h || l < 32 || l == 127) {
return c;
} else if (l < 127) {
c = (l - 32) * 2;
assert(c < 190);
l = _convTable1[c];
h = _convTable1[c + 1];
} else if (l < 212) {
h = l - 64;
l = 0x83;
} else {
c = (l - 212) * 2;
assert(c < 8);
l = _convTable2[c];
h = _convTable2[c + 1];
}
return (h << 8) | l;
}
Font12x12PC98::Font12x12PC98(uint8 shadowColor, const uint16 *convTable1, const uint16 *convTable2, const uint8 *lookupTable) : OldDOSFont(Common::kRenderDefault, 12), _convTable1(convTable1), _convTable2(convTable2) {
assert(convTable1);
assert(convTable2);
assert(lookupTable);
_width = _height = 12;
_numGlyphs = 275;
_bmpOffs = new uint16[_numGlyphs];
for (int i = 0; i < _numGlyphs; ++i)
_bmpOffs[i] = lookupTable[i] * 24;
}
Font12x12PC98::~Font12x12PC98() {
delete[] _bmpOffs;
}
bool Font12x12PC98::load(Common::SeekableReadStream &file) {
unload();
_width = _height = 12;
_numGlyphs = 275;
_bitmapOffsets = _bmpOffs;
_data = new uint8[file.size()];
assert(_data);
file.read(_data, file.size());
if (file.err())
return false;
return true;
}
uint16 Font12x12PC98::convert(uint16 c) const {
uint8 l = c & 0xFF;
uint8 h = c >> 8;
if (c < 128) {
c = _convTable2[l - 32];
} else if (l > 160 && l < 225) {
bool done = false;
if (h == 0xDE) {
if ((l >= 182 && l <= 196) || (l >= 202 && l <= 206)) {
c = _convTable1[l - 182];
done = true;
}
} else if (h == 0xDF) {
if (l >= 202 && l <= 206) {
c = _convTable1[l - 177];
done = true;
}
}
if (!done)
c = _convTable2[l - 64];
}
c = SWAP_BYTES_16(c);
if (c < 0x813F)
c = 1;
else if (c < 0x824F)
c -= 0x813F;
else if (c < 0x833F)
c -= 0x81EE;
else if (c > 0x839F)
c = 1;
else
c -= 0x828D;
return c;
}
PC98Font::PC98Font(uint8 shadowColor, bool useOverlay, int scaleV, const uint8 *convTable1, const char *convTable2, const char *convTable3) : OldDOSFont(Common::kRenderVGA, shadowColor),
_convTable1(convTable1), _convTable2(convTable2), _convTable3(convTable3), _outputWidth(0), _outputHeight(0), _type(convTable1 && convTable2 && convTable3 ? kSJIS : kASCII) {
_numGlyphsMax = 256;
_useOverlay = useOverlay;
_scaleV = scaleV;
}
bool PC98Font::load(Common::SeekableReadStream &file) {
bool res = OldDOSFont::load(file);
_outputWidth = _width;
_outputHeight = _height * _scaleV;
if (_useOverlay) {
_outputWidth >>= 1;
_outputHeight >>= 1;
}
return res;
}
uint16 PC98Font::convert(uint16 c) const {
if (_type == kSJIS)
c = makeTwoByte(c);
if (!_convTable1 || c < 128)
return c;
uint8 lo = c & 0xff;
uint8 hi = c >> 8;
if (lo == 0x81) {
if (hi >= 0x40 && hi <= 0xac)
return _convTable1[hi - 0x40];
} else if (lo == 0x82) {
if (hi >= 0x4f && hi <= 0x58)
return hi - 31;
if (hi >= 0x60 && hi <= 0x79)
return hi - 31;
if (hi >= 0x81 && hi <= 0x9a)
return hi - 32;
} else if (lo == 0x83 && hi >= 0x40 && hi <= 0x93) {
return hi + 64;
}
return 0;
}
uint16 PC98Font::makeTwoByte(uint16 c) const {
if (!_convTable2 || !_convTable3)
return c;
uint8 l = c & 0xFF;
uint8 h = c >> 8;
if (h || l < 32 || l == 127) {
return c;
} else if (l < 127) {
c = (l - 32) * 2;
assert(c < 190);
l = _convTable2[c];
h = _convTable2[c + 1];
} else if (l < 212) {
h = l - 64;
l = 0x83;
} else {
c = (l - 212) * 2;
assert(c < 8);
l = _convTable3[c];
h = _convTable3[c + 1];
}
return (h << 8) | l;
}
} // End of namespace Kyra
#endif // ENABLE_EOB

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,212 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef KYRA_SCREEN_EOB_SEGACD_H
#define KYRA_SCREEN_EOB_SEGACD_H
#ifdef ENABLE_EOB
#define SEGA_PERFORMANCE true
#define SEGA_USE_MEMPOOL true
#include "kyra/graphics/screen_eob.h"
#if SEGA_USE_MEMPOOL
#include "common/memorypool.h"
#endif
namespace Kyra {
class SegaRenderer {
public:
enum Plane {
kPlaneA = 0,
kPlaneB = 1,
kWindowPlane = 2
};
enum WindowMode {
kWinToLeft = 0,
kWinToTop = 0,
kWinToRight = 1,
kWinToBottom = 1
};
enum HScrollMode {
kHScrollFullScreen = 0,
kHScroll8PixelRows,
kHScroll1PixelRows
};
enum VScrollMode {
kVScrollFullScreen = 0,
kVScroll16PixelStrips
};
public:
SegaRenderer(Screen_EoB *screen);
~SegaRenderer();
void setResolution(int w, int h);
void setPlaneTableLocation(int plane, uint16 addr);
// The hardware allows/demands separate modification of the vertical and horizontal properties.
// To allow this without making another function the w/h parameters can be set to -1 which will
// keep the existing value for that property.
void setupPlaneAB(int pixelWidth, int pixelHeigth);
// The hardware allows/demands separate modification of the vertical and horizontal properties.
// To allow this without making another function the blockX/Y parameters can be set to -1 which
// will keep the existing value for that property.
void setupWindowPlane(int blockX, int blockY, int horizontalMode, int verticalMode);
void setHScrollTableLocation(int addr);
void setSpriteTableLocation(int addr);
void setPitch(int pitch);
void setHScrollMode(int mode);
void setVScrollMode(int mode);
void loadToVRAM(const void *data, uint16 dataSize, uint16 addr);
void loadStreamToVRAM(Common::SeekableReadStream *in, uint16 addr, bool compressedData = false);
void memsetVRAM(int addr, uint8 val, int len);
void fillRectWithTiles(int vramArea, int x, int y, int w, int h, uint16 nameTblEntry, bool incr = false, bool topToBottom = false, const uint16 *patternTable = 0);
void writeUint16VSRAM(int addr, uint16 value);
void writeUint8VRAM(int addr, uint8 value);
void writeUint16VRAM(int addr, uint16 value);
void clearPlanes();
void render(int destPageNum, int renderLeft = -1, int renderTop = -1, int renderWidth = -1, int renderHeight = -1, bool spritesOnly = false);
private:
void renderPlanePart(int plane, uint8 *dstBuffer, int x1, int y1, int x2, int y2);
void renderPlaneTile(uint8 *dst, int destX, const uint16 *nameTable, int vScrollLSBStart, int vScrollLSBEnd, int hScrollTableIndex, uint16 pitch);
void renderSpriteTile(uint8 *dst, uint8 *mask, int x, int y, uint16 tile, uint8 pal, bool vflip, bool hflip, bool prio);
#if SEGA_PERFORMANCE
template<bool hflip, bool oddStart, bool oddEnd> void renderLineFragmentM(uint8 *dst, uint8 *mask, const uint8 *src, int start, int end, uint8 pal);
template<bool hflip, bool oddStart, bool oddEnd> void renderLineFragmentD(uint8 *dst, const uint8 *src, int start, int end, uint8 pal);
typedef void(SegaRenderer::*renderFuncM)(uint8*, uint8*, const uint8*, int, int, uint8);
typedef void(SegaRenderer::*renderFuncD)(uint8*, const uint8*, int, int, uint8);
const renderFuncM *_renderLineFragmentM;
const renderFuncD *_renderLineFragmentD;
#else
template<bool hflip> void renderLineFragment(uint8 *dst, uint8 *mask, const uint8 *src, int start, int end, uint8 pal);
#endif
void initPrioRenderTask(uint8 *dst, uint8 *mask, const uint8 *src, int start, int end, uint8 pal, bool hflip);
void clearPrioChain();
struct SegaPlane {
SegaPlane() : blockX(0), blockY(0), w(0), h(0), mod(0), nameTable(0), nameTableSize(0) {}
int blockX, blockY;
uint16 w, h, mod;
uint16 *nameTable;
uint16 nameTableSize;
};
SegaPlane _planes[3];
uint8 *_vram;
uint16 *_vsram;
uint16 *_hScrollTable;
uint16 *_spriteTable;
uint8 *_spriteMask;
uint8 _hScrollMode;
uint8 _vScrollMode;
uint16 _pitch;
uint16 _numSpritesMax;
struct PrioTileRenderObj {
PrioTileRenderObj(PrioTileRenderObj *chainEnd, uint8 *dst, uint8 *mask, const uint8 *src, int start, int end, uint8 pal, bool hflip) :
_pred(chainEnd), _next(0), _dst(dst), _mask(mask), _src(src), _start(start), _end(end), _pal(pal), _hflip(hflip) {
if (_pred)
_pred->_next = this;
}
uint8 *_dst;
uint8 *_mask;
const uint8 *_src;
int _start;
int _end;
uint8 _pal;
bool _hflip;
PrioTileRenderObj *_pred;
PrioTileRenderObj *_next;
};
#if SEGA_USE_MEMPOOL
Common::ObjectPool<PrioTileRenderObj> _prioRenderMemPool;
#endif
PrioTileRenderObj *_prioChainStart, *_prioChainEnd;
uint16 _screenW, _screenH, _blocksW, _blocksH;
Screen_EoB *_screen;
};
class SegaAnimator {
public:
SegaAnimator(SegaRenderer *renderer);
~SegaAnimator();
void initSprite(int id, int16 x, int16 y, uint16 nameTbl, uint16 hw);
void clearSprites();
void moveMorphSprite(int id, uint16 nameTbl, int16 addX, int16 addY);
void moveSprites(int id, uint16 num, int16 addX, int16 addY);
void moveSprites2(int id, uint16 num, int16 addX, int16 addY);
void update();
private:
struct Sprite {
int16 x;
int16 y;
uint16 nameTbl;
uint16 hw;
};
uint16 *_tempBuffer;
Sprite *_sprites;
SegaRenderer *_renderer;
bool _needUpdate;
};
class ScrollManager {
public:
ScrollManager(SegaRenderer *renderer);
~ScrollManager();
void setVScrollTimers(uint16 destA, int incrA, int delayA, uint16 destB, int incrB, int delayB);
void setHScrollTimers(uint16 destA, int incrA, int delayA, uint16 destB, int incrB, int delayB);
void updateScrollTimers();
void fastForward();
private:
struct ScrollTimer {
ScrollTimer() : _offsCur(0), _offsDest(0), _incr(0), _delay(0), _timer(0) {}
int16 _offsCur;
int16 _offsDest;
int16 _incr;
int16 _delay;
int16 _timer;
};
ScrollTimer *_vScrollTimers;
ScrollTimer *_hScrollTimers;
SegaRenderer *_renderer;
};
} // End of namespace Kyra
#endif // ENABLE_EOB
#endif

View File

@@ -0,0 +1,173 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifdef ENABLE_EOB
#include "kyra/resource/resource.h"
#include "graphics/sjis.h"
namespace Kyra {
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;
}
SJISFontLarge::SJISFontLarge(Common::SharedPtr<Graphics::FontSJIS> &font) : SJISFont(font, 0, false, false, 0) {
}
int SJISFontLarge::getHeight() const {
return _font->getFontHeight();
}
int SJISFontLarge::getWidth() const {
return _font->getMaxFontWidth();
}
int SJISFontLarge::getCharWidth(uint16 c) const {
if (c <= 0x7F || (c >= 0xA1 && c <= 0xDF))
return _font->getCharWidth('a');
else
return getWidth();
}
void SJISFontLarge::drawChar(uint16 c, byte *dst, int pitch, int) const {
_font->setDrawingMode(Graphics::FontSJIS::kDefaultMode);
_font->toggleFatPrint(false);
_font->drawChar(dst, c, 320, 1, _colorMap[1], _colorMap[0], 320, 200);
}
SJISFont12x12::SJISFont12x12(const uint16 *searchTable) : _height(6), _width(6), _data(0), _colorMap(nullptr) {
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 {
if (!_searchTable.contains(c))
return;
const uint8 *src = _data + (_searchTable[c] - 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

View File

@@ -0,0 +1,112 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "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;
}
}
void ChineseOneByteFontHOF::processColorMap() {
_textColor[0] = _colorMap[1];
_textColor[1] = _colorMap[0] | (_colorMap[0] << 8);
_pixelColorShading = false;
}
uint32 ChineseTwoByteFontHOF::getFontOffset(uint16 c) const {
c = ((c & 0x7F00) >> 2) | (c & 0x3F);
return c * 28;
}
void ChineseTwoByteFontHOF::processColorMap() {
_textColor[0] = TO_LE_16(_colorMap[1] | ((_colorMap[1] + 1) << 8));
_textColor[1] = _colorMap[0] | (_colorMap[0] << 8);
_pixelColorShading = !(_colorMap[1] == 207 || _colorMap[1] > 240);
}
} // End of namespace Kyra

View File

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

View File

@@ -0,0 +1,679 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "kyra/graphics/screen_lok.h"
#include "kyra/engine/kyra_lok.h"
#include "common/system.h"
#include "graphics/paletteman.h"
namespace Kyra {
Screen_LoK::Screen_LoK(KyraEngine_LoK *vm, OSystem *system)
: Screen(vm, system, _screenDimTable, _screenDimTableCount) {
_vm = vm;
_unkPtr1 = _unkPtr2 = nullptr;
_bitBlitNum = 0;
memset(_saveLoadPage, 0, sizeof(_saveLoadPage));
memset(_saveLoadPageOvl, 0, sizeof(_saveLoadPageOvl));
}
Screen_LoK::~Screen_LoK() {
for (int i = 0; i < ARRAYSIZE(_saveLoadPage); ++i) {
delete[] _saveLoadPage[i];
_saveLoadPage[i] = nullptr;
}
for (int i = 0; i < ARRAYSIZE(_saveLoadPageOvl); ++i) {
delete[] _saveLoadPageOvl[i];
_saveLoadPageOvl[i] = nullptr;
}
delete[] _unkPtr1;
delete[] _unkPtr2;
}
bool Screen_LoK::init() {
if (!Screen::init())
return false;
for (uint i = 0; i < ARRAYSIZE(_bitBlitRects); i++) {
_bitBlitRects[i].left = 0;
_bitBlitRects[i].top = 0;
_bitBlitRects[i].right = 0;
_bitBlitRects[i].bottom = 0;
}
_bitBlitNum = 0;
memset(_saveLoadPage, 0, sizeof(_saveLoadPage));
memset(_saveLoadPageOvl, 0, sizeof(_saveLoadPageOvl));
_unkPtr1 = new uint8[getRectSize(1, 144)]();
assert(_unkPtr1);
_unkPtr2 = new uint8[getRectSize(1, 144)]();
assert(_unkPtr2);
return true;
}
void Screen_LoK::loadBitmap(const char *filename, int tempPage, int dstPage, Palette *pal, bool skip) {
const char *ext = filename + strlen(filename) - 3;
Screen::loadBitmap(filename, tempPage, dstPage, pal, skip);
if (_isAmiga) {
if (!scumm_stricmp(ext, "MSC"))
Screen::convertAmigaMsc(getPagePtr(dstPage));
else
Screen::convertAmigaGfx(getPagePtr(dstPage), 320, 200);
}
}
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));
updateBackendScreen(true);
}
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] = nullptr;
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] = nullptr;
}
}
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] = nullptr;
if (_saveLoadPageOvl[page / 2]) {
delete[] _saveLoadPageOvl[page / 2];
_saveLoadPageOvl[page / 2] = nullptr;
}
}
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::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_LoK::postProcessCursor(uint8 *data, int width, int height, int pitch) {
if (_vm->gameFlags().platform == Common::kPlatformAmiga && _dualPaletteModeSplitY) {
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
updateBackendScreen(true);
_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);
}
ChineseOneByteFontLoK::ChineseOneByteFontLoK(int pitch) : ChineseFont(pitch, 8, 14, 9, 17, 0, 0) {
_border = _pixelColorShading = false;
}
void ChineseOneByteFontLoK::processColorMap() {
_textColor[0] = _colorMap[1];
_textColor[1] = _colorMap[0];
}
ChineseTwoByteFontLoK::ChineseTwoByteFontLoK(int pitch, const uint16 *lookupTable, uint32 lookupTableSize) : ChineseFont(pitch, 15, 14, 18, 17, 0, 3),
_lookupTable(lookupTable), _lookupTableSize(lookupTableSize) {
assert(lookupTable);
}
bool ChineseTwoByteFontLoK::hasGlyphForCharacter(uint16 c) const {
for (uint32 i = 0; i < _lookupTableSize; ++i) {
if (_lookupTable[i] == c)
return true;
}
return false;
}
uint32 ChineseTwoByteFontLoK::getFontOffset(uint16 c) const {
for (uint32 i = 0; i < _lookupTableSize; ++i) {
if (_lookupTable[i] == c)
return i * 28;
}
return 0;
}
void ChineseTwoByteFontLoK::processColorMap() {
_border = (_colorMap[0] == 12);
uint8 cs = _colorMap[1];
if (_colorMap[1] == 9)
cs = 83;
else if (_colorMap[1] == 5)
cs = 207;
else if (_colorMap[1] == 2)
cs = 74;
else if (_colorMap[1] == 15)
cs = 161;
else if (_colorMap[1] > 15 && _colorMap[1] < 248)
cs += 1;
_textColor[0] = _colorMap[1] | (cs << 8);
_textColor[0] = TO_LE_16(_textColor[0]);
_textColor[1] = _colorMap[0] | (_colorMap[0] << 8);
}
JohabFontLoK::JohabFontLoK(Font *&font8fat, const uint16 *lookupTable, uint32 lookupTableSize) : _font8fat(font8fat), _height(15), _width(15), _fileData(0), _colorMap(0), _glyphTemp(0) {
assert(lookupTable);
assert(lookupTableSize == 224);
for (int i = 0; i < 7; ++i)
_2byteTables[i] = &lookupTable[i << 5];
memset(_glyphData, 0, sizeof(_glyphData));
_glyphTemp = new uint8[30];
}
JohabFontLoK::~JohabFontLoK() {
delete[] _fileData;
delete[] _glyphTemp;
}
bool JohabFontLoK::load(Common::SeekableReadStream &data) {
if (_fileData)
return false;
if (!data.size())
return false;
uint32 fileSize = data.size();
if (fileSize != (kNumJongseong + kNumJungseong + kNumChoseong) * 30) {
warning("HangulFontLoK::load(): Invalid font file size '%d' (expected: '%d').", fileSize, (kNumJongseong + kNumJungseong + kNumChoseong) * 30);
return false;
}
uint8 *dst = new uint8[fileSize];
if (!dst)
return false;
data.read(dst, fileSize);
_fileData = dst;
_glyphData[0] = _fileData;
_glyphData[1] = _glyphData[0] + (kNumJongseong * 30);
_glyphData[2] = _glyphData[1] + (kNumJungseong * 30);
return true;
}
int JohabFontLoK::getCharWidth(uint16 c) const {
assert(_font8fat);
return (c >= 0x80) ? _width + 1 : _font8fat->getCharWidth(c);
}
int JohabFontLoK::getCharHeight(uint16 c) const {
return _colorMap[3] ? _height + 2 : _height;
}
void JohabFontLoK::setColorMap(const uint8 *src) {
_colorMap = src;
assert(_font8fat);
_font8fat->setColorMap(src);
}
void JohabFontLoK::drawChar(uint16 c, byte *dst, int pitch, int) const {
if (c < 0x80) {
assert(_font8fat);
_font8fat->drawChar(c, dst + (c == '\"' ? 0 : 5) * pitch, pitch, 0);
return;
}
const uint8 *glyph = createGlyph(c);
dst += (pitch + 1);
if (_colorMap[3]) {
renderGlyph(dst - 1, glyph, _colorMap[3], pitch);
renderGlyph(dst + 1, glyph, _colorMap[3], pitch);
renderGlyph(dst - pitch, glyph, _colorMap[3], pitch);
renderGlyph(dst + pitch, glyph, _colorMap[3], pitch);
}
renderGlyph(dst, glyph, _colorMap[1], pitch);
}
const uint8 *JohabFontLoK::createGlyph(uint16 chr) const {
memset(_glyphTemp, 0, 30);
uint16 t[3];
memset(t, 0, sizeof(t));
chr = (chr << 8) | (chr >> 8);
uint8 i1 = chr & 0x1f;
uint8 i2 = (chr >> 5) & 0x1f;
uint8 i3 = (chr >> 10) & 0x1f;
// determine jungseong glyph part
uint16 r1 = _2byteTables[1][i2];
if ((int16)r1 > 0)
r1 += (_2byteTables[3][i3] + _2byteTables[6][i1] - 3);
// determine jongseong glyph part
uint16 r2 = _2byteTables[0][i3];
if ((int16)r2 > 0)
r2 += (_2byteTables[4][i2] + _2byteTables[6][i1]);
// determine choseong glyph part
uint16 r3 = _2byteTables[2][i1];
if ((int16)r3 > 0)
r3 += (_2byteTables[5][i2] - 3);
t[0] = r2 >> 5;
t[1] = (r1 >> 5) - 2;
t[2] = (r3 >> 5) - 2;
const uint8 lim[3] = { kNumJongseong, kNumJungseong, kNumChoseong };
for (int l = 0; l < 3; ++l) {
if (t[l] <= lim[l]) {
const uint8 *src = &_glyphData[l][t[l] * 30];
for (int i = 0; i < 30; ++i)
_glyphTemp[i] |= *src++;
}
}
return _glyphTemp;
}
void JohabFontLoK::renderGlyph(byte *dst, const uint8 *glyph, uint8 col, int pitch) const {
const uint8 *src = glyph;
pitch -= 15;
for (int y = 0; y < _height; ++y) {
uint8 m = 0;
uint8 in = 0;
for (int x = 0; x < _width; ++x) {
if (m == 0) {
in = *src++;
m = 0x80;
}
if (in & m)
*dst = col;
dst++;
m >>= 1;
}
dst += pitch;
}
}
} // End of namespace Kyra

View File

@@ -0,0 +1,113 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef 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);
~Screen_LoK() override;
bool init() override;
void loadBitmap(const char *filename, int tempPage, int dstPage, Palette *pal, bool skip = false) override;
int getRectSize(int w, int h) override;
void setTextColorMap(const uint8 *cmap) override;
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
void setInterfacePalette(const Palette &pal, uint8 r, uint8 g, uint8 b);
void postProcessCursor(uint8 *data, int width, int height, int pitch) override;
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) override;
void fadePalette(const Palette &pal, int delay, const UpdateFunctor *upFunc = 0) override;
void getFadeParams(const Palette &pal, int delay, int &delayInc, int &diff) override;
int fadePalStep(const Palette &pal, int diff) override;
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) override {
convertTo16Colors(data, width, height, pitch, _cursorColorKey);
}
void mergeOverlay(int x, int y, int w, int h) override;
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

View File

@@ -0,0 +1,904 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifdef ENABLE_LOL
#include "kyra/graphics/screen_lol.h"
#include "kyra/engine/lol.h"
#include "common/system.h"
#include "graphics/paletteman.h"
namespace Kyra {
Screen_LoL::Screen_LoL(LoLEngine *vm, OSystem *system) : Screen_v2(vm, system, vm->gameFlags().use16ColorMode ? _screenDimTable16C : vm->gameFlags().lang == Common::Language::ZH_TWN ? _screenDimTableZH : _screenDimTable256C, _screenDimTableCount) {
_paletteOverlay1 = new uint8[0x100]();
_paletteOverlay2 = new uint8[0x100]();
_grayOverlay = new uint8[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;
bool oddWidth = w & 1;
w >>= 1;
int w2 = w;
while (h--) {
if (w) {
while (w--) {
*(p + tmp) = col;
p += 2;
}
}
if (oddWidth) {
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;
}
}
void ChineseOneByteFontLoL::processColorMap() {
_textColor[0] = _colorMap[1];
_textColor[1] = _colorMap[0];
}
uint32 ChineseTwoByteFontLoL::getFontOffset(uint16 c) const {
c = ((c & 0x7F00) >> 2) | (c & 0x3F);
return c * 28;
}
void ChineseTwoByteFontLoL::processColorMap() {
_textColor[0] = _colorMap[1];
_textColor[1] = _colorMap[0];
}
} // End of namespace Kyra
#endif // ENABLE_LOL

View File

@@ -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 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#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() override;
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) override;
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 ScreenDim _screenDimTableZH[];
static const int _screenDimTableCount;
uint8 *_levelOverlays[8];
void mergeOverlay(int x, int y, int w, int h) override;
void postProcessCursor(uint8 *data, int width, int height, int pitch) override;
};
} // End of namespace Kyra
#endif
#endif // ENABLE_LOL

View File

@@ -0,0 +1,156 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "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), _interfaceCommandLineY1(vm->gameFlags().extraLang != Common::UNK_LANG ? 185 : 188) {
}
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 >= 187) {
y = 187;
// The original actually limits the _maskMin/MaxY check to cases where y has already been clipped to 187.
// Whether this was intentional or not: Scenes actually require that we do it that way or animations may
// be drawn on the wrong layer (bug #11312).
if (y < _maskMinY || y > _maskMaxY)
return 15;
}
uint8 pixel = *(getCPagePtr(5) + y * 320 + x);
pixel &= 0x7F;
pixel >>= 3;
return CLIP<uint8>(pixel, 1, 15);
}
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);
}
void ChineseOneByteFontMR::processColorMap() {
_textColor[0] = _colorMap[1] | (_colorMap[1] << 8);
if (_textColor[0]) {
_textColor[0] -= 0x100;
if (_colorMap[1] == 0xFF)
_textColor[0] -= 0x100;
}
_textColor[0] = TO_LE_16(_textColor[0]);
_textColor[1] = _colorMap[0] | (_colorMap[0] << 8);
}
uint32 ChineseTwoByteFontMR::getFontOffset(uint16 c) const {
c = ((c & 0x7F00) >> 2) | (c & 0x3F);
return c * 28;
}
void ChineseTwoByteFontMR::processColorMap() {
_textColor[0] = _colorMap[1] | (_colorMap[1] << 8);
if (_textColor[0]) {
_textColor[0] -= 0x100;
if (_colorMap[1] == 0xFF)
_textColor[0] -= 0x100;
}
_textColor[0] = TO_LE_16(_textColor[0]);
_textColor[1] = _colorMap[0] | (_colorMap[0] << 8);
}
} // End of namespace Kyra

View File

@@ -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 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef 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() override;
int getLayer(int x, int y) override;
byte getShapeFlag1(int x, int y) override;
byte getShapeFlag2(int x, int y) override;
int getDrawLayer(int x, int y) override;
int getDrawLayer2(int x, int y, int height) override;
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;
const int _interfaceCommandLineY1;
};
} // End of namespace Kyra
#endif

View File

@@ -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 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "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(nullptr) {
_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 nullptr;
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 nullptr;
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 nullptr;
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;
return CLIP<uint8>(pixel, 1, 15);
}
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

View File

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

View File

@@ -0,0 +1,662 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
// Player for Kyrandia 3 VQA movies, based on the information found at
// https://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/paletteman.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() : _fileStream(nullptr), _frameInfo(nullptr) {
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 = nullptr;
// 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]();
_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_RETURN_TO_LAUNCHER:
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);
}
_screen->updateBackendScreen(true);
_system->delayMillis(10);
}
}
}
} // End of namespace Kyra

160
engines/kyra/graphics/vqa.h Normal file
View File

@@ -0,0 +1,160 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef 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();
~VQADecoder() override;
bool loadStream(Common::SeekableReadStream *stream) override;
void readNextPacket() override;
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() override;
void handleSND0(Common::SeekableReadStream *stream);
void handleSND1(Common::SeekableReadStream *stream);
void handleSND2(Common::SeekableReadStream *stream);
protected:
Audio::AudioStream *getAudioStream() const override;
private:
Audio::QueuingAudioStream *_audioStream;
};
class VQAVideoTrack : public FixedRateVideoTrack {
public:
VQAVideoTrack(const VQAHeader *header);
~VQAVideoTrack() override;
uint16 getWidth() const override;
uint16 getHeight() const override;
Graphics::PixelFormat getPixelFormat() const override;
int getCurFrame() const override;
int getFrameCount() const override;
const Graphics::Surface *decodeNextFrame() override;
void setHasDirtyPalette();
bool hasDirtyPalette() const override;
const byte *getPalette() const override;
void handleVQFR(Common::SeekableReadStream *stream);
protected:
Common::Rational getFrameRate() const override;
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

View File

@@ -0,0 +1,458 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "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(nullptr), _frameOffsTable(nullptr),
_offscreenBuffer(nullptr), _deltaBuffer(nullptr), _currentFrame(0), _numFrames(0), _width(0), _height(0), _flags(0), _deltaBufferSize(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 = nullptr;
_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]();
}
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]();
// 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 = nullptr;
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(nullptr) {}
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 = nullptr;
}
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 = nullptr;
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 = nullptr;
_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]();
}
if (_numFrames & 0x8000) {
warning("Unhandled wsa flags 0x80");
_flags |= 0x80;
_numFrames &= 0x7FFF;
}
_currentFrame = _numFrames;
_deltaBuffer = new uint8[_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

View File

@@ -0,0 +1,133 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef 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);
~WSAMovie_v1() override;
int width() const override { return _width; }
int height() const override { return _height; }
int open(const char *filename, int offscreen, Palette *palette) override;
void close() override;
int frames() override { return _opened ? _numFrames : -1; }
void displayFrame(int frameNum, int pageNum, int x, int y, uint16 flags, const uint8 *table1, const uint8 *table2) override;
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) override;
void close() override;
void displayFrame(int frameNum, int pageNum, int x, int y, uint16 flags, const uint8 *table1, const uint8 *table2) override;
private:
void processFrame(int frameNum, uint8 *dst) override;
uint8 *_buffer;
};
class WSAMovie_v2 : public WSAMovie_v1 {
public:
WSAMovie_v2(KyraEngine_v1 *vm);
int open(const char *filename, int unk1, Palette *palette) override;
void displayFrame(int frameNum, int pageNum, int x, int y, uint16 flags, const uint8 *table1, const uint8 *table2) override {
WSAMovie_v1::displayFrame(frameNum, pageNum, x + _xAdd, y + _yAdd, flags, table1, table2);
}
int xAdd() const override { return _xAdd; }
int yAdd() const override { return _yAdd; }
void setWidth(int w) { _width = w; }
void setHeight(int h) { _height = h; }
protected:
int16 _xAdd;
int16 _yAdd;
};
} // End of namespace Kyra
#endif