Initial commit
This commit is contained in:
2
engines/sky/POTFILES
Normal file
2
engines/sky/POTFILES
Normal file
@@ -0,0 +1,2 @@
|
||||
engines/sky/compact.cpp
|
||||
engines/sky/metaengine.cpp
|
||||
280
engines/sky/autoroute.cpp
Normal file
280
engines/sky/autoroute.cpp
Normal file
@@ -0,0 +1,280 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#include "common/endian.h"
|
||||
#include "common/textconsole.h"
|
||||
#include "common/util.h"
|
||||
|
||||
#include "sky/autoroute.h"
|
||||
#include "sky/compact.h"
|
||||
#include "sky/grid.h"
|
||||
#include "sky/skydefs.h"
|
||||
#include "sky/struc.h"
|
||||
|
||||
namespace Sky {
|
||||
|
||||
#define ROUTE_GRID_WIDTH ((GAME_SCREEN_WIDTH/8)+2)
|
||||
#define ROUTE_GRID_HEIGHT ((GAME_SCREEN_HEIGHT/8)+2)
|
||||
#define ROUTE_GRID_SIZE (ROUTE_GRID_WIDTH*ROUTE_GRID_HEIGHT*2)
|
||||
#define WALK_JUMP 8 // walk in blocks of 8
|
||||
|
||||
const int16 AutoRoute::_routeDirections[4] = { -1, 1, -ROUTE_GRID_WIDTH, ROUTE_GRID_WIDTH };
|
||||
const uint16 AutoRoute::_logicCommands[4] = { RIGHTY, LEFTY, DOWNY, UPY };
|
||||
|
||||
AutoRoute::AutoRoute(Grid *pGrid, SkyCompact *compact) {
|
||||
_grid = pGrid;
|
||||
_skyCompact = compact;
|
||||
_routeGrid = (uint16 *)malloc(ROUTE_GRID_SIZE);
|
||||
_routeBuf = (uint16 *)malloc(ROUTE_SPACE);
|
||||
}
|
||||
|
||||
AutoRoute::~AutoRoute() {
|
||||
free(_routeGrid);
|
||||
free(_routeBuf);
|
||||
}
|
||||
|
||||
uint16 AutoRoute::checkBlock(uint16 *blockPos) {
|
||||
uint16 retVal = 0xFFFF;
|
||||
|
||||
for (uint8 cnt = 0; cnt < 4; cnt++) {
|
||||
uint16 fieldVal = *(blockPos + _routeDirections[cnt]);
|
||||
if (fieldVal && (fieldVal < retVal))
|
||||
retVal = fieldVal;
|
||||
}
|
||||
return retVal;
|
||||
}
|
||||
|
||||
void AutoRoute::clipCoordX(uint16 x, uint8 &blkX, int16 &initX) {
|
||||
if (x < TOP_LEFT_X) {
|
||||
blkX = 0;
|
||||
initX = x - TOP_LEFT_X;
|
||||
} else if (x >= TOP_LEFT_X + GAME_SCREEN_WIDTH) {
|
||||
blkX = (GAME_SCREEN_WIDTH - 1) >> 3;
|
||||
initX = x - (TOP_LEFT_X + GAME_SCREEN_WIDTH - 1);
|
||||
} else {
|
||||
blkX = (x - TOP_LEFT_X) >> 3;
|
||||
initX = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void AutoRoute::clipCoordY(uint16 y, uint8 &blkY, int16 &initY) {
|
||||
if (y < TOP_LEFT_Y) {
|
||||
blkY = 0;
|
||||
initY = y - TOP_LEFT_Y;
|
||||
} else if (y >= TOP_LEFT_Y + GAME_SCREEN_HEIGHT) {
|
||||
blkY = (GAME_SCREEN_HEIGHT - 1) >> 3;
|
||||
initY = y - (TOP_LEFT_Y + GAME_SCREEN_HEIGHT);
|
||||
} else {
|
||||
blkY = (y - TOP_LEFT_Y) >> 3;
|
||||
initY = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void AutoRoute::initWalkGrid(uint8 screen, uint8 width) {
|
||||
uint16 *wGridPos;
|
||||
uint8 stretch = 0;
|
||||
uint8 *screenGrid = _grid->giveGrid(screen);
|
||||
screenGrid += GRID_SIZE;
|
||||
wGridPos = _routeGrid + (ROUTE_GRID_SIZE >> 1) - ROUTE_GRID_WIDTH - 2;
|
||||
|
||||
memset(_routeGrid, 0, ROUTE_GRID_SIZE);
|
||||
uint8 bitsLeft = 0; uint32 gridData = 0;
|
||||
for (uint8 gridCntY = 0; gridCntY < ROUTE_GRID_HEIGHT - 2; gridCntY++) {
|
||||
for (uint8 gridCntX = 0; gridCntX < ROUTE_GRID_WIDTH - 2; gridCntX++) {
|
||||
if (!bitsLeft) {
|
||||
screenGrid -= 4;
|
||||
gridData = READ_LE_UINT32(screenGrid);
|
||||
bitsLeft = 32;
|
||||
}
|
||||
if (gridData & 1) {
|
||||
*wGridPos = 0xFFFF; // block is not accessible
|
||||
stretch = width;
|
||||
} else if (stretch) {
|
||||
*wGridPos = 0xFFFF;
|
||||
stretch--;
|
||||
}
|
||||
wGridPos--;
|
||||
bitsLeft--;
|
||||
gridData >>= 1;
|
||||
}
|
||||
wGridPos -= 2;
|
||||
stretch = 0;
|
||||
}
|
||||
}
|
||||
|
||||
bool AutoRoute::calcWalkGrid(uint8 startX, uint8 startY, uint8 destX, uint8 destY) {
|
||||
int16 directionX, directionY;
|
||||
uint8 roiX, roiY; // Rectangle Of Interest in the walk grid
|
||||
if (startY > destY) {
|
||||
directionY = -ROUTE_GRID_WIDTH;
|
||||
roiY = startY;
|
||||
} else {
|
||||
directionY = ROUTE_GRID_WIDTH;
|
||||
roiY = (ROUTE_GRID_HEIGHT-1) - startY;
|
||||
}
|
||||
if (startX > destX) {
|
||||
directionX = -1;
|
||||
roiX = startX + 2;
|
||||
} else {
|
||||
directionX = 1;
|
||||
roiX = (ROUTE_GRID_WIDTH - 1) - startX;
|
||||
}
|
||||
|
||||
uint16 *walkDest = _routeGrid + (destY + 1) * ROUTE_GRID_WIDTH + destX + 1;
|
||||
uint16 *walkStart = _routeGrid + (startY + 1) * ROUTE_GRID_WIDTH + startX + 1;
|
||||
*walkStart = 1;
|
||||
|
||||
// if we are on the edge, move diagonally from start
|
||||
if (roiY < ROUTE_GRID_HEIGHT-3)
|
||||
walkStart -= directionY;
|
||||
|
||||
if (roiX < ROUTE_GRID_WIDTH-2)
|
||||
walkStart -= directionX;
|
||||
|
||||
bool gridChanged = true;
|
||||
bool foundRoute = false;
|
||||
|
||||
while ((!foundRoute) && gridChanged) {
|
||||
gridChanged = false;
|
||||
uint16 *yWalkCalc = walkStart;
|
||||
for (uint8 cnty = 0; cnty < roiY; cnty++) {
|
||||
uint16 *xWalkCalc = yWalkCalc;
|
||||
for (uint8 cntx = 0; cntx < roiX; cntx++) {
|
||||
if (!*xWalkCalc) { // block wasn't done, yet
|
||||
uint16 blockRet = checkBlock(xWalkCalc);
|
||||
if (blockRet < 0xFFFF) {
|
||||
*xWalkCalc = blockRet + 1;
|
||||
gridChanged = true;
|
||||
}
|
||||
}
|
||||
xWalkCalc += directionX;
|
||||
}
|
||||
yWalkCalc += directionY;
|
||||
}
|
||||
if (*walkDest) { // okay, finished
|
||||
foundRoute = true;
|
||||
} else { // we couldn't find the route, let's extend the ROI
|
||||
if (roiY < ROUTE_GRID_HEIGHT - 4) {
|
||||
walkStart -= directionY;
|
||||
roiY++;
|
||||
}
|
||||
if (roiX < ROUTE_GRID_WIDTH - 4) {
|
||||
walkStart -= directionX;
|
||||
roiX++;
|
||||
}
|
||||
}
|
||||
}
|
||||
return foundRoute;
|
||||
}
|
||||
|
||||
uint16 *AutoRoute::makeRouteData(uint8 startX, uint8 startY, uint8 destX, uint8 destY) {
|
||||
memset(_routeBuf, 0, ROUTE_SPACE);
|
||||
|
||||
uint16 *routePos = _routeGrid + (destY + 1) * ROUTE_GRID_WIDTH + destX + 1;
|
||||
uint16 *dataTrg = _routeBuf + (ROUTE_SPACE >> 1) - 2;
|
||||
|
||||
uint16 lastVal = (*routePos) - 1;
|
||||
while (lastVal) { // lastVal == 0 means route is done.
|
||||
dataTrg -= 2;
|
||||
|
||||
int16 walkDirection = 0;
|
||||
for (uint8 cnt = 0; cnt < 4; cnt++)
|
||||
if (lastVal == *(routePos + _routeDirections[cnt])) {
|
||||
*(dataTrg + 1) = _logicCommands[cnt];
|
||||
walkDirection = _routeDirections[cnt];
|
||||
break;
|
||||
}
|
||||
|
||||
if (!walkDirection)
|
||||
error("makeRouteData:: can't find way through walkGrid (pos %d)", lastVal);
|
||||
while (lastVal && (lastVal == *(routePos + walkDirection))) {
|
||||
*dataTrg += WALK_JUMP;
|
||||
lastVal--;
|
||||
routePos += walkDirection;
|
||||
}
|
||||
}
|
||||
return dataTrg;
|
||||
}
|
||||
|
||||
uint16 *AutoRoute::checkInitMove(uint16 *data, int16 initStaX) {
|
||||
if (initStaX < 0) {
|
||||
data -= 2;
|
||||
*(data + 1) = RIGHTY;
|
||||
*data = ((-initStaX) + 7) & 0xFFF8;
|
||||
} else if (initStaX > 0) {
|
||||
data -= 2;
|
||||
*(data + 1) = LEFTY;
|
||||
*data = (initStaX + 7) & 0xFFF8;
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
uint16 AutoRoute::autoRoute(Compact *cpt) {
|
||||
uint8 cptScreen = (uint8)cpt->screen;
|
||||
uint8 cptWidth = (uint8)SkyCompact::getMegaSet(cpt)->gridWidth;
|
||||
initWalkGrid(cptScreen, cptWidth);
|
||||
|
||||
uint8 startX, startY, destX, destY;
|
||||
int16 initStaX, initStaY, initDestX, initDestY;
|
||||
|
||||
clipCoordX(cpt->xcood, startX, initStaX);
|
||||
clipCoordY(cpt->ycood, startY, initStaY);
|
||||
clipCoordX(cpt->arTargetX, destX, initDestX);
|
||||
clipCoordY(cpt->arTargetY, destY, initDestY);
|
||||
|
||||
uint16 *routeDest = (uint16 *)_skyCompact->fetchCpt(cpt->animScratchId);
|
||||
memset(routeDest, 0, 64);
|
||||
if ((startX == destX) && (startY == destY))
|
||||
return 2;
|
||||
|
||||
if (_routeGrid[(destY + 1) * ROUTE_GRID_WIDTH + destX + 1]) {
|
||||
//if ((cpt == &Sky::SkyCompact::foster) && (cptScreen == 12) && (destX == 2) && (destY == 14)) {
|
||||
if (_skyCompact->cptIsId(cpt, CPT_FOSTER) && (cptScreen == 12) && (destX == 2) && (destY == 14)) {
|
||||
/* workaround for Script bug #1804
|
||||
In screen 12 (the pipe factory) Joey can block Foster's target
|
||||
coordinates (2/14). This is normally not too tragic, but in the
|
||||
scene when foster gets thrown out by Lamb (first time you enter
|
||||
the pipe factory), the game would enter an infinite loop. */
|
||||
_routeGrid[(destY + 1) * ROUTE_GRID_WIDTH + destX + 1] = 0;
|
||||
// hide this part joey from the grid
|
||||
} else
|
||||
return 1; // AR destination is an unaccessible block
|
||||
}
|
||||
|
||||
if (!calcWalkGrid(startX, startY, destX, destY))
|
||||
return 1; // can't find route to block
|
||||
|
||||
uint16 *routeData = makeRouteData(startX, startY, destX, destY);
|
||||
// the route is done.
|
||||
// if there was an initial x movement (due to clipping) tag it onto the start
|
||||
routeData = checkInitMove(routeData, initStaX);
|
||||
|
||||
uint8 cnt = 0;
|
||||
do {
|
||||
routeDest[cnt] = routeData[cnt];
|
||||
routeDest[cnt + 1] = routeData[cnt + 1];
|
||||
cnt += 2;
|
||||
} while (routeData[cnt - 2]);
|
||||
return 0;
|
||||
}
|
||||
|
||||
} // End of namespace Sky
|
||||
57
engines/sky/autoroute.h
Normal file
57
engines/sky/autoroute.h
Normal file
@@ -0,0 +1,57 @@
|
||||
/* 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 SKY_AUTOROUTE_H
|
||||
#define SKY_AUTOROUTE_H
|
||||
|
||||
|
||||
#include "common/scummsys.h"
|
||||
|
||||
namespace Sky {
|
||||
|
||||
struct Compact;
|
||||
class Grid;
|
||||
class SkyCompact;
|
||||
|
||||
class AutoRoute {
|
||||
public:
|
||||
AutoRoute(Grid *pGrid, SkyCompact *compact);
|
||||
~AutoRoute();
|
||||
uint16 autoRoute(Compact *cpt);
|
||||
private:
|
||||
uint16 checkBlock(uint16 *blockPos);
|
||||
void clipCoordX(uint16 x, uint8 &blkX, int16 &initX);
|
||||
void clipCoordY(uint16 y, uint8 &blkY, int16 &initY);
|
||||
void initWalkGrid(uint8 screen, uint8 width);
|
||||
bool calcWalkGrid(uint8 startX, uint8 startY, uint8 destX, uint8 destY);
|
||||
uint16 *makeRouteData(uint8 startX, uint8 startY, uint8 destX, uint8 destY);
|
||||
uint16 *checkInitMove(uint16 *data, int16 initStaX);
|
||||
Grid *_grid;
|
||||
SkyCompact *_skyCompact;
|
||||
uint16 *_routeGrid;
|
||||
uint16 *_routeBuf;
|
||||
static const int16 _routeDirections[4];
|
||||
static const uint16 _logicCommands[4];
|
||||
};
|
||||
|
||||
} // End of namespace Sky
|
||||
|
||||
#endif // AUTOROUTE_H
|
||||
532
engines/sky/compact.cpp
Normal file
532
engines/sky/compact.cpp
Normal file
@@ -0,0 +1,532 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#include "common/debug.h"
|
||||
#include "common/endian.h"
|
||||
#include "common/file.h"
|
||||
#include "common/textconsole.h"
|
||||
#include "common/translation.h"
|
||||
#include "sky/compact.h"
|
||||
#include "gui/message.h"
|
||||
|
||||
namespace Sky {
|
||||
|
||||
#define SKY_CPT_SIZE 419427
|
||||
|
||||
#define OFFS(type,item) ((uint32)(((ptrdiff_t)(&((type *)42)->item))-42))
|
||||
#define MK32(type,item) OFFS(type, item),0,0,0
|
||||
#define MK16(type,item) OFFS(type, item),0
|
||||
#define MK32_A5(type, item) MK32(type, item[0]), MK32(type, item[1]), \
|
||||
MK32(type, item[2]), MK32(type, item[3]), MK32(type, item[4])
|
||||
|
||||
static const uint32 compactOffsets[] = {
|
||||
MK16(Compact, logic),
|
||||
MK16(Compact, status),
|
||||
MK16(Compact, sync),
|
||||
MK16(Compact, screen),
|
||||
MK16(Compact, place),
|
||||
MK32(Compact, getToTableId),
|
||||
MK16(Compact, xcood),
|
||||
MK16(Compact, ycood),
|
||||
MK16(Compact, frame),
|
||||
MK16(Compact, cursorText),
|
||||
MK16(Compact, mouseOn),
|
||||
MK16(Compact, mouseOff),
|
||||
MK16(Compact, mouseClick),
|
||||
MK16(Compact, mouseRelX),
|
||||
MK16(Compact, mouseRelY),
|
||||
MK16(Compact, mouseSizeX),
|
||||
MK16(Compact, mouseSizeY),
|
||||
MK16(Compact, actionScript),
|
||||
MK16(Compact, upFlag),
|
||||
MK16(Compact, downFlag),
|
||||
MK16(Compact, getToFlag),
|
||||
MK16(Compact, flag),
|
||||
MK16(Compact, mood),
|
||||
MK32(Compact, grafixProgId),
|
||||
MK16(Compact, offset),
|
||||
MK16(Compact, mode),
|
||||
MK16(Compact, baseSub),
|
||||
MK16(Compact, baseSub_off),
|
||||
MK16(Compact, actionSub),
|
||||
MK16(Compact, actionSub_off),
|
||||
MK16(Compact, getToSub),
|
||||
MK16(Compact, getToSub_off),
|
||||
MK16(Compact, extraSub),
|
||||
MK16(Compact, extraSub_off),
|
||||
MK16(Compact, dir),
|
||||
MK16(Compact, stopScript),
|
||||
MK16(Compact, miniBump),
|
||||
MK16(Compact, leaving),
|
||||
MK16(Compact, atWatch),
|
||||
MK16(Compact, atWas),
|
||||
MK16(Compact, alt),
|
||||
MK16(Compact, request),
|
||||
MK16(Compact, spWidth_xx),
|
||||
MK16(Compact, spColor),
|
||||
MK16(Compact, spTextId),
|
||||
MK16(Compact, spTime),
|
||||
MK16(Compact, arAnimIndex),
|
||||
MK32(Compact, turnProgId),
|
||||
MK16(Compact, waitingFor),
|
||||
MK16(Compact, arTargetX),
|
||||
MK16(Compact, arTargetY),
|
||||
MK32(Compact, animScratchId),
|
||||
MK16(Compact, megaSet),
|
||||
};
|
||||
|
||||
static const uint32 megaSetOffsets[] = {
|
||||
MK16(MegaSet, gridWidth),
|
||||
MK16(MegaSet, colOffset),
|
||||
MK16(MegaSet, colWidth),
|
||||
MK16(MegaSet, lastChr),
|
||||
MK32(MegaSet, animUpId),
|
||||
MK32(MegaSet, animDownId),
|
||||
MK32(MegaSet, animLeftId),
|
||||
MK32(MegaSet, animRightId),
|
||||
MK32(MegaSet, standUpId),
|
||||
MK32(MegaSet, standDownId),
|
||||
MK32(MegaSet, standLeftId),
|
||||
MK32(MegaSet, standRightId),
|
||||
MK32(MegaSet, standTalkId),
|
||||
};
|
||||
|
||||
static const uint32 turnTableOffsets[] = {
|
||||
MK32_A5(TurnTable, turnTableUp),
|
||||
MK32_A5(TurnTable, turnTableDown),
|
||||
MK32_A5(TurnTable, turnTableLeft),
|
||||
MK32_A5(TurnTable, turnTableRight),
|
||||
MK32_A5(TurnTable, turnTableTalk),
|
||||
};
|
||||
|
||||
#define COMPACT_SIZE (sizeof(compactOffsets)/sizeof(uint32))
|
||||
#define MEGASET_SIZE (sizeof(megaSetOffsets)/sizeof(uint32))
|
||||
#define TURNTABLE_SIZE (sizeof(turnTableOffsets)/sizeof(uint32))
|
||||
|
||||
SkyCompact::SkyCompact() {
|
||||
_cptFile = new Common::File();
|
||||
Common::String filename = "sky.cpt";
|
||||
if (!_cptFile->open(filename.c_str())) {
|
||||
const char *msg = _s("Unable to locate the '%s' engine data file.");
|
||||
Common::U32String errorMessage = Common::U32String::format(_(msg), filename.c_str());
|
||||
GUIErrorMessage(errorMessage);
|
||||
error(msg, filename.c_str());
|
||||
}
|
||||
|
||||
uint16 fileVersion = _cptFile->readUint16LE();
|
||||
if (fileVersion != 0)
|
||||
error("unknown \"sky.cpt\" version");
|
||||
|
||||
if (SKY_CPT_SIZE != _cptFile->size()) {
|
||||
GUI::MessageDialog dialog(_("The \"sky.cpt\" engine data file has an incorrect size."));
|
||||
dialog.runModal();
|
||||
error("Incorrect sky.cpt size (%d, expected: %d)", (int)_cptFile->size(), SKY_CPT_SIZE);
|
||||
}
|
||||
|
||||
// set the necessary data structs up...
|
||||
_numDataLists = _cptFile->readUint16LE();
|
||||
_cptNames = (char***)malloc(_numDataLists * sizeof(char**));
|
||||
_dataListLen = (uint16 *)malloc(_numDataLists * sizeof(uint16));
|
||||
_cptSizes = (uint16 **)malloc(_numDataLists * sizeof(uint16 *));
|
||||
_cptTypes = (uint16 **)malloc(_numDataLists * sizeof(uint16 *));
|
||||
_compacts = (Compact***)malloc(_numDataLists * sizeof(Compact**));
|
||||
|
||||
for (int i = 0; i < _numDataLists; i++) {
|
||||
_dataListLen[i] = _cptFile->readUint16LE();
|
||||
_cptNames[i] = (char**)malloc(_dataListLen[i] * sizeof(char *));
|
||||
_cptSizes[i] = (uint16 *)malloc(_dataListLen[i] * sizeof(uint16));
|
||||
_cptTypes[i] = (uint16 *)malloc(_dataListLen[i] * sizeof(uint16));
|
||||
_compacts[i] = (Compact**)malloc(_dataListLen[i] * sizeof(Compact *));
|
||||
}
|
||||
|
||||
uint32 rawSize = _cptFile->readUint32LE() * sizeof(uint16);
|
||||
uint16 *rawPos = _rawBuf = (uint16 *)malloc(rawSize);
|
||||
|
||||
uint32 srcSize = _cptFile->readUint32LE() * sizeof(uint16);
|
||||
uint16 *srcBuf = (uint16 *)malloc(srcSize);
|
||||
uint16 *srcPos = srcBuf;
|
||||
_cptFile->read(srcBuf, srcSize);
|
||||
|
||||
uint32 asciiSize = _cptFile->readUint32LE();
|
||||
char *asciiPos = _asciiBuf = (char *)malloc(asciiSize);
|
||||
_cptFile->read(_asciiBuf, asciiSize);
|
||||
|
||||
// and fill them with the compact data
|
||||
for (uint32 lcnt = 0; lcnt < _numDataLists; lcnt++) {
|
||||
for (uint32 ecnt = 0; ecnt < _dataListLen[lcnt]; ecnt++) {
|
||||
_cptSizes[lcnt][ecnt] = READ_LE_UINT16(srcPos++);
|
||||
if (_cptSizes[lcnt][ecnt]) {
|
||||
_cptTypes[lcnt][ecnt] = READ_LE_UINT16(srcPos++);
|
||||
_compacts[lcnt][ecnt] = (Compact *)rawPos;
|
||||
_cptNames[lcnt][ecnt] = asciiPos;
|
||||
asciiPos += strlen(asciiPos) + 1;
|
||||
|
||||
for (uint16 elemCnt = 0; elemCnt < _cptSizes[lcnt][ecnt]; elemCnt++)
|
||||
*rawPos++ = READ_LE_UINT16(srcPos++);
|
||||
} else {
|
||||
_cptTypes[lcnt][ecnt] = 0;
|
||||
_compacts[lcnt][ecnt] = NULL;
|
||||
_cptNames[lcnt][ecnt] = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
free(srcBuf);
|
||||
|
||||
uint16 numDlincs = _cptFile->readUint16LE();
|
||||
uint16 *dlincBuf = (uint16 *)malloc(numDlincs * 2 * sizeof(uint16));
|
||||
uint16 *dlincPos = dlincBuf;
|
||||
_cptFile->read(dlincBuf, numDlincs * 2 * sizeof(uint16));
|
||||
// these compacts don't actually exist but only point to other ones...
|
||||
uint16 cnt;
|
||||
for (cnt = 0; cnt < numDlincs; cnt++) {
|
||||
uint16 dlincId = READ_LE_UINT16(dlincPos++);
|
||||
uint16 destId = READ_LE_UINT16(dlincPos++);
|
||||
assert(((dlincId >> 12) < _numDataLists) && ((dlincId & 0xFFF) < _dataListLen[dlincId >> 12]) && (_compacts[dlincId >> 12][dlincId & 0xFFF] == NULL));
|
||||
_compacts[dlincId >> 12][dlincId & 0xFFF] = _compacts[destId >> 12][destId & 0xFFF];
|
||||
|
||||
assert(_cptNames[dlincId >> 12][dlincId & 0xFFF] == NULL);
|
||||
_cptNames[dlincId >> 12][dlincId & 0xFFF] = asciiPos;
|
||||
asciiPos += strlen(asciiPos) + 1;
|
||||
}
|
||||
free(dlincBuf);
|
||||
|
||||
// if this is v0.0288, parse this diff data
|
||||
uint16 numDiffs = _cptFile->readUint16LE();
|
||||
uint16 diffSize = _cptFile->readUint16LE();
|
||||
uint16 *diffBuf = (uint16 *)malloc(diffSize * sizeof(uint16));
|
||||
_cptFile->read(diffBuf, diffSize * sizeof(uint16));
|
||||
if (SkyEngine::_systemVars->gameVersion == 288) {
|
||||
uint16 *diffPos = diffBuf;
|
||||
for (cnt = 0; cnt < numDiffs; cnt++) {
|
||||
uint16 cptId = READ_LE_UINT16(diffPos++);
|
||||
uint16 *rawCpt = (uint16 *)fetchCpt(cptId);
|
||||
rawCpt += READ_LE_UINT16(diffPos++);
|
||||
uint16 len = READ_LE_UINT16(diffPos++);
|
||||
for (uint16 elemCnt = 0; elemCnt < len; elemCnt++)
|
||||
rawCpt[elemCnt] = READ_LE_UINT16(diffPos++);
|
||||
}
|
||||
assert(diffPos == (diffBuf + diffSize));
|
||||
}
|
||||
free(diffBuf);
|
||||
|
||||
// these are the IDs that have to be saved into savegame files.
|
||||
_numSaveIds = _cptFile->readUint16LE();
|
||||
_saveIds = (uint16 *)malloc(_numSaveIds * sizeof(uint16));
|
||||
_cptFile->read(_saveIds, _numSaveIds * sizeof(uint16));
|
||||
for (cnt = 0; cnt < _numSaveIds; cnt++)
|
||||
_saveIds[cnt] = FROM_LE_16(_saveIds[cnt]);
|
||||
_resetDataPos = _cptFile->pos();
|
||||
|
||||
checkAndFixOfficerBluntError();
|
||||
}
|
||||
|
||||
SkyCompact::~SkyCompact() {
|
||||
free(_rawBuf);
|
||||
free(_asciiBuf);
|
||||
free(_saveIds);
|
||||
for (int i = 0; i < _numDataLists; i++) {
|
||||
free(_cptNames[i]);
|
||||
free(_cptSizes[i]);
|
||||
free(_cptTypes[i]);
|
||||
free(_compacts[i]);
|
||||
}
|
||||
free(_cptNames);
|
||||
free(_dataListLen);
|
||||
free(_cptSizes);
|
||||
free(_cptTypes);
|
||||
free(_compacts);
|
||||
_cptFile->close();
|
||||
delete _cptFile;
|
||||
}
|
||||
|
||||
/* WORKAROUND for bug #2687:
|
||||
The first release of scummvm with externalized, binary compact data has one broken 16 bit reference.
|
||||
When talking to Officer Blunt on ground level while in a crouched position, the game enters an
|
||||
unfinishable state because Blunt jumps into the lake and can no longer be interacted with.
|
||||
This fixes the problem when playing with a broken sky.cpt */
|
||||
#define SCUMMVM_BROKEN_TALK_INDEX 158
|
||||
void SkyCompact::checkAndFixOfficerBluntError() {
|
||||
// Retrieve the table with the animation ids to use for talking
|
||||
uint16 *talkTable = (uint16*)fetchCpt(CPT_TALK_TABLE_LIST);
|
||||
if (talkTable[SCUMMVM_BROKEN_TALK_INDEX] == ID_SC31_GUARD_TALK) {
|
||||
debug(1, "SKY.CPT with Officer Blunt bug encountered, fixing talk gfx.");
|
||||
talkTable[SCUMMVM_BROKEN_TALK_INDEX] = ID_SC31_GUARD_TALK2;
|
||||
}
|
||||
}
|
||||
|
||||
// needed for some workaround where the engine has to check if it's currently processing joey, for example
|
||||
bool SkyCompact::cptIsId(Compact *cpt, uint16 id) {
|
||||
return (cpt == fetchCpt(id));
|
||||
}
|
||||
|
||||
Compact *SkyCompact::fetchCpt(uint16 cptId) {
|
||||
if (cptId == 0xFFFF) // is this really still necessary?
|
||||
return NULL;
|
||||
assert(((cptId >> 12) < _numDataLists) && ((cptId & 0xFFF) < _dataListLen[cptId >> 12]));
|
||||
|
||||
debug(8, "Loading Compact %s [%s] (%04X=%d,%d)", _cptNames[cptId >> 12][cptId & 0xFFF], nameForType(_cptTypes[cptId >> 12][cptId & 0xFFF]), cptId, cptId >> 12, cptId & 0xFFF);
|
||||
|
||||
return _compacts[cptId >> 12][cptId & 0xFFF];
|
||||
}
|
||||
|
||||
Compact *SkyCompact::fetchCptInfo(uint16 cptId, uint16 *elems, uint16 *type, char *name, size_t nameSize) {
|
||||
assert(((cptId >> 12) < _numDataLists) && ((cptId & 0xFFF) < _dataListLen[cptId >> 12]));
|
||||
if (elems)
|
||||
*elems = _cptSizes[cptId >> 12][cptId & 0xFFF];
|
||||
if (type)
|
||||
*type = _cptTypes[cptId >> 12][cptId & 0xFFF];
|
||||
if (name) {
|
||||
if (_cptNames[cptId >> 12][cptId & 0xFFF] != NULL)
|
||||
Common::strcpy_s(name, nameSize, _cptNames[cptId >> 12][cptId & 0xFFF]);
|
||||
else
|
||||
Common::strcpy_s(name, nameSize, "(null)");
|
||||
}
|
||||
return fetchCpt(cptId);
|
||||
}
|
||||
|
||||
const char *SkyCompact::nameForType(uint16 type) {
|
||||
if (type >= NUM_CPT_TYPES)
|
||||
return "unknown";
|
||||
else
|
||||
return _typeNames[type];
|
||||
}
|
||||
|
||||
uint16 SkyCompact::getSub(Compact *cpt, uint16 mode) {
|
||||
switch (mode) {
|
||||
case 0:
|
||||
return cpt->baseSub;
|
||||
case 2:
|
||||
return cpt->baseSub_off;
|
||||
case 4:
|
||||
return cpt->actionSub;
|
||||
case 6:
|
||||
return cpt->actionSub_off;
|
||||
case 8:
|
||||
return cpt->getToSub;
|
||||
case 10:
|
||||
return cpt->getToSub_off;
|
||||
case 12:
|
||||
return cpt->extraSub;
|
||||
case 14:
|
||||
return cpt->extraSub_off;
|
||||
default:
|
||||
error("Invalid Mode (%d)", mode);
|
||||
}
|
||||
}
|
||||
|
||||
void SkyCompact::setSub(Compact *cpt, uint16 mode, uint16 value) {
|
||||
switch (mode) {
|
||||
case 0:
|
||||
cpt->baseSub = value;
|
||||
return;
|
||||
case 2:
|
||||
cpt->baseSub_off = value;
|
||||
return;
|
||||
case 4:
|
||||
cpt->actionSub = value;
|
||||
return;
|
||||
case 6:
|
||||
cpt->actionSub_off = value;
|
||||
return;
|
||||
case 8:
|
||||
cpt->getToSub = value;
|
||||
return;
|
||||
case 10:
|
||||
cpt->getToSub_off = value;
|
||||
return;
|
||||
case 12:
|
||||
cpt->extraSub = value;
|
||||
return;
|
||||
case 14:
|
||||
cpt->extraSub_off = value;
|
||||
return;
|
||||
default:
|
||||
error("Invalid Mode (%d)", mode);
|
||||
}
|
||||
}
|
||||
|
||||
uint16 *SkyCompact::getGrafixPtr(Compact *cpt) {
|
||||
uint16 *gfxBase = (uint16 *)fetchCpt(cpt->grafixProgId);
|
||||
if (gfxBase == NULL)
|
||||
return NULL;
|
||||
|
||||
return gfxBase + cpt->grafixProgPos;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the n'th mega set specified by \a megaSet from Compact \a cpt.
|
||||
*/
|
||||
MegaSet *SkyCompact::getMegaSet(Compact *cpt) {
|
||||
switch (cpt->megaSet) {
|
||||
case 0:
|
||||
return &cpt->megaSet0;
|
||||
case NEXT_MEGA_SET:
|
||||
return &cpt->megaSet1;
|
||||
case NEXT_MEGA_SET*2:
|
||||
return &cpt->megaSet2;
|
||||
case NEXT_MEGA_SET*3:
|
||||
return &cpt->megaSet3;
|
||||
default:
|
||||
error("Invalid MegaSet (%d)", cpt->megaSet);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
\brief Returns the turn table for direction \a dir
|
||||
from Compact \a cpt in \a megaSet.
|
||||
|
||||
Functionally equivalent to:
|
||||
\verbatim
|
||||
clear eax
|
||||
mov al,20
|
||||
mul (cpt[esi]).c_dir
|
||||
add ax,(cpt[esi]).c_mega_set
|
||||
lea eax,(cpt[esi+eax]).c_turn_table_up
|
||||
\endverbatim
|
||||
*/
|
||||
uint16 *SkyCompact::getTurnTable(Compact *cpt, uint16 dir) {
|
||||
MegaSet *m = getMegaSet(cpt);
|
||||
TurnTable *turnTable = (TurnTable *)fetchCpt(m->turnTableId);
|
||||
switch (dir) {
|
||||
case 0:
|
||||
return turnTable->turnTableUp;
|
||||
case 1:
|
||||
return turnTable->turnTableDown;
|
||||
case 2:
|
||||
return turnTable->turnTableLeft;
|
||||
case 3:
|
||||
return turnTable->turnTableRight;
|
||||
case 4:
|
||||
return turnTable->turnTableTalk;
|
||||
default:
|
||||
error("No TurnTable (%d) in MegaSet (%d)", dir, cpt->megaSet);
|
||||
}
|
||||
}
|
||||
|
||||
void *SkyCompact::getCompactElem(Compact *cpt, uint16 off) {
|
||||
if (off < COMPACT_SIZE)
|
||||
return((uint8 *)cpt + compactOffsets[off]);
|
||||
off -= COMPACT_SIZE;
|
||||
|
||||
if (off < MEGASET_SIZE)
|
||||
return((uint8 *)&(cpt->megaSet0) + megaSetOffsets[off]);
|
||||
|
||||
off -= MEGASET_SIZE;
|
||||
if (off < TURNTABLE_SIZE)
|
||||
return ((uint8 *)fetchCpt(cpt->megaSet0.turnTableId) + turnTableOffsets[off]);
|
||||
|
||||
off -= TURNTABLE_SIZE;
|
||||
if (off < MEGASET_SIZE)
|
||||
return((uint8 *)&(cpt->megaSet1) + megaSetOffsets[off]);
|
||||
|
||||
off -= MEGASET_SIZE;
|
||||
if (off < TURNTABLE_SIZE)
|
||||
return ((uint8 *)fetchCpt(cpt->megaSet1.turnTableId) + turnTableOffsets[off]);
|
||||
|
||||
off -= TURNTABLE_SIZE;
|
||||
if (off < MEGASET_SIZE)
|
||||
return((uint8 *)&(cpt->megaSet2) + megaSetOffsets[off]);
|
||||
|
||||
off -= MEGASET_SIZE;
|
||||
if (off < TURNTABLE_SIZE)
|
||||
return ((uint8 *)fetchCpt(cpt->megaSet2.turnTableId) + turnTableOffsets[off]);
|
||||
|
||||
off -= TURNTABLE_SIZE;
|
||||
if (off < MEGASET_SIZE)
|
||||
return((uint8 *)&(cpt->megaSet3) + megaSetOffsets[off]);
|
||||
|
||||
off -= MEGASET_SIZE;
|
||||
if (off < TURNTABLE_SIZE)
|
||||
return ((uint8 *)fetchCpt(cpt->megaSet3.turnTableId) + turnTableOffsets[off]);
|
||||
off -= TURNTABLE_SIZE;
|
||||
|
||||
error("Offset %X out of bounds of compact", (int)(off + COMPACT_SIZE + 4 * MEGASET_SIZE + 4 * TURNTABLE_SIZE));
|
||||
}
|
||||
|
||||
uint8 *SkyCompact::createResetData(uint16 gameVersion) {
|
||||
_cptFile->seek(_resetDataPos);
|
||||
uint32 dataSize = _cptFile->readUint16LE() * sizeof(uint16);
|
||||
uint16 *resetBuf = (uint16 *)malloc(dataSize);
|
||||
_cptFile->read(resetBuf, dataSize);
|
||||
uint16 numDiffs = _cptFile->readUint16LE();
|
||||
for (uint16 cnt = 0; cnt < numDiffs; cnt++) {
|
||||
uint16 version = _cptFile->readUint16LE();
|
||||
uint16 diffFields = _cptFile->readUint16LE();
|
||||
if (version == gameVersion) {
|
||||
for (uint16 diffCnt = 0; diffCnt < diffFields; diffCnt++) {
|
||||
uint16 pos = _cptFile->readUint16LE();
|
||||
resetBuf[pos] = TO_LE_16(_cptFile->readUint16LE());
|
||||
}
|
||||
return (uint8 *)resetBuf;
|
||||
} else
|
||||
_cptFile->seek(diffFields * 2 * sizeof(uint16), SEEK_CUR);
|
||||
}
|
||||
free(resetBuf);
|
||||
error("Unable to find reset data for Beneath a Steel Sky Version 0.0%03d", gameVersion);
|
||||
}
|
||||
|
||||
// - debugging functions
|
||||
|
||||
uint16 SkyCompact::findCptId(void *cpt) {
|
||||
for (uint16 listCnt = 0; listCnt < _numDataLists; listCnt++)
|
||||
for (uint16 elemCnt = 0; elemCnt < _dataListLen[listCnt]; elemCnt++)
|
||||
if (_compacts[listCnt][elemCnt] == cpt)
|
||||
return (listCnt << 12) | elemCnt;
|
||||
// not found
|
||||
debug(1, "Id for Compact %p wasn't found", cpt);
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint16 SkyCompact::findCptId(const char *cptName) {
|
||||
for (uint16 listCnt = 0; listCnt < _numDataLists; listCnt++)
|
||||
for (uint16 elemCnt = 0; elemCnt < _dataListLen[listCnt]; elemCnt++)
|
||||
if (_cptNames[listCnt][elemCnt] != 0)
|
||||
if (scumm_stricmp(cptName, _cptNames[listCnt][elemCnt]) == 0)
|
||||
return (listCnt << 12) | elemCnt;
|
||||
// not found
|
||||
debug(1, "Id for Compact %s wasn't found", cptName);
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint16 SkyCompact::giveNumDataLists() {
|
||||
return _numDataLists;
|
||||
}
|
||||
|
||||
uint16 SkyCompact::giveDataListLen(uint16 listNum) {
|
||||
if (listNum >= _numDataLists) // list doesn't exist
|
||||
return 0;
|
||||
else
|
||||
return _dataListLen[listNum];
|
||||
}
|
||||
|
||||
const char *const SkyCompact::_typeNames[NUM_CPT_TYPES] = {
|
||||
"null",
|
||||
"COMPACT",
|
||||
"TURNTABLE",
|
||||
"ANIM SEQ",
|
||||
"UNKNOWN",
|
||||
"GETTOTABLE",
|
||||
"AR BUFFER",
|
||||
"MAIN LIST"
|
||||
};
|
||||
|
||||
} // End of namespace Sky
|
||||
98
engines/sky/compact.h
Normal file
98
engines/sky/compact.h
Normal file
@@ -0,0 +1,98 @@
|
||||
/* 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 SKY_COMPACT_H
|
||||
#define SKY_COMPACT_H
|
||||
|
||||
#include "sky/sky.h"
|
||||
#include "sky/struc.h"
|
||||
#include "sky/skydefs.h"
|
||||
|
||||
namespace Common {
|
||||
class File;
|
||||
}
|
||||
|
||||
enum CptIds {
|
||||
CPT_JOEY = 1,
|
||||
CPT_FOSTER = 3,
|
||||
CPT_TEXT_1 = 0x17,
|
||||
CPT_TEXT_11 = 0x21,
|
||||
CPT_MENU_BAR = 0x2E,
|
||||
CPT_REICH_DOOR_20 = 0x30AB,
|
||||
CPT_MOVE_LIST = 0xBD,
|
||||
CPT_TALK_TABLE_LIST = 0xBC
|
||||
};
|
||||
|
||||
enum CptTypeIds {
|
||||
CPT_NULL = 0,
|
||||
COMPACT,
|
||||
TURNTAB,
|
||||
ANIMSEQ,
|
||||
MISCBIN,
|
||||
GETTOTAB,
|
||||
ROUTEBUF,
|
||||
MAINLIST,
|
||||
NUM_CPT_TYPES
|
||||
};
|
||||
|
||||
namespace Sky {
|
||||
|
||||
class SkyCompact {
|
||||
public:
|
||||
SkyCompact();
|
||||
~SkyCompact();
|
||||
Compact *fetchCpt(uint16 cptId);
|
||||
Compact *fetchCptInfo(uint16 cptId, uint16 *elems = NULL, uint16 *type = NULL, char *name = NULL, size_t nameSize = 0);
|
||||
static uint16 getSub(Compact *cpt, uint16 mode);
|
||||
static void setSub(Compact *cpt, uint16 mode, uint16 value);
|
||||
static MegaSet *getMegaSet(Compact *cpt);
|
||||
uint16 *getGrafixPtr(Compact *cpt);
|
||||
uint16 *getTurnTable(Compact *cpt, uint16 dir);
|
||||
void *getCompactElem(Compact *cpt, uint16 off);
|
||||
bool cptIsId(Compact *cpt, uint16 id);
|
||||
uint8 *createResetData(uint16 gameVersion);
|
||||
uint16 _numSaveIds;
|
||||
uint16 *_saveIds;
|
||||
// - debugging functions
|
||||
uint16 findCptId(void *cpt);
|
||||
uint16 findCptId(const char *cptName);
|
||||
uint16 giveNumDataLists();
|
||||
uint16 giveDataListLen(uint16 listNum);
|
||||
const char *nameForType(uint16 type);
|
||||
private:
|
||||
void checkAndFixOfficerBluntError();
|
||||
|
||||
uint16 _numDataLists;
|
||||
uint16 *_dataListLen;
|
||||
uint16 *_rawBuf;
|
||||
char *_asciiBuf;
|
||||
Compact ***_compacts;
|
||||
char ***_cptNames;
|
||||
uint16 **_cptSizes;
|
||||
uint16 **_cptTypes;
|
||||
Common::File *_cptFile;
|
||||
uint32 _resetDataPos;
|
||||
static const char *const _typeNames[NUM_CPT_TYPES];
|
||||
};
|
||||
|
||||
} // End of namespace Sky
|
||||
|
||||
#endif
|
||||
3
engines/sky/configure.engine
Normal file
3
engines/sky/configure.engine
Normal file
@@ -0,0 +1,3 @@
|
||||
# This file is included from the main "configure" script
|
||||
# add_engine [name] [desc] [build-by-default] [subengines] [base games] [deps] [components]
|
||||
add_engine sky "Beneath a Steel Sky" yes "" "" "" "midi"
|
||||
1726
engines/sky/control.cpp
Normal file
1726
engines/sky/control.cpp
Normal file
File diff suppressed because it is too large
Load Diff
305
engines/sky/control.h
Normal file
305
engines/sky/control.h
Normal file
@@ -0,0 +1,305 @@
|
||||
/* 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 SKY_CONTROL_H
|
||||
#define SKY_CONTROL_H
|
||||
|
||||
|
||||
#include "common/events.h"
|
||||
#include "common/scummsys.h"
|
||||
#include "common/str-array.h"
|
||||
|
||||
class OSystem;
|
||||
namespace Common {
|
||||
class SaveFileManager;
|
||||
}
|
||||
|
||||
namespace Sky {
|
||||
|
||||
class Disk;
|
||||
class Screen;
|
||||
class Logic;
|
||||
class Mouse;
|
||||
class Text;
|
||||
class MusicBase;
|
||||
class Sound;
|
||||
class SkyCompact;
|
||||
class SkyEngine;
|
||||
struct Compact;
|
||||
struct DataFileHeader;
|
||||
struct MegaSet;
|
||||
|
||||
#define MAX_SAVE_GAMES 999
|
||||
#define MAX_TEXT_LEN 80
|
||||
#define PAN_LINE_WIDTH 184
|
||||
#define PAN_CHAR_HEIGHT 12
|
||||
#define STATUS_WIDTH 146
|
||||
#define MPNL_X 60 // Main Panel
|
||||
#define MPNL_Y 10
|
||||
|
||||
#define SPNL_X 20 // Save Panel
|
||||
#define SPNL_Y 20
|
||||
#define SP_HEIGHT 149
|
||||
#define SP_TOP_GAP 12
|
||||
#define SP_BOT_GAP 27
|
||||
#define CROSS_SZ_X 27
|
||||
#define CROSS_SZ_Y 22
|
||||
|
||||
#define TEXT_FLAG_MASK (SF_ALLOW_SPEECH | SF_ALLOW_TEXT)
|
||||
|
||||
#define GAME_NAME_X (SPNL_X + 18) // x coordinate of game names
|
||||
#define GAME_NAME_Y (SPNL_Y + SP_TOP_GAP) // start y coord of game names
|
||||
#define MAX_ON_SCREEN ((SP_HEIGHT - SP_TOP_GAP - SP_BOT_GAP) / PAN_CHAR_HEIGHT) // no of save games on screen
|
||||
#define CP_PANEL 60400 // main panel sprite
|
||||
|
||||
#define MAINPANEL 0
|
||||
#define SAVEPANEL 1
|
||||
|
||||
#define NO_MASK false
|
||||
#define WITH_MASK true
|
||||
|
||||
// resource's onClick routines
|
||||
#define DO_NOTHING 0
|
||||
#define REST_GAME_PANEL 1
|
||||
#define SAVE_GAME_PANEL 2
|
||||
#define SAVE_A_GAME 3
|
||||
#define RESTORE_A_GAME 4
|
||||
#define SP_CANCEL 5
|
||||
#define SHIFT_DOWN_FAST 6
|
||||
#define SHIFT_DOWN_SLOW 7
|
||||
#define SHIFT_UP_FAST 8
|
||||
#define SHIFT_UP_SLOW 9
|
||||
#define SPEED_SLIDE 10
|
||||
#define MUSIC_SLIDE 11
|
||||
#define TOGGLE_FX 12
|
||||
#define TOGGLE_MS 13
|
||||
#define TOGGLE_TEXT 14
|
||||
#define EXIT 15
|
||||
#define RESTART 16
|
||||
#define QUIT_TO_DOS 17
|
||||
#define RESTORE_AUTO 18
|
||||
|
||||
// onClick return codes
|
||||
#define CANCEL_PRESSED 100
|
||||
#define NAME_TOO_SHORT 101
|
||||
#define GAME_SAVED 102
|
||||
#define SHIFTED 103
|
||||
#define TOGGLED 104
|
||||
#define RESTARTED 105
|
||||
#define GAME_RESTORED 106
|
||||
#define RESTORE_FAILED 107
|
||||
#define NO_DISK_SPACE 108
|
||||
#define SPEED_CHANGED 109
|
||||
#define QUIT_PANEL 110
|
||||
|
||||
#define SLOW 0
|
||||
#define FAST 1
|
||||
|
||||
#define SPEED_MULTIPLY 12
|
||||
|
||||
//-
|
||||
#define SAVE_EXT 1
|
||||
#define SAVE_MEGA0 2
|
||||
#define SAVE_MEGA1 4
|
||||
#define SAVE_MEGA2 8
|
||||
#define SAVE_MEGA3 16
|
||||
#define SAVE_GRAFX 32
|
||||
#define SAVE_TURNP 64
|
||||
|
||||
#define SAVE_FILE_REVISION 6
|
||||
#define OLD_SAVEGAME_TYPE 5
|
||||
|
||||
struct AllocedMem {
|
||||
uint16 *mem;
|
||||
AllocedMem *next;
|
||||
};
|
||||
|
||||
class ConResource {
|
||||
public:
|
||||
ConResource(void *pSpData, uint32 pNSprites, uint32 pCurSprite, uint16 pX, uint16 pY, uint32 pText, uint8 pOnClick, OSystem *system, uint8 *screen);
|
||||
virtual ~ConResource() {}
|
||||
void setSprite(void *pSpData) { _spriteData = (DataFileHeader *)pSpData; }
|
||||
void setText(uint32 pText) { if (pText) _text = pText + 0x7000; else _text = 0; }
|
||||
void setXY(uint16 x, uint16 y) { _x = x; _y = y; }
|
||||
bool isMouseOver(uint32 mouseX, uint32 mouseY);
|
||||
virtual void drawToScreen(bool doMask);
|
||||
|
||||
DataFileHeader *_spriteData;
|
||||
uint32 _numSprites, _curSprite;
|
||||
uint16 _x, _y;
|
||||
uint32 _text;
|
||||
uint8 _onClick;
|
||||
OSystem *_system;
|
||||
uint8 *_screen;
|
||||
private:
|
||||
};
|
||||
|
||||
class TextResource : public ConResource {
|
||||
public:
|
||||
TextResource(void *pSpData, uint32 pNSprites, uint32 pCurSprite, uint16 pX, uint16 pY, uint32 pText, uint8 pOnClick, OSystem *system, uint8 *screen);
|
||||
~TextResource() override;
|
||||
void drawToScreen(bool doMask) override;
|
||||
void flushForRedraw();
|
||||
private:
|
||||
uint16 _oldX, _oldY;
|
||||
uint8 *_oldScreen;
|
||||
};
|
||||
|
||||
class ControlStatus {
|
||||
public:
|
||||
ControlStatus(Text *skyText, OSystem *system, uint8 *scrBuf);
|
||||
~ControlStatus();
|
||||
void setToText(const char *newText);
|
||||
void setToText(uint16 textNum);
|
||||
void drawToScreen();
|
||||
private:
|
||||
TextResource *_statusText;
|
||||
DataFileHeader *_textData;
|
||||
Text *_skyText;
|
||||
OSystem *_system;
|
||||
uint8 *_screenBuf;
|
||||
};
|
||||
|
||||
class Control {
|
||||
public:
|
||||
Control(SkyEngine *vm, Common::SaveFileManager *saveFileMan, Screen *screen, Disk *disk, Mouse *mouse, Text *text, MusicBase *music, Logic *logic, Sound *sound, SkyCompact *skyCompact, OSystem *system, Common::Keymap *shortcutsKeymap);
|
||||
void doControlPanel();
|
||||
void doLoadSavePanel();
|
||||
void restartGame();
|
||||
void showGameQuitMsg();
|
||||
uint16 quickXRestore(uint16 slot);
|
||||
bool loadSaveAllowed();
|
||||
bool isControlPanelOpen();
|
||||
|
||||
SkyEngine *_vm;
|
||||
|
||||
uint16 _selectedGame;
|
||||
uint16 saveGameToFile(bool fromControlPanel, const char *filename = 0, bool isAutosave = false);
|
||||
|
||||
void loadDescriptions(Common::StringArray &list);
|
||||
void saveDescriptions(const Common::StringArray &list);
|
||||
|
||||
private:
|
||||
int displayMessage(MSVC_PRINTF const char *message, ...) GCC_PRINTF(2, 3);
|
||||
|
||||
void initPanel();
|
||||
void removePanel();
|
||||
|
||||
void drawMainPanel();
|
||||
|
||||
/**
|
||||
* Waits for a specified amount while still processing events.
|
||||
*
|
||||
* @param amount The duration in milliseconds to wait
|
||||
*/
|
||||
void delay(unsigned int amount);
|
||||
|
||||
void animClick(ConResource *pButton);
|
||||
bool getYesNo(char *text, uint bufSize);
|
||||
void buttonControl(ConResource *pButton);
|
||||
uint16 handleClick(ConResource *pButton);
|
||||
uint16 doMusicSlide();
|
||||
uint16 doSpeedSlide();
|
||||
void toggleFx(ConResource *pButton);
|
||||
uint16 toggleText();
|
||||
void toggleMusic(ConResource *pButton);
|
||||
uint16 shiftDown(uint8 speed);
|
||||
uint16 shiftUp(uint8 speed);
|
||||
void drawTextCross(uint32 flags);
|
||||
void drawCross(uint16 x, uint16 y);
|
||||
|
||||
uint16 saveRestorePanel(bool allowSave);
|
||||
void setUpGameSprites(const Common::StringArray &saveGameNames, DataFileHeader **nameSprites, uint16 firstNum, uint16 selectedGame, const Common::String &dirtyString);
|
||||
void showSprites(DataFileHeader **nameSprites, bool allowSave);
|
||||
void handleKeyPress(Common::KeyState kbd, Common::String &textBuf);
|
||||
|
||||
uint32 prepareSaveData(uint8 *destBuf);
|
||||
|
||||
bool autoSaveExists();
|
||||
uint16 restoreGameFromFile(bool autoSave);
|
||||
void importOldMegaSet(uint8 **srcPos, MegaSet *mega);
|
||||
void importOldCompact(Compact* destCpt, uint8 **srcPos, uint16 numElems, uint16 type, char *name);
|
||||
uint16 parseSaveData(uint8 *srcBuf);
|
||||
|
||||
Common::SaveFileManager *_saveFileMan;
|
||||
SkyCompact *_skyCompact;
|
||||
Screen *_skyScreen;
|
||||
Disk *_skyDisk;
|
||||
Mouse *_skyMouse;
|
||||
Text *_skyText;
|
||||
MusicBase *_skyMusic;
|
||||
Logic *_skyLogic;
|
||||
Sound *_skySound;
|
||||
OSystem *_system;
|
||||
bool _mouseClicked;
|
||||
Common::KeyState _keyPressed;
|
||||
Common::CustomEventType _action;
|
||||
int _mouseWheel;
|
||||
Common::Keymap *_shortcutsKeymap;
|
||||
|
||||
struct {
|
||||
uint8 *controlPanel;
|
||||
uint8 *button;
|
||||
uint8 *buttonDown;
|
||||
uint8 *savePanel;
|
||||
uint8 *yesNo;
|
||||
uint8 *slide;
|
||||
uint8 *slode;
|
||||
uint8 *slode2;
|
||||
uint8 *slide2;
|
||||
uint8 *musicBodge;
|
||||
} _sprites;
|
||||
|
||||
uint8 *_screenBuf;
|
||||
int _lastButton;
|
||||
uint32 _curButtonText;
|
||||
uint16 _firstText;
|
||||
uint16 _savedMouse;
|
||||
uint32 _savedCharSet;
|
||||
uint16 _enteredTextWidth;
|
||||
|
||||
ConResource *createResource(void *pSpData, uint32 pNSprites, uint32 pCurSprite, int16 pX, int16 pY, uint32 pText, uint8 pOnClick, uint8 panelType);
|
||||
|
||||
DataFileHeader *_textSprite;
|
||||
TextResource *_text;
|
||||
|
||||
ConResource *_controlPanel, *_exitButton, *_slide, *_slide2, *_slode;
|
||||
ConResource *_restorePanButton, *_savePanButton, *_dosPanButton, *_restartPanButton, *_fxPanButton, *_musicPanButton;
|
||||
ConResource *_bodge, *_yesNo;
|
||||
ConResource *_controlPanLookList[9];
|
||||
|
||||
//- Save/restore panel
|
||||
ConResource *_savePanel;
|
||||
ConResource *_saveButton, *_downFastButton, *_downSlowButton;
|
||||
ConResource *_upFastButton, *_upSlowButton, *_quitButton, *_restoreButton;
|
||||
ConResource *_autoSaveButton;
|
||||
|
||||
ConResource *_savePanLookList[6], *_restorePanLookList[7];
|
||||
|
||||
ControlStatus *_statusBar;
|
||||
|
||||
static char _quitTexts[20][45];
|
||||
static uint8 _crossImg[594];
|
||||
};
|
||||
|
||||
} // End of namespace Sky
|
||||
|
||||
#endif // CONTROL_H
|
||||
5
engines/sky/credits.pl
Normal file
5
engines/sky/credits.pl
Normal file
@@ -0,0 +1,5 @@
|
||||
begin_section("Sky");
|
||||
add_person("Robert Göffringmann", "lavosspawn", "(retired)");
|
||||
add_person("Oliver Kiehl", "olki", "(retired)");
|
||||
add_person("Joost Peters", "joostp", "");
|
||||
end_section();
|
||||
1353
engines/sky/debug.cpp
Normal file
1353
engines/sky/debug.cpp
Normal file
File diff suppressed because it is too large
Load Diff
75
engines/sky/debug.h
Normal file
75
engines/sky/debug.h
Normal file
@@ -0,0 +1,75 @@
|
||||
/* 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 SKY_DEBUG_H
|
||||
#define SKY_DEBUG_H
|
||||
|
||||
|
||||
#include "common/scummsys.h"
|
||||
#include "gui/debugger.h"
|
||||
|
||||
namespace Sky {
|
||||
|
||||
class Logic;
|
||||
class Mouse;
|
||||
class Screen;
|
||||
class SkyCompact;
|
||||
|
||||
class Debugger : public GUI::Debugger {
|
||||
public:
|
||||
Debugger(Logic *logic, Mouse *mouse, Screen *screen, SkyCompact *skyCompact);
|
||||
bool showGrid() { return _showGrid; }
|
||||
|
||||
private:
|
||||
void preEnter() override;
|
||||
void postEnter() override;
|
||||
|
||||
private:
|
||||
bool Cmd_ShowGrid(int argc, const char **argv);
|
||||
bool Cmd_ReloadGrid(int argc, const char **argv);
|
||||
bool Cmd_ShowCompact(int argc, const char **argv);
|
||||
bool Cmd_LogicCommand(int argc, const char **argv);
|
||||
bool Cmd_Info(int argc, const char **argv);
|
||||
bool Cmd_ScriptVar(int argc, const char **argv);
|
||||
bool Cmd_Section(int argc, const char **argv);
|
||||
bool Cmd_LogicList(int argc, const char **argv);
|
||||
|
||||
void dumpCompact(uint16 cptId);
|
||||
|
||||
Logic *_logic;
|
||||
Mouse *_mouse;
|
||||
Screen *_screen;
|
||||
SkyCompact *_skyCompact;
|
||||
|
||||
bool _showGrid;
|
||||
};
|
||||
|
||||
|
||||
class Debug {
|
||||
public:
|
||||
static void logic(uint32 logic);
|
||||
static void script(uint32 command, uint16 *scriptData);
|
||||
static void mcode(uint32 mcode, uint32 a, uint32 b, uint32 c);
|
||||
};
|
||||
|
||||
} // End of namespace Sky
|
||||
|
||||
#endif
|
||||
217
engines/sky/detection.cpp
Normal file
217
engines/sky/detection.cpp
Normal file
@@ -0,0 +1,217 @@
|
||||
/* 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 "base/plugins.h"
|
||||
|
||||
#include "common/config-manager.h"
|
||||
#include "common/md5.h"
|
||||
#include "engines/advancedDetector.h"
|
||||
#include "engines/metaengine.h"
|
||||
#include "common/system.h"
|
||||
#include "common/file.h"
|
||||
#include "common/textconsole.h"
|
||||
|
||||
static const PlainGameDescriptor skySetting =
|
||||
{"sky", "Beneath a Steel Sky" };
|
||||
|
||||
struct SkyVersion {
|
||||
int dinnerTableEntries;
|
||||
int dataDiskSize;
|
||||
const char *extraDesc;
|
||||
int version;
|
||||
const char *guioptions;
|
||||
};
|
||||
|
||||
// TODO: Would be nice if Disk::determineGameVersion() used this table, too.
|
||||
static const SkyVersion skyVersions[] = {
|
||||
{ 232, 734425, "Floppy Demo", 272, GUIO1(GUIO_NOSPEECH) }, // German
|
||||
{ 243, 1328979, "PC Gamer Demo", 109, GUIO1(GUIO_NOSPEECH) },
|
||||
{ 247, 814147, "Floppy Demo", 267, GUIO1(GUIO_NOSPEECH) }, // English
|
||||
{ 1404, 8252443, "Floppy", 288, GUIO1(GUIO_NOSPEECH) },
|
||||
{ 1413, 8387069, "Floppy", 303, GUIO1(GUIO_NOSPEECH) },
|
||||
{ 1445, 8830435, "Floppy", 348, GUIO1(GUIO_NOSPEECH) },
|
||||
{ 1445, -1, "Floppy", 331, GUIO1(GUIO_NOSPEECH) },
|
||||
{ 1711, 26623798, "CD Demo", 365, GUIO0() },
|
||||
{ 5099, 72429382, "CD", 368, GUIO0() },
|
||||
{ 5097, 72395713, "CD", 372, GUIO0() },
|
||||
{ 5097, 73123264, "CD", 372, GUIO0() },
|
||||
{ 0, 0, 0, 0, 0 }
|
||||
};
|
||||
|
||||
class SkyMetaEngineDetection : public MetaEngineDetection {
|
||||
public:
|
||||
const char *getEngineName() const override;
|
||||
const char *getOriginalCopyright() const override;
|
||||
|
||||
const char *getName() const override {
|
||||
return "sky";
|
||||
}
|
||||
|
||||
PlainGameList getSupportedGames() const override;
|
||||
PlainGameDescriptor findGame(const char *gameid) const override;
|
||||
Common::Error identifyGame(DetectedGame &game, const void **descriptor) override;
|
||||
DetectedGames detectGames(const Common::FSList &fslist, uint32 /*skipADFlags*/, bool /*skipIncomplete*/) override;
|
||||
|
||||
uint getMD5Bytes() const override {
|
||||
return 0;
|
||||
}
|
||||
|
||||
void dumpDetectionEntries() const override final {}
|
||||
|
||||
int getGameVariantCount() const override {
|
||||
int entries = 0;
|
||||
for (const SkyVersion *sv = skyVersions; sv->dinnerTableEntries; ++sv)
|
||||
++entries;
|
||||
return entries;
|
||||
}
|
||||
};
|
||||
|
||||
const char *SkyMetaEngineDetection::getEngineName() const {
|
||||
return "Beneath a Steel Sky";
|
||||
}
|
||||
|
||||
const char *SkyMetaEngineDetection::getOriginalCopyright() const {
|
||||
return "Beneath a Steel Sky (C) Revolution";
|
||||
}
|
||||
|
||||
PlainGameList SkyMetaEngineDetection::getSupportedGames() const {
|
||||
PlainGameList games;
|
||||
games.push_back(skySetting);
|
||||
return games;
|
||||
}
|
||||
|
||||
PlainGameDescriptor SkyMetaEngineDetection::findGame(const char *gameid) const {
|
||||
if (0 == scumm_stricmp(gameid, skySetting.gameId))
|
||||
return skySetting;
|
||||
return PlainGameDescriptor::empty();
|
||||
}
|
||||
|
||||
Common::Error SkyMetaEngineDetection::identifyGame(DetectedGame &game, const void **descriptor) {
|
||||
*descriptor = nullptr;
|
||||
game = DetectedGame(getName(), findGame(ConfMan.get("gameid").c_str()));
|
||||
return game.gameId.empty() ? Common::kUnknownError : Common::kNoError;
|
||||
}
|
||||
|
||||
DetectedGames SkyMetaEngineDetection::detectGames(const Common::FSList &fslist, uint32 /*skipADFlags*/, bool /*skipIncomplete*/) {
|
||||
DetectedGames detectedGames;
|
||||
bool hasSkyDsk = false;
|
||||
bool hasSkyDnr = false;
|
||||
int dinnerTableEntries = -1;
|
||||
int dataDiskSize = -1;
|
||||
Common::String dataDiskHeadMD5 = "";
|
||||
int exeSize = -1;
|
||||
const Common::Language langs[] = {
|
||||
Common::EN_GRB,
|
||||
Common::DE_DEU,
|
||||
Common::FR_FRA,
|
||||
Common::EN_USA,
|
||||
Common::SV_SWE,
|
||||
Common::IT_ITA,
|
||||
Common::PT_BRA,
|
||||
Common::ES_ESP,
|
||||
};
|
||||
bool langTable[ARRAYSIZE(langs)];
|
||||
|
||||
memset(langTable, 0, sizeof(langTable));
|
||||
|
||||
// Iterate over all files in the given directory
|
||||
for (Common::FSList::const_iterator file = fslist.begin(); file != fslist.end(); ++file) {
|
||||
if (!file->isDirectory()) {
|
||||
if (0 == scumm_stricmp("sky.dsk", file->getName().c_str())) {
|
||||
Common::File dataDisk;
|
||||
if (dataDisk.open(*file)) {
|
||||
hasSkyDsk = true;
|
||||
dataDiskSize = dataDisk.size();
|
||||
if (dataDiskSize == 73123264 || dataDiskSize == 75893200)
|
||||
dataDiskHeadMD5 = Common::computeStreamMD5AsString(dataDisk, 5000);
|
||||
}
|
||||
}
|
||||
|
||||
if (0 == scumm_stricmp("sky.dnr", file->getName().c_str())) {
|
||||
Common::File dinner;
|
||||
if (dinner.open(*file)) {
|
||||
hasSkyDnr = true;
|
||||
dinnerTableEntries = dinner.readUint32LE();
|
||||
for (int i = 0; i < dinnerTableEntries; i++) {
|
||||
uint16 entry = dinner.readUint16LE();
|
||||
dinner.readUint16LE();
|
||||
if (entry >= 60600 && entry < 60600 + 8 * ARRAYSIZE(langs) && (entry % 8) == 0)
|
||||
langTable[(entry - 60600) / 8] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (0 == scumm_stricmp("sky.exe", file->getName().c_str())) {
|
||||
Common::File skyExe;
|
||||
if (skyExe.open(*file)) {
|
||||
exeSize = skyExe.size();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (hasSkyDsk && hasSkyDnr) {
|
||||
// Match found, add to list of candidates, then abort inner loop.
|
||||
// The game detector uses US English by default. We want British
|
||||
// English to match the recorded voices better.
|
||||
const SkyVersion *sv = skyVersions;
|
||||
while (sv->dinnerTableEntries) {
|
||||
if (dinnerTableEntries == sv->dinnerTableEntries &&
|
||||
(sv->dataDiskSize == dataDiskSize || sv->dataDiskSize == -1)) {
|
||||
break;
|
||||
}
|
||||
++sv;
|
||||
}
|
||||
|
||||
DetectedGame game;
|
||||
Common::Language lang = Common::Language::UNK_LANG;
|
||||
if (dataDiskSize == 73123264 && dataDiskHeadMD5 == "886d6faecd97488be09b73f4f87b92d9")
|
||||
lang = Common::Language::RU_RUS;
|
||||
if (dataDiskSize == 75893200 && dataDiskHeadMD5 == "886d6faecd97488be09b73f4f87b92d9")
|
||||
lang = Common::Language::HE_ISR;
|
||||
|
||||
if (sv->dinnerTableEntries) {
|
||||
Common::String extra = Common::String::format("v0.0%d %s", sv->version, sv->extraDesc);
|
||||
|
||||
game = DetectedGame(getName(), skySetting.gameId, skySetting.description, lang, Common::kPlatformDOS, extra);
|
||||
game.setGUIOptions(sv->guioptions);
|
||||
} else {
|
||||
game = DetectedGame(getName(), skySetting.gameId, skySetting.description, lang);
|
||||
}
|
||||
|
||||
if (lang == Common::Language::UNK_LANG) {
|
||||
for (uint i = 0; i < ARRAYSIZE(langs); i++) {
|
||||
if (langTable[i])
|
||||
game.appendGUIOptions(Common::getGameGUIOptionsDescriptionLanguage(langs[i]));
|
||||
}
|
||||
|
||||
if (exeSize == 575538)
|
||||
game.appendGUIOptions(Common::getGameGUIOptionsDescriptionLanguage(Common::ZH_TWN));
|
||||
} else
|
||||
game.appendGUIOptions(Common::getGameGUIOptionsDescriptionLanguage(lang));
|
||||
|
||||
detectedGames.push_back(game);
|
||||
}
|
||||
|
||||
return detectedGames;
|
||||
}
|
||||
|
||||
REGISTER_PLUGIN_STATIC(SKY_DETECTION, PLUGIN_TYPE_ENGINE_DETECTION, SkyMetaEngineDetection);
|
||||
371
engines/sky/disk.cpp
Normal file
371
engines/sky/disk.cpp
Normal file
@@ -0,0 +1,371 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#include "common/debug.h"
|
||||
#include "common/textconsole.h"
|
||||
#include "common/endian.h"
|
||||
#include "common/file.h"
|
||||
|
||||
#include "sky/disk.h"
|
||||
#include "sky/sky.h"
|
||||
#include "sky/struc.h"
|
||||
|
||||
namespace Sky {
|
||||
|
||||
static const char *const dataFilename = "sky.dsk";
|
||||
static const char *const dinnerFilename = "sky.dnr";
|
||||
|
||||
Disk::Disk() {
|
||||
_dataDiskHandle = new Common::File();
|
||||
Common::File *dnrHandle = new Common::File();
|
||||
|
||||
dnrHandle->open(dinnerFilename);
|
||||
if (!dnrHandle->isOpen())
|
||||
error("Could not open %s", dinnerFilename);
|
||||
|
||||
if (!(_dinnerTableEntries = dnrHandle->readUint32LE()))
|
||||
error("Error reading from sky.dnr"); //even though it was opened correctly?!
|
||||
|
||||
_dinnerTableArea = (uint8 *)malloc(_dinnerTableEntries * 8);
|
||||
uint32 entriesRead = dnrHandle->read(_dinnerTableArea, 8 * _dinnerTableEntries) / 8;
|
||||
|
||||
if (entriesRead != _dinnerTableEntries)
|
||||
error("entriesRead != dinnerTableEntries. [%d/%d]", entriesRead, _dinnerTableEntries);
|
||||
|
||||
_dataDiskHandle->open(dataFilename);
|
||||
if (!_dataDiskHandle->isOpen())
|
||||
error("Error opening %s", dataFilename);
|
||||
|
||||
debug("Found BASS version v0.0%d (%d dnr entries)", determineGameVersion(), _dinnerTableEntries);
|
||||
|
||||
memset(_buildList, 0, 60 * 2);
|
||||
memset(_loadedFilesList, 0, 60 * 4);
|
||||
|
||||
dnrHandle->close();
|
||||
delete dnrHandle;
|
||||
}
|
||||
|
||||
Disk::~Disk() {
|
||||
if (_dataDiskHandle->isOpen())
|
||||
_dataDiskHandle->close();
|
||||
fnFlushBuffers();
|
||||
free(_dinnerTableArea);
|
||||
delete _dataDiskHandle;
|
||||
}
|
||||
|
||||
bool Disk::fileExists(uint16 fileNr) {
|
||||
return (getFileInfo(fileNr) != NULL);
|
||||
}
|
||||
|
||||
// allocate memory, load the file and return a pointer
|
||||
uint8 *Disk::loadFile(uint16 fileNr) {
|
||||
uint8 cflag;
|
||||
|
||||
debug(3, "load file %d,%d (%d)", (fileNr >> 11), (fileNr & 2047), fileNr);
|
||||
|
||||
uint8 *fileInfoPtr = getFileInfo(fileNr);
|
||||
if (fileInfoPtr == NULL) {
|
||||
debug(1, "File %d not found", fileNr);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
uint32 fileFlags = READ_LE_UINT24(fileInfoPtr + 5);
|
||||
uint32 fileSize = fileFlags & 0x03fffff;
|
||||
uint32 fileOffset = READ_LE_UINT32(fileInfoPtr + 2) & 0x0ffffff;
|
||||
|
||||
_lastLoadedFileSize = fileSize;
|
||||
cflag = (uint8)((fileOffset >> 23) & 0x1);
|
||||
fileOffset &= 0x7FFFFF;
|
||||
|
||||
if (cflag) {
|
||||
if (SkyEngine::_systemVars->gameVersion == 331)
|
||||
fileOffset <<= 3;
|
||||
else
|
||||
fileOffset <<= 4;
|
||||
}
|
||||
|
||||
uint8 *fileDest = (uint8 *)malloc(fileSize + 4); // allocate memory for file
|
||||
|
||||
_dataDiskHandle->seek(fileOffset, SEEK_SET);
|
||||
|
||||
//now read in the data
|
||||
int32 bytesRead = _dataDiskHandle->read(fileDest, fileSize);
|
||||
|
||||
if (bytesRead != (int32)fileSize)
|
||||
warning("Unable to read %d bytes from datadisk (%d bytes read)", fileSize, bytesRead);
|
||||
|
||||
cflag = (uint8)((fileFlags >> 23) & 0x1);
|
||||
//if cflag == 0 then file is compressed, 1 == uncompressed
|
||||
|
||||
DataFileHeader *fileHeader = (DataFileHeader *)fileDest;
|
||||
|
||||
if ((!cflag) && ((FROM_LE_16(fileHeader->flag) >> 7) & 1)) {
|
||||
debug(4, "File is RNC compressed.");
|
||||
|
||||
uint32 decompSize = (FROM_LE_16(fileHeader->flag) & ~0xFF) << 8;
|
||||
decompSize |= FROM_LE_16(fileHeader->s_tot_size);
|
||||
|
||||
uint8 *uncompDest = (uint8 *)malloc(decompSize);
|
||||
|
||||
int32 unpackLen;
|
||||
void *output, *input = fileDest + sizeof(DataFileHeader);
|
||||
|
||||
if ((fileFlags >> 22) & 0x1) { //do we include the header?
|
||||
// don't return the file's header
|
||||
output = uncompDest;
|
||||
unpackLen = _rncDecoder.unpackM1(input, fileSize - sizeof(DataFileHeader), output);
|
||||
} else {
|
||||
#ifdef SCUMM_BIG_ENDIAN
|
||||
// Convert DataFileHeader to BE (it only consists of 16 bit words)
|
||||
uint16 *headPtr = (uint16 *)fileDest;
|
||||
for (uint i = 0; i < sizeof(DataFileHeader) / 2; i++)
|
||||
*(headPtr + i) = READ_LE_UINT16(headPtr + i);
|
||||
#endif
|
||||
|
||||
memcpy(uncompDest, fileDest, sizeof(DataFileHeader));
|
||||
output = uncompDest + sizeof(DataFileHeader);
|
||||
unpackLen = _rncDecoder.unpackM1(input, fileSize - sizeof(DataFileHeader), output);
|
||||
if (unpackLen)
|
||||
unpackLen += sizeof(DataFileHeader);
|
||||
}
|
||||
|
||||
debug(5, "UnpackM1 returned: %d", unpackLen);
|
||||
|
||||
if (unpackLen == 0) { //Unpack returned 0: file was probably not packed.
|
||||
free(uncompDest);
|
||||
return fileDest;
|
||||
} else {
|
||||
if (unpackLen != (int32)decompSize)
|
||||
debug(1, "ERROR: File %d: invalid decomp size! (was: %d, should be: %d)", fileNr, unpackLen, decompSize);
|
||||
_lastLoadedFileSize = decompSize;
|
||||
|
||||
free(fileDest);
|
||||
return uncompDest;
|
||||
}
|
||||
} else {
|
||||
#ifdef SCUMM_BIG_ENDIAN
|
||||
if (!cflag) {
|
||||
uint16 *headPtr = (uint16 *)fileDest;
|
||||
for (uint i = 0; i < sizeof(DataFileHeader) / 2; i++)
|
||||
*(headPtr + i) = READ_LE_UINT16(headPtr + i);
|
||||
}
|
||||
#endif
|
||||
return fileDest;
|
||||
}
|
||||
}
|
||||
|
||||
uint16 *Disk::loadScriptFile(uint16 fileNr) {
|
||||
uint16 *buf = (uint16 *)loadFile(fileNr);
|
||||
#ifdef SCUMM_BIG_ENDIAN
|
||||
for (uint i = 0; i < _lastLoadedFileSize / 2; i++)
|
||||
buf[i] = FROM_LE_16(buf[i]);
|
||||
#endif
|
||||
return buf;
|
||||
}
|
||||
|
||||
uint8 *Disk::getFileInfo(uint16 fileNr) {
|
||||
uint16 i;
|
||||
uint16 *dnrTbl16Ptr = (uint16 *)_dinnerTableArea;
|
||||
|
||||
for (i = 0; i < _dinnerTableEntries; i++) {
|
||||
if (READ_LE_UINT16(dnrTbl16Ptr) == fileNr) {
|
||||
debug(4, "file %d found", fileNr);
|
||||
return (uint8 *)dnrTbl16Ptr;
|
||||
}
|
||||
dnrTbl16Ptr += 4;
|
||||
}
|
||||
|
||||
return 0; //not found
|
||||
}
|
||||
|
||||
void Disk::fnCacheChip(uint16 *fList) {
|
||||
// fnCacheChip is called after fnCacheFast
|
||||
uint16 cnt = 0;
|
||||
while (_buildList[cnt])
|
||||
cnt++;
|
||||
uint16 fCnt = 0;
|
||||
do {
|
||||
_buildList[cnt + fCnt] = fList[fCnt] & 0x7FFFU;
|
||||
fCnt++;
|
||||
} while (fList[fCnt-1]);
|
||||
fnCacheFiles();
|
||||
}
|
||||
|
||||
void Disk::fnCacheFast(uint16 *fList) {
|
||||
if (fList != NULL) {
|
||||
uint8 cnt = 0;
|
||||
do {
|
||||
_buildList[cnt] = fList[cnt] & 0x7FFFU;
|
||||
cnt++;
|
||||
} while (fList[cnt-1]);
|
||||
}
|
||||
}
|
||||
|
||||
void Disk::fnCacheFiles() {
|
||||
uint16 lCnt, bCnt, targCnt;
|
||||
targCnt = lCnt = 0;
|
||||
bool found;
|
||||
while (_loadedFilesList[lCnt]) {
|
||||
bCnt = 0;
|
||||
found = false;
|
||||
while (_buildList[bCnt] && (!found)) {
|
||||
if ((_buildList[bCnt] & 0x7FFFU) == _loadedFilesList[lCnt])
|
||||
found = true;
|
||||
else
|
||||
bCnt++;
|
||||
}
|
||||
if (found) {
|
||||
_loadedFilesList[targCnt] = _loadedFilesList[lCnt];
|
||||
targCnt++;
|
||||
} else {
|
||||
free(SkyEngine::_itemList[_loadedFilesList[lCnt] & 2047]);
|
||||
SkyEngine::_itemList[_loadedFilesList[lCnt] & 2047] = NULL;
|
||||
}
|
||||
lCnt++;
|
||||
}
|
||||
_loadedFilesList[targCnt] = 0; // mark end of list
|
||||
bCnt = 0;
|
||||
while (_buildList[bCnt]) {
|
||||
if ((_buildList[bCnt] & 0x7FF) == 0x7FF) {
|
||||
// amiga dummy files
|
||||
bCnt++;
|
||||
continue;
|
||||
}
|
||||
lCnt = 0;
|
||||
found = false;
|
||||
while (_loadedFilesList[lCnt] && (!found)) {
|
||||
if (_loadedFilesList[lCnt] == (_buildList[bCnt] & 0x7FFFU))
|
||||
found = true;
|
||||
lCnt++;
|
||||
}
|
||||
if (found) {
|
||||
bCnt++;
|
||||
continue;
|
||||
}
|
||||
// ok, we really have to load the file.
|
||||
_loadedFilesList[targCnt] = _buildList[bCnt] & 0x7FFFU;
|
||||
targCnt++;
|
||||
_loadedFilesList[targCnt] = 0;
|
||||
SkyEngine::_itemList[_buildList[bCnt] & 2047] = (void**)loadFile(_buildList[bCnt] & 0x7FFF);
|
||||
if (!SkyEngine::_itemList[_buildList[bCnt] & 2047])
|
||||
warning("fnCacheFiles: Disk::loadFile() returned NULL for file %d",_buildList[bCnt] & 0x7FFF);
|
||||
bCnt++;
|
||||
}
|
||||
_buildList[0] = 0;
|
||||
}
|
||||
|
||||
void Disk::refreshFilesList(uint32 *list) {
|
||||
uint8 cnt = 0;
|
||||
while (_loadedFilesList[cnt]) {
|
||||
if (SkyEngine::_itemList[_loadedFilesList[cnt] & 2047])
|
||||
free(SkyEngine::_itemList[_loadedFilesList[cnt] & 2047]);
|
||||
SkyEngine::_itemList[_loadedFilesList[cnt] & 2047] = NULL;
|
||||
cnt++;
|
||||
}
|
||||
cnt = 0;
|
||||
while (list[cnt]) {
|
||||
_loadedFilesList[cnt] = list[cnt];
|
||||
SkyEngine::_itemList[_loadedFilesList[cnt] & 2047] = (void**)loadFile((uint16)(_loadedFilesList[cnt] & 0x7FFF));
|
||||
cnt++;
|
||||
}
|
||||
_loadedFilesList[cnt] = 0;
|
||||
}
|
||||
|
||||
void Disk::fnMiniLoad(uint16 fileNum) {
|
||||
uint16 cnt = 0;
|
||||
while (_loadedFilesList[cnt]) {
|
||||
if (_loadedFilesList[cnt] == fileNum)
|
||||
return;
|
||||
cnt++;
|
||||
}
|
||||
_loadedFilesList[cnt] = fileNum & 0x7FFFU;
|
||||
_loadedFilesList[cnt + 1] = 0;
|
||||
SkyEngine::_itemList[fileNum & 2047] = (void**)loadFile(fileNum);
|
||||
}
|
||||
|
||||
void Disk::fnFlushBuffers() {
|
||||
// dump all loaded sprites
|
||||
uint8 lCnt = 0;
|
||||
while (_loadedFilesList[lCnt]) {
|
||||
free(SkyEngine::_itemList[_loadedFilesList[lCnt] & 2047]);
|
||||
SkyEngine::_itemList[_loadedFilesList[lCnt] & 2047] = NULL;
|
||||
lCnt++;
|
||||
}
|
||||
_loadedFilesList[0] = 0;
|
||||
}
|
||||
|
||||
void Disk::dumpFile(uint16 fileNr) {
|
||||
char buf[128];
|
||||
Common::DumpFile out;
|
||||
byte* filePtr;
|
||||
|
||||
filePtr = loadFile(fileNr);
|
||||
Common::sprintf_s(buf, "dumps/file-%d.dmp", fileNr);
|
||||
|
||||
if (!Common::File::exists(buf)) {
|
||||
if (out.open(buf))
|
||||
out.write(filePtr, _lastLoadedFileSize);
|
||||
}
|
||||
free(filePtr);
|
||||
}
|
||||
|
||||
uint32 Disk::determineGameVersion() {
|
||||
//determine game version based on number of entries in dinner table
|
||||
switch (_dinnerTableEntries) {
|
||||
case 232:
|
||||
// German floppy demo (v0.0272)
|
||||
return 272;
|
||||
case 243:
|
||||
// pc gamer demo (v0.0109)
|
||||
return 109;
|
||||
case 247:
|
||||
// English floppy demo (v0.0267)
|
||||
return 267;
|
||||
case 1404:
|
||||
//floppy (v0.0288)
|
||||
return 288;
|
||||
case 1413:
|
||||
//floppy (v0.0303)
|
||||
return 303;
|
||||
case 1445:
|
||||
//floppy (v0.0331 or v0.0348)
|
||||
if (_dataDiskHandle->size() == 8830435)
|
||||
return 348;
|
||||
else
|
||||
return 331;
|
||||
case 1711:
|
||||
//cd demo (v0.0365)
|
||||
return 365;
|
||||
case 5099:
|
||||
//cd (v0.0368)
|
||||
return 368;
|
||||
case 5097:
|
||||
//cd (v0.0372)
|
||||
return 372;
|
||||
default:
|
||||
//unknown version
|
||||
error("Unknown game version! %d dinner table entries", _dinnerTableEntries);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
} // End of namespace Sky
|
||||
73
engines/sky/disk.h
Normal file
73
engines/sky/disk.h
Normal file
@@ -0,0 +1,73 @@
|
||||
/* 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 SKY_DISK_H
|
||||
#define SKY_DISK_H
|
||||
|
||||
|
||||
#include "common/scummsys.h"
|
||||
#include "common/compression/rnc_deco.h"
|
||||
|
||||
#define MAX_FILES_IN_LIST 60
|
||||
|
||||
namespace Common {
|
||||
class File;
|
||||
}
|
||||
|
||||
namespace Sky {
|
||||
|
||||
class Disk {
|
||||
public:
|
||||
Disk();
|
||||
~Disk();
|
||||
|
||||
uint8 *loadFile(uint16 fileNr);
|
||||
uint16 *loadScriptFile(uint16 fileNr);
|
||||
bool fileExists(uint16 fileNr);
|
||||
|
||||
uint32 determineGameVersion();
|
||||
|
||||
uint32 _lastLoadedFileSize;
|
||||
|
||||
void fnMiniLoad(uint16 fileNum);
|
||||
void fnCacheFast(uint16 *fList);
|
||||
void fnCacheChip(uint16 *fList);
|
||||
void fnCacheFiles();
|
||||
void fnFlushBuffers();
|
||||
uint32 *giveLoadedFilesList() { return _loadedFilesList; }
|
||||
void refreshFilesList(uint32 *list);
|
||||
|
||||
protected:
|
||||
uint8 *getFileInfo(uint16 fileNr);
|
||||
void dumpFile(uint16 fileNr);
|
||||
|
||||
uint32 _dinnerTableEntries;
|
||||
uint8 *_dinnerTableArea;
|
||||
Common::File *_dataDiskHandle;
|
||||
Common::RncDecoder _rncDecoder;
|
||||
|
||||
uint16 _buildList[MAX_FILES_IN_LIST];
|
||||
uint32 _loadedFilesList[MAX_FILES_IN_LIST];
|
||||
};
|
||||
|
||||
} // End of namespace Sky
|
||||
|
||||
#endif
|
||||
260
engines/sky/grid.cpp
Normal file
260
engines/sky/grid.cpp
Normal file
@@ -0,0 +1,260 @@
|
||||
/* 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 "sky/compact.h"
|
||||
#include "sky/disk.h"
|
||||
#include "sky/grid.h"
|
||||
#include "sky/logic.h"
|
||||
#include "sky/compact.h"
|
||||
|
||||
namespace Sky {
|
||||
|
||||
#define GRID_FILE_START 60000
|
||||
|
||||
int8 Grid::_gridConvertTable[] = {
|
||||
0, //0
|
||||
1, //1
|
||||
2, //2
|
||||
3, //3
|
||||
4, //4
|
||||
5, //5
|
||||
6, //6
|
||||
7, //7
|
||||
8, //8
|
||||
9, //9
|
||||
10, //10
|
||||
11, //11
|
||||
12, //12
|
||||
13, //13
|
||||
14, //14
|
||||
15, //15
|
||||
16, //16
|
||||
17, //17
|
||||
18, //18
|
||||
19, //19
|
||||
20, //20
|
||||
21, //21
|
||||
22, //22
|
||||
23, //23
|
||||
24, //24
|
||||
25, //25
|
||||
26, //26
|
||||
27, //27
|
||||
28, //28
|
||||
29, //29
|
||||
30, //30
|
||||
31, //31
|
||||
32, //32
|
||||
33, //33
|
||||
34, //34
|
||||
-1, //35
|
||||
35, //36
|
||||
36, //37
|
||||
37, //38
|
||||
38, //39
|
||||
39, //40
|
||||
40, //41
|
||||
41, //42
|
||||
-1, //43
|
||||
42, //44
|
||||
43, //45
|
||||
44, //46
|
||||
45, //47
|
||||
46, //48
|
||||
-1, //49
|
||||
-1, //50
|
||||
-1, //51
|
||||
-1, //52
|
||||
-1, //53
|
||||
-1, //54
|
||||
-1, //55
|
||||
-1, //56
|
||||
-1, //57
|
||||
-1, //58
|
||||
-1, //59
|
||||
-1, //60
|
||||
-1, //61
|
||||
-1, //62
|
||||
-1, //63
|
||||
-1, //64
|
||||
47, //65
|
||||
TOT_NO_GRIDS, //66
|
||||
48, //67
|
||||
49, //68
|
||||
50, //69
|
||||
51, //70
|
||||
52, //71
|
||||
53, //72
|
||||
54, //73
|
||||
55, //74
|
||||
56, //75
|
||||
57, //76
|
||||
58, //77
|
||||
59, //78
|
||||
60, //79
|
||||
-1, //80
|
||||
61, //81
|
||||
62, //82
|
||||
-1, //83
|
||||
-1, //84
|
||||
-1, //85
|
||||
-1, //86
|
||||
-1, //87
|
||||
-1, //88
|
||||
TOT_NO_GRIDS, //89
|
||||
63, //90
|
||||
64, //91
|
||||
65, //92
|
||||
66, //93
|
||||
67, //94
|
||||
68, //95
|
||||
69, //96
|
||||
};
|
||||
|
||||
Grid::Grid(Disk *pDisk, SkyCompact *skyCompact) {
|
||||
for (int cnt = 0; cnt < TOT_NO_GRIDS; cnt++)
|
||||
_gameGrids[cnt] = NULL;
|
||||
_skyDisk = pDisk;
|
||||
_skyCompact = skyCompact;
|
||||
}
|
||||
|
||||
Grid::~Grid() {
|
||||
for (uint8 cnt = 0; cnt < TOT_NO_GRIDS; cnt++)
|
||||
if (_gameGrids[cnt])
|
||||
free(_gameGrids[cnt]);
|
||||
}
|
||||
|
||||
void Grid::loadGrids() {
|
||||
// no endian conversion necessary as I'm using uint8* instead of uint32*
|
||||
for (uint8 cnt = 0; cnt < TOT_NO_GRIDS; cnt++) {
|
||||
if (_gameGrids[cnt])
|
||||
free(_gameGrids[cnt]);
|
||||
_gameGrids[cnt] = _skyDisk->loadFile(GRID_FILE_START + cnt);
|
||||
}
|
||||
if (!SkyEngine::isDemo()) { // single disk demos never get that far
|
||||
// Reloading the grids can sometimes cause problems eg when reichs door is
|
||||
// open the door grid bit gets replaced so you can't get back in (or out)
|
||||
if (Logic::_scriptVariables[REICH_DOOR_FLAG])
|
||||
removeGrid(256, 280, 1, _skyCompact->fetchCpt(CPT_REICH_DOOR_20));
|
||||
//removeGrid(256, 280, 1, &SkyCompact::reich_door_20);
|
||||
}
|
||||
}
|
||||
|
||||
bool Grid::getGridValues(Compact *cpt, uint8 *resGrid, uint32 *resBitNum, uint32 *resWidth) {
|
||||
uint16 width = SkyCompact::getMegaSet(cpt)->gridWidth;
|
||||
return getGridValues(cpt->xcood, cpt->ycood, width, cpt, resGrid, resBitNum, resWidth);
|
||||
}
|
||||
|
||||
bool Grid::getGridValues(uint32 x, uint32 y, uint32 width, Compact *cpt, uint8 *resGrid, uint32 *resBitNum, uint32 *resWidth) {
|
||||
uint32 bitPos;
|
||||
if (y < TOP_LEFT_Y)
|
||||
return false; // off screen
|
||||
y -= TOP_LEFT_Y;
|
||||
y >>= 3; // convert to blocks
|
||||
if (y >= GAME_SCREEN_HEIGHT >> 3)
|
||||
return false; // off screen
|
||||
bitPos = y * 40;
|
||||
width++;
|
||||
x >>= 3; // convert to blocks
|
||||
|
||||
if (x < (TOP_LEFT_X >> 3)) { // at least partially off screen
|
||||
if (x + width < (TOP_LEFT_X >> 3))
|
||||
return false; // completely off screen
|
||||
else {
|
||||
width -= (TOP_LEFT_X >> 3) - x;
|
||||
x = 0;
|
||||
}
|
||||
} else
|
||||
x -= TOP_LEFT_X >> 3;
|
||||
|
||||
if ((GAME_SCREEN_WIDTH >> 3) <= x)
|
||||
return false; // off screen
|
||||
if ((GAME_SCREEN_WIDTH >> 3) < x + width) // partially off screen
|
||||
width = (GAME_SCREEN_WIDTH >> 3) - x;
|
||||
|
||||
bitPos += x;
|
||||
assert((_gridConvertTable[cpt->screen] >= 0) && (_gridConvertTable[cpt->screen] < TOT_NO_GRIDS));
|
||||
*resGrid = (uint8)_gridConvertTable[cpt->screen];
|
||||
|
||||
uint32 tmpBits = 0x1F - (bitPos&0x1F);
|
||||
bitPos &= ~0x1F; // divide into dword address and bit number
|
||||
bitPos += tmpBits;
|
||||
*resBitNum = bitPos;
|
||||
*resWidth = width;
|
||||
return true;
|
||||
}
|
||||
|
||||
void Grid::removeObjectFromWalk(Compact *cpt) {
|
||||
uint32 bitNum, width;
|
||||
uint8 gridIdx;
|
||||
if (getGridValues(cpt, &gridIdx, &bitNum, &width))
|
||||
removeObjectFromWalk(gridIdx, bitNum, width);
|
||||
}
|
||||
|
||||
void Grid::removeObjectFromWalk(uint8 gridIdx, uint32 bitNum, uint32 width) {
|
||||
for (uint32 cnt = 0; cnt < width; cnt++) {
|
||||
_gameGrids[gridIdx][bitNum >> 3] &= ~(1 << (bitNum & 0x7));
|
||||
if ((bitNum & 0x1F) == 0)
|
||||
bitNum += 0x3F;
|
||||
else
|
||||
bitNum--;
|
||||
}
|
||||
}
|
||||
|
||||
void Grid::objectToWalk(Compact *cpt) {
|
||||
uint32 bitNum, width;
|
||||
uint8 gridIdx;
|
||||
if (getGridValues(cpt, &gridIdx, &bitNum, &width))
|
||||
objectToWalk(gridIdx, bitNum, width);
|
||||
}
|
||||
|
||||
void Grid::objectToWalk(uint8 gridIdx, uint32 bitNum, uint32 width) {
|
||||
for (uint32 cnt = 0; cnt < width; cnt++) {
|
||||
_gameGrids[gridIdx][bitNum >> 3] |= (1 << (bitNum & 0x7));
|
||||
if ((bitNum & 0x1F) == 0)
|
||||
bitNum += 0x3F;
|
||||
else
|
||||
bitNum--;
|
||||
}
|
||||
}
|
||||
|
||||
void Grid::plotGrid(uint32 x, uint32 y, uint32 width, Compact *cpt) {
|
||||
uint32 resBitPos, resWidth;
|
||||
uint8 resGridIdx;
|
||||
if (getGridValues(x, y, width-1, cpt, &resGridIdx, &resBitPos, &resWidth))
|
||||
objectToWalk(resGridIdx, resBitPos, resWidth);
|
||||
}
|
||||
|
||||
void Grid::removeGrid(uint32 x, uint32 y, uint32 width, Compact *cpt) {
|
||||
uint32 resBitPos, resWidth;
|
||||
uint8 resGridIdx;
|
||||
if (getGridValues(x, y, width, cpt, &resGridIdx, &resBitPos, &resWidth))
|
||||
removeObjectFromWalk(resGridIdx, resBitPos, resWidth);
|
||||
}
|
||||
|
||||
uint8 *Grid::giveGrid(uint32 pScreen) {
|
||||
if ((_gridConvertTable[pScreen] >= 0) && (_gridConvertTable[pScreen] < TOT_NO_GRIDS)) {
|
||||
return _gameGrids[_gridConvertTable[pScreen]];
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
} // End of namespace Sky
|
||||
67
engines/sky/grid.h
Normal file
67
engines/sky/grid.h
Normal file
@@ -0,0 +1,67 @@
|
||||
/* 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 SKY_GRID_H
|
||||
#define SKY_GRID_H
|
||||
|
||||
|
||||
#include "common/scummsys.h"
|
||||
#include "skydefs.h"
|
||||
|
||||
namespace Sky {
|
||||
|
||||
struct Compact;
|
||||
class Disk;
|
||||
class SkyCompact;
|
||||
|
||||
class Grid {
|
||||
public:
|
||||
Grid(Disk *pDisk, SkyCompact *skyCompact);
|
||||
~Grid();
|
||||
|
||||
// grid.asm routines
|
||||
void loadGrids();
|
||||
void removeObjectFromWalk(Compact *cpt);
|
||||
void objectToWalk(Compact *cpt);
|
||||
|
||||
// function.asm
|
||||
// note that this routine does the same as objectToWalk, it just doesn't get
|
||||
// its x, y, width parameters from cpt.
|
||||
void plotGrid(uint32 x, uint32 y, uint32 width, Compact *cpt);
|
||||
// same here, it's basically the same as removeObjectFromWalk
|
||||
void removeGrid(uint32 x, uint32 y, uint32 width, Compact *cpt);
|
||||
uint8 *giveGrid(uint32 pScreen);
|
||||
|
||||
private:
|
||||
void objectToWalk(uint8 gridIdx, uint32 bitNum, uint32 width);
|
||||
void removeObjectFromWalk(uint8 gridIdx, uint32 bitNum, uint32 width);
|
||||
bool getGridValues(Compact *cpt, uint8 *resGrid, uint32 *resBitNum, uint32 *resWidth);
|
||||
bool getGridValues(uint32 x, uint32 y, uint32 width, Compact *cpt, uint8 *resGrid, uint32 *resBitNum, uint32 *resWidth);
|
||||
|
||||
static int8 _gridConvertTable[];
|
||||
uint8 *_gameGrids[TOT_NO_GRIDS];
|
||||
Disk *_skyDisk;
|
||||
SkyCompact *_skyCompact;
|
||||
};
|
||||
|
||||
} // End of namespace Sky
|
||||
|
||||
#endif //SKYGRID_H
|
||||
2004
engines/sky/hufftext.cpp
Normal file
2004
engines/sky/hufftext.cpp
Normal file
File diff suppressed because it is too large
Load Diff
951
engines/sky/intro.cpp
Normal file
951
engines/sky/intro.cpp
Normal file
@@ -0,0 +1,951 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#include "common/endian.h"
|
||||
#include "common/util.h"
|
||||
#include "common/events.h"
|
||||
#include "common/system.h"
|
||||
#include "common/textconsole.h"
|
||||
|
||||
#include "sky/disk.h"
|
||||
#include "sky/intro.h"
|
||||
#include "sky/music/musicbase.h"
|
||||
#include "sky/screen.h"
|
||||
#include "sky/sky.h"
|
||||
#include "sky/sound.h"
|
||||
#include "sky/struc.h"
|
||||
#include "sky/text.h"
|
||||
|
||||
#include "audio/audiostream.h"
|
||||
#include "audio/decoders/raw.h"
|
||||
|
||||
namespace Sky {
|
||||
|
||||
#define SHOWSCREEN 0
|
||||
#define COMMANDEND 0 // end of COMMANDFLIRT block
|
||||
#define FADEUP 1 // fade up palette
|
||||
#define FADEDOWN 2
|
||||
#define DELAY 3
|
||||
#define DOFLIRT 4 // start flirt sequence (and wait for it to finish)
|
||||
#define SCROLLFLIRT 5 // start special floppy intro flirt sequence (and wait for it)
|
||||
#define COMMANDFLIRT 6 // start flirt sequence and wait for it, while processing command block
|
||||
#define BGFLIRT 7 // start flirt sequence without waiting for it
|
||||
#define WAITFLIRT 8 // wait for sequence started by BGFLIRT
|
||||
#define STOPFLIRT 9
|
||||
#define STARTMUSIC 10
|
||||
#define WAITMUSIC 11
|
||||
#define PLAYVOICE 12
|
||||
#define WAITVOICE 13
|
||||
#define LOADBG 14 // load new background sound
|
||||
#define PLAYBG 15 // play background sound
|
||||
#define LOOPBG 16 // loop background sound
|
||||
#define STOPBG 17 // stop background sound
|
||||
#define CLEARBOTTOM 18 // clear the screen
|
||||
#define SEQEND 65535 // end of intro sequence
|
||||
|
||||
// Modifier flag for SHOWSCREEN when we want the image to cover the entire
|
||||
// screen.
|
||||
#define FULLSCREEN 0x8000
|
||||
|
||||
#define IC_PREPARE_TEXT 20 // commands used in COMMANDFLIRT block
|
||||
#define IC_SHOW_TEXT 21
|
||||
#define IC_REMOVE_TEXT 22
|
||||
#define IC_MAKE_SOUND 23
|
||||
#define IC_FX_VOLUME 24
|
||||
|
||||
#define FRAME_SIZE (GAME_SCREEN_WIDTH * GAME_SCREEN_HEIGHT)
|
||||
#define INTRO_TEXT_WIDTH 128
|
||||
//CD intro file defines
|
||||
#define CDV_00 59500
|
||||
#define CD_PAL 59501
|
||||
#define CD_1_LOG 59502
|
||||
#define CD_1 59503
|
||||
#define CDV_01 59504
|
||||
#define CDV_02 59505
|
||||
#define CD_2 59506
|
||||
#define CDV_03 59507
|
||||
#define CDV_04 59508
|
||||
#define CD_3 59509
|
||||
#define CDV_05 59510
|
||||
#define CDV_06 59511
|
||||
#define CD_5 59512
|
||||
#define CDV_07 59513
|
||||
#define CDV_08 59514
|
||||
#define CDV_09 59515
|
||||
#define CD_7 59516
|
||||
#define CDV_10 59518
|
||||
#define CD_11 59519
|
||||
#define CDV_11 59520
|
||||
#define CD_11_PAL 59521
|
||||
#define CD_11_LOG 59522
|
||||
#define CDV_12 59523
|
||||
#define CD_13 59524
|
||||
#define CDV_13 59525
|
||||
#define CDV_14 59527
|
||||
#define CDV_15 59528
|
||||
#define CD_15_PAL 59529
|
||||
#define CD_15_LOG 59530
|
||||
#define CDV_16 59531
|
||||
#define CD_17_LOG 59532
|
||||
#define CD_17 59533
|
||||
#define CDV_17 59534
|
||||
#define CDV_18 59535
|
||||
#define CDV_19 59536
|
||||
#define CD_19_PAL 59537
|
||||
#define CD_19_LOG 59538
|
||||
#define CDV_20 59539
|
||||
#define CD_20_LOG 59540
|
||||
#define CDV_21 59541
|
||||
#define CD_21_LOG 59542
|
||||
#define CDV_22 59545
|
||||
#define CDV_23 59546
|
||||
#define CD_23_PAL 59547
|
||||
#define CD_24_LOG 59550
|
||||
#define CDV_24 59551
|
||||
#define CDV_25 59554
|
||||
#define CDV_26 59556
|
||||
#define CD_27 59557
|
||||
#define CDV_27 59558
|
||||
#define CD_27_PAL 59559
|
||||
#define CD_27_LOG 59560
|
||||
#define CDV_28 59561
|
||||
#define CDV_29 59562
|
||||
#define CDV_30 59563
|
||||
#define CDV_31 59565
|
||||
#define CDV_32 59566
|
||||
#define CDV_33 59567
|
||||
#define CDV_34 59568
|
||||
#define CD_35 59569
|
||||
#define CDV_35 59570
|
||||
#define CD_35_PAL 59571
|
||||
#define CD_35_LOG 59572
|
||||
#define CDV_36 59574
|
||||
#define CD_37 59575
|
||||
#define CDV_37 59576
|
||||
#define CD_37_PAL 59577
|
||||
#define CD_37_LOG 59578
|
||||
#define CDV_38 59579
|
||||
#define CDV_39 59581
|
||||
#define CDV_40 59583
|
||||
#define CD_40_PAL 59584
|
||||
#define CD_40_LOG 59585
|
||||
#define CDV_41 59587
|
||||
#define CDV_42 59588
|
||||
#define CD_43 59589
|
||||
#define CDV_43 59590
|
||||
#define CD_43_PAL 59591
|
||||
#define CD_43_LOG 59592
|
||||
#define CDV_44 59594
|
||||
#define CD_45 59595
|
||||
#define CDV_45 59596
|
||||
#define CD_45_PAL 59597
|
||||
#define CD_45_LOG 59598
|
||||
#define CDV_46 59600
|
||||
#define CDV_47 59602
|
||||
#define CD_47_PAL 59603
|
||||
#define CD_47_LOG 59604
|
||||
#define CD_48 59605
|
||||
#define CDV_48 59606
|
||||
#define CD_48_PAL 59607
|
||||
#define CD_48_LOG 59608
|
||||
#define CD_49 59609
|
||||
#define CDV_49 59610
|
||||
#define CD_50 59611
|
||||
#define CDV_50 59612
|
||||
#define CDV_51 59613
|
||||
#define CDV_52 59614
|
||||
#define CDV_53 59615
|
||||
#define CDV_54 59616
|
||||
#define CDV_55 59618
|
||||
#define CD_55_PAL 59619
|
||||
#define CD_55_LOG 59620
|
||||
#define CDV_56 59621
|
||||
#define CDV_57 59622
|
||||
#define CD_58 59623
|
||||
#define CDV_58 59624
|
||||
#define CD_58_PAL 59625
|
||||
#define CD_58_LOG 59626
|
||||
#define CDV_59 59627
|
||||
#define CDV_60 59628
|
||||
#define CDV_61 59629
|
||||
#define CDV_62 59630
|
||||
#define CDV_63 59631
|
||||
#define CDV_64 59632
|
||||
#define CDV_65 59633
|
||||
#define CDV_66 59635
|
||||
#define CD_66_PAL 59636
|
||||
#define CD_66_LOG 59637
|
||||
#define CDV_67 59639
|
||||
#define CD_67_PAL 59640
|
||||
#define CD_67_LOG 59641
|
||||
#define CDV_68 59642
|
||||
#define CD_69 59643
|
||||
#define CDV_69 59644
|
||||
#define CD_69_PAL 59645
|
||||
#define CD_69_LOG 59646
|
||||
#define CDV_70 59647
|
||||
#define CDV_71 59648
|
||||
#define CDV_72 59649
|
||||
#define CD_72_PAL 59650
|
||||
#define CD_72_LOG 59651
|
||||
#define CD_73_PAL 59652
|
||||
#define CD_73_LOG 59653
|
||||
#define CDV_73 59654
|
||||
#define CDV_74 59655
|
||||
#define CDV_75 59656
|
||||
#define CD_76_PAL 59657
|
||||
#define CD_76_LOG 59658
|
||||
#define CDV_76 59659
|
||||
#define CDV_77 59660
|
||||
#define CD_78_PAL 59661
|
||||
#define CD_78_LOG 59662
|
||||
#define CDV_78 59663
|
||||
#define CDV_79 59664
|
||||
#define CDV_80 59665
|
||||
#define CDV_81 59666
|
||||
#define CDV_82 59667
|
||||
#define CDV_83 59668
|
||||
#define CDV_84 59669
|
||||
#define CDV_85 59670
|
||||
#define CDV_86 59671
|
||||
#define CDV_87 59672
|
||||
#define CD_100 60087
|
||||
#define CD_101_LOG 60088
|
||||
#define CD_101 60099
|
||||
#define CD_102_LOG 60090
|
||||
#define CD_102 60091
|
||||
#define CD_103_PAL 60092
|
||||
#define CD_103_LOG 60093
|
||||
#define CD_103 60094
|
||||
#define CD_104_PAL 60095
|
||||
#define CD_104_LOG 60096
|
||||
#define CD_104 60097
|
||||
#define CD_105 60098
|
||||
|
||||
|
||||
uint16 Intro::_mainIntroSeq[] = {
|
||||
DELAY, 3000, // keep virgin screen up
|
||||
FADEDOWN,
|
||||
SHOWSCREEN, 60112, // revo screen + palette
|
||||
FADEUP, 60113,
|
||||
DELAY, 8000,
|
||||
FADEDOWN,
|
||||
SHOWSCREEN, 60114, // gibbo screen + palette
|
||||
FADEUP, 60115,
|
||||
DELAY, 2000,
|
||||
FADEDOWN,
|
||||
SEQEND
|
||||
};
|
||||
|
||||
uint16 Intro::_cdIntroSeq[] = {
|
||||
/* black screen */
|
||||
PLAYVOICE, CDV_00, // Foster: "The old man was trying to tell the future. Looking for pictures in the campfire..."
|
||||
LOADBG, 59499, // Fire crackle
|
||||
LOOPBG,
|
||||
WAITVOICE,
|
||||
PLAYVOICE, CDV_01, // Shaman: "ohhh, I see evil..."
|
||||
/* Fade up shaman image while he says his line... */
|
||||
SHOWSCREEN, CD_1_LOG,
|
||||
FADEUP, CD_PAL,
|
||||
/* And then play the animation showing the shadows of the fire on his face */
|
||||
BGFLIRT, CD_1,
|
||||
WAITVOICE,
|
||||
PLAYVOICE, CDV_02, // Shaman: "Evil born deep beneath the city... far from the light of day."
|
||||
WAITVOICE,
|
||||
STOPFLIRT,
|
||||
BGFLIRT, CD_2,
|
||||
PLAYVOICE, CDV_03, // Shaman: "I see it growing, safe beneath a sky of steel..."
|
||||
WAITVOICE,
|
||||
PLAYVOICE, CDV_04, // Shaman: "Scheming in the dark... gathering strength."
|
||||
WAITFLIRT,
|
||||
WAITVOICE,
|
||||
PLAYVOICE, CDV_05, // Shaman: "And now... ohhh.... now the evil spreads..."
|
||||
DELAY, 2000,
|
||||
BGFLIRT, CD_3,
|
||||
WAITVOICE,
|
||||
PLAYVOICE, CDV_06, // Shaman: "It sends deadly feelers over the land above..."
|
||||
WAITFLIRT,
|
||||
WAITVOICE,
|
||||
PLAYVOICE, CDV_07, // Shaman: "Across the gap... reaching towards this very place!"
|
||||
BGFLIRT, CD_5,
|
||||
WAITVOICE,
|
||||
PLAYVOICE, CDV_08, // Foster: "I'd seen him do this a hundred times, but I humoured him."
|
||||
WAITVOICE,
|
||||
PLAYVOICE, CDV_09, // Foster: "After all, he'd been like a father to me."
|
||||
WAITFLIRT,
|
||||
WAITVOICE,
|
||||
PLAYVOICE, CDV_10, // Foster: "And what does this evil want here?"
|
||||
BGFLIRT, CD_7,
|
||||
WAITVOICE,
|
||||
PLAYVOICE, CDV_11, // Shaman: "Oh, my son. I fear..."
|
||||
WAITFLIRT,
|
||||
FADEDOWN,
|
||||
SHOWSCREEN, CD_11_LOG,
|
||||
FADEUP, CD_11_PAL,
|
||||
WAITVOICE,
|
||||
PLAYVOICE, CDV_12, // Shaman: "I fear the evil wants you!"
|
||||
DELAY, 1600,
|
||||
BGFLIRT, CD_11,
|
||||
WAITVOICE,
|
||||
PLAYVOICE, CDV_13, // Foster: "That was when Joey piped up..."
|
||||
WAITVOICE,
|
||||
WAITFLIRT,
|
||||
WAITVOICE,
|
||||
PLAYVOICE, CDV_14, // Joey: "Foster! Sensors detect incoming audio source!"
|
||||
LOADBG, 59498, // fire crackle to heli start
|
||||
PLAYBG,
|
||||
DOFLIRT, CD_13,
|
||||
WAITVOICE,
|
||||
PLAYVOICE, CDV_15, // Shaman: "The evil! The evil is nearly here...!"
|
||||
FADEDOWN,
|
||||
SHOWSCREEN, CD_15_LOG,
|
||||
FADEUP, CD_15_PAL,
|
||||
WAITVOICE,
|
||||
LOADBG, 59496, // quiet heli
|
||||
LOOPBG,
|
||||
PLAYVOICE, CDV_16, // Foster: "It sounded more like a 'copter than a demon."
|
||||
WAITVOICE,
|
||||
PLAYVOICE, CDV_17, // Foster: "But next thing, all hell let loose anyway..."
|
||||
DELAY, 2000,
|
||||
SHOWSCREEN, CD_17_LOG,
|
||||
WAITVOICE,
|
||||
BGFLIRT, CD_17,
|
||||
PLAYVOICE, CDV_18, // Shaman: "Run, Foster! Run! Hide from the evil!"
|
||||
LOADBG, 59497, // loud heli
|
||||
LOOPBG,
|
||||
WAITFLIRT,
|
||||
WAITVOICE,
|
||||
FADEDOWN,
|
||||
SHOWSCREEN | FULLSCREEN, CD_19_LOG,
|
||||
FADEUP, CD_19_PAL,
|
||||
PLAYVOICE, CDV_19, // Joey: "Foster! (zzzt) H-Help!"
|
||||
WAITVOICE,
|
||||
PLAYVOICE, CDV_20, // Joey: "Better make my next body move faster, Foster..."
|
||||
FADEDOWN,
|
||||
SHOWSCREEN | FULLSCREEN, CD_20_LOG,
|
||||
FADEUP, CD_19_PAL,
|
||||
WAITVOICE,
|
||||
LOADBG, 59496, // quiet heli
|
||||
LOOPBG,
|
||||
PLAYVOICE, CDV_21, // Foster: "He was only a robot, but, well, I loved the little guy."
|
||||
FADEDOWN,
|
||||
SHOWSCREEN | FULLSCREEN, CD_21_LOG,
|
||||
FADEUP, CD_19_PAL,
|
||||
WAITVOICE,
|
||||
PLAYVOICE, CDV_22, // Foster: "Then, as suddenly as it started, the shooting stopped."
|
||||
LOADBG, 59494, // heli whine
|
||||
PLAYBG,
|
||||
WAITVOICE,
|
||||
PLAYVOICE, CDV_23, // Foster: "There was a moment's silence as the copter cut its rotors, then..."
|
||||
/* fade down while Foster's saying his line */
|
||||
FADEDOWN,
|
||||
WAITVOICE,
|
||||
SHOWSCREEN | FULLSCREEN, CD_24_LOG,
|
||||
FADEUP, CD_23_PAL,
|
||||
PLAYVOICE, CDV_24, // Reich: "Whoever is in charge here, come forward..."
|
||||
WAITVOICE,
|
||||
PLAYVOICE, CDV_25, // Reich: "Now!!"
|
||||
WAITVOICE,
|
||||
PLAYVOICE, CDV_26, // Foster: "Only a fool would have argued with that firepower."
|
||||
WAITVOICE,
|
||||
FADEDOWN,
|
||||
SHOWSCREEN | FULLSCREEN, CD_27_LOG,
|
||||
FADEUP, CD_27_PAL,
|
||||
PLAYVOICE, CDV_27, // Shaman: "... I am the leader of these people... We are peaceful..."
|
||||
WAITVOICE,
|
||||
PLAYVOICE, CDV_29, // Reich: "Bring him here."
|
||||
WAITVOICE,
|
||||
PLAYVOICE, CDV_30, // Guard: "At once, Commander Reich."
|
||||
WAITVOICE,
|
||||
BGFLIRT, CD_27,
|
||||
PLAYVOICE, CDV_31, // Reich: "We're looking for someone..."
|
||||
WAITFLIRT,
|
||||
CLEARBOTTOM,
|
||||
WAITVOICE,
|
||||
PLAYVOICE, CDV_32, // Reich: "Someone who doesn't belong here..."
|
||||
WAITVOICE,
|
||||
PLAYVOICE, CDV_33, // Reich: "Who wasn't born in this garbage dump..."
|
||||
WAITVOICE,
|
||||
PLAYVOICE, CDV_34, // Reich: "Who came from the city as a child..."
|
||||
WAITVOICE,
|
||||
PLAYVOICE, CDV_35, // Reich: "We want to take him home again."
|
||||
WAITVOICE,
|
||||
PLAYVOICE, CDV_36, // Foster: "My mind racing, I remembered where I'd seen that symbol before..."
|
||||
FADEDOWN,
|
||||
SHOWSCREEN | FULLSCREEN, CD_35_LOG,
|
||||
FADEUP, CD_35_PAL,
|
||||
WAITVOICE,
|
||||
PLAYVOICE, CDV_37, // Foster: "It was the day the tribe found me..."
|
||||
DOFLIRT, CD_35,
|
||||
CLEARBOTTOM,
|
||||
WAITVOICE,
|
||||
PLAYVOICE, CDV_38, // Foster: "The day of the crash..."
|
||||
DOFLIRT, CD_37,
|
||||
WAITVOICE,
|
||||
PLAYVOICE, CDV_39, // Foster: "The day my mother died."
|
||||
WAITVOICE,
|
||||
FADEDOWN,
|
||||
SHOWSCREEN | FULLSCREEN, CD_40_LOG,
|
||||
FADEUP, CD_40_PAL,
|
||||
PLAYVOICE, CDV_40, // Shaman: "You alright, city boy?"
|
||||
WAITVOICE,
|
||||
PLAYVOICE, CDV_41, // Shaman: "Got a name, son?"
|
||||
WAITVOICE,
|
||||
PLAYVOICE, CDV_42, // Foster: "R-Robert."
|
||||
WAITVOICE,
|
||||
FADEDOWN,
|
||||
SHOWSCREEN | FULLSCREEN, CD_43_LOG,
|
||||
FADEUP, CD_43_PAL,
|
||||
PLAYVOICE, CDV_43, // Shaman: "Hah! Welcome to the Gap, Robert!"
|
||||
WAITVOICE,
|
||||
DOFLIRT, CD_43,
|
||||
PLAYVOICE, CDV_45, // Foster: "As he patched me up, the old man had gently explained that there was no way back into the City..."
|
||||
FADEDOWN,
|
||||
SHOWSCREEN, CD_45_LOG,
|
||||
FADEUP, CD_45_PAL,
|
||||
WAITVOICE,
|
||||
PLAYVOICE, CDV_46, // Foster: "And I already knew there was nothing he could do for mother."
|
||||
DOFLIRT, CD_45,
|
||||
WAITVOICE,
|
||||
FADEDOWN,
|
||||
SHOWSCREEN | FULLSCREEN, CD_47_LOG,
|
||||
FADEUP, CD_47_PAL,
|
||||
PLAYVOICE, CDV_47, // Foster: "His tribe was poor, but they treated me like one of their own..."
|
||||
WAITVOICE,
|
||||
PLAYVOICE, CDV_48, // Foster: "I learned how to survive in the wasteland they called the Gap..."
|
||||
FADEDOWN,
|
||||
SHOWSCREEN | FULLSCREEN, CD_48_LOG,
|
||||
FADEUP, CD_48_PAL,
|
||||
WAITVOICE,
|
||||
BGFLIRT, CD_48,
|
||||
PLAYVOICE, CDV_49, // Foster: "And scavenging from the City dumps."
|
||||
WAITVOICE,
|
||||
PLAYVOICE, CDV_50, // Foster: "As the years passed, I forgot my life in the City."
|
||||
WAITFLIRT,
|
||||
WAITVOICE,
|
||||
PLAYVOICE, CDV_51, // Foster: "Discovered new talents..."
|
||||
BGFLIRT, CD_49,
|
||||
WAITVOICE,
|
||||
PLAYVOICE, CDV_52, // Foster: "Hah!"
|
||||
WAITVOICE,
|
||||
PLAYVOICE, CDV_53, // Joey: "I'm your (zzzt) friend... call me (zzzt) Joey."
|
||||
WAITVOICE,
|
||||
WAITFLIRT,
|
||||
PLAYVOICE, CDV_54, // Foster: "And got a second name."
|
||||
DOFLIRT, CD_50,
|
||||
WAITVOICE,
|
||||
PLAYVOICE, CDV_55, // Shaman: "This is what we'll call you now you've come of age, son."
|
||||
WAITVOICE,
|
||||
PLAYVOICE, CDV_56, // Shaman: "We found you... we fostered you..."
|
||||
FADEDOWN,
|
||||
SHOWSCREEN, CD_55_LOG,
|
||||
FADEUP, CD_55_PAL,
|
||||
WAITVOICE,
|
||||
PLAYVOICE, CDV_57, // Shaman: "So that makes you Robert Foster."
|
||||
WAITVOICE,
|
||||
FADEDOWN,
|
||||
SHOWSCREEN, CD_58_LOG,
|
||||
FADEUP, CD_58_PAL,
|
||||
PLAYVOICE, CDV_58, // Reich: "...Wasted enough time!"
|
||||
WAITVOICE,
|
||||
PLAYVOICE, CDV_59, // Reich: "Give us the runaway or we'll shoot everyone..."
|
||||
WAITVOICE,
|
||||
PLAYVOICE, CDV_60, // Reich: "Starting with you, grandad!"
|
||||
WAITVOICE,
|
||||
PLAYVOICE, CDV_61, // Foster: "The old man had been right, for once..."
|
||||
WAITVOICE,
|
||||
PLAYVOICE, CDV_62, // Foster: "It was me they wanted."
|
||||
BGFLIRT, CD_58,
|
||||
WAITVOICE,
|
||||
PLAYVOICE, CDV_63, // Shaman: "No, my son! Don't let the evil take you! Run!"
|
||||
WAITVOICE,
|
||||
PLAYVOICE, CDV_64, // Guard: "DNA scan confirms it's him, sir."
|
||||
WAITFLIRT,
|
||||
WAITVOICE,
|
||||
PLAYVOICE, CDV_65, // Foster: "Evil had come to the Gap, just as he said."
|
||||
FADEDOWN,
|
||||
WAITVOICE,
|
||||
SHOWSCREEN, CD_66_LOG,
|
||||
FADEUP, CD_66_PAL,
|
||||
PLAYVOICE, CDV_66, // Reich: "Take him."
|
||||
WAITVOICE,
|
||||
PLAYVOICE, CDV_67, // Foster: "But had the old man seen why it wanted me?"
|
||||
FADEDOWN,
|
||||
SHOWSCREEN, CD_67_LOG,
|
||||
FADEUP, CD_67_PAL,
|
||||
WAITVOICE,
|
||||
PLAYVOICE, CDV_68, // Foster: "Or what it would do next?"
|
||||
WAITVOICE,
|
||||
PLAYVOICE, CDV_69, // Foster: "It was too late to ask him now."
|
||||
FADEDOWN,
|
||||
SHOWSCREEN, CD_69_LOG,
|
||||
FADEUP, CD_69_PAL,
|
||||
WAITVOICE,
|
||||
PLAYVOICE, CDV_70, // Guard: "Leaving destruction zone, Commander Reich."
|
||||
DOFLIRT, CD_69,
|
||||
WAITVOICE,
|
||||
FADEDOWN,
|
||||
PLAYVOICE, CDV_71, // Reich: "Good. Detonate."
|
||||
WAITVOICE,
|
||||
SHOWSCREEN | FULLSCREEN, CD_72_LOG,
|
||||
FADEUP, CD_72_PAL,
|
||||
PLAYVOICE, CDV_72, // Foster: "Much too late."
|
||||
WAITVOICE,
|
||||
FADEDOWN,
|
||||
SHOWSCREEN | FULLSCREEN, CD_73_LOG,
|
||||
FADEUP, CD_73_PAL,
|
||||
PLAYVOICE, CDV_73, // Foster: "Why, you murdering..."
|
||||
WAITVOICE,
|
||||
PLAYVOICE, CDV_74, // Reich: "Keep him quiet."
|
||||
WAITVOICE,
|
||||
PLAYVOICE, CDV_75, // Foster: "All I could do was wait."
|
||||
FADEDOWN,
|
||||
SHOWSCREEN | FULLSCREEN, CD_76_LOG,
|
||||
FADEUP, CD_76_PAL,
|
||||
WAITVOICE,
|
||||
PLAYVOICE, CDV_76, // Foster: "Just like on a hunt. Just like the old man taught me."
|
||||
WAITVOICE,
|
||||
PLAYVOICE, CDV_77, // Foster: "Wait... and be ready."
|
||||
WAITVOICE,
|
||||
FADEDOWN,
|
||||
CLEARBOTTOM,
|
||||
SHOWSCREEN, CD_78_LOG,
|
||||
FADEUP, CD_78_PAL,
|
||||
PLAYVOICE, CDV_78, // Foster: "It was dawn when we reached the City."
|
||||
WAITVOICE,
|
||||
PLAYVOICE, CDV_79, // Reich: "Land in the central Security compound."
|
||||
WAITVOICE,
|
||||
PLAYVOICE, CDV_80, // Foster: "A dawn my tribe would never see."
|
||||
BGFLIRT, CD_100,
|
||||
WAITVOICE,
|
||||
PLAYVOICE, CDV_81, // Foster: "They were no more than a note in Reich's book now."
|
||||
WAITVOICE,
|
||||
PLAYVOICE, CDV_82, // Guard: "Yes, sir. Locking on automatic landing beacon."
|
||||
WAITVOICE,
|
||||
WAITFLIRT,
|
||||
SHOWSCREEN, CD_101_LOG,
|
||||
BGFLIRT, CD_101,
|
||||
PLAYVOICE, CDV_83, // Foster: "But what was I? Why did..."
|
||||
WAITVOICE,
|
||||
PLAYVOICE, CDV_84, // Guard: "Sir! The guidance system! It's gone crazy!"
|
||||
WAITVOICE,
|
||||
PLAYVOICE, CDV_85, // Guard: "We're going to HIT!"
|
||||
WAITVOICE,
|
||||
WAITFLIRT,
|
||||
CLEARBOTTOM,
|
||||
SHOWSCREEN, CD_102_LOG,
|
||||
PLAYVOICE, CDV_86, // Foster: "Maybe I'd get some answers now."
|
||||
DOFLIRT, CD_102,
|
||||
FADEDOWN,
|
||||
// This one could be fullscreen, but that causes animation glitches.
|
||||
SHOWSCREEN, CD_103_LOG,
|
||||
FADEUP, CD_103_PAL,
|
||||
BGFLIRT, CD_103,
|
||||
WAITVOICE,
|
||||
PLAYVOICE, CDV_87, // Foster: "If I survived another 'copter crash."
|
||||
WAITFLIRT,
|
||||
WAITVOICE,
|
||||
STARTMUSIC, 2,
|
||||
FADEDOWN,
|
||||
SHOWSCREEN | FULLSCREEN, CD_104_LOG,
|
||||
FADEUP, CD_104_PAL,
|
||||
DOFLIRT, CD_104,
|
||||
DOFLIRT, CD_105,
|
||||
SEQEND
|
||||
};
|
||||
|
||||
uint16 Intro::_floppyIntroSeq[] = {
|
||||
// This one could be fullscreen, but that causes animation glitches.
|
||||
SHOWSCREEN, 60081,
|
||||
FADEUP, 60080,
|
||||
DOFLIRT, 60082,
|
||||
DOFLIRT, 60083,
|
||||
DOFLIRT, 60084, // Beneath a Steel Sky
|
||||
DOFLIRT, 60085,
|
||||
DOFLIRT, 60086,
|
||||
SCROLLFLIRT,
|
||||
COMMANDFLIRT, 60087, // => command list 4a
|
||||
136, IC_MAKE_SOUND, 1, 70,
|
||||
90, IC_FX_VOLUME, 80,
|
||||
50, IC_FX_VOLUME, 90,
|
||||
5, IC_FX_VOLUME, 100,
|
||||
COMMANDEND,
|
||||
SHOWSCREEN, 60088,
|
||||
COMMANDFLIRT, 60089, // => command list 4b (cockpit)
|
||||
1000, IC_PREPARE_TEXT, 77,
|
||||
220, IC_SHOW_TEXT, 20, 160, // radar detects jamming signal
|
||||
105, IC_REMOVE_TEXT,
|
||||
105, IC_PREPARE_TEXT, 81,
|
||||
105, IC_SHOW_TEXT, 170, 86, // well switch to override you fool
|
||||
35, IC_REMOVE_TEXT,
|
||||
35, IC_PREPARE_TEXT, 477,
|
||||
35, IC_SHOW_TEXT, 30, 160,
|
||||
3, IC_REMOVE_TEXT,
|
||||
COMMANDEND,
|
||||
CLEARBOTTOM,
|
||||
SHOWSCREEN, 60090,
|
||||
COMMANDFLIRT, 60091, // => command list 4c
|
||||
1000, IC_FX_VOLUME, 100,
|
||||
25, IC_FX_VOLUME, 110,
|
||||
15, IC_FX_VOLUME, 120,
|
||||
4, IC_FX_VOLUME, 127,
|
||||
COMMANDEND,
|
||||
FADEDOWN,
|
||||
// This one could be fullscreen, but that causes animation glitches.
|
||||
SHOWSCREEN, 60093,
|
||||
FADEUP, 60092,
|
||||
COMMANDFLIRT, 60094, // => command list 5
|
||||
31, IC_MAKE_SOUND, 2, 127,
|
||||
COMMANDEND,
|
||||
WAITMUSIC,
|
||||
FADEDOWN,
|
||||
SHOWSCREEN | FULLSCREEN, 60096,
|
||||
STARTMUSIC, 2,
|
||||
FADEUP, 60095,
|
||||
COMMANDFLIRT, 60097, // => command list 6a
|
||||
1000, IC_PREPARE_TEXT, 478,
|
||||
13, IC_SHOW_TEXT, 175, 155,
|
||||
COMMANDEND,
|
||||
COMMANDFLIRT, 60098, // => command list 6b
|
||||
131, IC_REMOVE_TEXT,
|
||||
131, IC_PREPARE_TEXT, 479,
|
||||
74, IC_SHOW_TEXT, 175, 155,
|
||||
45, IC_REMOVE_TEXT,
|
||||
45, IC_PREPARE_TEXT, 162,
|
||||
44, IC_SHOW_TEXT, 175, 155,
|
||||
4, IC_REMOVE_TEXT,
|
||||
COMMANDEND,
|
||||
SEQEND
|
||||
};
|
||||
|
||||
Intro::Intro(Disk *disk, Screen *screen, MusicBase *music, Sound *sound, Text *text, Audio::Mixer *mixer, OSystem *system) {
|
||||
_skyDisk = disk;
|
||||
_skyScreen = screen;
|
||||
_skyMusic = music;
|
||||
_skySound = sound;
|
||||
_skyText = text;
|
||||
_mixer = mixer;
|
||||
_system = system;
|
||||
_textBuf = (uint8 *)malloc(10000);
|
||||
_saveBuf = (uint8 *)malloc(10000);
|
||||
_bgBuf = NULL;
|
||||
_relDelay = 0;
|
||||
}
|
||||
|
||||
Intro::~Intro() {
|
||||
if (_skyScreen->sequenceRunning())
|
||||
_skyScreen->stopSequence();
|
||||
|
||||
free(_textBuf);
|
||||
free(_saveBuf);
|
||||
_mixer->stopID(SOUND_BG);
|
||||
free(_bgBuf);
|
||||
}
|
||||
|
||||
bool Intro::doIntro(bool floppyIntro) {
|
||||
if (!SkyEngine::isCDVersion())
|
||||
floppyIntro = true;
|
||||
|
||||
_skyMusic->loadSection(0);
|
||||
_skySound->loadSection(0);
|
||||
|
||||
if (!escDelay(3000))
|
||||
return false;
|
||||
|
||||
if (floppyIntro)
|
||||
_skyMusic->startMusic(1);
|
||||
|
||||
uint16 *seqData = _mainIntroSeq;
|
||||
while (*seqData != SEQEND) {
|
||||
if (!nextPart(seqData))
|
||||
return false;
|
||||
}
|
||||
if (floppyIntro)
|
||||
seqData = _floppyIntroSeq;
|
||||
else
|
||||
seqData = _cdIntroSeq;
|
||||
|
||||
while (*seqData != SEQEND) {
|
||||
if (!nextPart(seqData))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Intro::nextPart(uint16 *&data) {
|
||||
uint8 *vData = NULL;
|
||||
Audio::RewindableAudioStream *stream = 0;
|
||||
|
||||
// return false means cancel intro
|
||||
uint16 command = *data++;
|
||||
switch (command & 0x7fff) {
|
||||
case SHOWSCREEN:
|
||||
_skyScreen->showScreen(*data++, (command & FULLSCREEN) ? true : false);
|
||||
return true;
|
||||
case FADEUP:
|
||||
_skyScreen->paletteFadeUp(*data++);
|
||||
_relDelay += 32 * 20; // hack: the screen uses a separate delay function for the
|
||||
// blocking fadeups. So add 32*20 msecs to out delay counter.
|
||||
return true;
|
||||
case FADEDOWN:
|
||||
_skyScreen->fnFadeDown(0);
|
||||
_relDelay += 32 * 20; // hack: see above.
|
||||
return true;
|
||||
case DELAY:
|
||||
if (!escDelay(*data++))
|
||||
return false;
|
||||
return true;
|
||||
case DOFLIRT:
|
||||
_skyScreen->startSequence(*data++);
|
||||
while (_skyScreen->sequenceRunning())
|
||||
if (!escDelay(50))
|
||||
return false;
|
||||
return true;
|
||||
case SCROLLFLIRT:
|
||||
return floppyScrollFlirt();
|
||||
case COMMANDFLIRT:
|
||||
return commandFlirt(data);
|
||||
case STOPFLIRT:
|
||||
_skyScreen->stopSequence();
|
||||
return true;
|
||||
case STARTMUSIC:
|
||||
_skyMusic->startMusic(*data++);
|
||||
return true;
|
||||
case WAITMUSIC:
|
||||
while (_skyMusic->musicIsPlaying())
|
||||
if (!escDelay(50))
|
||||
return false;
|
||||
return true;
|
||||
case BGFLIRT:
|
||||
_skyScreen->startSequence(*data++);
|
||||
return true;
|
||||
case WAITFLIRT:
|
||||
while (_skyScreen->sequenceRunning())
|
||||
if (!escDelay(50))
|
||||
return false;
|
||||
return true;
|
||||
case PLAYVOICE:
|
||||
if (!escDelay(200))
|
||||
return false;
|
||||
vData = _skyDisk->loadFile(*data++);
|
||||
// HACK: Fill the header with silence. We should
|
||||
// probably use _skySound instead of calling playStream()
|
||||
// directly, but this will have to do for now.
|
||||
memset(vData, 127, sizeof(DataFileHeader));
|
||||
|
||||
stream = Audio::makeRawStream(vData, _skyDisk->_lastLoadedFileSize, 11025, Audio::FLAG_UNSIGNED);
|
||||
_mixer->playStream(Audio::Mixer::kSpeechSoundType, &_voice, stream, SOUND_VOICE);
|
||||
return true;
|
||||
case WAITVOICE:
|
||||
while (_mixer->isSoundHandleActive(_voice))
|
||||
if (!escDelay(50))
|
||||
return false;
|
||||
return true;
|
||||
case LOADBG:
|
||||
_mixer->stopID(SOUND_BG);
|
||||
free(_bgBuf);
|
||||
_bgBuf = _skyDisk->loadFile(*data++);
|
||||
_bgSize = _skyDisk->_lastLoadedFileSize;
|
||||
return true;
|
||||
case LOOPBG:
|
||||
_mixer->stopID(SOUND_BG);
|
||||
stream = Audio::makeRawStream(_bgBuf + 256, _bgSize - 768, 11025, Audio::FLAG_UNSIGNED, DisposeAfterUse::NO);
|
||||
_mixer->playStream(Audio::Mixer::kSFXSoundType, &_bgSfx, Audio::makeLoopingAudioStream(stream, 0), SOUND_BG);
|
||||
return true;
|
||||
case PLAYBG:
|
||||
_mixer->stopID(SOUND_BG);
|
||||
stream = Audio::makeRawStream(_bgBuf + 256, _bgSize - 768, 11025, Audio::FLAG_UNSIGNED, DisposeAfterUse::NO);
|
||||
_mixer->playStream(Audio::Mixer::kSFXSoundType, &_bgSfx, stream, SOUND_BG);
|
||||
return true;
|
||||
case STOPBG:
|
||||
_mixer->stopID(SOUND_BG);
|
||||
return true;
|
||||
case CLEARBOTTOM:
|
||||
{
|
||||
byte *screenBuf = _skyScreen->giveCurrent() + GAME_SCREEN_HEIGHT * GAME_SCREEN_WIDTH;
|
||||
memset(screenBuf, 0, GAME_SCREEN_WIDTH * (FULL_SCREEN_HEIGHT - GAME_SCREEN_HEIGHT));
|
||||
_system->copyRectToScreen(screenBuf, GAME_SCREEN_WIDTH, 0, GAME_SCREEN_HEIGHT, GAME_SCREEN_WIDTH, FULL_SCREEN_HEIGHT - GAME_SCREEN_HEIGHT);
|
||||
_system->updateScreen();
|
||||
}
|
||||
return true;
|
||||
default:
|
||||
error("Unknown intro command %X", command);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Intro::floppyScrollFlirt() {
|
||||
uint8 *scrollScreen = (uint8 *)malloc(FRAME_SIZE * 2);
|
||||
memset(scrollScreen, 0, FRAME_SIZE);
|
||||
memcpy(scrollScreen + FRAME_SIZE, _skyScreen->giveCurrent(), FRAME_SIZE);
|
||||
uint8 *scrollPos = scrollScreen + FRAME_SIZE;
|
||||
uint8 *vgaData = _skyDisk->loadFile(60100);
|
||||
uint8 *diffData = _skyDisk->loadFile(60101);
|
||||
uint16 frameNum = READ_LE_UINT16(diffData);
|
||||
uint8 *diffPtr = diffData + 2;
|
||||
uint8 *vgaPtr = vgaData;
|
||||
bool doContinue = true;
|
||||
|
||||
for (uint16 frameCnt = 1; (frameCnt < frameNum) && doContinue; frameCnt++) {
|
||||
uint8 scrollVal = *diffPtr++;
|
||||
if (scrollVal)
|
||||
scrollPos -= scrollVal * GAME_SCREEN_WIDTH;
|
||||
|
||||
uint16 scrPos = 0;
|
||||
while (scrPos < FRAME_SIZE) {
|
||||
uint8 nrToDo, nrToSkip;
|
||||
do {
|
||||
nrToSkip = *diffPtr++;
|
||||
scrPos += nrToSkip;
|
||||
} while (nrToSkip == 255);
|
||||
do {
|
||||
nrToDo = *diffPtr++;
|
||||
memcpy(scrollPos + scrPos, vgaPtr, nrToDo);
|
||||
scrPos += nrToDo;
|
||||
vgaPtr += nrToDo;
|
||||
} while (nrToDo == 255);
|
||||
}
|
||||
_system->copyRectToScreen(scrollPos, GAME_SCREEN_WIDTH, 0, 0, GAME_SCREEN_WIDTH, GAME_SCREEN_HEIGHT);
|
||||
_system->updateScreen();
|
||||
if (!escDelay(60))
|
||||
doContinue = false;
|
||||
}
|
||||
memcpy(_skyScreen->giveCurrent(), scrollPos, FRAME_SIZE);
|
||||
free(diffData);
|
||||
free(vgaData);
|
||||
free(scrollScreen);
|
||||
return doContinue;
|
||||
}
|
||||
|
||||
bool Intro::commandFlirt(uint16 *&data) {
|
||||
_skyScreen->startSequence(*data++);
|
||||
|
||||
while ((*data != COMMANDEND) || _skyScreen->sequenceRunning()) {
|
||||
while ((_skyScreen->seqFramesLeft() < *data)) {
|
||||
data++;
|
||||
uint16 command = *data++;
|
||||
switch (command) {
|
||||
case IC_PREPARE_TEXT:
|
||||
_skyText->displayText(*data++, _textBuf, Graphics::kTextAlignCenter, INTRO_TEXT_WIDTH, 255);
|
||||
break;
|
||||
case IC_SHOW_TEXT:
|
||||
((DataFileHeader *)_textBuf)->s_x = *data++;
|
||||
((DataFileHeader *)_textBuf)->s_y = *data++;
|
||||
showTextBuf();
|
||||
break;
|
||||
case IC_REMOVE_TEXT:
|
||||
restoreScreen();
|
||||
break;
|
||||
case IC_MAKE_SOUND:
|
||||
_skySound->playSound(data[0], data[1], 0);
|
||||
data += 2;
|
||||
break;
|
||||
case IC_FX_VOLUME:
|
||||
_skySound->playSound(1, *data++, 0);
|
||||
break;
|
||||
default:
|
||||
error("Unknown FLIRT command %X", command);
|
||||
}
|
||||
}
|
||||
|
||||
if (!escDelay(50)) {
|
||||
_skyScreen->stopSequence();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
data++; // move pointer over "COMMANDEND"
|
||||
return true;
|
||||
}
|
||||
|
||||
void Intro::showTextBuf() {
|
||||
uint16 x = ((DataFileHeader *)_textBuf)->s_x;
|
||||
uint16 y = ((DataFileHeader *)_textBuf)->s_y;
|
||||
uint16 width = ((DataFileHeader *)_textBuf)->s_width;
|
||||
uint16 height = ((DataFileHeader *)_textBuf)->s_height;
|
||||
uint8 *screenBuf = _skyScreen->giveCurrent() + y * GAME_SCREEN_WIDTH + x;
|
||||
memcpy(_saveBuf, _textBuf, sizeof(DataFileHeader));
|
||||
uint8 *saveBuf = _saveBuf + sizeof(DataFileHeader);
|
||||
uint8 *textBuf = _textBuf + sizeof(DataFileHeader);
|
||||
for (uint16 cnty = 0; cnty < height; cnty++) {
|
||||
memcpy(saveBuf, screenBuf, width);
|
||||
for (uint16 cntx = 0; cntx < width; cntx++)
|
||||
if (textBuf[cntx])
|
||||
screenBuf[cntx] = textBuf[cntx];
|
||||
screenBuf += GAME_SCREEN_WIDTH;
|
||||
textBuf += width;
|
||||
saveBuf += width;
|
||||
}
|
||||
screenBuf = _skyScreen->giveCurrent() + y * GAME_SCREEN_WIDTH + x;
|
||||
_system->copyRectToScreen(screenBuf, GAME_SCREEN_WIDTH, x, y, width, height);
|
||||
}
|
||||
|
||||
void Intro::restoreScreen() {
|
||||
uint16 x = ((DataFileHeader *)_saveBuf)->s_x;
|
||||
uint16 y = ((DataFileHeader *)_saveBuf)->s_y;
|
||||
uint16 width = ((DataFileHeader *)_saveBuf)->s_width;
|
||||
uint16 height = ((DataFileHeader *)_saveBuf)->s_height;
|
||||
uint8 *screenBuf = _skyScreen->giveCurrent() + y * GAME_SCREEN_WIDTH + x;
|
||||
uint8 *saveBuf = _saveBuf + sizeof(DataFileHeader);
|
||||
for (uint16 cnt = 0; cnt < height; cnt++) {
|
||||
memcpy(screenBuf, saveBuf, width);
|
||||
screenBuf += GAME_SCREEN_WIDTH;
|
||||
saveBuf += width;
|
||||
}
|
||||
_system->copyRectToScreen(_saveBuf + sizeof(DataFileHeader), width, x, y, width, height);
|
||||
}
|
||||
|
||||
bool Intro::escDelay(uint32 msecs) {
|
||||
Common::EventManager *eventMan = _system->getEventManager();
|
||||
Common::Event event;
|
||||
|
||||
if (_relDelay == 0) // first call, init with system time
|
||||
_relDelay = (int32)_system->getMillis();
|
||||
|
||||
_relDelay += msecs; // now wait until _system->getMillis() >= _relDelay
|
||||
|
||||
int32 nDelay = 0;
|
||||
do {
|
||||
while (eventMan->pollEvent(event)) {
|
||||
if (event.type == Common::EVENT_CUSTOM_ENGINE_ACTION_START) {
|
||||
if (event.customType == kSkyActionSkip)
|
||||
return false;
|
||||
} else if (event.type == Common::EVENT_QUIT || event.type == Common::EVENT_RETURN_TO_LAUNCHER) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
nDelay = _relDelay - _system->getMillis();
|
||||
if (nDelay < 0)
|
||||
nDelay = 0;
|
||||
else if (nDelay > 20)
|
||||
nDelay = 20;
|
||||
|
||||
_system->delayMillis(nDelay);
|
||||
|
||||
_skyScreen->processSequence();
|
||||
_system->updateScreen();
|
||||
} while (nDelay == 20);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // End of namespace Sky
|
||||
72
engines/sky/intro.h
Normal file
72
engines/sky/intro.h
Normal file
@@ -0,0 +1,72 @@
|
||||
/* 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 SKY_INTRO_H
|
||||
#define SKY_INTRO_H
|
||||
|
||||
|
||||
#include "common/scummsys.h"
|
||||
#include "audio/mixer.h"
|
||||
|
||||
namespace Sky {
|
||||
|
||||
class Disk;
|
||||
class Screen;
|
||||
class MusicBase;
|
||||
class Sound;
|
||||
class Text;
|
||||
|
||||
class Intro {
|
||||
public:
|
||||
Intro(Disk *disk, Screen *screen, MusicBase *music, Sound *sound, Text *text, Audio::Mixer *mixer, OSystem *system);
|
||||
~Intro();
|
||||
bool doIntro(bool floppyIntro);
|
||||
private:
|
||||
static uint16 _mainIntroSeq[];
|
||||
static uint16 _floppyIntroSeq[];
|
||||
static uint16 _cdIntroSeq[];
|
||||
|
||||
Disk *_skyDisk;
|
||||
Screen *_skyScreen;
|
||||
MusicBase *_skyMusic;
|
||||
Sound *_skySound;
|
||||
Text *_skyText;
|
||||
OSystem *_system;
|
||||
Audio::Mixer *_mixer;
|
||||
|
||||
uint8 *_textBuf, *_saveBuf;
|
||||
uint8 *_bgBuf;
|
||||
uint32 _bgSize;
|
||||
Audio::SoundHandle _voice, _bgSfx;
|
||||
|
||||
int32 _relDelay;
|
||||
|
||||
bool escDelay(uint32 msecs);
|
||||
bool nextPart(uint16 *&data);
|
||||
bool floppyScrollFlirt();
|
||||
bool commandFlirt(uint16 *&data);
|
||||
void showTextBuf();
|
||||
void restoreScreen();
|
||||
};
|
||||
|
||||
} // End of namespace Sky
|
||||
|
||||
#endif // INTRO_H
|
||||
2585
engines/sky/logic.cpp
Normal file
2585
engines/sky/logic.cpp
Normal file
File diff suppressed because it is too large
Load Diff
337
engines/sky/logic.h
Normal file
337
engines/sky/logic.h
Normal file
@@ -0,0 +1,337 @@
|
||||
/* 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 SKY_LOGIC_H
|
||||
#define SKY_LOGIC_H
|
||||
|
||||
#include "common/util.h"
|
||||
#include "common/random.h"
|
||||
|
||||
namespace Sky {
|
||||
|
||||
struct Compact;
|
||||
|
||||
enum scriptVariableOffsets {
|
||||
RESULT = 0,
|
||||
SCREEN = 1,
|
||||
LOGIC_LIST_NO = 2,
|
||||
MOUSE_LIST_NO = 6,
|
||||
DRAW_LIST_NO = 8,
|
||||
CUR_ID = 12,
|
||||
MOUSE_STATUS = 13,
|
||||
MOUSE_STOP = 14,
|
||||
BUTTON = 15,
|
||||
SPECIAL_ITEM = 17,
|
||||
GET_OFF = 18,
|
||||
CURSOR_ID = 22,
|
||||
SAFEX = 25,
|
||||
SAFEY = 26,
|
||||
PLAYER_X = 27,
|
||||
PLAYER_Y = 28,
|
||||
PLAYER_MOOD = 29,
|
||||
PLAYER_SCREEN = 30,
|
||||
HIT_ID = 37,
|
||||
LAYER_0_ID = 41,
|
||||
LAYER_1_ID = 42,
|
||||
LAYER_2_ID = 43,
|
||||
LAYER_3_ID = 44,
|
||||
GRID_1_ID = 45,
|
||||
GRID_2_ID = 46,
|
||||
GRID_3_ID = 47,
|
||||
THE_CHOSEN_ONE = 51,
|
||||
TEXT1 = 53,
|
||||
MENU_LENGTH = 100,
|
||||
SCROLL_OFFSET = 101,
|
||||
MENU = 102,
|
||||
OBJECT_HELD = 103,
|
||||
LAMB_GREET = 109,
|
||||
RND = 115,
|
||||
CUR_SECTION = 143,
|
||||
JOEY_SECTION = 145,
|
||||
LAMB_SECTION = 146,
|
||||
KNOWS_PORT = 190,
|
||||
GOT_SPONSOR = 240,
|
||||
GOT_JAMMER = 258,
|
||||
CONSOLE_TYPE = 345,
|
||||
S15_FLOOR = 450,
|
||||
FOREMAN_FRIEND = 451,
|
||||
REICH_DOOR_FLAG = 470,
|
||||
CARD_STATUS = 479,
|
||||
CARD_FIX = 480,
|
||||
GUARDIAN_THERE = 640,
|
||||
FS_COMMAND = 643,
|
||||
ENTER_DIGITS = 644,
|
||||
LINC_DIGIT_0 = 646,
|
||||
LINC_DIGIT_1 = 647,
|
||||
LINC_DIGIT_2 = 648,
|
||||
LINC_DIGIT_3 = 649,
|
||||
LINC_DIGIT_4 = 650,
|
||||
LINC_DIGIT_5 = 651,
|
||||
LINC_DIGIT_6 = 651,
|
||||
LINC_DIGIT_7 = 653,
|
||||
LINC_DIGIT_8 = 654,
|
||||
LINC_DIGIT_9 = 655,
|
||||
DOOR_67_68_FLAG = 678,
|
||||
SC70_IRIS_FLAG = 693,
|
||||
DOOR_73_75_FLAG = 704,
|
||||
SC76_CABINET1_FLAG = 709,
|
||||
SC76_CABINET2_FLAG = 710,
|
||||
SC76_CABINET3_FLAG = 711,
|
||||
DOOR_77_78_FLAG = 719,
|
||||
SC80_EXIT_FLAG = 720,
|
||||
SC31_LIFT_FLAG = 793,
|
||||
SC32_LIFT_FLAG = 797,
|
||||
SC33_SHED_DOOR_FLAG = 798,
|
||||
BAND_PLAYING = 804,
|
||||
COLSTON_AT_TABLE = 805,
|
||||
SC36_NEXT_DEALER = 806,
|
||||
SC36_DOOR_FLAG = 807,
|
||||
SC37_DOOR_FLAG = 808,
|
||||
SC40_LOCKER_1_FLAG = 817,
|
||||
SC40_LOCKER_2_FLAG = 818,
|
||||
SC40_LOCKER_3_FLAG = 819,
|
||||
SC40_LOCKER_4_FLAG = 820,
|
||||
SC40_LOCKER_5_FLAG = 821
|
||||
};
|
||||
|
||||
#define NUM_SKY_SCRIPTVARS 838
|
||||
|
||||
class AutoRoute;
|
||||
class Control;
|
||||
class Disk;
|
||||
class Grid;
|
||||
class Mouse;
|
||||
class MusicBase;
|
||||
class Screen;
|
||||
class Sound;
|
||||
class Text;
|
||||
class SkyCompact;
|
||||
|
||||
class Logic;
|
||||
|
||||
typedef void (Logic::*LogicTable) ();
|
||||
typedef bool (Logic::*McodeTable) (uint32, uint32, uint32);
|
||||
|
||||
class Logic {
|
||||
public:
|
||||
Logic(
|
||||
SkyCompact *skyCompact,
|
||||
Screen *skyScreen,
|
||||
Disk *skyDisk,
|
||||
Text *skyText,
|
||||
MusicBase *skyMusic,
|
||||
Mouse *skyMouse,
|
||||
Sound *skySound);
|
||||
~Logic();
|
||||
void engine();
|
||||
void useControlInstance(Control *control) { _skyControl = control; }
|
||||
|
||||
uint16 mouseScript(uint32 scrNum, Compact *scriptComp);
|
||||
|
||||
static uint32 _scriptVariables[NUM_SKY_SCRIPTVARS];
|
||||
Grid *_skyGrid;
|
||||
|
||||
uint16 script(uint16 scriptNo, uint16 offset);
|
||||
void initScreen0();
|
||||
void parseSaveData(uint32 *data);
|
||||
|
||||
private:
|
||||
void setupLogicTable();
|
||||
void setupMcodeTable();
|
||||
const LogicTable *_logicTable;
|
||||
const McodeTable *_mcodeTable;
|
||||
|
||||
protected:
|
||||
void push(uint32);
|
||||
uint32 pop();
|
||||
void checkModuleLoaded(uint16 moduleNo);
|
||||
bool isCollision(Compact *cpt);
|
||||
void initScriptVariables();
|
||||
void mainAnim();
|
||||
void runGetOff();
|
||||
void stopAndWait();
|
||||
bool checkProtection();
|
||||
|
||||
void nop();
|
||||
void logicScript();
|
||||
void autoRoute();
|
||||
void arAnim();
|
||||
void arTurn();
|
||||
void alt();
|
||||
void anim();
|
||||
void turn();
|
||||
void cursor();
|
||||
void talk();
|
||||
void listen();
|
||||
void stopped();
|
||||
void choose();
|
||||
void frames();
|
||||
void pause();
|
||||
void waitSync();
|
||||
void simpleAnim();
|
||||
|
||||
bool fnCacheChip(uint32 a, uint32 b, uint32 c);
|
||||
bool fnCacheFast(uint32 a, uint32 b, uint32 c);
|
||||
bool fnDrawScreen(uint32 a, uint32 b, uint32 c);
|
||||
bool fnAr(uint32 a, uint32 b, uint32 c);
|
||||
bool fnArAnimate(uint32 a, uint32 b, uint32 c);
|
||||
bool fnIdle(uint32 a, uint32 b, uint32 c);
|
||||
bool fnInteract(uint32 a, uint32 b, uint32 c);
|
||||
bool fnStartSub(uint32 a, uint32 b, uint32 c);
|
||||
bool fnTheyStartSub(uint32 a, uint32 b, uint32 c);
|
||||
bool fnAssignBase(uint32 a, uint32 b, uint32 c);
|
||||
bool fnDiskMouse(uint32 a, uint32 b, uint32 c);
|
||||
bool fnNormalMouse(uint32 a, uint32 b, uint32 c);
|
||||
bool fnBlankMouse(uint32 a, uint32 b, uint32 c);
|
||||
bool fnCrossMouse(uint32 a, uint32 b, uint32 c);
|
||||
bool fnCursorRight(uint32 a, uint32 b, uint32 c);
|
||||
bool fnCursorLeft(uint32 a, uint32 b, uint32 c);
|
||||
bool fnCursorDown(uint32 a, uint32 b, uint32 c);
|
||||
bool fnOpenHand(uint32 a, uint32 b, uint32 c);
|
||||
bool fnCloseHand(uint32 a, uint32 b, uint32 c);
|
||||
bool fnGetTo(uint32 a, uint32 b, uint32 c);
|
||||
bool fnSetToStand(uint32 a, uint32 b, uint32 c);
|
||||
bool fnTurnTo(uint32 a, uint32 b, uint32 c);
|
||||
bool fnArrived(uint32 a, uint32 b, uint32 c);
|
||||
bool fnLeaving(uint32 a, uint32 b, uint32 c);
|
||||
bool fnSetAlternate(uint32 a, uint32 b, uint32 c);
|
||||
bool fnAltSetAlternate(uint32 a, uint32 b, uint32 c);
|
||||
bool fnKillId(uint32 a, uint32 b, uint32 c);
|
||||
bool fnNoHuman(uint32 a, uint32 b, uint32 c);
|
||||
bool fnAddHuman(uint32 a, uint32 b, uint32 c);
|
||||
bool fnAddButtons(uint32 a, uint32 b, uint32 c);
|
||||
bool fnNoButtons(uint32 a, uint32 b, uint32 c);
|
||||
bool fnSetStop(uint32 a, uint32 b, uint32 c);
|
||||
bool fnClearStop(uint32 a, uint32 b, uint32 c);
|
||||
bool fnPointerText(uint32 a, uint32 b, uint32 c);
|
||||
bool fnQuit(uint32 a, uint32 b, uint32 c);
|
||||
bool fnSpeakMe(uint32 targetId, uint32 mesgNum, uint32 animNum);
|
||||
bool fnSpeakMeDir(uint32 targetId, uint32 mesgNum, uint32 animNum);
|
||||
bool fnSpeakWait(uint32 a, uint32 b, uint32 c);
|
||||
bool fnSpeakWaitDir(uint32 a, uint32 b, uint32 c);
|
||||
bool fnChooser(uint32 a, uint32 b, uint32 c);
|
||||
bool fnHighlight(uint32 a, uint32 b, uint32 c);
|
||||
bool fnTextKill(uint32 a, uint32 b, uint32 c);
|
||||
bool fnStopMode(uint32 a, uint32 b, uint32 c);
|
||||
bool fnWeWait(uint32 a, uint32 b, uint32 c);
|
||||
bool fnSendSync(uint32 a, uint32 b, uint32 c);
|
||||
bool fnSendFastSync(uint32 a, uint32 b, uint32 c);
|
||||
bool fnSendRequest(uint32 a, uint32 b, uint32 c);
|
||||
bool fnClearRequest(uint32 a, uint32 b, uint32 c);
|
||||
bool fnCheckRequest(uint32 a, uint32 b, uint32 c);
|
||||
bool fnStartMenu(uint32 a, uint32 b, uint32 c);
|
||||
bool fnUnhighlight(uint32 a, uint32 b, uint32 c);
|
||||
bool fnFaceId(uint32 a, uint32 b, uint32 c);
|
||||
bool fnForeground(uint32 a, uint32 b, uint32 c);
|
||||
bool fnBackground(uint32 a, uint32 b, uint32 c);
|
||||
bool fnNewBackground(uint32 a, uint32 b, uint32 c);
|
||||
bool fnSort(uint32 a, uint32 b, uint32 c);
|
||||
bool fnNoSpriteEngine(uint32 a, uint32 b, uint32 c);
|
||||
bool fnNoSpritesA6(uint32 a, uint32 b, uint32 c);
|
||||
bool fnResetId(uint32 a, uint32 b, uint32 c);
|
||||
bool fnToggleGrid(uint32 a, uint32 b, uint32 c);
|
||||
bool fnPause(uint32 a, uint32 b, uint32 c);
|
||||
bool fnRunAnimMod(uint32 a, uint32 b, uint32 c);
|
||||
bool fnSimpleMod(uint32 a, uint32 b, uint32 c);
|
||||
bool fnRunFrames(uint32 a, uint32 b, uint32 c);
|
||||
bool fnAwaitSync(uint32 a, uint32 b, uint32 c);
|
||||
bool fnIncMegaSet(uint32 a, uint32 b, uint32 c);
|
||||
bool fnDecMegaSet(uint32 a, uint32 b, uint32 c);
|
||||
bool fnSetMegaSet(uint32 a, uint32 b, uint32 c);
|
||||
bool fnMoveItems(uint32 a, uint32 b, uint32 c);
|
||||
bool fnNewList(uint32 a, uint32 b, uint32 c);
|
||||
bool fnAskThis(uint32 a, uint32 b, uint32 c);
|
||||
bool fnRandom(uint32 a, uint32 b, uint32 c);
|
||||
bool fnPersonHere(uint32 a, uint32 b, uint32 c);
|
||||
bool fnToggleMouse(uint32 a, uint32 b, uint32 c);
|
||||
bool fnMouseOn(uint32 a, uint32 b, uint32 c);
|
||||
bool fnMouseOff(uint32 a, uint32 b, uint32 c);
|
||||
bool fnFetchX(uint32 a, uint32 b, uint32 c);
|
||||
bool fnFetchY(uint32 a, uint32 b, uint32 c);
|
||||
bool fnTestList(uint32 a, uint32 b, uint32 c);
|
||||
bool fnFetchPlace(uint32 a, uint32 b, uint32 c);
|
||||
bool fnCustomJoey(uint32 a, uint32 b, uint32 c);
|
||||
bool fnSetPalette(uint32 a, uint32 b, uint32 c);
|
||||
bool fnTextModule(uint32 a, uint32 b, uint32 c);
|
||||
bool fnChangeName(uint32 a, uint32 b, uint32 c);
|
||||
bool fnMiniLoad(uint32 a, uint32 b, uint32 c);
|
||||
bool fnFlushBuffers(uint32 a, uint32 b, uint32 c);
|
||||
bool fnFlushChip(uint32 a, uint32 b, uint32 c);
|
||||
bool fnSaveCoods(uint32 a, uint32 b, uint32 c);
|
||||
bool fnPlotGrid(uint32 a, uint32 b, uint32 c);
|
||||
bool fnRemoveGrid(uint32 a, uint32 b, uint32 c);
|
||||
bool fnEyeball(uint32 a, uint32 b, uint32 c);
|
||||
bool fnCursorUp(uint32 a, uint32 b, uint32 c);
|
||||
bool fnLeaveSection(uint32 a, uint32 b, uint32 c);
|
||||
bool fnEnterSection(uint32 sectionNo, uint32 b, uint32 c);
|
||||
bool fnRestoreGame(uint32 a, uint32 b, uint32 c);
|
||||
bool fnRestartGame(uint32 a, uint32 b, uint32 c);
|
||||
bool fnNewSwingSeq(uint32 a, uint32 b, uint32 c);
|
||||
bool fnWaitSwingEnd(uint32 a, uint32 b, uint32 c);
|
||||
bool fnSkipIntroCode(uint32 a, uint32 b, uint32 c);
|
||||
bool fnBlankScreen(uint32 a, uint32 b, uint32 c);
|
||||
bool fnPrintCredit(uint32 a, uint32 b, uint32 c);
|
||||
bool fnLookAt(uint32 a, uint32 b, uint32 c);
|
||||
bool fnLincTextModule(uint32 a, uint32 b, uint32 c);
|
||||
bool fnTextKill2(uint32 a, uint32 b, uint32 c);
|
||||
bool fnSetFont(uint32 a, uint32 b, uint32 c);
|
||||
bool fnStartFx(uint32 a, uint32 b, uint32 c);
|
||||
bool fnStopFx(uint32 a, uint32 b, uint32 c);
|
||||
bool fnStartMusic(uint32 a, uint32 b, uint32 c);
|
||||
bool fnStopMusic(uint32 a, uint32 b, uint32 c);
|
||||
bool fnFadeDown(uint32 a, uint32 b, uint32 c);
|
||||
bool fnFadeUp(uint32 a, uint32 b, uint32 c);
|
||||
bool fnQuitToDos(uint32 a, uint32 b, uint32 c);
|
||||
bool fnPauseFx(uint32 a, uint32 b, uint32 c);
|
||||
bool fnUnPauseFx(uint32 a, uint32 b, uint32 c);
|
||||
bool fnPrintf(uint32 a, uint32 b, uint32 c);
|
||||
|
||||
void stdSpeak(Compact *target, uint32 textNum, uint32 animNum, uint32 base);
|
||||
void fnExec(uint16 num, uint32 a, uint32 b, uint32 c);
|
||||
|
||||
uint16 *_moduleList[16];
|
||||
uint32 _stack[20];
|
||||
byte _stackPtr;
|
||||
|
||||
Compact *_compact;
|
||||
|
||||
uint32 _objectList[30];
|
||||
|
||||
uint32 _currentSection;
|
||||
|
||||
Common::RandomSource _rnd;
|
||||
|
||||
SkyCompact *_skyCompact;
|
||||
Screen *_skyScreen;
|
||||
Disk *_skyDisk;
|
||||
Text *_skyText;
|
||||
MusicBase *_skyMusic;
|
||||
Sound *_skySound;
|
||||
AutoRoute *_skyAutoRoute;
|
||||
Mouse *_skyMouse;
|
||||
Control *_skyControl;
|
||||
|
||||
friend class Debugger;
|
||||
};
|
||||
|
||||
} // End of namespace Sky
|
||||
|
||||
#endif
|
||||
404
engines/sky/metaengine.cpp
Normal file
404
engines/sky/metaengine.cpp
Normal file
@@ -0,0 +1,404 @@
|
||||
/* 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 "engines/metaengine.h"
|
||||
|
||||
#include "backends/keymapper/action.h"
|
||||
#include "backends/keymapper/keymap.h"
|
||||
#include "backends/keymapper/standard-actions.h"
|
||||
|
||||
#include "common/gui_options.h"
|
||||
#include "common/system.h"
|
||||
#include "common/savefile.h"
|
||||
#include "common/translation.h"
|
||||
#include "common/file.h"
|
||||
#include "common/fs.h"
|
||||
|
||||
#include "gui/message.h"
|
||||
|
||||
#include "sky/control.h"
|
||||
#include "sky/sky.h"
|
||||
|
||||
class SkyMetaEngine : public MetaEngine {
|
||||
const char *getName() const override {
|
||||
return "sky";
|
||||
}
|
||||
|
||||
bool hasFeature(MetaEngineFeature f) const override;
|
||||
|
||||
Common::Error createInstance(OSystem *syst, Engine **engine,
|
||||
const DetectedGame &gameDescriptor, const void *metaEngineDescriptor) override;
|
||||
|
||||
const ExtraGuiOptions getExtraGuiOptions(const Common::String &target) const override;
|
||||
|
||||
SaveStateList listSaves(const char *target) const override;
|
||||
int getMaximumSaveSlot() const override;
|
||||
bool removeSaveState(const char *target, int slot) const override;
|
||||
SaveStateDescriptor querySaveMetaInfos(const char *target, int slot) const override;
|
||||
|
||||
Common::KeymapArray initKeymaps(const char *target) const override;
|
||||
Common::String getSavegameFile(int saveGameIdx, const char *target) const override {
|
||||
if (saveGameIdx == kSavegameFilePattern)
|
||||
return Common::String::format("SKY-VM.###");
|
||||
else
|
||||
return Common::String::format("SKY-VM.%03d", saveGameIdx);
|
||||
}
|
||||
};
|
||||
|
||||
bool SkyMetaEngine::hasFeature(MetaEngineFeature f) const {
|
||||
return
|
||||
(f == kSupportsListSaves) ||
|
||||
(f == kSupportsLoadingDuringStartup) ||
|
||||
(f == kSupportsDeleteSave) ||
|
||||
(f == kSavesSupportMetaInfo);
|
||||
}
|
||||
|
||||
bool Sky::SkyEngine::hasFeature(EngineFeature f) const {
|
||||
return
|
||||
(f == kSupportsReturnToLauncher) ||
|
||||
(f == kSupportsLoadingDuringRuntime) ||
|
||||
(f == kSupportsSavingDuringRuntime);
|
||||
}
|
||||
|
||||
Common::KeymapArray SkyMetaEngine::initKeymaps(const char *target) const {
|
||||
using namespace Common;
|
||||
using namespace Sky;
|
||||
|
||||
Keymap *mainKeymap = new Keymap(Keymap::kKeymapTypeGame, "sky-main", "Beneath a Steel Sky");
|
||||
|
||||
Action *act;
|
||||
|
||||
act = new Action(kStandardActionLeftClick, _("Walk / Look / Talk"));
|
||||
act->setLeftClickEvent();
|
||||
act->addDefaultInputMapping("MOUSE_LEFT");
|
||||
act->addDefaultInputMapping("JOY_A");
|
||||
mainKeymap->addAction(act);
|
||||
|
||||
act = new Action(kStandardActionRightClick, _("Use"));
|
||||
act->setRightClickEvent();
|
||||
act->addDefaultInputMapping("MOUSE_RIGHT");
|
||||
act->addDefaultInputMapping("JOY_B");
|
||||
mainKeymap->addAction(act);
|
||||
|
||||
act = new Action("CONFIRM", _("Confirm"));
|
||||
act->setCustomEngineActionEvent(kSkyActionConfirm);
|
||||
act->addDefaultInputMapping("RETURN");
|
||||
act->addDefaultInputMapping("KP_ENTER");
|
||||
mainKeymap->addAction(act);
|
||||
|
||||
act = new Action(kStandardActionSkip, _("Skip / Close"));
|
||||
act->setCustomEngineActionEvent(kSkyActionSkip);
|
||||
act->addDefaultInputMapping("ESCAPE");
|
||||
act->addDefaultInputMapping("JOY_Y");
|
||||
mainKeymap->addAction(act);
|
||||
|
||||
Keymap *shortcutsKeymap = new Keymap(Keymap::kKeymapTypeGame, SkyEngine::shortcutsKeymapId, "Beneath a Steel Sky - Shortcuts");
|
||||
|
||||
act = new Action(kStandardActionOpenMainMenu, _("Open control panel"));
|
||||
act->setCustomEngineActionEvent(kSkyActionOpenControlPanel);
|
||||
act->addDefaultInputMapping("F5");
|
||||
act->addDefaultInputMapping("JOY_X");
|
||||
shortcutsKeymap->addAction(act);
|
||||
|
||||
act = new Action("SKPL", _("Skip line"));
|
||||
act->setCustomEngineActionEvent(kSkyActionSkipLine);
|
||||
act->addDefaultInputMapping("PERIOD");
|
||||
shortcutsKeymap->addAction(act);
|
||||
|
||||
act = new Action(kStandardActionPause, _("Pause"));
|
||||
act->setCustomEngineActionEvent(kSkyActionPause);
|
||||
act->addDefaultInputMapping("p");
|
||||
shortcutsKeymap->addAction(act);
|
||||
|
||||
act = new Action("FAST", _("Toggle fast mode"));
|
||||
act->setCustomEngineActionEvent(kSkyActionToggleFastMode);
|
||||
act->addDefaultInputMapping("C+f");
|
||||
shortcutsKeymap->addAction(act);
|
||||
|
||||
act = new Action("RFAST", _("Toggle really fast mode"));
|
||||
act->setCustomEngineActionEvent(kSkyActionToggleReallyFastMode);
|
||||
act->addDefaultInputMapping("C+g");
|
||||
shortcutsKeymap->addAction(act);
|
||||
|
||||
KeymapArray keymaps(2);
|
||||
keymaps[0] = mainKeymap;
|
||||
keymaps[1] = shortcutsKeymap;
|
||||
|
||||
return keymaps;
|
||||
}
|
||||
|
||||
Common::Error SkyMetaEngine::createInstance(OSystem *syst, Engine **engine,
|
||||
const DetectedGame &gameDescriptor, const void *metaEngineDescriptor) {
|
||||
assert(engine);
|
||||
*engine = new Sky::SkyEngine(syst);
|
||||
return Common::kNoError;
|
||||
}
|
||||
|
||||
static const ExtraGuiOption skyExtraGuiOption = {
|
||||
_s("Floppy intro"),
|
||||
_s("Use the floppy version's intro (CD version only)"),
|
||||
"alt_intro",
|
||||
false,
|
||||
0,
|
||||
0
|
||||
};
|
||||
|
||||
const ExtraGuiOptions SkyMetaEngine::getExtraGuiOptions(const Common::String &target) const {
|
||||
Common::String guiOptions;
|
||||
ExtraGuiOptions options;
|
||||
|
||||
if (target.empty()) {
|
||||
options.push_back(skyExtraGuiOption);
|
||||
return options;
|
||||
}
|
||||
|
||||
if (ConfMan.hasKey("guioptions", target)) {
|
||||
guiOptions = ConfMan.get("guioptions", target);
|
||||
guiOptions = parseGameGUIOptions(guiOptions);
|
||||
}
|
||||
|
||||
if (!guiOptions.contains(GUIO_NOSPEECH))
|
||||
options.push_back(skyExtraGuiOption);
|
||||
return options;
|
||||
}
|
||||
|
||||
SaveStateList SkyMetaEngine::listSaves(const char *target) const {
|
||||
Common::SaveFileManager *saveFileMan = g_system->getSavefileManager();
|
||||
SaveStateList saveList;
|
||||
|
||||
// Load the descriptions
|
||||
Common::StringArray savenames;
|
||||
savenames.resize(MAX_SAVE_GAMES+1);
|
||||
|
||||
Common::InSaveFile *inf;
|
||||
inf = saveFileMan->openForLoading("SKY-VM.SAV");
|
||||
if (inf != NULL) {
|
||||
char *tmpBuf = new char[MAX_SAVE_GAMES * MAX_TEXT_LEN];
|
||||
char *tmpPtr = tmpBuf;
|
||||
inf->read(tmpBuf, MAX_SAVE_GAMES * MAX_TEXT_LEN);
|
||||
for (int i = 0; i < MAX_SAVE_GAMES; ++i) {
|
||||
savenames[i] = tmpPtr;
|
||||
tmpPtr += savenames[i].size() + 1;
|
||||
}
|
||||
delete inf;
|
||||
delete[] tmpBuf;
|
||||
}
|
||||
|
||||
// Find all saves
|
||||
Common::StringArray filenames;
|
||||
filenames = saveFileMan->listSavefiles("SKY-VM.###");
|
||||
|
||||
// Prepare the list of savestates by looping over all matching savefiles
|
||||
for (Common::StringArray::const_iterator file = filenames.begin(); file != filenames.end(); ++file) {
|
||||
// Extract the extension
|
||||
Common::String ext = file->c_str() + file->size() - 3;
|
||||
ext.toUppercase();
|
||||
int slotNum = atoi(ext.c_str());
|
||||
Common::InSaveFile *in = saveFileMan->openForLoading(*file);
|
||||
if (in) {
|
||||
saveList.push_back(SaveStateDescriptor(this, slotNum,
|
||||
(slotNum == 0) ? _("Autosave") : Common::U32String(savenames[slotNum - 1])));
|
||||
delete in;
|
||||
}
|
||||
}
|
||||
|
||||
// Sort saves based on slot number.
|
||||
Common::sort(saveList.begin(), saveList.end(), SaveStateDescriptorSlotComparator());
|
||||
return saveList;
|
||||
}
|
||||
|
||||
int SkyMetaEngine::getMaximumSaveSlot() const { return MAX_SAVE_GAMES; }
|
||||
|
||||
bool SkyMetaEngine::removeSaveState(const char *target, int slot) const {
|
||||
if (slot == 0) {
|
||||
// Do not delete the auto save
|
||||
// Note: Setting the autosave slot as write protected (with setWriteProtectedFlag())
|
||||
// does not disable the delete action on the slot.
|
||||
const Common::U32String message = _("WARNING: Deleting the autosave slot is not supported by this engine");
|
||||
GUI::MessageDialog warn(message);
|
||||
warn.runModal();
|
||||
return false;
|
||||
}
|
||||
|
||||
Common::SaveFileManager *saveFileMan = g_system->getSavefileManager();
|
||||
char fName[20];
|
||||
Common::sprintf_s(fName,"SKY-VM.%03d", slot);
|
||||
saveFileMan->removeSavefile(fName);
|
||||
|
||||
// Load current save game descriptions
|
||||
Common::StringArray savenames;
|
||||
savenames.resize(MAX_SAVE_GAMES+1);
|
||||
Common::InSaveFile *inf;
|
||||
inf = saveFileMan->openForLoading("SKY-VM.SAV");
|
||||
if (inf != NULL) {
|
||||
char *tmpBuf = new char[MAX_SAVE_GAMES * MAX_TEXT_LEN];
|
||||
char *tmpPtr = tmpBuf;
|
||||
inf->read(tmpBuf, MAX_SAVE_GAMES * MAX_TEXT_LEN);
|
||||
for (int i = 0; i < MAX_SAVE_GAMES; ++i) {
|
||||
savenames[i] = tmpPtr;
|
||||
tmpPtr += savenames[i].size() + 1;
|
||||
}
|
||||
delete inf;
|
||||
delete[] tmpBuf;
|
||||
}
|
||||
|
||||
// Update the save game description at the given slot
|
||||
savenames[slot - 1] = "";
|
||||
|
||||
// Save the updated descriptions
|
||||
Common::OutSaveFile *outf;
|
||||
|
||||
outf = saveFileMan->openForSaving("SKY-VM.SAV");
|
||||
bool ioFailed = true;
|
||||
if (outf) {
|
||||
for (uint16 cnt = 0; cnt < MAX_SAVE_GAMES; cnt++) {
|
||||
outf->write(savenames[cnt].c_str(), savenames[cnt].size() + 1);
|
||||
}
|
||||
outf->finalize();
|
||||
if (!outf->err())
|
||||
ioFailed = false;
|
||||
delete outf;
|
||||
}
|
||||
if (ioFailed)
|
||||
warning("Unable to store Savegame names to file SKY-VM.SAV. (%s)", saveFileMan->popErrorDesc().c_str());
|
||||
return !ioFailed;
|
||||
}
|
||||
|
||||
SaveStateDescriptor SkyMetaEngine::querySaveMetaInfos(const char *target, int slot) const {
|
||||
Common::SaveFileManager *saveFileMan = g_system->getSavefileManager();
|
||||
|
||||
if (slot > 0) {
|
||||
// Search current save game descriptions
|
||||
// for the description of the specified slot, if any
|
||||
Common::String tmpSavename;
|
||||
Common::InSaveFile *inf;
|
||||
inf = saveFileMan->openForLoading("SKY-VM.SAV");
|
||||
if (inf != NULL) {
|
||||
char *tmpBuf = new char[MAX_SAVE_GAMES * MAX_TEXT_LEN];
|
||||
char *tmpPtr = tmpBuf;
|
||||
inf->read(tmpBuf, MAX_SAVE_GAMES * MAX_TEXT_LEN);
|
||||
for (int i = 0; i < MAX_SAVE_GAMES; ++i) {
|
||||
tmpSavename = tmpPtr;
|
||||
tmpPtr += tmpSavename.size() + 1;
|
||||
if (i == slot - 1) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
delete inf;
|
||||
delete[] tmpBuf;
|
||||
}
|
||||
|
||||
// Make sure the file exists
|
||||
// Note: there can be valid saved file names with empty savename
|
||||
char fName[20];
|
||||
Common::sprintf_s(fName,"SKY-VM.%03d", slot);
|
||||
Common::InSaveFile *in = saveFileMan->openForLoading(fName);
|
||||
if (in) {
|
||||
delete in;
|
||||
SaveStateDescriptor descriptor(this, slot, tmpSavename);
|
||||
return descriptor;
|
||||
}
|
||||
}
|
||||
|
||||
// Reaching here, means we selected an empty save slot, that does not correspond to a save file
|
||||
SaveStateDescriptor emptySave;
|
||||
// Do not allow save slot 0 (used for auto-saving) to be overwritten.
|
||||
if (slot == 0) {
|
||||
emptySave.setAutosave(true);
|
||||
emptySave.setWriteProtectedFlag(true);
|
||||
} else {
|
||||
emptySave.setWriteProtectedFlag(false);
|
||||
}
|
||||
return emptySave;
|
||||
}
|
||||
|
||||
#if PLUGIN_ENABLED_DYNAMIC(SKY)
|
||||
REGISTER_PLUGIN_DYNAMIC(SKY, PLUGIN_TYPE_ENGINE, SkyMetaEngine);
|
||||
#else
|
||||
REGISTER_PLUGIN_STATIC(SKY, PLUGIN_TYPE_ENGINE, SkyMetaEngine);
|
||||
#endif
|
||||
|
||||
namespace Sky {
|
||||
|
||||
Common::Error SkyEngine::loadGameState(int slot) {
|
||||
// We don't need to offset "slot" here. Both loadGameState and quickXRestore
|
||||
// are called with the ScummVM Save File Manager's "slot" as argument
|
||||
uint16 result = _skyControl->quickXRestore(slot);
|
||||
return (result == GAME_RESTORED) ? Common::kNoError : Common::kUnknownError;
|
||||
}
|
||||
|
||||
/**
|
||||
* Manually saving a game should save it into ScummVM Save File Managers slots 1 or greater.
|
||||
* ScummVM Save file manager's slot 0 is reserved for the autosave.
|
||||
* However, natively, the index 0 (_selectedGame) is the first manually saved game.
|
||||
* @param slot is the save slot on the ScummVM file manager's list
|
||||
*
|
||||
*/
|
||||
Common::Error SkyEngine::saveGameState(int slot, const Common::String &desc, bool isAutosave) {
|
||||
// prevent writing to autosave slot when user selects it manually
|
||||
// ie. from the ScummVM in-game menu Save feature
|
||||
// This also secures _selectedGame which is unsigned integer (uint16)
|
||||
// from overflowing in the subtraction below
|
||||
if (slot < 0 || (!isAutosave && slot == 0)) {
|
||||
return Common::kWritePermissionDenied;
|
||||
}
|
||||
// Set the save slot and save the game
|
||||
// _selectedGame value is one unit lower than the ScummVM's Save File Manager's slot value
|
||||
// Note that *_selectedGame* value 0 corresponds to a manually saved game (the first in order)
|
||||
// whereas *slot* value 0 corresponds to the autosave
|
||||
if (slot > 0) {
|
||||
// We don't care for updating the _selectedGame when slot == 0
|
||||
// (in the case of autosave) but we do include the check for slot > 0
|
||||
// to guard from overflow, which would be bad practice to allow.
|
||||
_skyControl->_selectedGame = slot - 1;
|
||||
}
|
||||
if (_skyControl->saveGameToFile(false, nullptr, isAutosave) != GAME_SAVED)
|
||||
return Common::kWritePermissionDenied;
|
||||
|
||||
// Load current save game descriptions
|
||||
Common::StringArray saveGameTexts;
|
||||
saveGameTexts.resize(MAX_SAVE_GAMES+1);
|
||||
_skyControl->loadDescriptions(saveGameTexts);
|
||||
|
||||
// Update the save game description at the given slot
|
||||
if (!isAutosave) {
|
||||
saveGameTexts[_skyControl->_selectedGame] = desc;
|
||||
}
|
||||
|
||||
// Save the updated descriptions
|
||||
_skyControl->saveDescriptions(saveGameTexts);
|
||||
|
||||
return Common::kNoError;
|
||||
}
|
||||
|
||||
bool SkyEngine::canLoadGameStateCurrently(Common::U32String *msg) {
|
||||
return _systemVars->pastIntro
|
||||
&& _skyControl->loadSaveAllowed()
|
||||
&& !_skyControl->isControlPanelOpen();
|
||||
}
|
||||
|
||||
bool SkyEngine::canSaveGameStateCurrently(Common::U32String *msg) {
|
||||
return _systemVars->pastIntro
|
||||
&& _skyControl->loadSaveAllowed()
|
||||
&& !_skyControl->isControlPanelOpen();
|
||||
}
|
||||
|
||||
} // End of namespace Sky
|
||||
35
engines/sky/module.mk
Normal file
35
engines/sky/module.mk
Normal file
@@ -0,0 +1,35 @@
|
||||
MODULE := engines/sky
|
||||
|
||||
MODULE_OBJS := \
|
||||
autoroute.o \
|
||||
compact.o \
|
||||
control.o \
|
||||
debug.o \
|
||||
disk.o \
|
||||
grid.o \
|
||||
hufftext.o \
|
||||
intro.o \
|
||||
logic.o \
|
||||
metaengine.o \
|
||||
mouse.o \
|
||||
screen.o \
|
||||
sky.o \
|
||||
sound.o \
|
||||
text.o \
|
||||
music/adlibchannel.o \
|
||||
music/adlibmusic.o \
|
||||
music/gmchannel.o \
|
||||
music/gmmusic.o \
|
||||
music/mt32music.o \
|
||||
music/musicbase.o
|
||||
|
||||
# This module can be built as a plugin
|
||||
ifeq ($(ENABLE_SKY), DYNAMIC_PLUGIN)
|
||||
PLUGIN := 1
|
||||
endif
|
||||
|
||||
# Include common rules
|
||||
include $(srcdir)/rules.mk
|
||||
|
||||
# Detection objects
|
||||
DETECT_OBJS += $(MODULE)/detection.o
|
||||
339
engines/sky/mouse.cpp
Normal file
339
engines/sky/mouse.cpp
Normal file
@@ -0,0 +1,339 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#include "common/events.h"
|
||||
#include "common/system.h"
|
||||
#include "common/textconsole.h"
|
||||
#include "graphics/cursorman.h"
|
||||
#include "sky/disk.h"
|
||||
#include "sky/logic.h"
|
||||
#include "sky/mouse.h"
|
||||
#include "sky/sky.h"
|
||||
#include "sky/skydefs.h"
|
||||
#include "sky/struc.h"
|
||||
#include "sky/compact.h"
|
||||
|
||||
namespace Sky {
|
||||
|
||||
#define MICE_FILE 60300
|
||||
#define NO_MAIN_OBJECTS 24
|
||||
#define NO_LINC_OBJECTS 21
|
||||
|
||||
uint32 Mouse::_mouseMainObjects[24] = {
|
||||
65,
|
||||
9,
|
||||
66,
|
||||
64,
|
||||
8,
|
||||
63,
|
||||
10,
|
||||
11,
|
||||
71,
|
||||
76,
|
||||
37,
|
||||
36,
|
||||
42,
|
||||
75,
|
||||
79,
|
||||
6,
|
||||
74,
|
||||
39,
|
||||
49,
|
||||
43,
|
||||
34,
|
||||
35,
|
||||
77,
|
||||
38
|
||||
};
|
||||
|
||||
uint32 Mouse::_mouseLincObjects[21] = {
|
||||
24625,
|
||||
24649,
|
||||
24827,
|
||||
24651,
|
||||
24583,
|
||||
24581,
|
||||
24582,
|
||||
24628,
|
||||
24650,
|
||||
24629,
|
||||
24732,
|
||||
24631,
|
||||
24584,
|
||||
24630,
|
||||
24626,
|
||||
24627,
|
||||
24632,
|
||||
24643,
|
||||
24828,
|
||||
24830,
|
||||
24829
|
||||
};
|
||||
|
||||
Mouse::Mouse(OSystem *system, Disk *skyDisk, SkyCompact *skyCompact) {
|
||||
_skyDisk = skyDisk;
|
||||
_skyCompact = skyCompact;
|
||||
_system = system;
|
||||
_mouseB = 0;
|
||||
_currentCursor = 6;
|
||||
_mouseX = GAME_SCREEN_WIDTH / 2;
|
||||
_mouseY = GAME_SCREEN_HEIGHT / 2;
|
||||
|
||||
_miceData = _skyDisk->loadFile(MICE_FILE);
|
||||
|
||||
//load in the object mouse file
|
||||
_objectMouseData = _skyDisk->loadFile(MICE_FILE + 1);
|
||||
}
|
||||
|
||||
Mouse::~Mouse( ){
|
||||
free (_miceData);
|
||||
free (_objectMouseData);
|
||||
}
|
||||
|
||||
void Mouse::replaceMouseCursors(uint16 fileNo) {
|
||||
free(_objectMouseData);
|
||||
_objectMouseData = _skyDisk->loadFile(fileNo);
|
||||
}
|
||||
|
||||
bool Mouse::fnAddHuman() {
|
||||
//reintroduce the mouse so that the human can control the player
|
||||
//could still be switched out at high-level
|
||||
|
||||
if (!Logic::_scriptVariables[MOUSE_STOP]) {
|
||||
Logic::_scriptVariables[MOUSE_STATUS] |= 6; //cursor & mouse
|
||||
|
||||
if (_mouseY < 2) //stop mouse activating top line
|
||||
_mouseY = 2;
|
||||
|
||||
_system->warpMouse(_mouseX, _mouseY);
|
||||
|
||||
//force the pointer engine into running a get-off
|
||||
//even if it's over nothing
|
||||
|
||||
//KWIK-FIX
|
||||
//get off may contain script to remove mouse pointer text
|
||||
//surely this script should be run just in case
|
||||
//I am going to try it anyway
|
||||
if (Logic::_scriptVariables[GET_OFF])
|
||||
_skyLogic->script((uint16)Logic::_scriptVariables[GET_OFF],(uint16)(Logic::_scriptVariables[GET_OFF] >> 16));
|
||||
|
||||
Logic::_scriptVariables[SPECIAL_ITEM] = 0xFFFFFFFF;
|
||||
Logic::_scriptVariables[GET_OFF] = RESET_MOUSE;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void Mouse::fnSaveCoods() {
|
||||
Logic::_scriptVariables[SAFEX] = _mouseX + TOP_LEFT_X;
|
||||
Logic::_scriptVariables[SAFEY] = _mouseY + TOP_LEFT_Y;
|
||||
}
|
||||
|
||||
void Mouse::lockMouse() {
|
||||
SkyEngine::_systemVars->systemFlags |= SF_MOUSE_LOCKED;
|
||||
}
|
||||
|
||||
void Mouse::unlockMouse() {
|
||||
SkyEngine::_systemVars->systemFlags &= ~SF_MOUSE_LOCKED;
|
||||
}
|
||||
|
||||
void Mouse::restoreMouseData(uint16 frameNum) {
|
||||
warning("Stub: Mouse::restoreMouseData");
|
||||
}
|
||||
|
||||
void Mouse::drawNewMouse() {
|
||||
warning("Stub: Mouse::drawNewMouse");
|
||||
//calculateMouseValues();
|
||||
//saveMouseData();
|
||||
//drawMouse();
|
||||
}
|
||||
|
||||
void Mouse::waitMouseNotPressed(int minDelay) {
|
||||
bool mousePressed = true;
|
||||
uint32 now = _system->getMillis();
|
||||
Common::Event event;
|
||||
Common::EventManager *eventMan = _system->getEventManager();
|
||||
while (mousePressed || _system->getMillis() < now + minDelay) {
|
||||
|
||||
if (eventMan->shouldQuit()) {
|
||||
minDelay = 0;
|
||||
mousePressed = false;
|
||||
}
|
||||
|
||||
if (!eventMan->getButtonState())
|
||||
mousePressed = false;
|
||||
|
||||
while (eventMan->pollEvent(event)) {
|
||||
switch (event.type) {
|
||||
case Common::EVENT_CUSTOM_ENGINE_ACTION_START:
|
||||
if (event.customType == kSkyActionSkip) {
|
||||
minDelay = 0;
|
||||
mousePressed = false;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
_system->updateScreen();
|
||||
_system->delayMillis(20);
|
||||
}
|
||||
}
|
||||
|
||||
void Mouse::spriteMouse(uint16 frameNum, uint8 mouseX, uint8 mouseY) {
|
||||
_currentCursor = frameNum;
|
||||
|
||||
byte *newCursor = _miceData;
|
||||
newCursor += ((DataFileHeader *)_miceData)->s_sp_size * frameNum;
|
||||
newCursor += sizeof(DataFileHeader);
|
||||
|
||||
uint16 mouseWidth = ((DataFileHeader *)_miceData)->s_width;
|
||||
uint16 mouseHeight = ((DataFileHeader *)_miceData)->s_height;
|
||||
|
||||
CursorMan.replaceCursor(newCursor, mouseWidth, mouseHeight, mouseX, mouseY, 0);
|
||||
if (frameNum == MOUSE_BLANK)
|
||||
CursorMan.showMouse(false);
|
||||
else
|
||||
CursorMan.showMouse(true);
|
||||
}
|
||||
|
||||
void Mouse::mouseEngine() {
|
||||
_logicClick = (_mouseB > 0); // click signal is available for Logic for one gamecycle
|
||||
|
||||
if (!Logic::_scriptVariables[MOUSE_STOP]) {
|
||||
if (Logic::_scriptVariables[MOUSE_STATUS] & (1 << 1)) {
|
||||
pointerEngine(_mouseX + TOP_LEFT_X, _mouseY + TOP_LEFT_Y);
|
||||
if (Logic::_scriptVariables[MOUSE_STATUS] & (1 << 2)) //buttons enabled?
|
||||
buttonEngine1();
|
||||
}
|
||||
}
|
||||
_mouseB = 0; //don't save up buttons
|
||||
}
|
||||
|
||||
void Mouse::pointerEngine(uint16 xPos, uint16 yPos) {
|
||||
uint32 currentListNum = Logic::_scriptVariables[MOUSE_LIST_NO];
|
||||
uint16 *currentList;
|
||||
do {
|
||||
currentList = (uint16 *)_skyCompact->fetchCpt(currentListNum);
|
||||
while ((*currentList != 0) && (*currentList != 0xFFFF)) {
|
||||
uint16 itemNum = *currentList;
|
||||
Compact *itemData = _skyCompact->fetchCpt(itemNum);
|
||||
currentList++;
|
||||
if ((itemData->screen == Logic::_scriptVariables[SCREEN]) && (itemData->status & 16)) {
|
||||
if (itemData->xcood + ((int16)itemData->mouseRelX) > xPos) continue;
|
||||
if (itemData->xcood + ((int16)itemData->mouseRelX) + itemData->mouseSizeX < xPos) continue;
|
||||
if (itemData->ycood + ((int16)itemData->mouseRelY) > yPos) continue;
|
||||
if (itemData->ycood + ((int16)itemData->mouseRelY) + itemData->mouseSizeY < yPos) continue;
|
||||
// we've hit the item
|
||||
if (Logic::_scriptVariables[SPECIAL_ITEM] == itemNum)
|
||||
return;
|
||||
Logic::_scriptVariables[SPECIAL_ITEM] = itemNum;
|
||||
if (Logic::_scriptVariables[GET_OFF])
|
||||
_skyLogic->mouseScript(Logic::_scriptVariables[GET_OFF], itemData);
|
||||
Logic::_scriptVariables[GET_OFF] = itemData->mouseOff;
|
||||
if (itemData->mouseOn)
|
||||
_skyLogic->mouseScript(itemData->mouseOn, itemData);
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (*currentList == 0xFFFF)
|
||||
currentListNum = currentList[1];
|
||||
} while (*currentList != 0);
|
||||
if (Logic::_scriptVariables[SPECIAL_ITEM] != 0) {
|
||||
Logic::_scriptVariables[SPECIAL_ITEM] = 0;
|
||||
|
||||
if (Logic::_scriptVariables[GET_OFF])
|
||||
_skyLogic->script((uint16)Logic::_scriptVariables[GET_OFF],(uint16)(Logic::_scriptVariables[GET_OFF] >> 16));
|
||||
Logic::_scriptVariables[GET_OFF] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void Mouse::buttonPressed(uint8 button) {
|
||||
_mouseB = button;
|
||||
}
|
||||
|
||||
void Mouse::mouseMoved(uint16 mouseX, uint16 mouseY) {
|
||||
_mouseX = mouseX;
|
||||
_mouseY = mouseY;
|
||||
}
|
||||
|
||||
void Mouse::buttonEngine1() {
|
||||
//checks for clicking on special item
|
||||
//"compare the size of this routine to S1 mouse_button"
|
||||
|
||||
if (_mouseB) { //anything pressed?
|
||||
Logic::_scriptVariables[BUTTON] = _mouseB;
|
||||
if (Logic::_scriptVariables[SPECIAL_ITEM]) { //over anything?
|
||||
Compact *item = _skyCompact->fetchCpt(Logic::_scriptVariables[SPECIAL_ITEM]);
|
||||
if (item->mouseClick)
|
||||
_skyLogic->mouseScript(item->mouseClick, item);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Mouse::resetCursor() {
|
||||
spriteMouse(_currentCursor, 0, 0);
|
||||
}
|
||||
|
||||
uint16 Mouse::findMouseCursor(uint32 itemNum) {
|
||||
uint8 cnt;
|
||||
for (cnt = 0; cnt < NO_MAIN_OBJECTS; cnt++) {
|
||||
if (itemNum == _mouseMainObjects[cnt]) {
|
||||
return cnt;
|
||||
}
|
||||
}
|
||||
for (cnt = 0; cnt < NO_LINC_OBJECTS; cnt++) {
|
||||
if (itemNum == _mouseLincObjects[cnt]) {
|
||||
return cnt;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void Mouse::fnOpenCloseHand(bool open) {
|
||||
if ((!open) && (!Logic::_scriptVariables[OBJECT_HELD])) {
|
||||
spriteMouse(1, 0, 0);
|
||||
return;
|
||||
}
|
||||
uint16 cursor = findMouseCursor(Logic::_scriptVariables[OBJECT_HELD]) << 1;
|
||||
if (open)
|
||||
cursor++;
|
||||
|
||||
uint32 size = ((DataFileHeader *)_objectMouseData)->s_sp_size;
|
||||
uint8 *srcData;
|
||||
uint8 *destData;
|
||||
|
||||
srcData = (uint8 *)_objectMouseData + size * cursor + sizeof(DataFileHeader);
|
||||
destData = (uint8 *)_miceData + sizeof(DataFileHeader);
|
||||
memcpy(destData, srcData, size);
|
||||
spriteMouse(0, 5, 5);
|
||||
}
|
||||
|
||||
bool Mouse::wasClicked() {
|
||||
if (_logicClick) {
|
||||
_logicClick = false;
|
||||
return true;
|
||||
} else
|
||||
return false;
|
||||
}
|
||||
|
||||
} // End of namespace Sky
|
||||
92
engines/sky/mouse.h
Normal file
92
engines/sky/mouse.h
Normal file
@@ -0,0 +1,92 @@
|
||||
/* 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 SKY_MOUSE_H
|
||||
#define SKY_MOUSE_H
|
||||
|
||||
|
||||
#include "common/scummsys.h"
|
||||
|
||||
class OSystem;
|
||||
|
||||
namespace Sky {
|
||||
|
||||
class Disk;
|
||||
class Logic;
|
||||
class SkyCompact;
|
||||
|
||||
class Mouse {
|
||||
|
||||
public:
|
||||
|
||||
Mouse(OSystem *system, Disk *skyDisk, SkyCompact *skyCompact);
|
||||
~Mouse();
|
||||
|
||||
void mouseEngine();
|
||||
void replaceMouseCursors(uint16 fileNo);
|
||||
bool fnAddHuman();
|
||||
void fnSaveCoods();
|
||||
void fnOpenCloseHand(bool open);
|
||||
uint16 findMouseCursor(uint32 itemNum);
|
||||
void lockMouse();
|
||||
void unlockMouse();
|
||||
void restoreMouseData(uint16 frameNum);
|
||||
void drawNewMouse();
|
||||
void spriteMouse(uint16 frameNum, uint8 mouseX, uint8 mouseY);
|
||||
void useLogicInstance(Logic *skyLogic) { _skyLogic = skyLogic; }
|
||||
void buttonPressed(uint8 button);
|
||||
void mouseMoved(uint16 mouseX, uint16 mouseY);
|
||||
void waitMouseNotPressed(int minDelay = 0);
|
||||
uint16 giveMouseX() { return _mouseX; }
|
||||
uint16 giveMouseY() { return _mouseY; }
|
||||
uint16 giveCurrentMouseType() { return _currentCursor; }
|
||||
bool wasClicked();
|
||||
void logicClick() { _logicClick = true; }
|
||||
void resetCursor();
|
||||
|
||||
protected:
|
||||
|
||||
void pointerEngine(uint16 xPos, uint16 yPos);
|
||||
void buttonEngine1();
|
||||
|
||||
bool _logicClick;
|
||||
|
||||
uint16 _mouseB; //mouse button
|
||||
uint16 _mouseX; //actual mouse coordinates
|
||||
uint16 _mouseY;
|
||||
|
||||
uint16 _currentCursor;
|
||||
|
||||
byte *_miceData; //address of mouse sprites
|
||||
byte *_objectMouseData; //address of object mouse sprites
|
||||
|
||||
static uint32 _mouseMainObjects[24];
|
||||
static uint32 _mouseLincObjects[21];
|
||||
|
||||
OSystem *_system;
|
||||
Disk *_skyDisk;
|
||||
Logic *_skyLogic;
|
||||
SkyCompact *_skyCompact;
|
||||
};
|
||||
|
||||
} // End of namespace Sky
|
||||
|
||||
#endif //SKYMOUSE_H
|
||||
316
engines/sky/music/adlibchannel.cpp
Normal file
316
engines/sky/music/adlibchannel.cpp
Normal file
@@ -0,0 +1,316 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#include "common/endian.h"
|
||||
#include "common/textconsole.h"
|
||||
#include "audio/fmopl.h"
|
||||
#include "sky/music/adlibchannel.h"
|
||||
#include "sky/sky.h"
|
||||
|
||||
namespace Sky {
|
||||
|
||||
AdLibChannel::AdLibChannel(OPL::OPL *opl, uint8 *pMusicData, uint16 startOfData) {
|
||||
_opl = opl;
|
||||
_musicData = pMusicData;
|
||||
_channelData.loopPoint = startOfData;
|
||||
_channelData.eventDataPtr = startOfData;
|
||||
_channelData.channelActive = true;
|
||||
_channelData.tremoVibro = 0;
|
||||
_channelData.assignedInstrument = 0xFF;
|
||||
_channelData.channelVolume = 0x7F;
|
||||
_channelData.nextEventTime = getNextEventTime();
|
||||
|
||||
_channelData.adlibChannelNumber = _channelData.lastCommand = _channelData.note =
|
||||
_channelData.adlibReg1 = _channelData.adlibReg2 = _channelData.freqOffset = 0;
|
||||
_channelData.frequency = 0;
|
||||
_channelData.instrumentData = NULL;
|
||||
|
||||
_musicVolume = 128;
|
||||
|
||||
uint16 instrumentDataLoc;
|
||||
|
||||
if (SkyEngine::_systemVars->gameVersion == 109) {
|
||||
//instrumentDataLoc = (_musicData[0x11D0] << 8) | _musicData[0x11CF];
|
||||
//_frequenceTable = (uint16 *)(_musicData + 0x835);
|
||||
//_registerTable = _musicData + 0xE35;
|
||||
//_opOutputTable = _musicData + 0xE47;
|
||||
//_adlibRegMirror = _musicData + 0xF4A;
|
||||
|
||||
instrumentDataLoc = READ_LE_UINT16(_musicData + 0x1204);
|
||||
_frequenceTable = (uint16 *)(_musicData + 0x868);
|
||||
_registerTable = _musicData + 0xE68;
|
||||
_opOutputTable = _musicData + 0xE7A;
|
||||
_adlibRegMirror = _musicData + 0xF7D;
|
||||
} else if (SkyEngine::_systemVars->gameVersion == 267) {
|
||||
instrumentDataLoc = READ_LE_UINT16(_musicData + 0x11FB);
|
||||
_frequenceTable = (uint16 *)(_musicData + 0x7F4);
|
||||
_registerTable = _musicData + 0xDF4;
|
||||
_opOutputTable = _musicData + 0xE06;
|
||||
_adlibRegMirror = _musicData + 0xF55;
|
||||
} else {
|
||||
instrumentDataLoc = READ_LE_UINT16(_musicData + 0x1205);
|
||||
_frequenceTable = (uint16 *)(_musicData + 0x7FE);
|
||||
_registerTable = _musicData + 0xDFE;
|
||||
_opOutputTable = _musicData + 0xE10;
|
||||
_adlibRegMirror = _musicData + 0xF5F;
|
||||
}
|
||||
|
||||
_instrumentMap = _musicData+instrumentDataLoc;
|
||||
_instruments = (InstrumentStruct *)(_instrumentMap+0x80);
|
||||
}
|
||||
|
||||
AdLibChannel::~AdLibChannel() {
|
||||
stopNote();
|
||||
}
|
||||
|
||||
bool AdLibChannel::isActive() {
|
||||
return _channelData.channelActive;
|
||||
}
|
||||
|
||||
void AdLibChannel::updateVolume(uint16 pVolume) {
|
||||
_musicVolume = pVolume;
|
||||
}
|
||||
|
||||
/* This class uses the same area for the register mirror as the original
|
||||
asm driver did (_musicData[0xF5F..0x105E]), so the cache is indeed shared
|
||||
by all instances of the class.
|
||||
*/
|
||||
void AdLibChannel::setRegister(uint8 regNum, uint8 value) {
|
||||
if (_adlibRegMirror[regNum] != value) {
|
||||
_opl->writeReg(regNum, value);
|
||||
_adlibRegMirror[regNum] = value;
|
||||
}
|
||||
}
|
||||
|
||||
void AdLibChannel::stopNote() {
|
||||
if (_channelData.note & 0x20) {
|
||||
_channelData.note &= ~0x20;
|
||||
setRegister(0xB0 | _channelData.adlibChannelNumber, _channelData.note);
|
||||
}
|
||||
}
|
||||
|
||||
int32 AdLibChannel::getNextEventTime() {
|
||||
int32 retV = 0;
|
||||
uint8 cnt, lVal = 0;
|
||||
for (cnt = 0; cnt < 4; cnt++) {
|
||||
lVal = _musicData[_channelData.eventDataPtr];
|
||||
_channelData.eventDataPtr++;
|
||||
retV = (retV << 7) | (lVal & 0x7F);
|
||||
if (!(lVal & 0x80))
|
||||
break;
|
||||
}
|
||||
if (lVal & 0x80) {
|
||||
return -1; // should never happen
|
||||
} else
|
||||
return retV;
|
||||
}
|
||||
|
||||
uint8 AdLibChannel::process(uint16 aktTime) {
|
||||
if (!_channelData.channelActive) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint8 returnVal = 0;
|
||||
|
||||
_channelData.nextEventTime -= aktTime;
|
||||
uint8 opcode;
|
||||
while ((_channelData.nextEventTime < 0) && (_channelData.channelActive)) {
|
||||
opcode = _musicData[_channelData.eventDataPtr];
|
||||
_channelData.eventDataPtr++;
|
||||
if (opcode & 0x80) {
|
||||
if (opcode == 0xFF) {
|
||||
// dummy opcode
|
||||
} else if (opcode >= 0x90) {
|
||||
switch (opcode&0xF) {
|
||||
case 0: com90_caseNoteOff(); break;
|
||||
case 1: com90_stopChannel(); break;
|
||||
case 2: com90_setupInstrument(); break;
|
||||
case 3:
|
||||
returnVal = com90_updateTempo();
|
||||
break;
|
||||
case 5: com90_getFreqOffset(); break;
|
||||
case 6: com90_getChannelVolume(); break;
|
||||
case 7: com90_getTremoVibro(); break;
|
||||
case 8: com90_loopMusic(); break;
|
||||
case 9: com90_keyOff(); break;
|
||||
case 12: com90_setLoopPoint(); break;
|
||||
|
||||
default:
|
||||
error("AdLibChannel: Unknown music opcode 0x%02X", opcode);
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
// new adlib channel assignment
|
||||
_channelData.adlibChannelNumber = opcode & 0xF;
|
||||
_channelData.adlibReg1 = _registerTable[((opcode & 0xF) << 1) | 0];
|
||||
_channelData.adlibReg2 = _registerTable[((opcode & 0xF) << 1) | 1];
|
||||
}
|
||||
} else {
|
||||
_channelData.lastCommand = opcode;
|
||||
stopNote();
|
||||
// not sure why this "if" is necessary...either a bug in my
|
||||
// code or a bug in the music data (section 1, music 2)
|
||||
if (_channelData.instrumentData || _channelData.tremoVibro) {
|
||||
setupInstrument(opcode);
|
||||
|
||||
opcode = _musicData[_channelData.eventDataPtr];
|
||||
_channelData.eventDataPtr++;
|
||||
setupChannelVolume(opcode);
|
||||
} else
|
||||
_channelData.eventDataPtr++;
|
||||
}
|
||||
if (_channelData.channelActive)
|
||||
_channelData.nextEventTime += getNextEventTime();
|
||||
}
|
||||
return returnVal;
|
||||
}
|
||||
|
||||
void AdLibChannel::setupInstrument(uint8 opcode) {
|
||||
uint16 nextNote;
|
||||
if (_channelData.tremoVibro) {
|
||||
uint8 newInstrument = _instrumentMap[opcode];
|
||||
if (newInstrument != _channelData.assignedInstrument) {
|
||||
_channelData.assignedInstrument = newInstrument;
|
||||
_channelData.instrumentData = _instruments + newInstrument;
|
||||
adlibSetupInstrument();
|
||||
}
|
||||
_channelData.lastCommand = _channelData.instrumentData->bindedEffect;
|
||||
nextNote = getNextNote(_channelData.lastCommand);
|
||||
} else {
|
||||
nextNote = getNextNote(opcode - 0x18 + _channelData.instrumentData->bindedEffect);
|
||||
}
|
||||
_channelData.frequency = nextNote;
|
||||
setRegister(0xA0 | _channelData.adlibChannelNumber, (uint8)nextNote);
|
||||
setRegister(0xB0 | _channelData.adlibChannelNumber, (uint8)((nextNote >> 8) | 0x20));
|
||||
_channelData.note = (uint8)((nextNote >> 8) | 0x20);
|
||||
}
|
||||
|
||||
void AdLibChannel::setupChannelVolume(uint8 volume) {
|
||||
uint8 resultOp;
|
||||
uint32 resVol = ((volume + 1) * (_channelData.instrumentData->totOutLev_Op2 + 1)) << 1;
|
||||
resVol &= 0xFFFF;
|
||||
resVol *= (_channelData.channelVolume + 1) << 1;
|
||||
resVol >>= 8;
|
||||
resVol *= _musicVolume << 1;
|
||||
resVol >>= 16;
|
||||
assert(resVol < 0x81);
|
||||
resultOp = ((_channelData.instrumentData->scalingLevel << 6) & 0xC0) | _opOutputTable[resVol];
|
||||
setRegister(0x40 | _channelData.adlibReg2, resultOp);
|
||||
if (_channelData.instrumentData->feedBack & 1) {
|
||||
resVol = ((volume + 1) * (_channelData.instrumentData->totOutLev_Op1 + 1)) << 1;
|
||||
resVol &= 0xFFFF;
|
||||
resVol *= (_channelData.channelVolume + 1) << 1;
|
||||
resVol >>= 8;
|
||||
resVol *= _musicVolume << 1;
|
||||
resVol >>= 16;
|
||||
} else
|
||||
resVol = _channelData.instrumentData->totOutLev_Op1;
|
||||
assert(resVol < 0x81);
|
||||
resultOp = ((_channelData.instrumentData->scalingLevel << 2) & 0xC0) | _opOutputTable[resVol];
|
||||
setRegister(0x40 | _channelData.adlibReg1, resultOp);
|
||||
}
|
||||
|
||||
void AdLibChannel::adlibSetupInstrument() {
|
||||
setRegister(0x60 | _channelData.adlibReg1, _channelData.instrumentData->ad_Op1);
|
||||
setRegister(0x60 | _channelData.adlibReg2, _channelData.instrumentData->ad_Op2);
|
||||
setRegister(0x80 | _channelData.adlibReg1, _channelData.instrumentData->sr_Op1);
|
||||
setRegister(0x80 | _channelData.adlibReg2, _channelData.instrumentData->sr_Op2);
|
||||
setRegister(0xE0 | _channelData.adlibReg1, _channelData.instrumentData->waveSelect_Op1);
|
||||
setRegister(0xE0 | _channelData.adlibReg2, _channelData.instrumentData->waveSelect_Op2);
|
||||
setRegister(0xC0 | _channelData.adlibChannelNumber, _channelData.instrumentData->feedBack);
|
||||
setRegister(0x20 | _channelData.adlibReg1, _channelData.instrumentData->ampMod_Op1);
|
||||
setRegister(0x20 | _channelData.adlibReg2, _channelData.instrumentData->ampMod_Op2);
|
||||
}
|
||||
|
||||
uint16 AdLibChannel::getNextNote(uint8 param) {
|
||||
int16 freqIndex = ((int16)_channelData.freqOffset) - 0x40;
|
||||
if (freqIndex >= 0x3F)
|
||||
freqIndex++;
|
||||
freqIndex *= 2;
|
||||
freqIndex += param << 6;
|
||||
uint16 freqData = FROM_LE_16(_frequenceTable[freqIndex % 0x300]);
|
||||
if ((freqIndex % 0x300 >= 0x1C0) || (freqIndex / 0x300 > 0)) {
|
||||
return (((freqIndex / 0x300) - 1) << 10) + (freqData & 0x7FF);
|
||||
} else {
|
||||
// looks like a bug. dunno why. It's what the ASM code says.
|
||||
return (uint16)(((int16)freqData) >> 1);
|
||||
}
|
||||
}
|
||||
|
||||
//- command 90h routines
|
||||
|
||||
void AdLibChannel::com90_caseNoteOff() {
|
||||
if (_musicData[_channelData.eventDataPtr] == _channelData.lastCommand)
|
||||
stopNote();
|
||||
_channelData.eventDataPtr++;
|
||||
}
|
||||
|
||||
void AdLibChannel::com90_stopChannel() {
|
||||
stopNote();
|
||||
_channelData.channelActive = false;
|
||||
}
|
||||
|
||||
void AdLibChannel::com90_setupInstrument() {
|
||||
_channelData.channelVolume = 0x7F;
|
||||
_channelData.freqOffset = 0x40;
|
||||
_channelData.assignedInstrument = _musicData[_channelData.eventDataPtr];
|
||||
_channelData.eventDataPtr++;
|
||||
_channelData.instrumentData = _instruments + _channelData.assignedInstrument;
|
||||
adlibSetupInstrument();
|
||||
}
|
||||
|
||||
uint8 AdLibChannel::com90_updateTempo() {
|
||||
return _musicData[_channelData.eventDataPtr++];
|
||||
}
|
||||
|
||||
void AdLibChannel::com90_getFreqOffset() {
|
||||
_channelData.freqOffset = _musicData[_channelData.eventDataPtr++];
|
||||
if (_channelData.note & 0x20) {
|
||||
uint16 nextNote = getNextNote(
|
||||
_channelData.lastCommand - 0x18 + _channelData.instrumentData->bindedEffect);
|
||||
setRegister(0xA0 | _channelData.adlibChannelNumber, (uint8)nextNote);
|
||||
setRegister(0xB0 | _channelData.adlibChannelNumber, (uint8)((nextNote >> 8) | 0x20));
|
||||
_channelData.note = (uint8)(nextNote >> 8) | 0x20;
|
||||
}
|
||||
}
|
||||
|
||||
void AdLibChannel::com90_getChannelVolume() {
|
||||
_channelData.channelVolume = _musicData[_channelData.eventDataPtr++];
|
||||
}
|
||||
|
||||
void AdLibChannel::com90_getTremoVibro() {
|
||||
_channelData.tremoVibro = _musicData[_channelData.eventDataPtr++];
|
||||
}
|
||||
|
||||
void AdLibChannel::com90_loopMusic() {
|
||||
_channelData.eventDataPtr = _channelData.loopPoint;
|
||||
}
|
||||
|
||||
void AdLibChannel::com90_keyOff() {
|
||||
stopNote();
|
||||
}
|
||||
|
||||
void AdLibChannel::com90_setLoopPoint() {
|
||||
_channelData.loopPoint = _channelData.eventDataPtr;
|
||||
}
|
||||
|
||||
} // End of namespace Sky
|
||||
110
engines/sky/music/adlibchannel.h
Normal file
110
engines/sky/music/adlibchannel.h
Normal file
@@ -0,0 +1,110 @@
|
||||
/* 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 SKY_MUSIC_ADLIBCHANNEL_H
|
||||
#define SKY_MUSIC_ADLIBCHANNEL_H
|
||||
|
||||
#include "sky/music/musicbase.h"
|
||||
|
||||
namespace OPL {
|
||||
class OPL;
|
||||
}
|
||||
|
||||
namespace Sky {
|
||||
|
||||
typedef struct {
|
||||
uint8 ad_Op1, ad_Op2;
|
||||
uint8 sr_Op1, sr_Op2;
|
||||
uint8 ampMod_Op1, ampMod_Op2;
|
||||
uint8 waveSelect_Op1, waveSelect_Op2;
|
||||
uint8 bindedEffect;
|
||||
uint8 feedBack;
|
||||
uint8 totOutLev_Op1, totOutLev_Op2;
|
||||
uint8 scalingLevel;
|
||||
uint8 pad1, pad2, pad3;
|
||||
} InstrumentStruct;
|
||||
|
||||
typedef struct {
|
||||
uint16 eventDataPtr;
|
||||
int32 nextEventTime;
|
||||
uint16 loopPoint;
|
||||
uint8 adlibChannelNumber;
|
||||
uint8 lastCommand;
|
||||
bool channelActive;
|
||||
uint8 note;
|
||||
uint8 adlibReg1, adlibReg2;
|
||||
InstrumentStruct *instrumentData;
|
||||
uint8 assignedInstrument;
|
||||
uint8 channelVolume;
|
||||
uint8 padding; // field_12 / not used by original driver
|
||||
uint8 tremoVibro;
|
||||
uint8 freqOffset;
|
||||
uint16 frequency;
|
||||
} AdLibChannelType;
|
||||
|
||||
class AdLibChannel : public ChannelBase {
|
||||
public:
|
||||
AdLibChannel (OPL::OPL *opl, uint8 *pMusicData, uint16 startOfData);
|
||||
~AdLibChannel() override;
|
||||
uint8 process(uint16 aktTime) override;
|
||||
void updateVolume(uint16 pVolume) override;
|
||||
bool isActive() override;
|
||||
private:
|
||||
OPL::OPL *_opl;
|
||||
uint8 *_musicData;
|
||||
uint16 _musicVolume;
|
||||
AdLibChannelType _channelData;
|
||||
|
||||
InstrumentStruct *_instruments;
|
||||
uint16 *_frequenceTable;
|
||||
uint8 *_instrumentMap;
|
||||
uint8 *_registerTable, *_opOutputTable;
|
||||
uint8 *_adlibRegMirror;
|
||||
|
||||
// normal subs
|
||||
void setRegister(uint8 regNum, uint8 value);
|
||||
int32 getNextEventTime();
|
||||
uint16 getNextNote(uint8 param);
|
||||
void adlibSetupInstrument();
|
||||
void setupInstrument(uint8 opcode);
|
||||
void setupChannelVolume(uint8 volume);
|
||||
void stopNote();
|
||||
|
||||
// Streamfunctions from Command90hTable
|
||||
void com90_caseNoteOff(); // 0
|
||||
void com90_stopChannel(); // 1
|
||||
void com90_setupInstrument(); // 2
|
||||
uint8 com90_updateTempo(); // 3
|
||||
//void com90_dummy(); // 4
|
||||
void com90_getFreqOffset(); // 5
|
||||
void com90_getChannelVolume(); // 6
|
||||
void com90_getTremoVibro(); // 7
|
||||
void com90_loopMusic(); // 8
|
||||
void com90_keyOff(); // 9
|
||||
//void com90_error(); // 10
|
||||
//void com90_doLodsb(); // 11
|
||||
void com90_setLoopPoint(); // 12
|
||||
//void com90_do_two_Lodsb(); // 13
|
||||
};
|
||||
|
||||
} // End of namespace Sky
|
||||
|
||||
#endif //ADLIBCHANNEL_H
|
||||
98
engines/sky/music/adlibmusic.cpp
Normal file
98
engines/sky/music/adlibmusic.cpp
Normal file
@@ -0,0 +1,98 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#include "common/endian.h"
|
||||
#include "common/textconsole.h"
|
||||
|
||||
#include "sky/music/adlibmusic.h"
|
||||
#include "sky/music/adlibchannel.h"
|
||||
#include "audio/fmopl.h"
|
||||
#include "sky/sky.h"
|
||||
|
||||
namespace Audio {
|
||||
class Mixer;
|
||||
}
|
||||
|
||||
namespace Sky {
|
||||
|
||||
AdLibMusic::AdLibMusic(Audio::Mixer *pMixer, Disk *pDisk) : MusicBase(pMixer, pDisk) {
|
||||
_driverFileBase = 60202;
|
||||
|
||||
_opl = OPL::Config::create();
|
||||
if (!_opl || !_opl->init())
|
||||
error("Failed to create OPL");
|
||||
|
||||
_opl->start(new Common::Functor0Mem<void, AdLibMusic>(this, &AdLibMusic::onTimer), 50);
|
||||
}
|
||||
|
||||
AdLibMusic::~AdLibMusic() {
|
||||
delete _opl;
|
||||
}
|
||||
|
||||
void AdLibMusic::onTimer() {
|
||||
if (_musicData != NULL)
|
||||
pollMusic();
|
||||
}
|
||||
|
||||
void AdLibMusic::setupPointers() {
|
||||
if (SkyEngine::_systemVars->gameVersion == 109) {
|
||||
// disk demo uses a different AdLib driver version, some offsets have changed
|
||||
//_musicDataLoc = (_musicData[0x11CC] << 8) | _musicData[0x11CB];
|
||||
//_initSequence = _musicData + 0xEC8;
|
||||
|
||||
_musicDataLoc = READ_LE_UINT16(_musicData + 0x1200);
|
||||
_initSequence = _musicData + 0xEFB;
|
||||
} else if (SkyEngine::_systemVars->gameVersion == 267) {
|
||||
_musicDataLoc = READ_LE_UINT16(_musicData + 0x11F7);
|
||||
_initSequence = _musicData + 0xE87;
|
||||
} else {
|
||||
_musicDataLoc = READ_LE_UINT16(_musicData + 0x1201);
|
||||
_initSequence = _musicData + 0xE91;
|
||||
}
|
||||
}
|
||||
|
||||
void AdLibMusic::setupChannels(uint8 *channelData) {
|
||||
_numberOfChannels = channelData[0];
|
||||
channelData++;
|
||||
for (uint8 cnt = 0; cnt < _numberOfChannels; cnt++) {
|
||||
uint16 chDataStart = READ_LE_UINT16((uint16 *)channelData + cnt) + _musicDataLoc;
|
||||
_channels[cnt] = new AdLibChannel(_opl, _musicData, chDataStart);
|
||||
// also set proper volume - Fixes bug where intro music plays at full volume
|
||||
_channels[cnt]->updateVolume(_musicVolume);
|
||||
}
|
||||
}
|
||||
|
||||
void AdLibMusic::startDriver() {
|
||||
uint16 cnt = 0;
|
||||
while (_initSequence[cnt] || _initSequence[cnt + 1]) {
|
||||
_opl->writeReg(_initSequence[cnt], _initSequence[cnt + 1]);
|
||||
cnt += 2;
|
||||
}
|
||||
}
|
||||
|
||||
void AdLibMusic::setVolume(uint16 param) {
|
||||
_musicVolume = param;
|
||||
for (uint8 cnt = 0; cnt < _numberOfChannels; cnt++)
|
||||
_channels[cnt]->updateVolume(_musicVolume);
|
||||
}
|
||||
|
||||
} // End of namespace Sky
|
||||
60
engines/sky/music/adlibmusic.h
Normal file
60
engines/sky/music/adlibmusic.h
Normal file
@@ -0,0 +1,60 @@
|
||||
/* 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 SKY_MUSIC_ADLIBMUSIC_H
|
||||
#define SKY_MUSIC_ADLIBMUSIC_H
|
||||
|
||||
#include "sky/music/musicbase.h"
|
||||
|
||||
namespace Audio {
|
||||
class Mixer;
|
||||
}
|
||||
|
||||
namespace OPL {
|
||||
class OPL;
|
||||
}
|
||||
|
||||
namespace Sky {
|
||||
|
||||
class AdLibMusic : public MusicBase {
|
||||
public:
|
||||
AdLibMusic(Audio::Mixer *pMixer, Disk *pDisk);
|
||||
~AdLibMusic() override;
|
||||
|
||||
// AudioStream API
|
||||
void setVolume(uint16 param) override;
|
||||
|
||||
private:
|
||||
OPL::OPL *_opl;
|
||||
uint8 *_initSequence;
|
||||
uint32 _sampleRate;
|
||||
void setupPointers() override;
|
||||
void setupChannels(uint8 *channelData) override;
|
||||
void startDriver() override;
|
||||
|
||||
void premixerCall(int16 *buf, uint len);
|
||||
|
||||
void onTimer();
|
||||
};
|
||||
|
||||
} // End of namespace Sky
|
||||
|
||||
#endif //ADLIBMUSIC_H
|
||||
195
engines/sky/music/gmchannel.cpp
Normal file
195
engines/sky/music/gmchannel.cpp
Normal file
@@ -0,0 +1,195 @@
|
||||
/* 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 "gmchannel.h"
|
||||
#include "common/util.h"
|
||||
#include "common/textconsole.h"
|
||||
#include "audio/mididrv.h"
|
||||
|
||||
namespace Sky {
|
||||
|
||||
GmChannel::GmChannel(uint8 *pMusicData, uint16 startOfData, MidiDriver *pMidiDrv, const byte *pInstMap, const byte *veloTab) {
|
||||
_musicData = pMusicData;
|
||||
_midiDrv = pMidiDrv;
|
||||
_channelData.midiChannelNumber = 0;
|
||||
_channelData.loopPoint = startOfData;
|
||||
_channelData.eventDataPtr = startOfData;
|
||||
_channelData.channelActive = true;
|
||||
_channelData.nextEventTime = getNextEventTime();
|
||||
_instMap = pInstMap;
|
||||
_veloTab = veloTab;
|
||||
|
||||
_musicVolume = 0x7F;
|
||||
_currentChannelVolume = 0x7F;
|
||||
}
|
||||
|
||||
GmChannel::~GmChannel() {
|
||||
stopNote();
|
||||
}
|
||||
|
||||
bool GmChannel::isActive() {
|
||||
return _channelData.channelActive;
|
||||
}
|
||||
|
||||
void GmChannel::updateVolume(uint16 pVolume) {
|
||||
_musicVolume = pVolume;
|
||||
if (_musicVolume > 0)
|
||||
_musicVolume = (_musicVolume * 2) / 3 + 43;
|
||||
|
||||
uint8 newVol = (_currentChannelVolume * _musicVolume) >> 7;
|
||||
_midiDrv->send((0xB0 | _channelData.midiChannelNumber) | 0x700 | (newVol << 16));
|
||||
}
|
||||
|
||||
void GmChannel::stopNote() {
|
||||
// All Notes Off
|
||||
_midiDrv->send((0xB0 | _channelData.midiChannelNumber) | 0x7B00 | 0 | 0x79000000);
|
||||
// Reset the Pitch Wheel. See bug #1742.
|
||||
_midiDrv->send((0xE0 | _channelData.midiChannelNumber) | 0x400000);
|
||||
}
|
||||
|
||||
int32 GmChannel::getNextEventTime() {
|
||||
int32 retV = 0;
|
||||
uint8 cnt, lVal = 0;
|
||||
for (cnt = 0; cnt < 4; cnt++) {
|
||||
lVal = _musicData[_channelData.eventDataPtr];
|
||||
_channelData.eventDataPtr++;
|
||||
retV = (retV << 7) | (lVal & 0x7F);
|
||||
if (!(lVal & 0x80))
|
||||
break;
|
||||
}
|
||||
if (lVal & 0x80) { // should never happen
|
||||
return -1;
|
||||
} else
|
||||
return retV;
|
||||
}
|
||||
|
||||
uint8 GmChannel::process(uint16 aktTime) {
|
||||
if (!_channelData.channelActive)
|
||||
return 0;
|
||||
|
||||
uint8 returnVal = 0;
|
||||
|
||||
_channelData.nextEventTime -= aktTime;
|
||||
uint8 opcode;
|
||||
|
||||
while ((_channelData.nextEventTime < 0) && (_channelData.channelActive)) {
|
||||
opcode = _musicData[_channelData.eventDataPtr];
|
||||
_channelData.eventDataPtr++;
|
||||
if (opcode&0x80) {
|
||||
if (opcode == 0xFF) {
|
||||
// dummy opcode
|
||||
} else if (opcode >= 0x90) {
|
||||
switch (opcode&0xF) {
|
||||
case 0: com90_caseNoteOff(); break;
|
||||
case 1: com90_stopChannel(); break;
|
||||
case 2: com90_setupInstrument(); break;
|
||||
case 3:
|
||||
returnVal = com90_updateTempo();
|
||||
break;
|
||||
case 5: com90_getPitch(); break;
|
||||
case 6: com90_getChannelVolume(); break;
|
||||
case 8: com90_loopMusic(); break;
|
||||
case 9: com90_keyOff(); break;
|
||||
case 11: com90_getChannelPanValue(); break;
|
||||
case 12: com90_setLoopPoint(); break;
|
||||
case 13: com90_getChannelControl(); break;
|
||||
|
||||
default:
|
||||
error("GmChannel: Unknown music opcode 0x%02X", opcode);
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
// new midi channel assignment
|
||||
_channelData.midiChannelNumber = opcode&0xF;
|
||||
}
|
||||
} else {
|
||||
_channelData.note = opcode;
|
||||
byte velocity = _musicData[_channelData.eventDataPtr];
|
||||
if (_veloTab)
|
||||
velocity = _veloTab[velocity];
|
||||
_channelData.eventDataPtr++;
|
||||
_midiDrv->send((0x90 | _channelData.midiChannelNumber) | (opcode << 8) | (velocity << 16));
|
||||
}
|
||||
if (_channelData.channelActive)
|
||||
_channelData.nextEventTime += getNextEventTime();
|
||||
}
|
||||
return returnVal;
|
||||
}
|
||||
|
||||
//- command 90h routines
|
||||
|
||||
void GmChannel::com90_caseNoteOff() {
|
||||
_midiDrv->send((0x90 | _channelData.midiChannelNumber) | (_musicData[_channelData.eventDataPtr] << 8));
|
||||
_channelData.eventDataPtr++;
|
||||
}
|
||||
|
||||
void GmChannel::com90_stopChannel() {
|
||||
stopNote();
|
||||
_channelData.channelActive = false;
|
||||
}
|
||||
|
||||
void GmChannel::com90_setupInstrument() {
|
||||
byte instrument = _musicData[_channelData.eventDataPtr];
|
||||
if (_instMap)
|
||||
instrument = _instMap[instrument];
|
||||
_midiDrv->send((0xC0 | _channelData.midiChannelNumber) | (instrument << 8));
|
||||
_channelData.eventDataPtr++;
|
||||
}
|
||||
|
||||
uint8 GmChannel::com90_updateTempo() {
|
||||
return _musicData[_channelData.eventDataPtr++];
|
||||
}
|
||||
|
||||
void GmChannel::com90_getPitch() {
|
||||
_midiDrv->send((0xE0 | _channelData.midiChannelNumber) | 0 | (_musicData[_channelData.eventDataPtr] << 16));
|
||||
_channelData.eventDataPtr++;
|
||||
}
|
||||
|
||||
void GmChannel::com90_getChannelVolume() {
|
||||
_currentChannelVolume = _musicData[_channelData.eventDataPtr++];
|
||||
uint8 newVol = (uint8)((_currentChannelVolume * _musicVolume) >> 7);
|
||||
_midiDrv->send((0xB0 | _channelData.midiChannelNumber) | 0x700 | (newVol << 16));
|
||||
}
|
||||
|
||||
void GmChannel::com90_loopMusic() {
|
||||
_channelData.eventDataPtr = _channelData.loopPoint;
|
||||
}
|
||||
|
||||
void GmChannel::com90_keyOff() {
|
||||
_midiDrv->send((0x90 | _channelData.midiChannelNumber) | (_channelData.note << 8) | 0);
|
||||
}
|
||||
|
||||
void GmChannel::com90_setLoopPoint() {
|
||||
_channelData.loopPoint = _channelData.eventDataPtr;
|
||||
}
|
||||
|
||||
void GmChannel::com90_getChannelPanValue() {
|
||||
_midiDrv->send((0xB0 | _channelData.midiChannelNumber) | 0x0A00 | (_musicData[_channelData.eventDataPtr] << 16));
|
||||
_channelData.eventDataPtr++;
|
||||
}
|
||||
|
||||
void GmChannel::com90_getChannelControl() {
|
||||
uint8 conNum = _musicData[_channelData.eventDataPtr++];
|
||||
uint8 conDat = _musicData[_channelData.eventDataPtr++];
|
||||
_midiDrv->send((0xB0 | _channelData.midiChannelNumber) | (conNum << 8) | (conDat << 16));
|
||||
}
|
||||
|
||||
} // End of namespace Sky
|
||||
82
engines/sky/music/gmchannel.h
Normal file
82
engines/sky/music/gmchannel.h
Normal file
@@ -0,0 +1,82 @@
|
||||
/* 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 SKY_MUSIC_GMCHANNEL_H
|
||||
#define SKY_MUSIC_GMCHANNEL_H
|
||||
|
||||
#include "sky/music/musicbase.h"
|
||||
|
||||
class MidiDriver;
|
||||
|
||||
namespace Sky {
|
||||
|
||||
typedef struct {
|
||||
uint16 eventDataPtr;
|
||||
int32 nextEventTime;
|
||||
uint16 loopPoint;
|
||||
uint8 midiChannelNumber;
|
||||
uint8 note;
|
||||
bool channelActive;
|
||||
} MidiChannelType;
|
||||
|
||||
class GmChannel : public ChannelBase {
|
||||
public:
|
||||
GmChannel(uint8 *pMusicData, uint16 startOfData, MidiDriver *pMidiDrv, const byte *pInstMap, const byte *veloTab);
|
||||
~GmChannel() override;
|
||||
virtual void stopNote();
|
||||
uint8 process(uint16 aktTime) override;
|
||||
void updateVolume(uint16 pVolume) override;
|
||||
bool isActive() override;
|
||||
private:
|
||||
const byte *_instMap;
|
||||
const byte *_veloTab;
|
||||
MidiDriver *_midiDrv;
|
||||
uint8 *_musicData;
|
||||
uint16 _musicVolume;
|
||||
MidiChannelType _channelData;
|
||||
uint8 _currentChannelVolume;
|
||||
//- normal subs
|
||||
void setRegister(uint8 regNum, uint8 value);
|
||||
int32 getNextEventTime();
|
||||
uint16 getNextNote(uint8 param);
|
||||
void adlibSetupInstrument();
|
||||
void setupInstrument(uint8 opcode);
|
||||
void setupChannelVolume(uint8 volume);
|
||||
//- Streamfunctions from Command90hTable
|
||||
void com90_caseNoteOff(); // 0
|
||||
void com90_stopChannel(); // 1
|
||||
void com90_setupInstrument(); // 2
|
||||
uint8 com90_updateTempo(); // 3
|
||||
//void com90_dummy(); // 4
|
||||
void com90_getPitch(); // 5
|
||||
void com90_getChannelVolume(); // 6
|
||||
//void com90_skipTremoVibro(); // 7
|
||||
void com90_loopMusic(); // 8
|
||||
void com90_keyOff(); // 9
|
||||
//void com90_error(); // 10
|
||||
void com90_getChannelPanValue(); // 11
|
||||
void com90_setLoopPoint(); // 12
|
||||
void com90_getChannelControl(); // 13
|
||||
};
|
||||
|
||||
} // End of namespace Sky
|
||||
|
||||
#endif //SKYGMCHANNEL_H
|
||||
120
engines/sky/music/gmmusic.cpp
Normal file
120
engines/sky/music/gmmusic.cpp
Normal file
@@ -0,0 +1,120 @@
|
||||
/* 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 "sky/music/gmmusic.h"
|
||||
#include "sky/music/gmchannel.h"
|
||||
#include "sky/sky.h"
|
||||
#include "common/util.h"
|
||||
#include "common/endian.h"
|
||||
#include "common/textconsole.h"
|
||||
#include "audio/mididrv.h"
|
||||
|
||||
namespace Sky {
|
||||
|
||||
void GmMusic::passTimerFunc(void *param) {
|
||||
((GmMusic *)param)->timerCall();
|
||||
}
|
||||
|
||||
GmMusic::GmMusic(MidiDriver *pMidiDrv, Audio::Mixer *pMixer, Disk *pDisk) : MusicBase(pMixer, pDisk) {
|
||||
_driverFileBase = 60200;
|
||||
_midiDrv = pMidiDrv;
|
||||
int midiRes = _midiDrv->open();
|
||||
if (midiRes != 0)
|
||||
error("Can't open midi device. Errorcode: %d", midiRes);
|
||||
_timerCount = 0;
|
||||
_midiDrv->setTimerCallback(this, passTimerFunc);
|
||||
_midiDrv->sendGMReset();
|
||||
}
|
||||
|
||||
GmMusic::~GmMusic() {
|
||||
_midiDrv->setTimerCallback(NULL, NULL);
|
||||
if (_currentMusic)
|
||||
stopMusic();
|
||||
// Send All Sound Off and All Notes Off (for external synths)
|
||||
for (int i = 0; i < 16; i++) {
|
||||
_midiDrv->send((120 << 8) | 0xB0 | i);
|
||||
_midiDrv->send((123 << 8) | 0xB0 | i);
|
||||
}
|
||||
_midiDrv->close();
|
||||
delete _midiDrv;
|
||||
}
|
||||
|
||||
void GmMusic::setVolume(uint16 param) {
|
||||
_musicVolume = param;
|
||||
for (uint8 cnt = 0; cnt < _numberOfChannels; cnt++)
|
||||
_channels[cnt]->updateVolume(_musicVolume);
|
||||
}
|
||||
|
||||
void GmMusic::timerCall() {
|
||||
_timerCount += _midiDrv->getBaseTempo();
|
||||
if (_timerCount > (1000 * 1000 / 50)) {
|
||||
// call pollMusic() 50 times per second
|
||||
_timerCount -= 1000 * 1000 / 50;
|
||||
if (_musicData != NULL)
|
||||
pollMusic();
|
||||
}
|
||||
}
|
||||
|
||||
void GmMusic::setupPointers() {
|
||||
if (SkyEngine::_systemVars->gameVersion == 109) {
|
||||
_musicDataLoc = READ_LE_UINT16(_musicData + 0x79B);
|
||||
_sysExSequence = _musicData + 0x1EF2;
|
||||
} else {
|
||||
_musicDataLoc = READ_LE_UINT16(_musicData + 0x7DC);
|
||||
_sysExSequence = READ_LE_UINT16(_musicData + 0x7E0) + _musicData;
|
||||
}
|
||||
}
|
||||
|
||||
void GmMusic::setupChannels(uint8 *channelData) {
|
||||
_numberOfChannels = channelData[0];
|
||||
channelData++;
|
||||
for (uint8 cnt = 0; cnt < _numberOfChannels; cnt++) {
|
||||
uint16 chDataStart = READ_LE_UINT16((uint16 *)channelData + cnt) + _musicDataLoc;
|
||||
_channels[cnt] = new GmChannel(_musicData, chDataStart, _midiDrv, MidiDriver::_mt32ToGm, _veloTab);
|
||||
_channels[cnt]->updateVolume(_musicVolume);
|
||||
}
|
||||
}
|
||||
|
||||
void GmMusic::startDriver() {
|
||||
// Send GM System On to reset channel parameters etc.
|
||||
uint8 sysEx[] = { 0x7e, 0x7f, 0x09, 0x01 };
|
||||
_midiDrv->sysEx(sysEx, sizeof(sysEx));
|
||||
//_midiDrv->send(0xFF); //ALSA can't handle this.
|
||||
// skip all sysEx as it can't be handled anyways.
|
||||
}
|
||||
|
||||
const byte GmMusic::_veloTab[128] = {
|
||||
0x00, 0x40, 0x41, 0x41, 0x42, 0x42, 0x43, 0x43, 0x44, 0x44,
|
||||
0x45, 0x45, 0x46, 0x46, 0x47, 0x47, 0x48, 0x48, 0x49, 0x49,
|
||||
0x4A, 0x4A, 0x4B, 0x4B, 0x4C, 0x4C, 0x4D, 0x4D, 0x4E, 0x4E,
|
||||
0x4F, 0x4F, 0x50, 0x50, 0x51, 0x51, 0x52, 0x52, 0x53, 0x53,
|
||||
0x54, 0x54, 0x55, 0x55, 0x56, 0x56, 0x57, 0x57, 0x58, 0x58,
|
||||
0x59, 0x59, 0x5A, 0x5A, 0x5B, 0x5B, 0x5C, 0x5C, 0x5D, 0x5D,
|
||||
0x5E, 0x5E, 0x5F, 0x5F, 0x60, 0x60, 0x61, 0x61, 0x62, 0x62,
|
||||
0x63, 0x63, 0x64, 0x64, 0x65, 0x65, 0x66, 0x66, 0x67, 0x67,
|
||||
0x68, 0x68, 0x69, 0x69, 0x6A, 0x6A, 0x6B, 0x6B, 0x6C, 0x6C,
|
||||
0x6D, 0x6D, 0x6E, 0x6E, 0x6F, 0x6F, 0x70, 0x70, 0x71, 0x71,
|
||||
0x72, 0x72, 0x73, 0x73, 0x74, 0x74, 0x75, 0x75, 0x76, 0x76,
|
||||
0x77, 0x77, 0x78, 0x78, 0x79, 0x79, 0x7A, 0x7A, 0x7B, 0x7B,
|
||||
0x7C, 0x7C, 0x7D, 0x7D, 0x7E, 0x7E, 0x7F, 0x7F
|
||||
};
|
||||
|
||||
} // End of namespace Sky
|
||||
52
engines/sky/music/gmmusic.h
Normal file
52
engines/sky/music/gmmusic.h
Normal file
@@ -0,0 +1,52 @@
|
||||
/* 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 SKY_MUSIC_GMMUSIC_H
|
||||
#define SKY_MUSIC_GMMUSIC_H
|
||||
|
||||
#include "sky/music/musicbase.h"
|
||||
|
||||
class MidiDriver;
|
||||
|
||||
namespace Sky {
|
||||
|
||||
class GmMusic : public MusicBase {
|
||||
public:
|
||||
GmMusic(MidiDriver *pMidiDrv, Audio::Mixer *pMixer, Disk *pDisk);
|
||||
~GmMusic() override;
|
||||
void setVolume(uint16 param) override;
|
||||
private:
|
||||
static void passTimerFunc(void *param);
|
||||
void timerCall();
|
||||
|
||||
uint32 _timerCount;
|
||||
uint8 *_sysExSequence;
|
||||
MidiDriver *_midiDrv;
|
||||
static const byte _veloTab[128];
|
||||
|
||||
void setupPointers() override;
|
||||
void setupChannels(uint8 *channelData) override;
|
||||
void startDriver() override;
|
||||
};
|
||||
|
||||
} // End of namespace Sky
|
||||
|
||||
#endif //GMMUSIC_H
|
||||
176
engines/sky/music/mt32music.cpp
Normal file
176
engines/sky/music/mt32music.cpp
Normal file
@@ -0,0 +1,176 @@
|
||||
/* 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 "sky/music/mt32music.h"
|
||||
#include "sky/music/gmchannel.h"
|
||||
#include "common/util.h"
|
||||
#include "common/system.h"
|
||||
#include "common/endian.h"
|
||||
#include "common/textconsole.h"
|
||||
#include "audio/mididrv.h"
|
||||
|
||||
namespace Sky {
|
||||
|
||||
void MT32Music::passTimerFunc(void *param) {
|
||||
((MT32Music *)param)->timerCall();
|
||||
}
|
||||
|
||||
MT32Music::MT32Music(MidiDriver *pMidiDrv, Audio::Mixer *pMixer, Disk *pDisk) : MusicBase(pMixer, pDisk) {
|
||||
_driverFileBase = 60200;
|
||||
_midiDrv = pMidiDrv;
|
||||
int midiRes = _midiDrv->open();
|
||||
if (midiRes != 0)
|
||||
error("Can't open midi device. Errorcode: %d",midiRes);
|
||||
_timerCount = 0;
|
||||
_midiDrv->setTimerCallback(this, passTimerFunc);
|
||||
_midiDrv->sendMT32Reset();
|
||||
}
|
||||
|
||||
MT32Music::~MT32Music() {
|
||||
_midiDrv->close();
|
||||
_midiDrv->setTimerCallback(NULL, NULL);
|
||||
delete _midiDrv;
|
||||
}
|
||||
|
||||
void MT32Music::timerCall() {
|
||||
_timerCount += _midiDrv->getBaseTempo();
|
||||
if (_timerCount > (1000000 / 50)) {
|
||||
// call pollMusic() 50 times per second
|
||||
_timerCount -= 1000000 / 50;
|
||||
if (_musicData != NULL)
|
||||
pollMusic();
|
||||
}
|
||||
}
|
||||
|
||||
void MT32Music::setVolume(uint16 volume) {
|
||||
uint8 sysEx[10] = "\x41\x10\x16\x12\x10\x00\x16\x00\x00";
|
||||
_musicVolume = volume;
|
||||
sysEx[7] = (volume > 100) ? 100 : (uint8)volume;
|
||||
sysEx[8] = 0x00;
|
||||
for (uint8 cnt = 4; cnt < 8; cnt++)
|
||||
sysEx[8] -= sysEx[cnt];
|
||||
sysEx[8] &= 0x7F;
|
||||
_midiDrv->sysEx(sysEx, 9);
|
||||
}
|
||||
|
||||
void MT32Music::setupPointers() {
|
||||
_musicDataLoc = READ_LE_UINT16(_musicData + 0x7DC);
|
||||
_sysExSequence = READ_LE_UINT16(_musicData + 0x7E0) + _musicData;
|
||||
}
|
||||
|
||||
void MT32Music::setupChannels(uint8 *channelData) {
|
||||
_numberOfChannels = channelData[0];
|
||||
channelData++;
|
||||
for (uint8 cnt = 0; cnt < _numberOfChannels; cnt++) {
|
||||
uint16 chDataStart = READ_LE_UINT16((uint16 *)channelData + cnt) + _musicDataLoc;
|
||||
_channels[cnt] = new GmChannel(_musicData, chDataStart, _midiDrv, NULL, NULL);
|
||||
_channels[cnt]->updateVolume(_musicVolume);
|
||||
}
|
||||
}
|
||||
|
||||
bool MT32Music::processPatchSysEx(uint8 *sysExData) {
|
||||
uint8 sysExBuf[15];
|
||||
uint8 crc = 0;
|
||||
if (sysExData[0] & 0x80)
|
||||
return false;
|
||||
|
||||
// decompress data from stream
|
||||
sysExBuf[ 0] = 0x41;
|
||||
sysExBuf[ 1] = 0x10;
|
||||
sysExBuf[ 2] = 0x16;
|
||||
sysExBuf[ 3] = 0x12;
|
||||
sysExBuf[ 4] = 0x5;
|
||||
sysExBuf[ 5] = sysExData[0] >> 4; // patch offset part 1
|
||||
sysExBuf[ 6] = (sysExData[0] & 0xF) << 3; // patch offset part 2
|
||||
sysExBuf[ 7] = sysExData[1] >> 6; // timbre group
|
||||
sysExBuf[ 8] = sysExData[1] & 0x3F; // timbre num
|
||||
sysExBuf[ 9] = sysExData[2] & 0x3F; // key shift
|
||||
sysExBuf[10] = sysExData[3] & 0x7F; // fine tune
|
||||
sysExBuf[11] = sysExData[4] & 0x7F; // bender range
|
||||
sysExBuf[12] = sysExData[2] >> 6; // assign mode
|
||||
sysExBuf[13] = sysExData[3] >> 7; // reverb switch
|
||||
for (uint8 cnt = 4; cnt < 14; cnt++)
|
||||
crc -= sysExBuf[cnt];
|
||||
sysExBuf[14] = crc & 0x7F; // crc
|
||||
_midiDrv->sysEx(sysExBuf, 15);
|
||||
// We delay the time it takes to send the sysEx plus an
|
||||
// additional 40ms, which is required for MT-32 rev00,
|
||||
// to assure no buffer overflow or missing bytes
|
||||
g_system->delayMillis(17 * 1000 / 3125 + 40);
|
||||
return true;
|
||||
}
|
||||
|
||||
void MT32Music::startDriver() {
|
||||
// setup timbres and patches using SysEx data
|
||||
uint8* sysExData = _sysExSequence;
|
||||
uint8 timbreNum = sysExData[0];
|
||||
uint8 cnt, crc;
|
||||
sysExData++;
|
||||
uint8 sendBuf[256];
|
||||
uint8 len;
|
||||
sendBuf[0] = 0x41;
|
||||
sendBuf[1] = 0x10;
|
||||
sendBuf[2] = 0x16;
|
||||
sendBuf[3] = 0x12;
|
||||
for (cnt = 0; cnt < timbreNum; cnt++) {
|
||||
len = 7;
|
||||
crc = 0;
|
||||
// Timbre address
|
||||
sendBuf[4] = 0x8 | (sysExData[0] >> 6);
|
||||
sendBuf[5] = (sysExData[0] & 0x3F) << 1;
|
||||
sendBuf[6] = 0xA;
|
||||
sysExData++;
|
||||
crc -= sendBuf[4] + sendBuf[5] + sendBuf[6];
|
||||
uint8 dataLen = sysExData[0];
|
||||
sysExData++;
|
||||
// Timbre data:
|
||||
do {
|
||||
uint8 rlVal = 1;
|
||||
uint8 codeVal = sysExData[0];
|
||||
sysExData++;
|
||||
|
||||
if (codeVal & 0x80) {
|
||||
codeVal &= 0x7F;
|
||||
rlVal = sysExData[0];
|
||||
sysExData++;
|
||||
dataLen--;
|
||||
}
|
||||
for (uint8 cnt2 = 0; cnt2 < rlVal; cnt2++) {
|
||||
sendBuf[len] = codeVal;
|
||||
len++;
|
||||
crc -= codeVal;
|
||||
}
|
||||
dataLen--;
|
||||
} while (dataLen > 0);
|
||||
sendBuf[len] = crc & 0x7F;
|
||||
len++;
|
||||
_midiDrv->sysEx(sendBuf, len);
|
||||
// We delay the time it takes to send the sysEx plus an
|
||||
// additional 40ms, which is required for MT-32 rev00,
|
||||
// to assure no buffer overflow or missing bytes
|
||||
g_system->delayMillis((len + 2) * 1000 / 3125 + 40);
|
||||
}
|
||||
|
||||
while (processPatchSysEx(sysExData))
|
||||
sysExData += 5;
|
||||
}
|
||||
|
||||
} // End of namespace Sky
|
||||
52
engines/sky/music/mt32music.h
Normal file
52
engines/sky/music/mt32music.h
Normal file
@@ -0,0 +1,52 @@
|
||||
/* 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 SKY_MUSIC_MT32MUSIC_H
|
||||
#define SKY_MUSIC_MT32MUSIC_H
|
||||
|
||||
#include "sky/music/musicbase.h"
|
||||
|
||||
class MidiDriver;
|
||||
|
||||
namespace Sky {
|
||||
|
||||
class MT32Music : public MusicBase {
|
||||
public:
|
||||
MT32Music(MidiDriver *pMidiDrv, Audio::Mixer *pMixer, Disk *pDisk);
|
||||
~MT32Music() override;
|
||||
private:
|
||||
static void passTimerFunc(void *param);
|
||||
void timerCall();
|
||||
bool processPatchSysEx(uint8 *sysExData);
|
||||
void setVolume(uint16 volume) override;
|
||||
|
||||
uint32 _timerCount;
|
||||
uint8 *_sysExSequence;
|
||||
MidiDriver *_midiDrv;
|
||||
|
||||
void setupPointers() override;
|
||||
void setupChannels(uint8 *channelData) override;
|
||||
void startDriver() override;
|
||||
};
|
||||
|
||||
} // End of namespace Sky
|
||||
|
||||
#endif //MT32MUSIC_H
|
||||
197
engines/sky/music/musicbase.cpp
Normal file
197
engines/sky/music/musicbase.cpp
Normal file
@@ -0,0 +1,197 @@
|
||||
/* 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 "sky/music/musicbase.h"
|
||||
#include "sky/disk.h"
|
||||
#include "common/util.h"
|
||||
#include "common/endian.h"
|
||||
#include "common/textconsole.h"
|
||||
#include "audio/audiostream.h"
|
||||
|
||||
namespace Sky {
|
||||
|
||||
MusicBase::MusicBase(Audio::Mixer *pMixer, Disk *pDisk) {
|
||||
_musicData = NULL;
|
||||
_mixer = pMixer;
|
||||
_skyDisk = pDisk;
|
||||
_currentMusic = 0;
|
||||
_musicVolume = 127;
|
||||
_numberOfChannels = _currentMusic = 0;
|
||||
}
|
||||
|
||||
MusicBase::~MusicBase() {
|
||||
stopMusic();
|
||||
free(_musicData);
|
||||
}
|
||||
|
||||
void MusicBase::loadSection(uint8 pSection) {
|
||||
if (_currentMusic)
|
||||
stopMusicInternal();
|
||||
Common::StackLock lock(_mutex);
|
||||
free(_musicData);
|
||||
_currentSection = pSection;
|
||||
_musicData = _skyDisk->loadFile(_driverFileBase + FILES_PER_SECTION * pSection);
|
||||
|
||||
_musicTempo0 = 0x78; // init constants taken from idb file, area ~0x1060
|
||||
_musicTempo1 = 0xC0;
|
||||
_onNextPoll.musicToProcess = 0;
|
||||
_onNextPoll.stream = nullptr;
|
||||
_tempo = _aktTime = 0x10001;
|
||||
_numberOfChannels = _currentMusic = 0;
|
||||
setupPointers();
|
||||
startDriver();
|
||||
}
|
||||
|
||||
bool MusicBase::musicIsPlaying() {
|
||||
if (_mixer->isSoundHandleActive(_musicHandle))
|
||||
return true;
|
||||
for (uint8 cnt = 0; cnt < _numberOfChannels; cnt++)
|
||||
if (_channels[cnt]->isActive())
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
void MusicBase::stopMusic() {
|
||||
stopMusicInternal();
|
||||
}
|
||||
|
||||
void MusicBase::stopMusicInternal() {
|
||||
_mixer->stopHandle(_musicHandle);
|
||||
|
||||
Common::StackLock lock(_mutex);
|
||||
|
||||
for (uint8 cnt = 0; cnt < _numberOfChannels; cnt++)
|
||||
delete _channels[cnt];
|
||||
_numberOfChannels = 0;
|
||||
}
|
||||
|
||||
void MusicBase::updateTempo() {
|
||||
uint16 tempoMul = _musicTempo0 * _musicTempo1;
|
||||
uint16 divisor = 0x4446390/ 23864;
|
||||
_tempo = (tempoMul / divisor) << 16;
|
||||
_tempo |= (((tempoMul % divisor) << 16) | (tempoMul / divisor)) / divisor;
|
||||
}
|
||||
|
||||
void MusicBase::loadNewMusic() {
|
||||
uint16 musicPos;
|
||||
if (_onNextPoll.musicToProcess > _musicData[_musicDataLoc]) {
|
||||
error("Music %d requested but doesn't exist in file.", _onNextPoll.musicToProcess);
|
||||
return;
|
||||
}
|
||||
if (_currentMusic != 0)
|
||||
stopMusicInternal();
|
||||
|
||||
_currentMusic = _onNextPoll.musicToProcess;
|
||||
|
||||
if (_currentMusic == 0)
|
||||
return;
|
||||
|
||||
// Try playing digital audio first (from the Music Enhancement Project).
|
||||
// TODO: This always prefers digital music over the MIDI music types!
|
||||
Audio::SeekableAudioStream *stream = _onNextPoll.stream;
|
||||
if (stream) {
|
||||
_onNextPoll.stream = nullptr;
|
||||
|
||||
// not all tracks should loop
|
||||
bool loops = true;
|
||||
uint8 section = _currentSection;
|
||||
uint8 song = _currentMusic;
|
||||
if ((section == 0 && song == 1)
|
||||
|| (section == 1 && song == 1) || (section == 1 && song == 4)
|
||||
|| (section == 2 && song == 1) || (section == 2 && song == 4)
|
||||
|| (section == 4 && song == 2) || (section == 4 && song == 3)
|
||||
|| (section == 4 && song == 5) || (section == 4 && song == 6)
|
||||
|| (section == 4 && song == 11) || (section == 5 && song == 1)
|
||||
|| (section == 5 && song == 3) || (section == 5 && song == 4))
|
||||
loops = false;
|
||||
_mixer->playStream(Audio::Mixer::kMusicSoundType, &_musicHandle, Audio::makeLoopingAudioStream(stream, loops ? 0 : 1));
|
||||
return;
|
||||
}
|
||||
|
||||
// no digital audio, resort to MIDI playback
|
||||
musicPos = READ_LE_UINT16(_musicData + _musicDataLoc + 1);
|
||||
musicPos += _musicDataLoc + ((_currentMusic - 1) << 1);
|
||||
musicPos = READ_LE_UINT16(_musicData + musicPos) + _musicDataLoc;
|
||||
|
||||
_musicTempo0 = _musicData[musicPos];
|
||||
_musicTempo1 = _musicData[musicPos+1];
|
||||
|
||||
setupChannels(_musicData + musicPos + 2);
|
||||
|
||||
updateTempo();
|
||||
}
|
||||
|
||||
void MusicBase::pollMusic() {
|
||||
Common::StackLock lock(_mutex);
|
||||
uint8 newTempo;
|
||||
if (_onNextPoll.musicToProcess != _currentMusic)
|
||||
loadNewMusic();
|
||||
|
||||
_aktTime += _tempo;
|
||||
|
||||
for (uint8 cnt = 0; cnt < _numberOfChannels; cnt++) {
|
||||
newTempo = _channels[cnt]->process((uint16)(_aktTime >> 16));
|
||||
if (newTempo) {
|
||||
_musicTempo1 = newTempo;
|
||||
updateTempo();
|
||||
}
|
||||
}
|
||||
_aktTime &= 0xFFFF;
|
||||
}
|
||||
|
||||
void MusicBase::startMusic(uint16 param) {
|
||||
uint8 song = param & 0xF;
|
||||
|
||||
_onNextPoll.musicToProcess = song;
|
||||
delete _onNextPoll.stream;
|
||||
_onNextPoll.stream = nullptr;
|
||||
|
||||
if (song == 0)
|
||||
return;
|
||||
|
||||
// Load digital audio now if available
|
||||
// This avoids doing it in the audio thread
|
||||
uint8 section = _currentSection;
|
||||
// handle duplicates
|
||||
if ((section == 2 && song == 1) || (section == 5 && song == 1)) {
|
||||
section = 1;
|
||||
song = 1;
|
||||
} else if ((section == 2 && song == 4) || (section == 5 && song == 4)) {
|
||||
section = 1;
|
||||
song = 4;
|
||||
} else if (section == 5 && song == 6) {
|
||||
section = 4;
|
||||
song = 4;
|
||||
}
|
||||
|
||||
Common::Path trackName(Common::String::format("music_%d%02d", section, song));
|
||||
_onNextPoll.stream = Audio::SeekableAudioStream::openStreamFile(trackName);
|
||||
}
|
||||
|
||||
uint8 MusicBase::giveVolume() {
|
||||
return (uint8)_musicVolume;
|
||||
}
|
||||
|
||||
uint8 MusicBase::giveCurrentMusic() {
|
||||
return _currentMusic;
|
||||
}
|
||||
|
||||
} // End of namespace Sky
|
||||
100
engines/sky/music/musicbase.h
Normal file
100
engines/sky/music/musicbase.h
Normal file
@@ -0,0 +1,100 @@
|
||||
/* 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 SKY_MUSIC_MUSICBASE_H
|
||||
#define SKY_MUSIC_MUSICBASE_H
|
||||
|
||||
|
||||
#include "common/scummsys.h"
|
||||
#include "common/mutex.h"
|
||||
|
||||
#include "audio/mixer.h"
|
||||
|
||||
namespace Audio {
|
||||
class SeekableAudioStream;
|
||||
}
|
||||
|
||||
namespace Sky {
|
||||
|
||||
class Disk;
|
||||
|
||||
#define FILES_PER_SECTION 4
|
||||
|
||||
typedef struct {
|
||||
uint8 musicToProcess;
|
||||
Audio::SeekableAudioStream *stream;
|
||||
} Actions;
|
||||
|
||||
class ChannelBase {
|
||||
public:
|
||||
virtual ~ChannelBase() {}
|
||||
virtual uint8 process(uint16 aktTime) = 0;
|
||||
virtual void updateVolume(uint16 pVolume) = 0;
|
||||
virtual bool isActive() = 0;
|
||||
private:
|
||||
};
|
||||
|
||||
class MusicBase {
|
||||
public:
|
||||
MusicBase(Audio::Mixer *pMixer, Disk *pDisk);
|
||||
virtual ~MusicBase();
|
||||
void loadSection(uint8 pSection);
|
||||
void startMusic(uint16 param);
|
||||
void stopMusic();
|
||||
bool musicIsPlaying();
|
||||
uint8 giveVolume();
|
||||
uint8 giveCurrentMusic();
|
||||
virtual void setVolume(uint16 param) = 0;
|
||||
|
||||
protected:
|
||||
|
||||
Audio::Mixer *_mixer;
|
||||
Disk *_skyDisk;
|
||||
uint8 *_musicData;
|
||||
|
||||
uint16 _musicDataLoc;
|
||||
uint16 _driverFileBase;
|
||||
|
||||
uint16 _musicVolume, _numberOfChannels;
|
||||
uint8 _currentMusic, _currentSection;
|
||||
uint8 _musicTempo0; // can be changed by music stream
|
||||
uint8 _musicTempo1; // given once per music
|
||||
uint32 _tempo; // calculated from musicTempo0 and musicTempo1
|
||||
uint32 _aktTime;
|
||||
Actions _onNextPoll;
|
||||
ChannelBase *_channels[10];
|
||||
Common::Mutex _mutex;
|
||||
Audio::SoundHandle _musicHandle;
|
||||
|
||||
virtual void setupPointers() = 0;
|
||||
virtual void setupChannels(uint8 *channelData) = 0;
|
||||
virtual void startDriver() = 0;
|
||||
|
||||
void updateTempo();
|
||||
void loadNewMusic();
|
||||
void pollMusic();
|
||||
|
||||
void stopMusicInternal();
|
||||
};
|
||||
|
||||
} // End of namespace Sky
|
||||
|
||||
#endif //MUSICBASE_H
|
||||
824
engines/sky/screen.cpp
Normal file
824
engines/sky/screen.cpp
Normal file
@@ -0,0 +1,824 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#include "common/endian.h"
|
||||
#include "common/events.h"
|
||||
#include "common/system.h"
|
||||
#include "common/textconsole.h"
|
||||
|
||||
#include "graphics/paletteman.h"
|
||||
|
||||
#include "sky/disk.h"
|
||||
#include "sky/logic.h"
|
||||
#include "sky/screen.h"
|
||||
#include "sky/compact.h"
|
||||
#include "sky/sky.h"
|
||||
#include "sky/skydefs.h"
|
||||
#include "sky/struc.h"
|
||||
|
||||
namespace Sky {
|
||||
|
||||
uint8 Screen::_top16Colors[16*3] = {
|
||||
0, 0, 0,
|
||||
38, 38, 38,
|
||||
63, 63, 63,
|
||||
0, 0, 0,
|
||||
0, 0, 0,
|
||||
0, 0, 0,
|
||||
0, 0, 0,
|
||||
54, 54, 54,
|
||||
45, 47, 49,
|
||||
32, 31, 41,
|
||||
29, 23, 37,
|
||||
23, 18, 30,
|
||||
49, 11, 11,
|
||||
39, 5, 5,
|
||||
29, 1, 1,
|
||||
63, 63, 63
|
||||
};
|
||||
|
||||
Screen::Screen(OSystem *pSystem, Disk *pDisk, SkyCompact *skyCompact) {
|
||||
_system = pSystem;
|
||||
_skyDisk = pDisk;
|
||||
_skyCompact = skyCompact;
|
||||
|
||||
int i;
|
||||
uint8 tmpPal[VGA_COLORS * 3];
|
||||
|
||||
_gameGrid = (uint8 *)malloc(GRID_X * GRID_Y * 2);
|
||||
forceRefresh();
|
||||
|
||||
_currentScreen = NULL;
|
||||
_scrollScreen = NULL;
|
||||
|
||||
//blank the first 240 colors of the palette
|
||||
memset(tmpPal, 0, GAME_COLORS * 3);
|
||||
|
||||
//set the remaining colors
|
||||
for (i = 0; i < (VGA_COLORS-GAME_COLORS); i++) {
|
||||
tmpPal[3 * GAME_COLORS + i * 3 + 0] = (_top16Colors[i * 3 + 0] << 2) + (_top16Colors[i * 3 + 0] >> 4);
|
||||
tmpPal[3 * GAME_COLORS + i * 3 + 1] = (_top16Colors[i * 3 + 1] << 2) + (_top16Colors[i * 3 + 1] >> 4);
|
||||
tmpPal[3 * GAME_COLORS + i * 3 + 2] = (_top16Colors[i * 3 + 2] << 2) + (_top16Colors[i * 3 + 2] >> 4);
|
||||
}
|
||||
|
||||
//set the palette
|
||||
_system->getPaletteManager()->setPalette(tmpPal, 0, VGA_COLORS);
|
||||
_currentPalette = 0;
|
||||
|
||||
_seqInfo.nextFrame = _seqInfo.framesLeft = 0;
|
||||
_seqInfo.seqData = _seqInfo.seqDataPos = NULL;
|
||||
_seqInfo.running = false;
|
||||
}
|
||||
|
||||
Screen::~Screen() {
|
||||
free(_gameGrid);
|
||||
free(_currentScreen);
|
||||
free(_scrollScreen);
|
||||
}
|
||||
|
||||
void Screen::clearScreen(bool fullscreen) {
|
||||
memset(_currentScreen, 0, FULL_SCREEN_WIDTH * FULL_SCREEN_HEIGHT);
|
||||
_system->copyRectToScreen(_currentScreen, GAME_SCREEN_WIDTH, 0, 0, GAME_SCREEN_WIDTH, fullscreen ? FULL_SCREEN_HEIGHT : GAME_SCREEN_HEIGHT);
|
||||
_system->updateScreen();
|
||||
}
|
||||
|
||||
void Screen::setFocusRectangle(const Common::Rect& rect) {
|
||||
_system->setFocusRectangle(rect);
|
||||
}
|
||||
|
||||
//set a new palette, pal is a pointer to dos vga rgb components 0..63
|
||||
void Screen::setPalette(uint8 *pal) {
|
||||
convertPalette(pal, _palette);
|
||||
_system->getPaletteManager()->setPalette(_palette, 0, GAME_COLORS);
|
||||
_system->updateScreen();
|
||||
}
|
||||
|
||||
void Screen::setPaletteEndian(uint8 *pal) {
|
||||
#ifdef SCUMM_BIG_ENDIAN
|
||||
uint8 endPalette[VGA_COLORS * 3];
|
||||
for (uint16 cnt = 0; cnt < VGA_COLORS * 3; cnt++)
|
||||
endPalette[cnt] = pal[cnt ^ 1];
|
||||
convertPalette(endPalette, _palette);
|
||||
#else
|
||||
convertPalette(pal, _palette);
|
||||
#endif
|
||||
_system->getPaletteManager()->setPalette(_palette, 0, GAME_COLORS);
|
||||
_system->updateScreen();
|
||||
}
|
||||
|
||||
void Screen::halvePalette() {
|
||||
uint8 halfPalette[VGA_COLORS * 3];
|
||||
|
||||
for (uint8 cnt = 0; cnt < GAME_COLORS; cnt++) {
|
||||
halfPalette[cnt * 3 + 0] = _palette[cnt * 3 + 0] >> 1;
|
||||
halfPalette[cnt * 3 + 1] = _palette[cnt * 3 + 1] >> 1;
|
||||
halfPalette[cnt * 3 + 2] = _palette[cnt * 3 + 2] >> 1;
|
||||
}
|
||||
_system->getPaletteManager()->setPalette(halfPalette, 0, GAME_COLORS);
|
||||
}
|
||||
|
||||
void Screen::setPalette(uint16 fileNum) {
|
||||
uint8 *tmpPal = _skyDisk->loadFile(fileNum);
|
||||
if (tmpPal) {
|
||||
setPalette(tmpPal);
|
||||
free(tmpPal);
|
||||
} else
|
||||
warning("Screen::setPalette: can't load file nr. %d",fileNum);
|
||||
}
|
||||
|
||||
void Screen::showScreen(uint16 fileNum, bool fullscreen) {
|
||||
// This is only used for static images in the floppy and cd intro
|
||||
free(_currentScreen);
|
||||
_currentScreen = _skyDisk->loadFile(fileNum);
|
||||
if (!fullscreen) {
|
||||
// make sure the last 8 lines are forced to black.
|
||||
memset(_currentScreen + GAME_SCREEN_HEIGHT * GAME_SCREEN_WIDTH, 0, (FULL_SCREEN_HEIGHT - GAME_SCREEN_HEIGHT) * GAME_SCREEN_WIDTH);
|
||||
}
|
||||
|
||||
if (_currentScreen)
|
||||
showScreen(_currentScreen, fullscreen);
|
||||
else
|
||||
warning("Screen::showScreen: can't load file nr. %d",fileNum);
|
||||
}
|
||||
|
||||
void Screen::showScreen(uint8 *pScreen, bool fullscreen) {
|
||||
_system->copyRectToScreen(pScreen, 320, 0, 0, GAME_SCREEN_WIDTH, fullscreen ? FULL_SCREEN_HEIGHT : GAME_SCREEN_HEIGHT);
|
||||
_system->updateScreen();
|
||||
}
|
||||
|
||||
//convert 3 byte 0..63 rgb to 3 byte 0..255 rgb
|
||||
void Screen::convertPalette(uint8 *inPal, uint8* outPal) {
|
||||
int i;
|
||||
|
||||
for (i = 0; i < VGA_COLORS; i++) {
|
||||
outPal[3 * i + 0] = (inPal[3 * i + 0] << 2) + (inPal[3 * i + 0] >> 4);
|
||||
outPal[3 * i + 1] = (inPal[3 * i + 1] << 2) + (inPal[3 * i + 1] >> 4);
|
||||
outPal[3 * i + 2] = (inPal[3 * i + 2] << 2) + (inPal[3 * i + 2] >> 4);
|
||||
}
|
||||
}
|
||||
|
||||
void Screen::recreate() {
|
||||
// check the game grid for changed blocks
|
||||
if (!Logic::_scriptVariables[LAYER_0_ID])
|
||||
return;
|
||||
uint8 *gridPos = _gameGrid;
|
||||
uint8 *screenData = (uint8 *)SkyEngine::fetchItem(Logic::_scriptVariables[LAYER_0_ID]);
|
||||
if (!screenData) {
|
||||
error("Screen::recreate():\nSkyEngine::fetchItem(Logic::_scriptVariables[LAYER_0_ID](%X)) returned NULL", Logic::_scriptVariables[LAYER_0_ID]);
|
||||
}
|
||||
uint8 *screenPos = _currentScreen;
|
||||
|
||||
for (uint8 cnty = 0; cnty < GRID_Y; cnty++) {
|
||||
for (uint8 cntx = 0; cntx < GRID_X; cntx++) {
|
||||
if (gridPos[0] & 0x80) {
|
||||
gridPos[0] &= 0x7F; // reset recreate flag
|
||||
gridPos[0] |= 1; // set bit for flip routine
|
||||
uint8 *savedScreenY = screenPos;
|
||||
for (uint8 gridCntY = 0; gridCntY < GRID_H; gridCntY++) {
|
||||
memcpy(screenPos, screenData, GRID_W);
|
||||
screenPos += GAME_SCREEN_WIDTH;
|
||||
screenData += GRID_W;
|
||||
}
|
||||
screenPos = savedScreenY + GRID_W;
|
||||
} else {
|
||||
screenPos += GRID_W;
|
||||
screenData += GRID_W * GRID_H;
|
||||
}
|
||||
gridPos++;
|
||||
}
|
||||
screenPos += (GRID_H - 1) * GAME_SCREEN_WIDTH;
|
||||
}
|
||||
}
|
||||
|
||||
void Screen::flip(bool doUpdate) {
|
||||
uint32 copyX, copyWidth;
|
||||
copyX = copyWidth = 0;
|
||||
for (uint8 cnty = 0; cnty < GRID_Y; cnty++) {
|
||||
for (uint8 cntx = 0; cntx < GRID_X; cntx++) {
|
||||
if (_gameGrid[cnty * GRID_X + cntx] & 1) {
|
||||
_gameGrid[cnty * GRID_X + cntx] &= ~1;
|
||||
if (!copyWidth)
|
||||
copyX = cntx * GRID_W;
|
||||
copyWidth += GRID_W;
|
||||
} else if (copyWidth) {
|
||||
_system->copyRectToScreen(_currentScreen + cnty * GRID_H * GAME_SCREEN_WIDTH + copyX, GAME_SCREEN_WIDTH, copyX, cnty * GRID_H, copyWidth, GRID_H);
|
||||
copyWidth = 0;
|
||||
}
|
||||
}
|
||||
if (copyWidth) {
|
||||
_system->copyRectToScreen(_currentScreen + cnty * GRID_H * GAME_SCREEN_WIDTH + copyX, GAME_SCREEN_WIDTH, copyX, cnty * GRID_H, copyWidth, GRID_H);
|
||||
copyWidth = 0;
|
||||
}
|
||||
}
|
||||
if (doUpdate)
|
||||
_system->updateScreen();
|
||||
}
|
||||
|
||||
void Screen::fnDrawScreen(uint32 palette, uint32 scroll) {
|
||||
// set up the new screen
|
||||
fnFadeDown(scroll);
|
||||
forceRefresh();
|
||||
recreate();
|
||||
spriteEngine();
|
||||
flip(false);
|
||||
fnFadeUp(palette, scroll);
|
||||
}
|
||||
|
||||
void Screen::fnFadeDown(uint32 scroll) {
|
||||
if (((scroll != 123) && (scroll != 321)) || (SkyEngine::_systemVars->systemFlags & SF_NO_SCROLL)) {
|
||||
uint32 delayTime = _system->getMillis();
|
||||
for (uint8 cnt = 0; cnt < 32; cnt++) {
|
||||
delayTime += 20;
|
||||
palette_fadedown_helper(_palette, GAME_COLORS);
|
||||
_system->getPaletteManager()->setPalette(_palette, 0, GAME_COLORS);
|
||||
_system->updateScreen();
|
||||
int32 waitTime = (int32)delayTime - _system->getMillis();
|
||||
if (waitTime < 0)
|
||||
waitTime = 0;
|
||||
_system->delayMillis((uint)waitTime);
|
||||
}
|
||||
} else {
|
||||
// scrolling is performed by fnFadeUp. It's just prepared here
|
||||
_scrollScreen = _currentScreen;
|
||||
_currentScreen = (uint8 *)malloc(FULL_SCREEN_WIDTH * FULL_SCREEN_HEIGHT);
|
||||
// the game will draw the new room into _currentScreen which
|
||||
// will be scrolled into the visible screen by fnFadeUp
|
||||
// fnFadeUp also frees the _scrollScreen
|
||||
}
|
||||
}
|
||||
|
||||
void Screen::palette_fadedown_helper(uint8 *pal, uint num) {
|
||||
do {
|
||||
if (pal[0] >= 8)
|
||||
pal[0] -= 8;
|
||||
else
|
||||
pal[0] = 0;
|
||||
|
||||
if (pal[1] >= 8)
|
||||
pal[1] -= 8;
|
||||
else
|
||||
pal[1] = 0;
|
||||
|
||||
if (pal[2] >= 8)
|
||||
pal[2] -= 8;
|
||||
else
|
||||
pal[2] = 0;
|
||||
|
||||
pal += 3;
|
||||
} while (--num);
|
||||
}
|
||||
|
||||
void Screen::paletteFadeUp(uint16 fileNr) {
|
||||
uint8 *pal = _skyDisk->loadFile(fileNr);
|
||||
if (pal) {
|
||||
paletteFadeUp(pal);
|
||||
free(pal);
|
||||
} else
|
||||
warning("Screen::paletteFadeUp: Can't load palette #%d",fileNr);
|
||||
}
|
||||
|
||||
void Screen::paletteFadeUp(uint8 *pal) {
|
||||
byte tmpPal[VGA_COLORS * 3];
|
||||
|
||||
convertPalette(pal, tmpPal);
|
||||
|
||||
uint32 delayTime = _system->getMillis();
|
||||
for (uint8 cnt = 1; cnt <= 32; cnt++) {
|
||||
delayTime += 20;
|
||||
|
||||
for (uint8 colCnt = 0; colCnt < GAME_COLORS; colCnt++) {
|
||||
_palette[colCnt * 3 + 0] = (tmpPal[colCnt * 3 + 0] * cnt) >> 5;
|
||||
_palette[colCnt * 3 + 1] = (tmpPal[colCnt * 3 + 1] * cnt) >> 5;
|
||||
_palette[colCnt * 3 + 2] = (tmpPal[colCnt * 3 + 2] * cnt) >> 5;
|
||||
}
|
||||
|
||||
_system->getPaletteManager()->setPalette(_palette, 0, GAME_COLORS);
|
||||
_system->updateScreen();
|
||||
|
||||
int32 waitTime = (int32)delayTime - _system->getMillis();
|
||||
if (waitTime < 0)
|
||||
waitTime = 0;
|
||||
_system->delayMillis((uint)waitTime);
|
||||
}
|
||||
}
|
||||
|
||||
void Screen::fnFadeUp(uint32 palNum, uint32 scroll) {
|
||||
//_currentScreen points to new screen,
|
||||
//_scrollScreen points to graphic showing old room
|
||||
if ((scroll != 123) && (scroll != 321))
|
||||
scroll = 0;
|
||||
|
||||
if ((scroll == 0) || (SkyEngine::_systemVars->systemFlags & SF_NO_SCROLL)) {
|
||||
uint8 *palette = (uint8 *)_skyCompact->fetchCpt(palNum);
|
||||
if (palette == NULL)
|
||||
error("Screen::fnFadeUp: can't fetch compact %X", palNum);
|
||||
#ifdef SCUMM_BIG_ENDIAN
|
||||
byte tmpPal[VGA_COLORS * 3];
|
||||
for (uint16 cnt = 0; cnt < VGA_COLORS * 3; cnt++)
|
||||
tmpPal[cnt] = palette[cnt ^ 1];
|
||||
paletteFadeUp(tmpPal);
|
||||
#else
|
||||
paletteFadeUp(palette);
|
||||
#endif
|
||||
} else if (scroll == 123) { // scroll left (going right)
|
||||
assert(_currentScreen && _scrollScreen);
|
||||
uint8 *scrNewPtr, *scrOldPtr;
|
||||
for (uint8 scrollCnt = 0; scrollCnt < (GAME_SCREEN_WIDTH / SCROLL_JUMP) - 1; scrollCnt++) {
|
||||
scrNewPtr = _currentScreen + scrollCnt * SCROLL_JUMP;
|
||||
scrOldPtr = _scrollScreen;
|
||||
for (uint8 lineCnt = 0; lineCnt < GAME_SCREEN_HEIGHT; lineCnt++) {
|
||||
memmove(scrOldPtr, scrOldPtr + SCROLL_JUMP, GAME_SCREEN_WIDTH - SCROLL_JUMP);
|
||||
memcpy(scrOldPtr + GAME_SCREEN_WIDTH - SCROLL_JUMP, scrNewPtr, SCROLL_JUMP);
|
||||
scrNewPtr += GAME_SCREEN_WIDTH;
|
||||
scrOldPtr += GAME_SCREEN_WIDTH;
|
||||
}
|
||||
showScreen(_scrollScreen);
|
||||
waitForTick();
|
||||
}
|
||||
showScreen(_currentScreen);
|
||||
} else if (scroll == 321) { // scroll right (going left)
|
||||
assert(_currentScreen && _scrollScreen);
|
||||
uint8 *scrNewPtr, *scrOldPtr;
|
||||
for (uint8 scrollCnt = 0; scrollCnt < (GAME_SCREEN_WIDTH / SCROLL_JUMP) - 1; scrollCnt++) {
|
||||
scrNewPtr = _currentScreen + GAME_SCREEN_WIDTH - (scrollCnt + 1) * SCROLL_JUMP;
|
||||
scrOldPtr = _scrollScreen;
|
||||
for (uint8 lineCnt = 0; lineCnt < GAME_SCREEN_HEIGHT; lineCnt++) {
|
||||
memmove(scrOldPtr + SCROLL_JUMP, scrOldPtr, GAME_SCREEN_WIDTH - SCROLL_JUMP);
|
||||
memcpy(scrOldPtr, scrNewPtr, SCROLL_JUMP);
|
||||
scrNewPtr += GAME_SCREEN_WIDTH;
|
||||
scrOldPtr += GAME_SCREEN_WIDTH;
|
||||
}
|
||||
showScreen(_scrollScreen);
|
||||
waitForTick();
|
||||
}
|
||||
showScreen(_currentScreen);
|
||||
}
|
||||
if (_scrollScreen) {
|
||||
free(_scrollScreen);
|
||||
_scrollScreen = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void Screen::waitForTick() {
|
||||
uint32 start = _system->getMillis();
|
||||
uint32 end = start + 20 - (start % 20);
|
||||
uint32 remain;
|
||||
|
||||
Common::EventManager *eventMan = _system->getEventManager();
|
||||
Common::Event event;
|
||||
|
||||
while (true) {
|
||||
while (eventMan->pollEvent(event))
|
||||
;
|
||||
|
||||
start = _system->getMillis();
|
||||
|
||||
if (start >= end)
|
||||
return;
|
||||
|
||||
remain = end - start;
|
||||
if (remain < 10) {
|
||||
_system->delayMillis(remain);
|
||||
return;
|
||||
}
|
||||
|
||||
_system->delayMillis(10);
|
||||
}
|
||||
}
|
||||
|
||||
void Screen::waitForSequence() {
|
||||
Common::EventManager *eventMan = _system->getEventManager();
|
||||
Common::Event event;
|
||||
|
||||
while (_seqInfo.running) {
|
||||
processSequence();
|
||||
|
||||
_system->delayMillis(20);
|
||||
while (eventMan->pollEvent(event))
|
||||
;
|
||||
}
|
||||
}
|
||||
|
||||
void Screen::startSequence(uint16 fileNum) {
|
||||
_seqInfo.seqData = _skyDisk->loadFile(fileNum);
|
||||
_seqInfo.nextFrame = _system->getMillis() + 60;
|
||||
_seqInfo.framesLeft = _seqInfo.seqData[0];
|
||||
_seqInfo.seqDataPos = _seqInfo.seqData + 1;
|
||||
_seqInfo.running = true;
|
||||
_seqInfo.runningItem = false;
|
||||
}
|
||||
|
||||
void Screen::startSequenceItem(uint16 itemNum) {
|
||||
_seqInfo.seqData = (uint8 *)SkyEngine::fetchItem(itemNum);
|
||||
_seqInfo.nextFrame = _system->getMillis() + 60;
|
||||
_seqInfo.framesLeft = _seqInfo.seqData[0] - 1;
|
||||
_seqInfo.seqDataPos = _seqInfo.seqData + 1;
|
||||
_seqInfo.running = true;
|
||||
_seqInfo.runningItem = true;
|
||||
}
|
||||
|
||||
void Screen::stopSequence() {
|
||||
_seqInfo.running = false;
|
||||
waitForTick();
|
||||
waitForTick();
|
||||
_seqInfo.nextFrame = _seqInfo.framesLeft = 0;
|
||||
free(_seqInfo.seqData);
|
||||
_seqInfo.seqData = _seqInfo.seqDataPos = NULL;
|
||||
}
|
||||
|
||||
void Screen::processSequence() {
|
||||
if (!_seqInfo.running)
|
||||
return;
|
||||
|
||||
if (_system->getMillis() < _seqInfo.nextFrame)
|
||||
return;
|
||||
|
||||
_seqInfo.nextFrame += 60;
|
||||
|
||||
memset(_seqGrid, 0, 12 * 20);
|
||||
|
||||
uint32 screenPos = 0;
|
||||
|
||||
uint8 nrToSkip, nrToDo, cnt;
|
||||
do {
|
||||
do {
|
||||
nrToSkip = _seqInfo.seqDataPos[0];
|
||||
_seqInfo.seqDataPos++;
|
||||
screenPos += nrToSkip;
|
||||
} while (nrToSkip == 0xFF);
|
||||
|
||||
do {
|
||||
nrToDo = _seqInfo.seqDataPos[0];
|
||||
_seqInfo.seqDataPos++;
|
||||
|
||||
uint8 gridSta = (uint8)((screenPos / (GAME_SCREEN_WIDTH * 16))*20 + ((screenPos % GAME_SCREEN_WIDTH) >> 4));
|
||||
uint8 gridEnd = (uint8)(((screenPos+nrToDo) / (GAME_SCREEN_WIDTH * 16))*20 + (((screenPos+nrToDo) % GAME_SCREEN_WIDTH) >> 4));
|
||||
gridSta = MIN(gridSta, (uint8)(12 * 20 - 1));
|
||||
gridEnd = MIN(gridEnd, (uint8)(12 * 20 - 1));
|
||||
if (gridEnd >= gridSta)
|
||||
for (cnt = gridSta; cnt <= gridEnd; cnt++)
|
||||
_seqGrid[cnt] = 1;
|
||||
else {
|
||||
for (cnt = gridSta; cnt < (gridSta / 20 + 1) * 20; cnt++)
|
||||
_seqGrid[cnt] = 1;
|
||||
for (cnt = (gridEnd / 20) * 20; cnt <= gridEnd; cnt++)
|
||||
_seqGrid[cnt] = 1;
|
||||
}
|
||||
|
||||
for (cnt = 0; cnt < nrToDo; cnt++) {
|
||||
_currentScreen[screenPos] = _seqInfo.seqDataPos[0];
|
||||
_seqInfo.seqDataPos++;
|
||||
screenPos++;
|
||||
}
|
||||
} while (nrToDo == 0xFF);
|
||||
} while (screenPos < (GAME_SCREEN_WIDTH * GAME_SCREEN_HEIGHT));
|
||||
uint8 *gridPtr = _seqGrid; uint8 *scrPtr = _currentScreen; uint8 *rectPtr = NULL;
|
||||
uint8 rectWid = 0, rectX = 0, rectY = 0;
|
||||
for (uint8 cnty = 0; cnty < 12; cnty++) {
|
||||
for (uint8 cntx = 0; cntx < 20; cntx++) {
|
||||
if (*gridPtr) {
|
||||
if (!rectWid) {
|
||||
rectX = cntx;
|
||||
rectY = cnty;
|
||||
rectPtr = scrPtr;
|
||||
}
|
||||
rectWid++;
|
||||
} else if (rectWid) {
|
||||
_system->copyRectToScreen(rectPtr, GAME_SCREEN_WIDTH, rectX << 4, rectY << 4, rectWid << 4, 16);
|
||||
rectWid = 0;
|
||||
}
|
||||
scrPtr += 16;
|
||||
gridPtr++;
|
||||
}
|
||||
if (rectWid) {
|
||||
_system->copyRectToScreen(rectPtr, GAME_SCREEN_WIDTH, rectX << 4, rectY << 4, rectWid << 4, 16);
|
||||
rectWid = 0;
|
||||
}
|
||||
scrPtr += 15 * GAME_SCREEN_WIDTH;
|
||||
}
|
||||
_system->updateScreen();
|
||||
_seqInfo.framesLeft--;
|
||||
|
||||
if (_seqInfo.framesLeft == 0) {
|
||||
_seqInfo.running = false;
|
||||
if (!_seqInfo.runningItem)
|
||||
free(_seqInfo.seqData);
|
||||
_seqInfo.seqData = _seqInfo.seqDataPos = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
//- sprites.asm routines
|
||||
|
||||
void Screen::spriteEngine() {
|
||||
doSprites(BACK);
|
||||
sortSprites();
|
||||
doSprites(FORE);
|
||||
}
|
||||
|
||||
void Screen::sortSprites() {
|
||||
StSortList sortList[30];
|
||||
uint32 currDrawList = DRAW_LIST_NO;
|
||||
uint32 loadDrawList;
|
||||
|
||||
bool nextDrawList = false;
|
||||
while (Logic::_scriptVariables[currDrawList]) {
|
||||
// big_sort_loop
|
||||
uint32 spriteCnt = 0;
|
||||
loadDrawList = Logic::_scriptVariables[currDrawList];
|
||||
currDrawList++;
|
||||
|
||||
do { // a_new_draw_list:
|
||||
uint16 *drawListData = (uint16 *)_skyCompact->fetchCpt(loadDrawList);
|
||||
nextDrawList = false;
|
||||
while ((!nextDrawList) && (drawListData[0])) {
|
||||
if (drawListData[0] == 0xFFFF) {
|
||||
loadDrawList = drawListData[1];
|
||||
nextDrawList = true;
|
||||
} else {
|
||||
// process_this_id:
|
||||
Compact *spriteComp = _skyCompact->fetchCpt(drawListData[0]);
|
||||
if ((spriteComp->status & 4) && // is it sortable playfield?(!?!)
|
||||
(spriteComp->screen == Logic::_scriptVariables[SCREEN])) { // on current screen
|
||||
DataFileHeader *spriteData =
|
||||
(DataFileHeader *)SkyEngine::fetchItem(spriteComp->frame >> 6);
|
||||
if (!spriteData) {
|
||||
debug(9,"Missing file %d", spriteComp->frame >> 6);
|
||||
spriteComp->status = 0;
|
||||
} else {
|
||||
sortList[spriteCnt].yCood = spriteComp->ycood + spriteData->s_offset_y + spriteData->s_height;
|
||||
sortList[spriteCnt].compact = spriteComp;
|
||||
sortList[spriteCnt].sprite = spriteData;
|
||||
spriteCnt++;
|
||||
}
|
||||
}
|
||||
drawListData++;
|
||||
}
|
||||
}
|
||||
} while (nextDrawList);
|
||||
// made_list:
|
||||
if (spriteCnt > 1) { // bubble sort
|
||||
for (uint32 cnt1 = 0; cnt1 < spriteCnt - 1; cnt1++)
|
||||
for (uint32 cnt2 = cnt1 + 1; cnt2 < spriteCnt; cnt2++)
|
||||
if (sortList[cnt1].yCood > sortList[cnt2].yCood) {
|
||||
StSortList tmp;
|
||||
tmp.yCood = sortList[cnt1].yCood;
|
||||
tmp.sprite = sortList[cnt1].sprite;
|
||||
tmp.compact = sortList[cnt1].compact;
|
||||
sortList[cnt1].yCood = sortList[cnt2].yCood;
|
||||
sortList[cnt1].sprite = sortList[cnt2].sprite;
|
||||
sortList[cnt1].compact = sortList[cnt2].compact;
|
||||
sortList[cnt2].yCood = tmp.yCood;
|
||||
sortList[cnt2].sprite = tmp.sprite;
|
||||
sortList[cnt2].compact = tmp.compact;
|
||||
}
|
||||
}
|
||||
for (uint32 cnt = 0; cnt < spriteCnt; cnt++) {
|
||||
drawSprite((uint8 *)sortList[cnt].sprite, sortList[cnt].compact);
|
||||
if (sortList[cnt].compact->status & 8)
|
||||
vectorToGame(0x81);
|
||||
else
|
||||
vectorToGame(1);
|
||||
if (!(sortList[cnt].compact->status & 0x200))
|
||||
verticalMask();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Screen::doSprites(uint8 layer) {
|
||||
uint16 drawListNum = DRAW_LIST_NO;
|
||||
uint32 idNum;
|
||||
uint16* drawList;
|
||||
while (Logic::_scriptVariables[drawListNum]) { // std sp loop
|
||||
idNum = Logic::_scriptVariables[drawListNum];
|
||||
drawListNum++;
|
||||
|
||||
drawList = (uint16 *)_skyCompact->fetchCpt(idNum);
|
||||
while (drawList[0]) {
|
||||
// new_draw_list:
|
||||
while ((drawList[0] != 0) && (drawList[0] != 0xFFFF)) {
|
||||
// back_loop:
|
||||
// not_new_list
|
||||
Compact *spriteData = _skyCompact->fetchCpt(drawList[0]);
|
||||
drawList++;
|
||||
if ((spriteData->status & (1 << layer)) &&
|
||||
(spriteData->screen == Logic::_scriptVariables[SCREEN])) {
|
||||
uint8 *toBeDrawn = (uint8 *)SkyEngine::fetchItem(spriteData->frame >> 6);
|
||||
if (!toBeDrawn) {
|
||||
debug(9, "Spritedata %d not loaded", spriteData->frame >> 6);
|
||||
spriteData->status = 0;
|
||||
} else {
|
||||
drawSprite(toBeDrawn, spriteData);
|
||||
if (layer == BACK)
|
||||
verticalMask();
|
||||
if (spriteData->status & 8)
|
||||
vectorToGame(0x81);
|
||||
else
|
||||
vectorToGame(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
while (drawList[0] == 0xFFFF)
|
||||
drawList = (uint16 *)_skyCompact->fetchCpt(drawList[1]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Screen::drawSprite(uint8 *spriteInfo, Compact *sprCompact) {
|
||||
if (spriteInfo == NULL) {
|
||||
warning("Screen::drawSprite Can't draw sprite. Data %d was not loaded", sprCompact->frame >> 6);
|
||||
sprCompact->status = 0;
|
||||
return;
|
||||
}
|
||||
DataFileHeader *sprDataFile = (DataFileHeader *)spriteInfo;
|
||||
_sprWidth = sprDataFile->s_width;
|
||||
_sprHeight = sprDataFile->s_height;
|
||||
_maskX1 = _maskX2 = 0;
|
||||
uint8 *spriteData = spriteInfo + (sprCompact->frame & 0x3F) * sprDataFile->s_sp_size;
|
||||
spriteData += sizeof(DataFileHeader);
|
||||
int32 spriteY = sprCompact->ycood + sprDataFile->s_offset_y - TOP_LEFT_Y;
|
||||
if (spriteY < 0) {
|
||||
spriteY = -spriteY;
|
||||
if (_sprHeight <= (uint32)spriteY) {
|
||||
_sprWidth = 0;
|
||||
return;
|
||||
}
|
||||
_sprHeight -= spriteY;
|
||||
spriteData += sprDataFile->s_width * spriteY;
|
||||
spriteY = 0;
|
||||
} else {
|
||||
int32 botClip = GAME_SCREEN_HEIGHT - sprDataFile->s_height - spriteY;
|
||||
if (botClip < 0) {
|
||||
botClip = -botClip;
|
||||
if (_sprHeight <= (uint32)botClip) {
|
||||
_sprWidth = 0;
|
||||
return;
|
||||
}
|
||||
_sprHeight -= botClip;
|
||||
}
|
||||
}
|
||||
_sprY = (uint32)spriteY;
|
||||
int32 spriteX = sprCompact->xcood + sprDataFile->s_offset_x - TOP_LEFT_X;
|
||||
if (spriteX < 0) {
|
||||
spriteX = -spriteX;
|
||||
if (_sprWidth <= (uint32)spriteX) {
|
||||
_sprWidth = 0;
|
||||
return;
|
||||
}
|
||||
_sprWidth -= spriteX;
|
||||
_maskX1 = spriteX;
|
||||
spriteX = 0;
|
||||
} else {
|
||||
int32 rightClip = GAME_SCREEN_WIDTH - (sprDataFile->s_width + spriteX);
|
||||
if (rightClip < 0) {
|
||||
rightClip = (-rightClip) + 1;
|
||||
if (_sprWidth <= (uint32)rightClip) {
|
||||
_sprWidth = 0;
|
||||
return;
|
||||
}
|
||||
_sprWidth -= rightClip;
|
||||
_maskX2 = rightClip;
|
||||
}
|
||||
}
|
||||
_sprX = (uint32)spriteX;
|
||||
uint8 *screenPtr = _currentScreen + _sprY * GAME_SCREEN_WIDTH + _sprX;
|
||||
if ((_sprHeight > 192) || (_sprY > 192)) {
|
||||
_sprWidth = 0;
|
||||
return;
|
||||
}
|
||||
if ((_sprX + _sprWidth > 320) || (_sprY + _sprHeight > 192)) {
|
||||
warning("Screen::drawSprite fatal error: got x = %d, y = %d, w = %d, h = %d",_sprX, _sprY, _sprWidth, _sprHeight);
|
||||
_sprWidth = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
for (uint16 cnty = 0; cnty < _sprHeight; cnty++) {
|
||||
for (uint16 cntx = 0; cntx < _sprWidth; cntx++)
|
||||
if (spriteData[cntx + _maskX1])
|
||||
screenPtr[cntx] = spriteData[cntx + _maskX1];
|
||||
spriteData += _sprWidth + _maskX2 + _maskX1;
|
||||
screenPtr += GAME_SCREEN_WIDTH;
|
||||
}
|
||||
// Convert the sprite coordinate/size values to blocks for vertical mask and/or vector to game
|
||||
_sprWidth += _sprX + GRID_W-1;
|
||||
_sprHeight += _sprY + GRID_H-1;
|
||||
|
||||
_sprX >>= GRID_W_SHIFT;
|
||||
_sprWidth >>= GRID_W_SHIFT;
|
||||
_sprY >>= GRID_H_SHIFT;
|
||||
_sprHeight >>= GRID_H_SHIFT;
|
||||
|
||||
_sprWidth -= _sprX;
|
||||
_sprHeight -= _sprY;
|
||||
}
|
||||
|
||||
void Screen::vectorToGame(uint8 gridVal) {
|
||||
if (_sprWidth == 0)
|
||||
return;
|
||||
uint8 *trgGrid = _gameGrid + _sprY * GRID_X +_sprX;
|
||||
for (uint32 cnty = 0; cnty < _sprHeight; cnty++) {
|
||||
for (uint32 cntx = 0; cntx < _sprWidth; cntx++)
|
||||
trgGrid[cntx] |= gridVal;
|
||||
trgGrid += GRID_X;
|
||||
}
|
||||
}
|
||||
|
||||
void Screen::vertMaskSub(uint16 *grid, uint32 gridOfs, uint8 *screenPtr, uint32 layerId) {
|
||||
for (uint32 cntx = 0; cntx < _sprHeight; cntx++) { // start_x | block_loop
|
||||
if (grid[gridOfs]) {
|
||||
if (!(FROM_LE_16(grid[gridOfs]) & 0x8000)) {
|
||||
uint32 gridVal = FROM_LE_16(grid[gridOfs]) - 1;
|
||||
gridVal *= GRID_W * GRID_H;
|
||||
uint8 *dataSrc = (uint8 *)SkyEngine::fetchItem(Logic::_scriptVariables[layerId]) + gridVal;
|
||||
uint8 *dataTrg = screenPtr;
|
||||
for (uint32 grdCntY = 0; grdCntY < GRID_H; grdCntY++) {
|
||||
for (uint32 grdCntX = 0; grdCntX < GRID_W; grdCntX++)
|
||||
if (dataSrc[grdCntX])
|
||||
dataTrg[grdCntX] = dataSrc[grdCntX];
|
||||
dataSrc += GRID_W;
|
||||
dataTrg += GAME_SCREEN_WIDTH;
|
||||
}
|
||||
} // dummy_end:
|
||||
screenPtr -= GRID_H * GAME_SCREEN_WIDTH;
|
||||
gridOfs -= GRID_X;
|
||||
} else
|
||||
return;
|
||||
} // next_x
|
||||
}
|
||||
|
||||
void Screen::verticalMask() {
|
||||
if (_sprWidth == 0)
|
||||
return;
|
||||
uint32 startGridOfs = (_sprY + _sprHeight - 1) * GRID_X + _sprX;
|
||||
uint8 *startScreenPtr = (_sprY + _sprHeight - 1) * GRID_H * GAME_SCREEN_WIDTH + _sprX * GRID_W + _currentScreen;
|
||||
|
||||
for (uint32 layerCnt = LAYER_1_ID; layerCnt <= LAYER_3_ID; layerCnt++) {
|
||||
uint32 gridOfs = startGridOfs;
|
||||
uint8 *screenPtr = startScreenPtr;
|
||||
for (uint32 widCnt = 0; widCnt < _sprWidth; widCnt++) { // x_loop
|
||||
uint32 nLayerCnt = layerCnt;
|
||||
while (Logic::_scriptVariables[nLayerCnt + 3]) {
|
||||
uint16 *scrGrid;
|
||||
scrGrid = (uint16 *)SkyEngine::fetchItem(Logic::_scriptVariables[layerCnt + 3]);
|
||||
if (scrGrid[gridOfs]) {
|
||||
vertMaskSub(scrGrid, gridOfs, screenPtr, layerCnt);
|
||||
break;
|
||||
} else
|
||||
nLayerCnt++;
|
||||
}
|
||||
// next_x:
|
||||
screenPtr += GRID_W;
|
||||
gridOfs++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Screen::paintBox(uint16 x, uint16 y) {
|
||||
uint8 *screenPos = _currentScreen + y * GAME_SCREEN_WIDTH + x;
|
||||
memset(screenPos, 255, 8);
|
||||
for (uint8 cnt = 1; cnt < 8; cnt++) {
|
||||
*(screenPos + cnt * GAME_SCREEN_WIDTH) = 255;
|
||||
*(screenPos + cnt * GAME_SCREEN_WIDTH + 7) = 255;
|
||||
}
|
||||
memset(screenPos + 7 * GAME_SCREEN_WIDTH, 255, 7);
|
||||
}
|
||||
|
||||
void Screen::showGrid(uint8 *gridBuf) {
|
||||
uint32 gridData = 0;
|
||||
uint8 bitsLeft = 0;
|
||||
for (uint16 cnty = 0; cnty < GAME_SCREEN_HEIGHT >> 3; cnty++) {
|
||||
for (uint16 cntx = 0; cntx < GAME_SCREEN_WIDTH >> 3; cntx++) {
|
||||
if (!bitsLeft) {
|
||||
bitsLeft = 32;
|
||||
gridData = *(uint32 *)gridBuf;
|
||||
gridBuf += 4;
|
||||
}
|
||||
if (gridData & 0x80000000)
|
||||
paintBox(cntx << 3, cnty << 3);
|
||||
bitsLeft--;
|
||||
gridData <<= 1;
|
||||
}
|
||||
}
|
||||
_system->copyRectToScreen(_currentScreen, GAME_SCREEN_WIDTH, 0, 0, GAME_SCREEN_WIDTH, GAME_SCREEN_HEIGHT);
|
||||
|
||||
}
|
||||
|
||||
} // End of namespace Sky
|
||||
136
engines/sky/screen.h
Normal file
136
engines/sky/screen.h
Normal file
@@ -0,0 +1,136 @@
|
||||
/* 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 SKY_SCREEN_H
|
||||
#define SKY_SCREEN_H
|
||||
|
||||
|
||||
#include "common/scummsys.h"
|
||||
#include "sky/skydefs.h"
|
||||
|
||||
class OSystem;
|
||||
|
||||
namespace Common {
|
||||
struct Rect;
|
||||
}
|
||||
|
||||
namespace Sky {
|
||||
|
||||
class Disk;
|
||||
class SkyEngine;
|
||||
class SkyCompact;
|
||||
struct Compact;
|
||||
struct DataFileHeader;
|
||||
|
||||
#define SCROLL_JUMP 16
|
||||
#define VGA_COLORS 256
|
||||
#define GAME_COLORS 240
|
||||
|
||||
#define FORE 1
|
||||
#define BACK 0
|
||||
|
||||
typedef struct {
|
||||
uint16 yCood;
|
||||
Compact *compact;
|
||||
DataFileHeader *sprite;
|
||||
} StSortList;
|
||||
|
||||
class Screen {
|
||||
public:
|
||||
Screen(OSystem *pSystem, Disk *pDisk, SkyCompact *skyCompact);
|
||||
~Screen();
|
||||
void setPalette(uint8 *pal);
|
||||
void setPaletteEndian(uint8 *pal);
|
||||
void setPalette(uint16 fileNum);
|
||||
void paletteFadeUp(uint8 *pal);
|
||||
void paletteFadeUp(uint16 fileNr);
|
||||
|
||||
void showScreen(uint16 fileNum, bool fullscreen = false);
|
||||
void showScreen(uint8 *pScreen, bool fullscreen = false);
|
||||
|
||||
void handleTimer();
|
||||
void startSequence(uint16 fileNum);
|
||||
void startSequenceItem(uint16 itemNum);
|
||||
void stopSequence();
|
||||
bool sequenceRunning() { return _seqInfo.running; }
|
||||
void processSequence();
|
||||
void waitForSequence();
|
||||
uint32 seqFramesLeft() { return _seqInfo.framesLeft; }
|
||||
uint8 *giveCurrent() { return _currentScreen; }
|
||||
void halvePalette();
|
||||
|
||||
//- regular screen.asm routines
|
||||
void forceRefresh() { memset(_gameGrid, 0x80, GRID_X * GRID_Y); }
|
||||
void fnFadeUp(uint32 palNum, uint32 scroll);
|
||||
void fnFadeDown(uint32 scroll);
|
||||
void fnDrawScreen(uint32 palette, uint32 scroll);
|
||||
void clearScreen(bool fullscreen = false);
|
||||
void setFocusRectangle(const Common::Rect& rect);
|
||||
|
||||
void recreate();
|
||||
void flip(bool doUpdate = true);
|
||||
|
||||
void spriteEngine();
|
||||
|
||||
void paintBox(uint16 x, uint16 y);
|
||||
void showGrid(uint8 *gridBuf);
|
||||
|
||||
private:
|
||||
OSystem *_system;
|
||||
Disk *_skyDisk;
|
||||
SkyCompact *_skyCompact;
|
||||
static uint8 _top16Colors[16 * 3];
|
||||
uint8 _palette[VGA_COLORS * 3];
|
||||
uint32 _currentPalette;
|
||||
uint8 _seqGrid[20 * 12];
|
||||
|
||||
void waitForTick();
|
||||
|
||||
uint8 *_gameGrid;
|
||||
uint8 *_currentScreen;
|
||||
uint8 *_scrollScreen;
|
||||
struct {
|
||||
uint32 nextFrame;
|
||||
uint32 framesLeft;
|
||||
uint8 *seqData;
|
||||
uint8 *seqDataPos;
|
||||
volatile bool running;
|
||||
bool runningItem; // when playing an item, don't free it afterwards.
|
||||
} _seqInfo;
|
||||
|
||||
//- more regular screen.asm + layer.asm routines
|
||||
void convertPalette(uint8 *inPal, uint8* outPal);
|
||||
void palette_fadedown_helper(uint8 *pal, uint num);
|
||||
|
||||
//- sprite.asm routines
|
||||
// fixme: get rid of these globals
|
||||
uint32 _sprWidth, _sprHeight, _sprX, _sprY, _maskX1, _maskX2;
|
||||
void doSprites(uint8 layer);
|
||||
void sortSprites();
|
||||
void drawSprite(uint8 *spriteData, Compact *sprCompact);
|
||||
void verticalMask();
|
||||
void vertMaskSub(uint16 *grid, uint32 gridOfs, uint8 *screenPtr, uint32 layerId);
|
||||
void vectorToGame(uint8 gridVal);
|
||||
};
|
||||
|
||||
} // End of namespace Sky
|
||||
|
||||
#endif //SKYSCREEN_H
|
||||
587
engines/sky/sky.cpp
Normal file
587
engines/sky/sky.cpp
Normal file
@@ -0,0 +1,587 @@
|
||||
/* 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 "backends/keymapper/keymapper.h"
|
||||
|
||||
#include "common/config-manager.h"
|
||||
#include "common/system.h"
|
||||
#include "common/textconsole.h"
|
||||
#include "common/file.h"
|
||||
#include "common/md5.h"
|
||||
|
||||
#include "sky/control.h"
|
||||
#include "sky/debug.h"
|
||||
#include "sky/disk.h"
|
||||
#include "sky/grid.h"
|
||||
#include "sky/intro.h"
|
||||
#include "sky/logic.h"
|
||||
#include "sky/mouse.h"
|
||||
#include "sky/music/adlibmusic.h"
|
||||
#include "sky/music/gmmusic.h"
|
||||
#include "sky/music/mt32music.h"
|
||||
#include "sky/music/musicbase.h"
|
||||
#include "sky/screen.h"
|
||||
#include "sky/sky.h"
|
||||
#include "sky/skydefs.h"
|
||||
#include "sky/sound.h"
|
||||
#include "sky/text.h"
|
||||
#include "sky/compact.h"
|
||||
|
||||
#include "audio/mididrv.h"
|
||||
#include "audio/mixer.h"
|
||||
|
||||
#include "engines/util.h"
|
||||
|
||||
/*
|
||||
At the beginning the reverse engineers were happy, and did rejoice at
|
||||
their task, for the engine before them did shineth and was full of
|
||||
promise. But then they did look closer and see'th the awful truth;
|
||||
its code was assembly and messy (rareth was its comments). And so large
|
||||
were its includes that did at first seem small; queereth also was its
|
||||
compact(s). Then they did findeth another version, and this was slightly
|
||||
different from the first. Then a third, and this was different again.
|
||||
All different, but not really better, for all were not really compatible.
|
||||
But, eventually, it did come to pass that Steel Sky was implemented on
|
||||
a modern platform. And the programmers looked and saw that it was indeed a
|
||||
miracle. But they were not joyous and instead did weep for nobody knew
|
||||
just what had been done. Except people who read the source. Hello.
|
||||
|
||||
With apologies to the CD32 SteelSky file.
|
||||
*/
|
||||
|
||||
namespace Sky {
|
||||
|
||||
void *SkyEngine::_itemList[300];
|
||||
SystemVars *SkyEngine::_systemVars = nullptr;
|
||||
const char *SkyEngine::shortcutsKeymapId = "sky-shortcuts";
|
||||
|
||||
SkyEngine::SkyEngine(OSystem *syst)
|
||||
: Engine(syst), _fastMode(0), _debugger(0), _big5Font(nullptr) {
|
||||
_systemVars = new SystemVars();
|
||||
_systemVars->systemFlags = 0;
|
||||
_systemVars->gameVersion = 0;
|
||||
_systemVars->mouseFlag = 0;
|
||||
_systemVars->language = 0;
|
||||
_systemVars->currentPalette = 4316;
|
||||
_systemVars->gameSpeed = 0;
|
||||
_systemVars->currentMusic = 0;
|
||||
_systemVars->pastIntro = false;
|
||||
_systemVars->paused = false;
|
||||
_systemVars->textDirRTL = false;
|
||||
|
||||
memset (_chineseTraditionalOffsets, 0, sizeof(_chineseTraditionalOffsets));
|
||||
_chineseTraditionalBlock = nullptr;
|
||||
|
||||
_action = kSkyActionNone;
|
||||
_skyLogic = nullptr;
|
||||
_skySound = nullptr;
|
||||
_skyMusic = nullptr;
|
||||
_skyText = nullptr;
|
||||
_skyMouse = nullptr;
|
||||
_skyScreen = nullptr;
|
||||
|
||||
_skyDisk = nullptr;
|
||||
_skyControl = nullptr;
|
||||
_skyCompact = nullptr;
|
||||
}
|
||||
|
||||
SkyEngine::~SkyEngine() {
|
||||
delete _skyLogic;
|
||||
delete _skySound;
|
||||
delete _skyMusic;
|
||||
delete _skyText;
|
||||
delete _skyMouse;
|
||||
delete _skyScreen;
|
||||
//_debugger is deleted by Engine
|
||||
delete _skyDisk;
|
||||
delete _skyControl;
|
||||
delete _skyCompact;
|
||||
|
||||
for (int i = 0; i < 300; i++)
|
||||
if (_itemList[i])
|
||||
free(_itemList[i]);
|
||||
|
||||
delete _systemVars;
|
||||
delete [] _chineseTraditionalBlock;
|
||||
_chineseTraditionalBlock = nullptr;
|
||||
|
||||
delete _big5Font;
|
||||
_big5Font = nullptr;
|
||||
}
|
||||
|
||||
void SkyEngine::syncSoundSettings() {
|
||||
Engine::syncSoundSettings();
|
||||
|
||||
bool mute = false;
|
||||
if (ConfMan.hasKey("mute"))
|
||||
mute = ConfMan.getBool("mute");
|
||||
|
||||
if (ConfMan.getBool("sfx_mute")) // set mute sfx status for native options menu (F5)
|
||||
SkyEngine::_systemVars->systemFlags |= SF_FX_OFF;
|
||||
|
||||
if (ConfMan.getBool("music_mute")) { // CD version allows to mute music from native options menu (F5)
|
||||
SkyEngine::_systemVars->systemFlags |= SF_MUS_OFF;
|
||||
}
|
||||
// SkyEngine native sound volume range is [0, 127]
|
||||
// However, via ScummVM UI, the volume range can be set within [0, 256]
|
||||
// so we "translate" between them
|
||||
_skyMusic->setVolume(mute ? 0: CLIP(ConfMan.getInt("music_volume") >> 1, 0, 127));
|
||||
|
||||
// write-back to ini file for persistence
|
||||
ConfMan.flushToDisk();
|
||||
}
|
||||
|
||||
void SkyEngine::initVirgin() {
|
||||
_skyScreen->setPalette(60111);
|
||||
_skyScreen->showScreen(60110);
|
||||
}
|
||||
|
||||
void SkyEngine::handleKey() {
|
||||
if ((_action != kSkyActionNone || _keyPressed.keycode) && _systemVars->paused) {
|
||||
_skySound->fnUnPauseFx();
|
||||
_systemVars->paused = false;
|
||||
_skyScreen->setPaletteEndian((uint8 *)_skyCompact->fetchCpt(SkyEngine::_systemVars->currentPalette));
|
||||
} else {
|
||||
switch (_action) {
|
||||
case kSkyActionToggleFastMode:
|
||||
_fastMode ^= 1;
|
||||
break;
|
||||
|
||||
case kSkyActionToggleReallyFastMode:
|
||||
_fastMode ^= 2;
|
||||
break;
|
||||
|
||||
case kSkyActionOpenControlPanel:
|
||||
_skyControl->doControlPanel();
|
||||
break;
|
||||
|
||||
case kSkyActionSkip:
|
||||
if (!_systemVars->pastIntro)
|
||||
_skyControl->restartGame();
|
||||
break;
|
||||
|
||||
case kSkyActionSkipLine:
|
||||
_skyMouse->logicClick();
|
||||
break;
|
||||
|
||||
case kSkyActionPause:
|
||||
_skyScreen->halvePalette();
|
||||
_skySound->fnPauseFx();
|
||||
_systemVars->paused = true;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
_action = kSkyActionNone;
|
||||
_keyPressed.reset();
|
||||
}
|
||||
|
||||
Common::Error SkyEngine::go() {
|
||||
_action = kSkyActionNone;
|
||||
|
||||
uint16 result = 0;
|
||||
if (ConfMan.hasKey("save_slot")) {
|
||||
int saveSlot = ConfMan.getInt("save_slot");
|
||||
if (saveSlot >= 0 && saveSlot <= MAX_SAVE_GAMES)
|
||||
result = _skyControl->quickXRestore(ConfMan.getInt("save_slot"));
|
||||
}
|
||||
|
||||
if (result != GAME_RESTORED) {
|
||||
bool introSkipped = false;
|
||||
// Clear pastIntro here (set to false) explicilty
|
||||
// It should be false already, but better to ensure it
|
||||
_systemVars->pastIntro = false;
|
||||
if (_systemVars->gameVersion > 272) { // don't do intro for floppydemos
|
||||
Intro *skyIntro = new Intro(_skyDisk, _skyScreen, _skyMusic, _skySound, _skyText, _mixer, _system);
|
||||
bool floppyIntro = ConfMan.getBool("alt_intro");
|
||||
introSkipped = !skyIntro->doIntro(floppyIntro);
|
||||
delete skyIntro;
|
||||
}
|
||||
|
||||
if (!shouldQuit()) {
|
||||
_skyScreen->clearScreen(true);
|
||||
// restartGame() takes us to the first scene, without showing the
|
||||
// initial animation where Foster is being chased. initScreen0()
|
||||
// shows the first scene together with that animation. We can't
|
||||
// call both, as they both load the same scene.
|
||||
if (introSkipped) {
|
||||
// restart game sets the _systemVars->pastIntro = true;
|
||||
_skyControl->restartGame();
|
||||
} else {
|
||||
|
||||
_skyLogic->initScreen0();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
uint32 delayCount = _system->getMillis();
|
||||
while (!shouldQuit()) {
|
||||
_skySound->checkFxQueue();
|
||||
_skyMouse->mouseEngine();
|
||||
handleKey();
|
||||
if (_systemVars->paused) {
|
||||
do {
|
||||
_system->updateScreen();
|
||||
delay(50);
|
||||
handleKey();
|
||||
} while (_systemVars->paused);
|
||||
delayCount = _system->getMillis();
|
||||
}
|
||||
|
||||
_skyLogic->engine();
|
||||
_skyScreen->processSequence();
|
||||
_skyScreen->recreate();
|
||||
_skyScreen->spriteEngine();
|
||||
if (_debugger->showGrid()) {
|
||||
uint8 *grid = _skyLogic->_skyGrid->giveGrid(Logic::_scriptVariables[SCREEN]);
|
||||
if (grid) {
|
||||
_skyScreen->showGrid(grid);
|
||||
_skyScreen->forceRefresh();
|
||||
}
|
||||
}
|
||||
_skyScreen->flip();
|
||||
|
||||
if (_fastMode & 2)
|
||||
delay(0);
|
||||
else if (_fastMode & 1)
|
||||
delay(10);
|
||||
else {
|
||||
delayCount += _systemVars->gameSpeed;
|
||||
int needDelay = delayCount - (int)_system->getMillis();
|
||||
if ((needDelay < 0) || (needDelay > _systemVars->gameSpeed)) {
|
||||
needDelay = 0;
|
||||
delayCount = _system->getMillis();
|
||||
}
|
||||
delay(needDelay);
|
||||
}
|
||||
}
|
||||
|
||||
_skyControl->showGameQuitMsg();
|
||||
_skyMusic->stopMusic();
|
||||
ConfMan.flushToDisk();
|
||||
delay(1500);
|
||||
return Common::kNoError;
|
||||
}
|
||||
|
||||
static const struct {
|
||||
// Identification
|
||||
const char *md5; // File MD5
|
||||
uint length; // File length
|
||||
// Main section
|
||||
// Offset from the beginning of file to virtual address 0
|
||||
uint32 virtualBase;
|
||||
// Offset to index of string sections
|
||||
uint stringSectionIndexOffset;
|
||||
// Offset to the font
|
||||
uint fontOffset;
|
||||
// Next one isn't strictly necessary but makes logic simpler
|
||||
// by allowing to read string block into memory as whole
|
||||
// without any parsing. Just has to cover the block containing
|
||||
// the strings. Reading more (up to whole file) is OK.
|
||||
// End of strings block.
|
||||
uint stringBlockEnd;
|
||||
} chineseExes[] = {
|
||||
{
|
||||
// Identification
|
||||
"7bc128ba9bfaecb9bb4ef328b756057a", 575538,
|
||||
// Main
|
||||
0x5191, 0x6427e, 0x54afc,
|
||||
// Value to simplify code
|
||||
0x7eee1
|
||||
}
|
||||
};
|
||||
|
||||
bool SkyEngine::loadChineseTraditional() {
|
||||
Common::File skyExe;
|
||||
if (!skyExe.open("sky.exe"))
|
||||
return false;
|
||||
uint length = skyExe.size();
|
||||
Common::String md5 = Common::computeStreamMD5AsString(skyExe, length);
|
||||
|
||||
for (uint i = 0; i < ARRAYSIZE(chineseExes); i++) {
|
||||
if (md5 == chineseExes[i].md5 && length == chineseExes[i].length) {
|
||||
skyExe.seek(chineseExes[i].stringSectionIndexOffset);
|
||||
for (uint j = 0; j < 8; j++)
|
||||
_chineseTraditionalOffsets[j] = skyExe.readUint32LE() + chineseExes[i].virtualBase;
|
||||
uint32 stringBlockOffset = _chineseTraditionalOffsets[0];
|
||||
for (uint j = 1; j < 8; j++)
|
||||
stringBlockOffset = MIN(_chineseTraditionalOffsets[j], stringBlockOffset);
|
||||
for (uint j = 0; j < 8; j++)
|
||||
_chineseTraditionalOffsets[j] -= stringBlockOffset;
|
||||
uint stringBlockLen = chineseExes[i].stringBlockEnd - stringBlockOffset;
|
||||
_chineseTraditionalBlock = new char[stringBlockLen];
|
||||
skyExe.seek(stringBlockOffset);
|
||||
skyExe.read(_chineseTraditionalBlock, stringBlockLen);
|
||||
|
||||
skyExe.seek(chineseExes[i].fontOffset);
|
||||
_big5Font = new Graphics::Big5Font();
|
||||
_big5Font->loadPrefixedRaw(skyExe, 15);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
Common::Error SkyEngine::init() {
|
||||
initGraphics(320, 200);
|
||||
|
||||
_skyDisk = new Disk();
|
||||
_skySound = new Sound(_mixer, _skyDisk, Audio::Mixer::kMaxChannelVolume);
|
||||
|
||||
_systemVars->gameVersion = _skyDisk->determineGameVersion();
|
||||
|
||||
MidiDriver::DeviceHandle dev = MidiDriver::detectDevice(MDT_ADLIB | MDT_MIDI | MDT_PREFER_MT32);
|
||||
if (MidiDriver::getMusicType(dev) == MT_ADLIB) {
|
||||
_systemVars->systemFlags |= SF_SBLASTER;
|
||||
_skyMusic = new AdLibMusic(_mixer, _skyDisk);
|
||||
} else {
|
||||
_systemVars->systemFlags |= SF_ROLAND;
|
||||
if ((MidiDriver::getMusicType(dev) == MT_MT32) || ConfMan.getBool("native_mt32"))
|
||||
_skyMusic = new MT32Music(MidiDriver::createMidi(dev), _mixer, _skyDisk);
|
||||
else
|
||||
_skyMusic = new GmMusic(MidiDriver::createMidi(dev), _mixer, _skyDisk);
|
||||
}
|
||||
|
||||
if (isCDVersion()) {
|
||||
if (ConfMan.hasKey("nosubtitles")) {
|
||||
warning("Configuration key 'nosubtitles' is deprecated. Use 'subtitles' instead");
|
||||
if (!ConfMan.getBool("nosubtitles"))
|
||||
_systemVars->systemFlags |= SF_ALLOW_TEXT;
|
||||
}
|
||||
|
||||
if (ConfMan.getBool("subtitles"))
|
||||
_systemVars->systemFlags |= SF_ALLOW_TEXT;
|
||||
|
||||
if (!ConfMan.getBool("speech_mute"))
|
||||
_systemVars->systemFlags |= SF_ALLOW_SPEECH;
|
||||
|
||||
} else
|
||||
_systemVars->systemFlags |= SF_ALLOW_TEXT;
|
||||
|
||||
_systemVars->systemFlags |= SF_PLAY_VOCS;
|
||||
_systemVars->gameSpeed = 80;
|
||||
|
||||
_skyCompact = new SkyCompact();
|
||||
_skyText = new Text(this, _skyDisk, _skyCompact);
|
||||
_skyMouse = new Mouse(_system, _skyDisk, _skyCompact);
|
||||
_skyScreen = new Screen(_system, _skyDisk, _skyCompact);
|
||||
|
||||
initVirgin();
|
||||
initItemList();
|
||||
loadFixedItems();
|
||||
_skyLogic = new Logic(_skyCompact, _skyScreen, _skyDisk, _skyText, _skyMusic, _skyMouse, _skySound);
|
||||
_skyMouse->useLogicInstance(_skyLogic);
|
||||
|
||||
Common::Keymapper *keymapper = _system->getEventManager()->getKeymapper();
|
||||
Common::Keymap *shortcutsKeymap = keymapper->getKeymap(shortcutsKeymapId);
|
||||
assert(shortcutsKeymap);
|
||||
|
||||
_skyControl = new Control(this, _saveFileMan, _skyScreen, _skyDisk, _skyMouse, _skyText, _skyMusic, _skyLogic, _skySound, _skyCompact, _system, shortcutsKeymap);
|
||||
_skyLogic->useControlInstance(_skyControl);
|
||||
|
||||
switch (Common::parseLanguage(ConfMan.get("language"))) {
|
||||
case Common::EN_USA:
|
||||
_systemVars->language = SKY_USA;
|
||||
break;
|
||||
case Common::DE_DEU:
|
||||
_systemVars->language = SKY_GERMAN;
|
||||
break;
|
||||
case Common::FR_FRA:
|
||||
_systemVars->language = SKY_FRENCH;
|
||||
break;
|
||||
case Common::IT_ITA:
|
||||
_systemVars->language = SKY_ITALIAN;
|
||||
break;
|
||||
case Common::PT_BRA:
|
||||
_systemVars->language = SKY_PORTUGUESE;
|
||||
break;
|
||||
case Common::ES_ESP:
|
||||
_systemVars->language = SKY_SPANISH;
|
||||
break;
|
||||
case Common::SV_SWE:
|
||||
_systemVars->language = SKY_SWEDISH;
|
||||
break;
|
||||
case Common::EN_GRB:
|
||||
_systemVars->language = SKY_ENGLISH;
|
||||
break;
|
||||
case Common::ZH_TWN:
|
||||
_systemVars->language = SKY_CHINESE_TRADITIONAL;
|
||||
break;
|
||||
case Common::HE_ISR:
|
||||
_systemVars->textDirRTL = true;
|
||||
break;
|
||||
|
||||
default:
|
||||
_systemVars->language = SKY_ENGLISH;
|
||||
break;
|
||||
}
|
||||
|
||||
if (_systemVars->language == SKY_CHINESE_TRADITIONAL && !loadChineseTraditional()) {
|
||||
_systemVars->language = SKY_ENGLISH;
|
||||
}
|
||||
|
||||
if (_systemVars->language != SKY_CHINESE_TRADITIONAL &&
|
||||
!_skyDisk->fileExists(60600 + SkyEngine::_systemVars->language * 8)) {
|
||||
warning("The language you selected does not exist in your BASS version");
|
||||
if (_skyDisk->fileExists(60600))
|
||||
SkyEngine::_systemVars->language = SKY_ENGLISH; // default to GB english if it exists..
|
||||
else if (_skyDisk->fileExists(60600 + SKY_USA * 8))
|
||||
SkyEngine::_systemVars->language = SKY_USA; // try US english...
|
||||
else
|
||||
for (uint8 cnt = SKY_ENGLISH; cnt <= SKY_SPANISH; cnt++)
|
||||
if (_skyDisk->fileExists(60600 + cnt * 8)) { // pick the first language we can find
|
||||
SkyEngine::_systemVars->language = cnt;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Setup mixer: Default volume is set to 192 (ScummVM)
|
||||
// which is 96 (in-game) volume
|
||||
ConfMan.registerDefault("sfx_volume", 192);
|
||||
ConfMan.registerDefault("music_volume", 192);
|
||||
ConfMan.registerDefault("speech_volume", 192);
|
||||
ConfMan.registerDefault("mute", "false");
|
||||
syncSoundSettings();
|
||||
|
||||
_debugger = new Debugger(_skyLogic, _skyMouse, _skyScreen, _skyCompact);
|
||||
setDebugger(_debugger);
|
||||
return Common::kNoError;
|
||||
}
|
||||
|
||||
void SkyEngine::initItemList() {
|
||||
//See List.asm for (cryptic) item # descriptions
|
||||
|
||||
for (int i = 0; i < 300; i++)
|
||||
_itemList[i] = NULL;
|
||||
}
|
||||
|
||||
void SkyEngine::loadFixedItems() {
|
||||
_itemList[49] = _skyDisk->loadFile(49);
|
||||
_itemList[50] = _skyDisk->loadFile(50);
|
||||
_itemList[73] = _skyDisk->loadFile(73);
|
||||
_itemList[262] = _skyDisk->loadFile(262);
|
||||
|
||||
if (!isDemo()) {
|
||||
_itemList[36] = _skyDisk->loadFile(36);
|
||||
_itemList[263] = _skyDisk->loadFile(263);
|
||||
_itemList[264] = _skyDisk->loadFile(264);
|
||||
_itemList[265] = _skyDisk->loadFile(265);
|
||||
_itemList[266] = _skyDisk->loadFile(266);
|
||||
_itemList[267] = _skyDisk->loadFile(267);
|
||||
_itemList[269] = _skyDisk->loadFile(269);
|
||||
_itemList[271] = _skyDisk->loadFile(271);
|
||||
_itemList[272] = _skyDisk->loadFile(272);
|
||||
}
|
||||
}
|
||||
|
||||
void *SkyEngine::fetchItem(uint32 num) {
|
||||
return _itemList[num];
|
||||
}
|
||||
|
||||
void SkyEngine::delay(int32 amount) {
|
||||
Common::Event event;
|
||||
|
||||
uint32 start = _system->getMillis();
|
||||
_action = kSkyActionNone;
|
||||
_keyPressed.reset();
|
||||
|
||||
if (amount < 0)
|
||||
amount = 0;
|
||||
|
||||
do {
|
||||
while (_eventMan->pollEvent(event)) {
|
||||
switch (event.type) {
|
||||
case Common::EVENT_CUSTOM_ENGINE_ACTION_START:
|
||||
_action = (SkyAction)event.customType;
|
||||
break;
|
||||
case Common::EVENT_KEYDOWN:
|
||||
_keyPressed = event.kbd;
|
||||
break;
|
||||
case Common::EVENT_MOUSEMOVE:
|
||||
if (!(_systemVars->systemFlags & SF_MOUSE_LOCKED))
|
||||
_skyMouse->mouseMoved(event.mouse.x, event.mouse.y);
|
||||
break;
|
||||
case Common::EVENT_LBUTTONDOWN:
|
||||
if (!(_systemVars->systemFlags & SF_MOUSE_LOCKED))
|
||||
_skyMouse->mouseMoved(event.mouse.x, event.mouse.y);
|
||||
_skyMouse->buttonPressed(2);
|
||||
break;
|
||||
case Common::EVENT_RBUTTONDOWN:
|
||||
if (!(_systemVars->systemFlags & SF_MOUSE_LOCKED))
|
||||
_skyMouse->mouseMoved(event.mouse.x, event.mouse.y);
|
||||
_skyMouse->buttonPressed(1);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
_system->updateScreen();
|
||||
|
||||
if (amount > 0)
|
||||
_system->delayMillis((amount > 10) ? 10 : amount);
|
||||
|
||||
} while (_system->getMillis() < start + amount);
|
||||
}
|
||||
|
||||
bool SkyEngine::isDemo() {
|
||||
switch (_systemVars->gameVersion) {
|
||||
case 109: // PC Gamer demo
|
||||
case 267: // English floppy demo
|
||||
case 272: // German floppy demo
|
||||
case 365: // CD demo
|
||||
return true;
|
||||
case 288:
|
||||
case 303:
|
||||
case 331:
|
||||
case 348:
|
||||
case 368:
|
||||
case 372:
|
||||
return false;
|
||||
default:
|
||||
error("Unknown game version %d", _systemVars->gameVersion);
|
||||
}
|
||||
}
|
||||
|
||||
bool SkyEngine::isCDVersion() {
|
||||
switch (_systemVars->gameVersion) {
|
||||
case 109:
|
||||
case 267:
|
||||
case 272:
|
||||
case 288:
|
||||
case 303:
|
||||
case 331:
|
||||
case 348:
|
||||
return false;
|
||||
case 365:
|
||||
case 368:
|
||||
case 372:
|
||||
return true;
|
||||
default:
|
||||
error("Unknown game version %d", _systemVars->gameVersion);
|
||||
}
|
||||
}
|
||||
|
||||
} // End of namespace Sky
|
||||
147
engines/sky/sky.h
Normal file
147
engines/sky/sky.h
Normal file
@@ -0,0 +1,147 @@
|
||||
/* 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 SKY_SKY_H
|
||||
#define SKY_SKY_H
|
||||
|
||||
|
||||
#include "common/array.h"
|
||||
#include "common/error.h"
|
||||
#include "common/keyboard.h"
|
||||
#include "engines/engine.h"
|
||||
#include "graphics/big5.h"
|
||||
|
||||
/**
|
||||
* This is the namespace of the Sky engine.
|
||||
*
|
||||
* Status of this engine: ???
|
||||
*
|
||||
* Games using this engine:
|
||||
* - Beneath a Steel Sky
|
||||
*/
|
||||
namespace Sky {
|
||||
|
||||
struct SystemVars {
|
||||
uint32 systemFlags;
|
||||
uint32 gameVersion;
|
||||
uint32 mouseFlag;
|
||||
uint16 language;
|
||||
uint32 currentPalette;
|
||||
uint16 gameSpeed;
|
||||
uint16 currentMusic;
|
||||
bool pastIntro;
|
||||
bool paused;
|
||||
bool textDirRTL;
|
||||
};
|
||||
|
||||
class Sound;
|
||||
class Disk;
|
||||
class Text;
|
||||
class Logic;
|
||||
class Mouse;
|
||||
class Screen;
|
||||
class Control;
|
||||
class MusicBase;
|
||||
class Debugger;
|
||||
class SkyCompact;
|
||||
|
||||
enum SkyAction {
|
||||
kSkyActionNone,
|
||||
kSkyActionToggleFastMode,
|
||||
kSkyActionToggleReallyFastMode,
|
||||
kSkyActionOpenControlPanel,
|
||||
kSkyActionConfirm,
|
||||
kSkyActionSkip,
|
||||
kSkyActionSkipLine,
|
||||
kSkyActionPause
|
||||
};
|
||||
|
||||
class SkyEngine : public Engine {
|
||||
protected:
|
||||
SkyAction _action;
|
||||
Common::KeyState _keyPressed;
|
||||
|
||||
Sound *_skySound;
|
||||
Disk *_skyDisk;
|
||||
Text *_skyText;
|
||||
Logic *_skyLogic;
|
||||
Mouse *_skyMouse;
|
||||
Screen *_skyScreen;
|
||||
Control *_skyControl;
|
||||
SkyCompact *_skyCompact;
|
||||
Debugger *_debugger;
|
||||
|
||||
MusicBase *_skyMusic;
|
||||
|
||||
public:
|
||||
SkyEngine(OSystem *syst);
|
||||
~SkyEngine() override;
|
||||
|
||||
void syncSoundSettings() override;
|
||||
|
||||
static bool isDemo();
|
||||
static bool isCDVersion();
|
||||
|
||||
Common::Error loadGameState(int slot) override;
|
||||
Common::Error saveGameState(int slot, const Common::String &desc, bool isAutosave = false) override;
|
||||
bool canLoadGameStateCurrently(Common::U32String *msg = nullptr) override;
|
||||
bool canSaveGameStateCurrently(Common::U32String *msg = nullptr) override;
|
||||
Common::String getSaveStateName(int slot) const override {
|
||||
return Common::String::format("SKY-VM.%03d", slot);
|
||||
}
|
||||
|
||||
static void *fetchItem(uint32 num);
|
||||
static void *_itemList[300];
|
||||
static SystemVars *_systemVars;
|
||||
static const char *shortcutsKeymapId;
|
||||
uint32 _chineseTraditionalOffsets[8];
|
||||
char *_chineseTraditionalBlock;
|
||||
Graphics::Big5Font *_big5Font;
|
||||
|
||||
protected:
|
||||
// Engine APIs
|
||||
Common::Error init();
|
||||
bool loadChineseTraditional();
|
||||
Common::Error go();
|
||||
Common::Error run() override {
|
||||
Common::Error err;
|
||||
err = init();
|
||||
if (err.getCode() != Common::kNoError)
|
||||
return err;
|
||||
return go();
|
||||
}
|
||||
|
||||
bool hasFeature(EngineFeature f) const override;
|
||||
|
||||
byte _fastMode;
|
||||
|
||||
void delay(int32 amount);
|
||||
void handleKey();
|
||||
|
||||
void initItemList();
|
||||
|
||||
void initVirgin();
|
||||
void loadFixedItems();
|
||||
};
|
||||
|
||||
} // End of namespace Sky
|
||||
|
||||
#endif
|
||||
4317
engines/sky/skydefs.h
Normal file
4317
engines/sky/skydefs.h
Normal file
File diff suppressed because it is too large
Load Diff
1273
engines/sky/sound.cpp
Normal file
1273
engines/sky/sound.cpp
Normal file
File diff suppressed because it is too large
Load Diff
96
engines/sky/sound.h
Normal file
96
engines/sky/sound.h
Normal file
@@ -0,0 +1,96 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 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 SKY_SOUND_H
|
||||
#define SKY_SOUND_H
|
||||
|
||||
|
||||
#include "common/scummsys.h"
|
||||
#include "audio/mixer.h"
|
||||
|
||||
namespace Sky {
|
||||
|
||||
class Disk;
|
||||
|
||||
enum {
|
||||
SOUND_CH0 = 0,
|
||||
SOUND_CH1 = 1,
|
||||
SOUND_BG = 2,
|
||||
SOUND_VOICE = 3,
|
||||
SOUND_SPEECH = 4
|
||||
};
|
||||
|
||||
struct SfxQueue {
|
||||
uint8 count, fxNo, chan, vol;
|
||||
};
|
||||
|
||||
#define MAX_QUEUED_FX 4
|
||||
|
||||
class Sound {
|
||||
protected:
|
||||
|
||||
public:
|
||||
|
||||
Audio::Mixer *_mixer;
|
||||
Audio::SoundHandle _voiceHandle;
|
||||
Audio::SoundHandle _effectHandle;
|
||||
Audio::SoundHandle _bgSoundHandle;
|
||||
Audio::SoundHandle _ingameSound0, _ingameSound1, _ingameSpeech;
|
||||
|
||||
uint16 _saveSounds[2];
|
||||
|
||||
protected:
|
||||
|
||||
void playSound(uint32 id, byte *sound, uint32 size, Audio::SoundHandle *handle);
|
||||
|
||||
public:
|
||||
Sound(Audio::Mixer *mixer, Disk *pDisk, uint8 pVolume);
|
||||
~Sound();
|
||||
|
||||
void loadSection(uint8 pSection);
|
||||
void playSound(uint16 sound, uint16 volume, uint8 channel);
|
||||
void fnStartFx(uint32 sound, uint8 channel);
|
||||
bool startSpeech(uint16 textNum);
|
||||
bool speechFinished() { return !_mixer->isSoundHandleActive(_ingameSpeech); }
|
||||
void fnPauseFx();
|
||||
void fnUnPauseFx();
|
||||
void fnStopFx();
|
||||
void stopSpeech();
|
||||
void checkFxQueue();
|
||||
void restoreSfx();
|
||||
uint8 _soundsTotal;
|
||||
|
||||
private:
|
||||
Disk *_skyDisk;
|
||||
uint16 _sfxBaseOfs;
|
||||
uint8 *_soundData;
|
||||
uint8 *_sampleRates, *_sfxInfo;
|
||||
uint8 _mainSfxVolume;
|
||||
|
||||
bool _isPaused;
|
||||
|
||||
static uint16 _speechConvertTable[8];
|
||||
static SfxQueue _sfxQueue[MAX_QUEUED_FX];
|
||||
};
|
||||
|
||||
} // End of namespace Sky
|
||||
|
||||
#endif
|
||||
165
engines/sky/struc.h
Normal file
165
engines/sky/struc.h
Normal file
@@ -0,0 +1,165 @@
|
||||
/* 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 SKY_STRUC_H
|
||||
#define SKY_STRUC_H
|
||||
|
||||
namespace Sky {
|
||||
|
||||
struct DisplayedText {
|
||||
byte *textData;
|
||||
uint32 textWidth;
|
||||
uint16 compactNum;
|
||||
};
|
||||
|
||||
#include "common/pack-start.h" // START STRUCT PACKING
|
||||
|
||||
struct DataFileHeader {
|
||||
uint16 flag; // bit 0: set for color data, clear for not
|
||||
// bit 1: set for compressed, clear for uncompressed
|
||||
// bit 2: set for 32 colors, clear for 16 colors
|
||||
uint16 s_x;
|
||||
uint16 s_y;
|
||||
uint16 s_width;
|
||||
uint16 s_height;
|
||||
uint16 s_sp_size;
|
||||
uint16 s_tot_size;
|
||||
uint16 s_n_sprites;
|
||||
int16 s_offset_x;
|
||||
int16 s_offset_y;
|
||||
uint16 s_compressed_size;
|
||||
} PACKED_STRUCT;
|
||||
|
||||
struct TurnTable {
|
||||
uint16 turnTableUp[5];
|
||||
uint16 turnTableDown[5];
|
||||
uint16 turnTableLeft[5];
|
||||
uint16 turnTableRight[5];
|
||||
uint16 turnTableTalk[5];
|
||||
} PACKED_STRUCT;
|
||||
|
||||
struct MegaSet {
|
||||
uint16 gridWidth; // 0
|
||||
uint16 colOffset; // 1
|
||||
uint16 colWidth; // 2
|
||||
uint16 lastChr; // 3
|
||||
|
||||
uint16 animUpId; // 4
|
||||
uint16 animDownId; // 5
|
||||
uint16 animLeftId; // 6
|
||||
uint16 animRightId; // 7
|
||||
|
||||
uint16 standUpId; // 8
|
||||
uint16 standDownId; // 9
|
||||
uint16 standLeftId; // 10
|
||||
uint16 standRightId; // 11
|
||||
uint16 standTalkId; // 12
|
||||
uint16 turnTableId; // 13
|
||||
} PACKED_STRUCT;
|
||||
|
||||
struct Compact {
|
||||
uint16 logic; // 0: Entry in logic table to run (byte as <256entries in logic table
|
||||
uint16 status; // 1
|
||||
uint16 sync; // 2: flag sent to compacts by other things
|
||||
|
||||
uint16 screen; // 3: current screen
|
||||
uint16 place; // 4: so's this one
|
||||
uint16 getToTableId; // 5: Address of how to get to things table
|
||||
|
||||
uint16 xcood; // 6
|
||||
uint16 ycood; // 7
|
||||
|
||||
uint16 frame; // 8
|
||||
|
||||
uint16 cursorText; // 9
|
||||
uint16 mouseOn; // 10
|
||||
uint16 mouseOff; // 11
|
||||
uint16 mouseClick; // 12
|
||||
|
||||
int16 mouseRelX; // 13
|
||||
int16 mouseRelY; // 14
|
||||
uint16 mouseSizeX; // 15
|
||||
uint16 mouseSizeY; // 16
|
||||
|
||||
uint16 actionScript; // 17
|
||||
|
||||
uint16 upFlag; // 18: usually holds the Action Mode
|
||||
uint16 downFlag; // 19: used for passing back
|
||||
uint16 getToFlag; // 20: used by action script for get to attempts, also frame store (hence word)
|
||||
uint16 flag; // 21: a use any time flag
|
||||
|
||||
uint16 mood; // 22: high level - stood or not
|
||||
|
||||
uint16 grafixProgId; // 23
|
||||
uint16 grafixProgPos;// 24
|
||||
|
||||
uint16 offset; // 25
|
||||
|
||||
uint16 mode; // 26: which mcode block
|
||||
|
||||
uint16 baseSub; // 27: 1st mcode block relative to start of compact
|
||||
uint16 baseSub_off; // 28
|
||||
uint16 actionSub; // 29
|
||||
uint16 actionSub_off;// 30
|
||||
uint16 getToSub; // 31
|
||||
uint16 getToSub_off; // 32
|
||||
uint16 extraSub; // 33
|
||||
uint16 extraSub_off; // 34
|
||||
|
||||
uint16 dir; // 35
|
||||
|
||||
uint16 stopScript; // 36
|
||||
uint16 miniBump; // 37
|
||||
uint16 leaving; // 38
|
||||
uint16 atWatch; // 39: pointer to script variable
|
||||
uint16 atWas; // 40: pointer to script variable
|
||||
uint16 alt; // 41: alternate script
|
||||
uint16 request; // 42
|
||||
|
||||
uint16 spWidth_xx; // 43
|
||||
uint16 spColor; // 44
|
||||
uint16 spTextId; // 45
|
||||
uint16 spTime; // 46
|
||||
|
||||
uint16 arAnimIndex; // 47
|
||||
uint16 turnProgId; // 48
|
||||
uint16 turnProgPos; // 49
|
||||
|
||||
uint16 waitingFor; // 50
|
||||
|
||||
uint16 arTargetX; // 51
|
||||
uint16 arTargetY; // 52
|
||||
|
||||
uint16 animScratchId;// 53: data area for AR
|
||||
|
||||
uint16 megaSet; // 54
|
||||
|
||||
MegaSet megaSet0; // 55
|
||||
MegaSet megaSet1; //
|
||||
MegaSet megaSet2; //
|
||||
MegaSet megaSet3; //
|
||||
} PACKED_STRUCT;
|
||||
|
||||
#include "common/pack-end.h" // END STRUCT PACKING
|
||||
|
||||
} // End of namespace Sky
|
||||
|
||||
#endif
|
||||
572
engines/sky/text.cpp
Normal file
572
engines/sky/text.cpp
Normal file
@@ -0,0 +1,572 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#include "common/debug.h"
|
||||
#include "common/endian.h"
|
||||
#include "common/textconsole.h"
|
||||
|
||||
#include "sky/disk.h"
|
||||
#include "sky/logic.h"
|
||||
#include "sky/text.h"
|
||||
#include "sky/sky.h"
|
||||
#include "sky/skydefs.h"
|
||||
#include "sky/struc.h"
|
||||
#include "sky/compact.h"
|
||||
|
||||
namespace Sky {
|
||||
|
||||
#define FIRST_TEXT_SEC 77
|
||||
#define FIRST_TEXT_BUFFER 274
|
||||
#define LAST_TEXT_BUFFER 284
|
||||
#define NO_OF_TEXT_SECTIONS 8 // 8 sections per language
|
||||
#define CHAR_SET_FILE 60150
|
||||
#define MAX_SPEECH_SECTION 7
|
||||
#define CHAR_SET_HEADER 128
|
||||
#define MAX_NO_LINES 10
|
||||
|
||||
Text::Text(SkyEngine *vm, Disk *skyDisk, SkyCompact *skyCompact) : _skyDisk(skyDisk), _skyCompact(skyCompact), _vm(vm) {
|
||||
initHuffTree();
|
||||
|
||||
_mainCharacterSet.addr = _skyDisk->loadFile(CHAR_SET_FILE);
|
||||
_mainCharacterSet.charHeight = MAIN_CHAR_HEIGHT;
|
||||
_mainCharacterSet.charSpacing = 0;
|
||||
|
||||
fnSetFont(0);
|
||||
|
||||
if (!SkyEngine::isDemo()) {
|
||||
_controlCharacterSet.addr = _skyDisk->loadFile(60520);
|
||||
_controlCharacterSet.charHeight = 12;
|
||||
_controlCharacterSet.charSpacing = 0;
|
||||
|
||||
_linkCharacterSet.addr = _skyDisk->loadFile(60521);
|
||||
_linkCharacterSet.charHeight = 12;
|
||||
_linkCharacterSet.charSpacing = 1;
|
||||
} else {
|
||||
_controlCharacterSet.addr = NULL;
|
||||
_linkCharacterSet.addr = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
Text::~Text() {
|
||||
for (int i = FIRST_TEXT_BUFFER; i <= LAST_TEXT_BUFFER; i++)
|
||||
if (SkyEngine::_itemList[i]) {
|
||||
free(SkyEngine::_itemList[i]);
|
||||
SkyEngine::_itemList[i] = NULL;
|
||||
}
|
||||
|
||||
free(_mainCharacterSet.addr);
|
||||
free(_controlCharacterSet.addr);
|
||||
free(_linkCharacterSet.addr);
|
||||
}
|
||||
|
||||
void Text::fnSetFont(uint32 fontNr) {
|
||||
charSet *newCharSet;
|
||||
|
||||
switch (fontNr) {
|
||||
case 0:
|
||||
newCharSet = &_mainCharacterSet;
|
||||
break;
|
||||
case 1:
|
||||
newCharSet = &_linkCharacterSet;
|
||||
break;
|
||||
case 2:
|
||||
newCharSet = &_controlCharacterSet;
|
||||
break;
|
||||
default:
|
||||
error("Tried to set invalid font (%d)", fontNr);
|
||||
}
|
||||
|
||||
_curCharSet = fontNr;
|
||||
_characterSet = newCharSet->addr;
|
||||
_charHeight = (byte)newCharSet->charHeight;
|
||||
_dtCharSpacing = newCharSet->charSpacing;
|
||||
}
|
||||
|
||||
void Text::fnTextModule(uint32 textInfoId, uint32 textNo) {
|
||||
fnSetFont(1);
|
||||
uint16* msgData = (uint16 *)_skyCompact->fetchCpt(textInfoId);
|
||||
DisplayedText textId = lowTextManager(textNo, msgData[1], msgData[2], 209, Graphics::kTextAlignStart);
|
||||
Logic::_scriptVariables[RESULT] = textId.compactNum;
|
||||
Compact *textCompact = _skyCompact->fetchCpt(textId.compactNum);
|
||||
textCompact->xcood = msgData[3];
|
||||
textCompact->ycood = msgData[4];
|
||||
fnSetFont(0);
|
||||
}
|
||||
|
||||
void Text::getText(uint32 textNr) { //load text #"textNr" into textBuffer
|
||||
if (patchMessage(textNr))
|
||||
return;
|
||||
|
||||
uint32 sectionNo = (textNr & 0x0F000) >> 12;
|
||||
|
||||
if (SkyEngine::_systemVars->language == SKY_CHINESE_TRADITIONAL) {
|
||||
uint32 sectionOffset = _vm->_chineseTraditionalOffsets[sectionNo];
|
||||
const char *ptr = _vm->_chineseTraditionalBlock + sectionOffset;
|
||||
uint nrInBlock = textNr & 0xFFF;
|
||||
if (sectionNo != 7)
|
||||
nrInBlock--;
|
||||
for (uint32 i = 0; i < nrInBlock; i++) {
|
||||
while (*ptr)
|
||||
ptr++;
|
||||
ptr++;
|
||||
}
|
||||
char *dest = (char *)_textBuffer;
|
||||
while (*ptr)
|
||||
*dest++ = *ptr++;
|
||||
*dest = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
if (SkyEngine::_itemList[FIRST_TEXT_SEC + sectionNo] == NULL) { //check if already loaded
|
||||
debug(5, "Loading Text item(s) for Section %d", (sectionNo >> 2));
|
||||
|
||||
uint32 fileNo = sectionNo + ((SkyEngine::_systemVars->language * NO_OF_TEXT_SECTIONS) + 60600);
|
||||
SkyEngine::_itemList[FIRST_TEXT_SEC + sectionNo] = (void **)_skyDisk->loadFile((uint16)fileNo);
|
||||
}
|
||||
uint8 *textDataPtr = (uint8 *)SkyEngine::_itemList[FIRST_TEXT_SEC + sectionNo];
|
||||
|
||||
uint32 offset = 0;
|
||||
|
||||
uint32 blockNr = textNr & 0xFE0;
|
||||
textNr &= 0x1F;
|
||||
|
||||
if (blockNr) {
|
||||
uint16 *blockPtr = (uint16 *)(textDataPtr + 4);
|
||||
uint32 nr32MsgBlocks = blockNr >> 5;
|
||||
|
||||
do {
|
||||
offset += READ_LE_UINT16(blockPtr);
|
||||
blockPtr++;
|
||||
} while (--nr32MsgBlocks);
|
||||
}
|
||||
|
||||
if (textNr) {
|
||||
uint8 *blockPtr = textDataPtr + blockNr + READ_LE_UINT16(textDataPtr);
|
||||
do {
|
||||
uint16 skipBytes = *blockPtr++;
|
||||
if (skipBytes & 0x80) {
|
||||
skipBytes &= 0x7F;
|
||||
skipBytes <<= 3;
|
||||
}
|
||||
offset += skipBytes;
|
||||
} while (--textNr);
|
||||
}
|
||||
|
||||
uint32 bitPos = offset & 3;
|
||||
offset >>= 2;
|
||||
offset += READ_LE_UINT16(textDataPtr + 2);
|
||||
|
||||
textDataPtr += offset;
|
||||
|
||||
//bit pointer: 0->8, 1->6, 2->4 ...
|
||||
bitPos ^= 3;
|
||||
bitPos++;
|
||||
bitPos <<= 1;
|
||||
|
||||
char *dest = (char *)_textBuffer;
|
||||
char textChar;
|
||||
|
||||
do {
|
||||
textChar = getTextChar(&textDataPtr, &bitPos);
|
||||
*dest++ = textChar;
|
||||
} while (textChar);
|
||||
}
|
||||
|
||||
void Text::fnPointerText(uint32 pointedId, uint16 mouseX, uint16 mouseY) {
|
||||
Compact *ptrComp = _skyCompact->fetchCpt(pointedId);
|
||||
DisplayedText text = lowTextManager(ptrComp->cursorText, TEXT_MOUSE_WIDTH, L_CURSOR, 242, Graphics::kTextAlignLeft);
|
||||
Logic::_scriptVariables[CURSOR_ID] = text.compactNum;
|
||||
if (Logic::_scriptVariables[MENU]) {
|
||||
_mouseOfsY = TOP_LEFT_Y - 2;
|
||||
if (mouseX < 150)
|
||||
_mouseOfsX = TOP_LEFT_X + 24;
|
||||
else
|
||||
_mouseOfsX = TOP_LEFT_X - 8 - text.textWidth;
|
||||
} else {
|
||||
_mouseOfsY = TOP_LEFT_Y - 10;
|
||||
if (mouseX < 150)
|
||||
_mouseOfsX = TOP_LEFT_X + 13;
|
||||
else
|
||||
_mouseOfsX = TOP_LEFT_X - 8 - text.textWidth;
|
||||
}
|
||||
Compact *textCompact = _skyCompact->fetchCpt(text.compactNum);
|
||||
logicCursor(textCompact, mouseX, mouseY);
|
||||
}
|
||||
|
||||
void Text::logicCursor(Compact *textCompact, uint16 mouseX, uint16 mouseY) {
|
||||
textCompact->xcood = (uint16)(mouseX + _mouseOfsX);
|
||||
textCompact->ycood = (uint16)(mouseY + _mouseOfsY);
|
||||
if (textCompact->ycood < TOP_LEFT_Y)
|
||||
textCompact->ycood = TOP_LEFT_Y;
|
||||
}
|
||||
|
||||
bool Text::getTextBit(uint8 **data, uint32 *bitPos) {
|
||||
if (*bitPos) {
|
||||
(*bitPos)--;
|
||||
} else {
|
||||
(*data)++;
|
||||
*bitPos = 7;
|
||||
}
|
||||
|
||||
return (bool)(((**data) >> (*bitPos)) & 1);
|
||||
}
|
||||
|
||||
char Text::getTextChar(uint8 **data, uint32 *bitPos) {
|
||||
int pos = 0;
|
||||
while (1) {
|
||||
if (getTextBit(data, bitPos))
|
||||
pos = _huffTree[pos].rChild;
|
||||
else
|
||||
pos = _huffTree[pos].lChild;
|
||||
|
||||
if (_huffTree[pos].lChild == 0 && _huffTree[pos].rChild == 0) {
|
||||
return _huffTree[pos].value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DisplayedText Text::displayText(uint32 textNum, uint8 *dest, Graphics::TextAlign align, uint16 pixelWidth, uint8 color) {
|
||||
//Render text into buffer *dest
|
||||
getText(textNum);
|
||||
return displayText(_textBuffer, sizeof(_textBuffer), dest, align, pixelWidth, color);
|
||||
}
|
||||
|
||||
// TODO: Don't use caller-supplied buffer for editing operations
|
||||
DisplayedText Text::displayText(char *textPtr, uint32 bufLen, uint8 *dest, Graphics::TextAlign align, uint16 pixelWidth, uint8 color) {
|
||||
//Render text pointed to by *textPtr in buffer *dest
|
||||
uint32 centerTable[10];
|
||||
uint16 lineWidth = 0;
|
||||
|
||||
uint32 numLines = 0;
|
||||
_numLetters = 2;
|
||||
|
||||
// work around bug #1080 (line width exceeded)
|
||||
char *tmpPtr = strstr(textPtr, "MUND-BEATMUNG!");
|
||||
if (tmpPtr)
|
||||
// We are sure there is at least this space and we replace it by something of same length
|
||||
Common::strcpy_s(tmpPtr, sizeof("MUND-BEATMUNG!"), "MUND BEATMUNG!");
|
||||
|
||||
// work around bug #1940 (line width exceeded when talking to gardener using spanish text)
|
||||
// This text apparently only is broken in the floppy versions, the CD versions contain
|
||||
// the correct string "MANIFESTACION - ARTISTICA.", which doesn't break the algorithm/game.
|
||||
tmpPtr = strstr(textPtr, "MANIFESTACION-ARTISTICA.");
|
||||
if (tmpPtr)
|
||||
// We are sure there is at least this space and we replace it by something of same length
|
||||
Common::strcpy_s(tmpPtr, sizeof("MANIFESTACION-ARTISTICA."), "MANIFESTACION ARTISTICA.");
|
||||
|
||||
char *curPos = textPtr;
|
||||
char *lastSpace = textPtr;
|
||||
uint8 textChar = (uint8)*curPos++;
|
||||
bool isBig5 = SkyEngine::_systemVars->language == SKY_CHINESE_TRADITIONAL;
|
||||
|
||||
while (textChar >= 0x20) {
|
||||
bool isDoubleChar = false;
|
||||
int oldLineWidth = lineWidth;
|
||||
if (isBig5 && (textChar & 0x80)) {
|
||||
isDoubleChar = true;
|
||||
curPos++;
|
||||
lineWidth += Graphics::Big5Font::kChineseTraditionalWidth;
|
||||
} else {
|
||||
if ((_curCharSet == 1) && (textChar >= 0x80) && !SkyEngine::_systemVars->textDirRTL)
|
||||
textChar = 0x20;
|
||||
|
||||
textChar -= 0x20;
|
||||
if (textChar == 0) {
|
||||
lastSpace = curPos; //keep track of last space
|
||||
centerTable[numLines] = lineWidth;
|
||||
}
|
||||
|
||||
lineWidth += _characterSet[textChar]; //add character width
|
||||
lineWidth += (uint16)_dtCharSpacing; //include character spacing
|
||||
}
|
||||
|
||||
if (pixelWidth <= lineWidth) {
|
||||
// If no space is found just break here. This is common in e.g. Chinese
|
||||
// that doesn't use spaces.
|
||||
if (lastSpace == textPtr || *(lastSpace-1) == 10) {
|
||||
curPos -= isDoubleChar ? 2 : 1;
|
||||
if (curPos < textPtr)
|
||||
curPos = textPtr;
|
||||
if (strlen(textPtr) + 2 >= bufLen)
|
||||
error("Ran out of buffer size when word-wrapping");
|
||||
// Add a place for linebreak
|
||||
memmove(curPos + 1, curPos, textPtr + bufLen - curPos - 2);
|
||||
textPtr[bufLen - 1] = 0;
|
||||
lastSpace = curPos + 1;
|
||||
centerTable[numLines] = oldLineWidth;
|
||||
}
|
||||
|
||||
*(lastSpace-1) = 10;
|
||||
lineWidth = 0;
|
||||
numLines++;
|
||||
curPos = lastSpace; //go back for new count
|
||||
}
|
||||
|
||||
textChar = (uint8)*curPos++;
|
||||
_numLetters++;
|
||||
}
|
||||
|
||||
uint32 dtLastWidth = lineWidth; //save width of last line
|
||||
centerTable[numLines] = lineWidth; //and update centering table
|
||||
numLines++;
|
||||
|
||||
if (numLines > MAX_NO_LINES)
|
||||
error("Maximum no. of lines exceeded");
|
||||
|
||||
int charHeight = isBig5 && _vm->_big5Font ? MAX<int>(_charHeight, _vm->_big5Font->getFontHeight()) : _charHeight;
|
||||
uint32 dtLineSize = pixelWidth * charHeight;
|
||||
uint32 numBytes = (dtLineSize * numLines) + sizeof(DataFileHeader) + 4;
|
||||
|
||||
if (!dest)
|
||||
dest = (uint8 *)malloc(numBytes);
|
||||
|
||||
// clear text sprite buffer
|
||||
memset(dest + sizeof(DataFileHeader), 0, numBytes - sizeof(DataFileHeader));
|
||||
|
||||
//make the header
|
||||
((DataFileHeader *)dest)->s_width = pixelWidth;
|
||||
((DataFileHeader *)dest)->s_height = (uint16)(charHeight * numLines);
|
||||
((DataFileHeader *)dest)->s_sp_size = (uint16)(pixelWidth * charHeight * numLines);
|
||||
((DataFileHeader *)dest)->s_offset_x = 0;
|
||||
((DataFileHeader *)dest)->s_offset_y = 0;
|
||||
|
||||
//reset position
|
||||
curPos = textPtr;
|
||||
|
||||
uint8 *curDest = dest + sizeof(DataFileHeader); //point to where pixels start
|
||||
byte *prevDest = curDest;
|
||||
uint32 *centerTblPtr = centerTable;
|
||||
|
||||
align = Graphics::convertTextAlignH(align, _vm->_systemVars->textDirRTL);
|
||||
|
||||
do {
|
||||
Common::String line("");
|
||||
|
||||
byte *lineEnd = curDest + pixelWidth;
|
||||
if (align == Graphics::kTextAlignCenter) {
|
||||
uint32 width = (pixelWidth - *centerTblPtr) >> 1;
|
||||
centerTblPtr++;
|
||||
curDest += width;
|
||||
} else if (align == Graphics::kTextAlignRight) {
|
||||
curDest += pixelWidth - *centerTblPtr - 1;
|
||||
centerTblPtr++;
|
||||
}
|
||||
|
||||
textChar = (uint8)*curPos++;
|
||||
while (textChar >= 0x20) {
|
||||
if (isBig5 && (textChar & 0x80)) {
|
||||
uint8 trail = *curPos++;
|
||||
uint16 fullCh = (textChar << 8) | trail;
|
||||
if (_vm->_big5Font->drawBig5Char(curDest, fullCh, lineEnd - curDest, charHeight, pixelWidth, color, 240)) {
|
||||
//update position
|
||||
curDest += Graphics::Big5Font::kChineseTraditionalWidth;
|
||||
textChar = *curPos++;
|
||||
continue;
|
||||
}
|
||||
|
||||
textChar = '?';
|
||||
}
|
||||
|
||||
line += textChar - 0x20;
|
||||
textChar = *curPos++;
|
||||
}
|
||||
|
||||
if (_vm->_systemVars->textDirRTL) {
|
||||
for (int i = line.size() - 1; i >= 0; --i) {
|
||||
makeGameCharacter(line[i], _characterSet, curDest, color, pixelWidth);
|
||||
}
|
||||
} else {
|
||||
for (auto &c : line) {
|
||||
makeGameCharacter(c, _characterSet, curDest, color, pixelWidth);
|
||||
}
|
||||
}
|
||||
|
||||
prevDest = curDest = prevDest + dtLineSize; //start of last line + start of next
|
||||
|
||||
} while (textChar >= 10);
|
||||
|
||||
DisplayedText ret;
|
||||
memset(&ret, 0, sizeof(ret));
|
||||
ret.textData = dest;
|
||||
ret.textWidth = dtLastWidth;
|
||||
return ret;
|
||||
}
|
||||
|
||||
void Text::makeGameCharacter(uint8 textChar, uint8 *charSetPtr, uint8 *&dest, uint8 color, uint16 bufPitch) {
|
||||
bool maskBit, dataBit;
|
||||
uint8 charWidth = (uint8)((*(charSetPtr + textChar)) + 1 - _dtCharSpacing);
|
||||
uint16 data, mask;
|
||||
byte *charSpritePtr = charSetPtr + (CHAR_SET_HEADER + ((_charHeight << 2) * textChar));
|
||||
byte *startPos = dest;
|
||||
byte *curPos = startPos;
|
||||
|
||||
for (int i = 0; i < _charHeight; i++) {
|
||||
byte *prevPos = curPos;
|
||||
|
||||
data = READ_BE_UINT16(charSpritePtr);
|
||||
mask = READ_BE_UINT16(charSpritePtr + 2);
|
||||
charSpritePtr += 4;
|
||||
|
||||
for (int j = 0; j < charWidth; j++) {
|
||||
|
||||
maskBit = (mask & 0x8000) != 0; //check mask
|
||||
mask <<= 1;
|
||||
dataBit = (data & 0x8000) != 0; //check data
|
||||
data <<= 1;
|
||||
|
||||
if (maskBit) {
|
||||
if (dataBit)
|
||||
*curPos = color;
|
||||
else
|
||||
*curPos = 240; //black edge
|
||||
}
|
||||
curPos++;
|
||||
}
|
||||
//advance a line
|
||||
curPos = prevPos + bufPitch;
|
||||
}
|
||||
//update position
|
||||
dest = startPos + charWidth + _dtCharSpacing * 2 - 1;
|
||||
}
|
||||
|
||||
DisplayedText Text::lowTextManager(uint32 textNum, uint16 width, uint16 logicNum, uint8 color, Graphics::TextAlign align) {
|
||||
getText(textNum);
|
||||
DisplayedText textInfo = displayText(_textBuffer, sizeof(_textBuffer), NULL, align, width, color);
|
||||
|
||||
uint32 compactNum = FIRST_TEXT_COMPACT;
|
||||
Compact *cpt = _skyCompact->fetchCpt(compactNum);
|
||||
while (cpt->status != 0) {
|
||||
compactNum++;
|
||||
cpt = _skyCompact->fetchCpt(compactNum);
|
||||
}
|
||||
|
||||
cpt->flag = (uint16)(compactNum - FIRST_TEXT_COMPACT) + FIRST_TEXT_BUFFER;
|
||||
|
||||
if (SkyEngine::_itemList[cpt->flag])
|
||||
free(SkyEngine::_itemList[cpt->flag]);
|
||||
|
||||
SkyEngine::_itemList[cpt->flag] = textInfo.textData;
|
||||
|
||||
cpt->logic = logicNum;
|
||||
cpt->status = ST_LOGIC | ST_FOREGROUND | ST_RECREATE;
|
||||
cpt->screen = (uint16) Logic::_scriptVariables[SCREEN];
|
||||
|
||||
textInfo.compactNum = (uint16)compactNum;
|
||||
return textInfo;
|
||||
}
|
||||
|
||||
void Text::changeTextSpriteColor(uint8 *sprData, uint8 newCol) {
|
||||
DataFileHeader *header = (DataFileHeader *)sprData;
|
||||
sprData += sizeof(DataFileHeader);
|
||||
for (uint16 cnt = 0; cnt < header->s_sp_size; cnt++)
|
||||
if (sprData[cnt] >= 241)
|
||||
sprData[cnt] = newCol;
|
||||
}
|
||||
|
||||
uint32 Text::giveCurrentCharSet() {
|
||||
return _curCharSet;
|
||||
}
|
||||
|
||||
void Text::initHuffTree() {
|
||||
switch (SkyEngine::_systemVars->gameVersion) {
|
||||
case 109:
|
||||
_huffTree = _huffTree_00109;
|
||||
break;
|
||||
case 272: // FIXME: Extract data
|
||||
case 267:
|
||||
_huffTree = _huffTree_00267;
|
||||
break;
|
||||
case 288:
|
||||
_huffTree = _huffTree_00288;
|
||||
break;
|
||||
case 303:
|
||||
_huffTree = _huffTree_00303;
|
||||
break;
|
||||
case 331:
|
||||
_huffTree = _huffTree_00331;
|
||||
break;
|
||||
case 348:
|
||||
_huffTree = _huffTree_00348;
|
||||
break;
|
||||
case 365:
|
||||
_huffTree = _huffTree_00365;
|
||||
break;
|
||||
case 368:
|
||||
_huffTree = _huffTree_00368;
|
||||
break;
|
||||
case 372:
|
||||
_huffTree = _huffTree_00372;
|
||||
break;
|
||||
default:
|
||||
error("Unknown game version %d", SkyEngine::_systemVars->gameVersion);
|
||||
}
|
||||
}
|
||||
|
||||
bool Text::patchMessage(uint32 textNum) {
|
||||
if (SkyEngine::_systemVars->language == SKY_CHINESE_TRADITIONAL)
|
||||
return false;
|
||||
uint16 patchIdx = _patchLangIdx[SkyEngine::_systemVars->language];
|
||||
uint16 patchNum = _patchLangNum[SkyEngine::_systemVars->language];
|
||||
for (uint16 cnt = 0; cnt < patchNum; cnt++) {
|
||||
if (_patchedMessages[cnt + patchIdx].textNr == textNum) {
|
||||
Common::strcpy_s(_textBuffer, _patchedMessages[cnt + patchIdx].text);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
const PatchMessage Text::_patchedMessages[NUM_PATCH_MSG] = {
|
||||
{ 28724, "Testo e Parlato" }, // - italian
|
||||
{ 28707, "Solo Testo" },
|
||||
{ 28693, "Solo Parlato" },
|
||||
{ 28724, "Text och tal" }, // - swedish
|
||||
{ 28707, "Endast text" },
|
||||
{ 28693, "Endast tal" },
|
||||
{ 28686, "Musikvolym" },
|
||||
{ 4336, "Wir befinden uns EINHUNDERTZWANZIG METER #ber dem ERBODEN!" }, // - german
|
||||
{ 28686, "Volume de musique" }, // - french
|
||||
};
|
||||
|
||||
const uint16 Text::_patchLangIdx[8] = {
|
||||
0xFFFF, // SKY_ENGLISH
|
||||
7, // SKY_GERMAN
|
||||
8, // SKY_FRENCH
|
||||
0xFFFF, // SKY_USA
|
||||
3, // SKY_SWEDISH
|
||||
0, // SKY_ITALIAN
|
||||
0xFFFF, // SKY_PORTUGUESE
|
||||
0xFFFF // SKY_SPANISH
|
||||
};
|
||||
|
||||
const uint16 Text::_patchLangNum[8] = {
|
||||
0, // SKY_ENGLISH
|
||||
1, // SKY_GERMAN
|
||||
1, // SKY_FRENCH
|
||||
0, // SKY_USA
|
||||
4, // SKY_SWEDISH
|
||||
3, // SKY_ITALIAN
|
||||
0, // SKY_PORTUGUESE
|
||||
0 // SKY_SPANISH
|
||||
};
|
||||
|
||||
} // End of namespace Sky
|
||||
112
engines/sky/text.h
Normal file
112
engines/sky/text.h
Normal 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef SKY_TEXT_H
|
||||
#define SKY_TEXT_H
|
||||
|
||||
|
||||
#include "common/scummsys.h"
|
||||
#include "graphics/font.h"
|
||||
#include "sky/sky.h"
|
||||
|
||||
namespace Sky {
|
||||
|
||||
struct Compact;
|
||||
class Disk;
|
||||
class SkyCompact;
|
||||
|
||||
struct HuffTree {
|
||||
unsigned char lChild;
|
||||
unsigned char rChild;
|
||||
unsigned char value;
|
||||
};
|
||||
|
||||
#define NUM_PATCH_MSG 9
|
||||
|
||||
struct PatchMessage {
|
||||
uint32 textNr;
|
||||
char text[100];
|
||||
};
|
||||
|
||||
class Text {
|
||||
public:
|
||||
Text(SkyEngine *vm, Disk *skyDisk, SkyCompact *skyCompact);
|
||||
~Text();
|
||||
struct DisplayedText displayText(uint32 textNum, uint8 *dest, Graphics::TextAlign align, uint16 pixelWidth, uint8 color);
|
||||
struct DisplayedText displayText(char *textPtr, uint32 bufLen, uint8 *dest, Graphics::TextAlign align, uint16 pixelWidth, uint8 color);
|
||||
struct DisplayedText lowTextManager(uint32 textNum, uint16 width, uint16 logicNum, uint8 color, Graphics::TextAlign align);
|
||||
void fnSetFont(uint32 fontNr);
|
||||
void fnTextModule(uint32 textInfoId, uint32 textNo);
|
||||
void fnPointerText(uint32 pointedId, uint16 mouseX, uint16 mouseY);
|
||||
void logicCursor(Compact *textCompact, uint16 mouseX, uint16 mouseY);
|
||||
void changeTextSpriteColor(uint8 *sprData, uint8 newCol);
|
||||
uint32 giveCurrentCharSet();
|
||||
|
||||
uint32 _numLetters; //no of chars in message
|
||||
|
||||
private:
|
||||
void initHuffTree();
|
||||
void getText(uint32 textNr);
|
||||
char getTextChar(uint8 **data, uint32 *bitPos);
|
||||
bool getTextBit(uint8 **data, uint32 *bitPos);
|
||||
void makeGameCharacter(uint8 textChar, uint8 *charSetPtr, uint8 *&data, uint8 color, uint16 bufPitch);
|
||||
void makeChineseGameCharacter(uint16 textChar, uint8 *charSetPtr, uint8 *&dest, uint8 color, uint16 bufPitch);
|
||||
|
||||
bool patchMessage(uint32 textNum);
|
||||
|
||||
Disk *_skyDisk;
|
||||
SkyCompact *_skyCompact;
|
||||
SkyEngine *_vm;
|
||||
|
||||
const HuffTree *_huffTree;
|
||||
|
||||
struct charSet {
|
||||
uint8 *addr;
|
||||
uint32 charHeight;
|
||||
uint32 charSpacing;
|
||||
} _mainCharacterSet, _linkCharacterSet, _controlCharacterSet;
|
||||
|
||||
uint32 _curCharSet;
|
||||
uint8 *_characterSet;
|
||||
uint8 _charHeight;
|
||||
|
||||
char _textBuffer[1024];
|
||||
|
||||
uint32 _dtCharSpacing; //character separation adjustment
|
||||
uint32 _mouseOfsX, _mouseOfsY;
|
||||
static const PatchMessage _patchedMessages[NUM_PATCH_MSG];
|
||||
static const uint16 _patchLangIdx[8];
|
||||
static const uint16 _patchLangNum[8];
|
||||
|
||||
static const HuffTree _huffTree_00109[]; // trees moved to hufftext.cpp
|
||||
static const HuffTree _huffTree_00267[];
|
||||
static const HuffTree _huffTree_00288[];
|
||||
static const HuffTree _huffTree_00303[];
|
||||
static const HuffTree _huffTree_00331[];
|
||||
static const HuffTree _huffTree_00348[];
|
||||
static const HuffTree _huffTree_00365[];
|
||||
static const HuffTree _huffTree_00368[];
|
||||
static const HuffTree _huffTree_00372[];
|
||||
};
|
||||
|
||||
} // End of namespace Sky
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user