Initial commit

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

3
engines/cine/POTFILES Normal file
View File

@@ -0,0 +1,3 @@
engines/cine/metaengine.cpp
engines/cine/saveload.cpp
engines/cine/various.cpp

921
engines/cine/anim.cpp Normal file
View File

@@ -0,0 +1,921 @@
/* 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/>.
*
*/
/** \file
* @todo Make resource manager class and make load* functions its members
*/
#include "common/endian.h"
#include "common/memstream.h"
#include "common/textconsole.h"
#include "audio/mididrv.h"
#include "cine/cine.h"
#include "cine/anim.h"
#include "cine/gfx.h"
#include "cine/pal.h"
#include "cine/part.h"
#include "cine/various.h"
namespace Cine {
struct AnimHeader2Struct {
uint32 field_0;
int16 width;
int16 height;
int16 type;
uint16 field_A;
uint16 field_C;
uint16 field_E;
};
static const AnimDataMapping resNameMapping[] = {
{"PLONGEON", "PLONG110"},
{"PNEUMATI", "PNEUMA05"},
{"RELAITRE", "RIDEAU__"},
{"TIRROIR_", "PORTE___"},
{"VERREDO_", "EAU_____"},
{"ZODIAC__", "TAXIGO__"}
};
static const AnimDataEntry transparencyData[] = {
{"ALPHA", 0xF},
{"TITRE2", 0xF},
{"ET", 0xC},
{"L311", 0x3},
{"L405", 0x1},
{"L515", 0xC},
{"L009", 0xE},
{"L010", 0xE},
{"FUTUR", 0x6},
{"PAYSAN3", 0xB},
{"L801", 0xC},
{"L802", 0xC},
{"L803", 0xC},
{"L901", 0xD},
{"L902", 0x8},
{"L903", 0xD},
{"L904", 0xD},
{"L905", 0xD},
{"L906", 0xD},
{"L907", 0xD},
{"LA03", 0x4},
{"MOINE", 0xB},
{"L908", 0x8},
{"L909", 0x8},
{"L807", 0xC},
{"L808", 0xC},
{"LA01", 0xB},
{"L1201", 0xC},
{"L1202", 0xC},
{"L1203", 0xC},
{"L1210", 0x5},
{"L1211", 0xC},
{"L1214", 0xC},
{"L1215", 0xC},
{"L1216", 0xC},
{"L1217", 0xC},
{"L1218", 0xC},
{"L1219", 0xC},
{"L1220", 0xC},
{"SEIGNEUR", 0x6},
{"PERE0", 0xD},
{"L1302", 0x4},
{"L1303", 0x4},
{"L1304", 0x4},
{"L1401", 0xF},
{"L1402", 0xF},
{"L1501", 0x8},
{"L1503", 0x8},
{"L1504", 0x4},
{"L1505", 0x8},
{"L1506", 0x8},
{"L1601", 0xB},
{"L1602", 0xB},
{"L1603", 0xB},
{"L1604", 0x4},
{"L1605", 0x4},
{"L1701", 0x4},
{"L1702", 0x4},
{"L1801", 0x6},
{"L1904", 0x8},
{"L2002", 0x8},
{"L2003", 0x8},
{"L2101", 0x4},
{"L2102", 0x4},
{"L2201", 0x7},
{"L2202", 0x7},
{"L2203", 0xE},
{"L2305", 0x9},
{"L2306", 0x9},
{"GARDE1", 0x7},
{"L2402", 0x7},
{"L2407", 0x7},
{"L2408", 0x7},
{"GARDE2", 0x6},
{"L2601", 0x6},
{"L2602", 0x6},
{"L2603", 0x6},
{"L2604", 0x6},
{"L2605", 0x8},
{"L2606", 0x8},
{"L2607", 0x8},
{"L2610", 0x6},
{"L2611", 0x6},
{"L2612", 0x6},
{"L2613", 0x8},
{"L2614", 0x6},
{"VOYAGEUR", 0x6},
{"L2701", 0xD},
{"L2702", 0xD},
{"L2703", 0x6},
{"L2801", 0xD},
{"L2802", 0xD},
{"L2803", 0xD},
{"L2804", 0xD},
{"L2807", 0xD},
{"L2902", 0x8},
{"L2903", 0x8},
{"L3101", 0xA},
{"L3102", 0xA},
{"L3103", 0xA},
{"L3203", 0xF},
{"L3204", 0xF},
{"L3001", 0x7},
{"L3002", 0x7},
{"L3416", 0xC},
{"L3601", 0x5},
{"L3602", 0x5},
{"L3603", 0x5},
{"L3607", 0x5},
{"L3701", 0x8},
{"L3702", 0x8},
{"L3703", 0x8},
{"L4001", 0xD},
{"L4002", 0xD},
{"L4103", 0xF},
{"L4106", 0xF},
{"CRUGHON1", 0xC},
{"L4203", 0xC},
{"L4301", 0xC},
{"L4302", 0xC},
{"L4303", 0xC},
{"FUTUR2", 0x6},
{"L4601", 0xE},
{"L4603", 0x1},
{"L4106", 0xF},
{"L4801", 0xD},
{"L4802", 0xD},
{"FIN01", 0xB},
{"FIN02", 0xB},
{"FIN03", 0xB},
{"FIN", 0x9},
};
void convertMask(byte *dest, const byte *source, int16 width, int16 height);
void convert8BBP(byte *dest, const byte *source, int16 width, int16 height);
void convert8BBP2(byte *dest, byte *source, int16 width, int16 height);
int loadSet(const char *resourceName, int16 idx, int16 frameIndex = -1);
void checkAnimDataTableBounds(int entry) {
if (entry < 0) {
error("Out of free animation space");
} else if (entry >= (int)g_cine->_animDataTable.size()) {
error("Animation entry (%d) out of bounds", entry);
}
}
int16 fixAnimDataTableEndFrame(int entry, int16 startFrame, int16 endFrame) {
checkAnimDataTableBounds(entry);
// Ensure that a non-empty range [entry, entry + endFrame - startFrame) stays in bounds
if (endFrame > startFrame &&
entry + (endFrame - startFrame - 1) >= (int)g_cine->_animDataTable.size()) {
warning("Restricting out of bounds animation data table write to in bounds");
return (int16)(g_cine->_animDataTable.size() - entry + startFrame);
} else {
return endFrame;
}
}
AnimData::AnimData() : _width(0), _height(0), _bpp(0), _var1(0), _data(nullptr),
_mask(nullptr), _fileIdx(-1), _frameIdx(-1), _realWidth(0), _size(0) {
memset(_name, 0, sizeof(_name));
}
/**
* Copy constructor
*/
AnimData::AnimData(const AnimData &src) : _width(src._width),
_height(src._height), _bpp(src._bpp), _var1(src._var1),
_data(nullptr), _mask(nullptr), _fileIdx(src._fileIdx),
_frameIdx(src._frameIdx), _realWidth(src._realWidth), _size(src._size) {
if (src._data) {
_data = new byte[_size];
assert(_data);
memcpy(_data, src._data, _size * sizeof(byte));
}
if (src._mask) {
_mask = new byte[_size];
assert(_mask);
memcpy(_mask, src._mask, _size * sizeof(byte));
}
memset(_name, 0, sizeof(_name));
Common::strcpy_s(_name, src._name);
}
/**
* Destructor
*/
AnimData::~AnimData() {
clear();
}
/**
* Assingment operator
*/
AnimData &AnimData::operator=(const AnimData &src) {
AnimData tmp = src;
byte *ptr;
_width = tmp._width;
_height = tmp._height;
_bpp = tmp._bpp;
_var1 = tmp._var1;
ptr = _data;
_data = tmp._data;
tmp._data = ptr;
ptr = _mask;
_mask = tmp._mask;
tmp._mask = ptr;
_fileIdx = tmp._fileIdx;
_frameIdx = tmp._frameIdx;
memset(_name, 0, sizeof(_name));
Common::strcpy_s(_name, tmp._name);
_realWidth = tmp._realWidth;
_size = tmp._size;
return *this;
}
byte AnimData::getColor(int x, int y) {
assert(_data);
assert(x >= 0 && x < _realWidth && y >= 0 && y <= _height);
assert(x + y * _realWidth < _size);
return _data[x + y * _realWidth];
}
/**
* Load and decode image frame
* @param d Encoded image data
* @param type Encoding type
* @param w Image width
* @param h Image height
* @param file Data file index in bundle
* @param frame Image frame index
* @param n Part name
* @param transparent Transparent color (for ANIM_MASKSPRITE)
*/
void AnimData::load(byte *d, int type, uint16 w, uint16 h, int16 file,
int16 frame, const char *n, byte transparent) {
assert(d);
if (_data) {
clear();
}
_width = w * 2;
_height = h;
_var1 = _width >> 3;
_data = nullptr;
_mask = nullptr;
_fileIdx = file;
_frameIdx = frame;
memset(_name, 0, sizeof(_name));
Common::strlcpy(_name, n, sizeof(_name));
_realWidth = w;
switch (type) {
case ANIM_RAW:
_width = w;
_var1 = w >> 3;
_bpp = 4;
_size = w * h;
_data = new byte[_size];
assert(_data);
memcpy(_data, d, _size * sizeof(byte));
break;
case ANIM_MASK:
_bpp = 1;
_size = w * h * 8;
_data = new byte[_size];
_realWidth = w * 8;
assert(_data);
convertMask(_data, d, w, h);
break;
case ANIM_SPRITE:
_bpp = 4;
_size = w * h * 2;
_data = new byte[_size];
_realWidth = w * 2;
assert(_data);
gfxConvertSpriteToRaw(_data, d, w, h);
break;
case ANIM_MASKSPRITE:
_bpp = 4;
_size = w * h * 2;
_data = new byte[_size];
_mask = new byte[_size];
_realWidth = w * 2;
assert(_data && _mask);
gfxConvertSpriteToRaw(_data, d, w, h);
generateMask(_data, _mask, _size, transparent);
break;
case ANIM_PALSPRITE:
_bpp = 5;
_size = w * h * 2;
_data = new byte[_size];
_realWidth = w * 2;
assert(_data);
convert8BBP(_data, d, w, h);
break;
case ANIM_FULLSPRITE:
_bpp = 8;
_var1 = _width >> 4;
_size = w * h;
_data = new byte[_size];
assert(_data);
convert8BBP2(_data, d, w, h);
break;
default:
error("AnimData::load: unknown image type");
}
}
/**
* Reset image
*/
void AnimData::clear() {
delete[] _data;
delete[] _mask;
_width = 0;
_height = 0;
_bpp = 0;
_var1 = 0;
_data = nullptr;
_mask = nullptr;
_fileIdx = -1;
_frameIdx = -1;
memset(_name, 0, sizeof(_name));
_size = 0;
}
/**
* Write image identifiers to savefile
* @param fHandle Savefile open for writing
*/
void AnimData::save(Common::OutSaveFile &fHandle) const {
fHandle.writeUint16BE(_width);
fHandle.writeUint16BE(_var1);
fHandle.writeUint16BE(_bpp);
fHandle.writeUint16BE(_height);
fHandle.writeUint32BE(_data != nullptr); // _data
fHandle.writeUint32BE(_mask != nullptr); // _mask
fHandle.writeUint16BE(_fileIdx);
fHandle.writeUint16BE(_frameIdx);
fHandle.write(_name, sizeof(_name));
}
/**
* Clear part of animDataTable
* @param startIdx First image frame to be cleared
* @param numIdx Number of image frames to be cleared
*/
void freeAnimDataRange(byte startIdx, byte numIdx) {
if (numIdx > 0) {
// Make sure starting index is in bounds
if (startIdx >= g_cine->_animDataTable.size()) {
startIdx = (byte)(MAX<int>(0, g_cine->_animDataTable.size() - 1));
}
// Make sure last accessed index is in bounds
if (static_cast<uint>(startIdx + numIdx) > g_cine->_animDataTable.size()) {
numIdx = (byte)(g_cine->_animDataTable.size() - startIdx);
}
assert(startIdx < g_cine->_animDataTable.size());
assert(static_cast<uint>(startIdx + numIdx) <= g_cine->_animDataTable.size());
}
for (byte i = 0; i < numIdx; i++) {
g_cine->_animDataTable[startIdx + i].clear();
}
}
/**
* Clear whole animDataTable
*/
void freeAnimDataTable() {
freeAnimDataRange(0, NUM_MAX_ANIMDATA);
}
/**
* Find transparent color index for image
* @param animName Image file name
*/
static byte getAnimTransparentColor(const char *animName) {
char name[15];
removeExtension(name, animName, sizeof(name));
for (int i = 0; i < ARRAYSIZE(transparencyData); i++) {
if (!strcmp(name, transparencyData[i].name)) {
return transparencyData[i].color;
}
}
return 0;
}
/**
* Generate mask for image
* @param[in] sprite Image data
* @param[out] mask Image mask
* @param size Image data length
* @param transparency Transparent color index
*/
void generateMask(const byte *sprite, byte *mask, uint16 size, byte transparency) {
for (uint16 i = 0; i < size; i++) {
if (*(sprite++) != transparency) {
*(mask++) = 0;
} else {
*(mask++) = 1;
}
}
}
/**
* Decode 1bpp mask
* @param[out] dest Decoded mask
* @param[in] source Encoded mask
* @param width Mask width
* @param height Mask height
*/
void convertMask(byte *dest, const byte *source, int16 width, int16 height) {
int16 i, j;
byte maskEntry;
for (i = 0; i < width * height; i++) {
maskEntry = *(source++);
for (j = 0; j < 8; j++) {
*(dest++) = (maskEntry & 0x80) ? 0 : 1;
maskEntry <<= 1;
}
}
}
/**
* Decode 4bpp sprite
* @param[out] dest Decoded image
* @param[in] source Encoded image
* @param width Image width
* @param height Image height
*/
void convert4BBP(byte *dest, const byte *source, int16 width, int16 height) {
byte maskEntry;
for (int16 i = 0; i < width * height; i++) {
maskEntry = *(source++);
*(dest++) = (maskEntry & 0xF0) >> 4;
*(dest++) = (maskEntry & 0xF);
}
}
/**
* Read image header
* @param[out] animHeader Image header reference
* @param readS Input stream open for reading
*/
void loadAnimHeader(AnimHeaderStruct &animHeader, Common::SeekableReadStream &readS) {
readS.read(animHeader.idString, sizeof(animHeader.idString));
animHeader.idString[sizeof(animHeader.idString) - 1] = 0;
animHeader.frameWidth = readS.readSint16BE();
animHeader.frameHeight = readS.readSint16BE();
animHeader.field_8 = readS.readByte();
animHeader.field_9 = readS.readByte();
animHeader.field_A = readS.readByte();
animHeader.field_B = readS.readByte();
animHeader.field_C = readS.readByte();
animHeader.field_D = readS.readByte();
animHeader.numFrames = readS.readSint16BE();
animHeader.field_10 = readS.readByte();
animHeader.field_11 = readS.readByte();
animHeader.field_12 = readS.readByte();
animHeader.field_13 = readS.readByte();
animHeader.field_14 = readS.readUint16BE();
}
/**
* Find next empty space animDataTable
* @param start First index to check
*/
int emptyAnimSpace(int start = 0) {
for (; start < NUM_MAX_ANIMDATA; start++) {
if (!g_cine->_animDataTable[start].data()) {
return start;
}
}
return -1;
}
/**
* Load SPL data into animDataTable
* @param resourceName SPL filename
* @param idx Target index in animDataTable (-1 if any empty space will do)
* @return The number of the animDataTable entry after the loaded SPL data (-1 if error)
*/
int loadSpl(const char *resourceName, int16 idx) {
int16 foundFileIdx = findFileInBundle(resourceName);
int entry;
if (foundFileIdx < 0) {
return -1;
}
byte *dataPtr = readBundleFile(foundFileIdx);
entry = idx < 0 ? emptyAnimSpace() : idx;
checkAnimDataTableBounds(entry);
g_cine->_animDataTable[entry].load(dataPtr, ANIM_RAW, g_cine->_partBuffer[foundFileIdx].unpackedSize, 1, foundFileIdx, 0, currentPartName);
free(dataPtr);
return entry + 1;
}
/**
* Load 1bpp mask
* @param resourceName Mask filename
* @param idx Target index in animDataTable (-1 if any empty space will do)
* @param frameIndex frame of animation to load (-1 for all frames)
* @return The number of the animDataTable entry after the loaded mask (-1 if error)
*/
int loadMsk(const char *resourceName, int16 idx, int16 frameIndex) {
int16 foundFileIdx = findFileInBundle(resourceName);
if (foundFileIdx < 0) {
return -1;
}
int entry = 0;
byte *dataPtr = readBundleFile(foundFileIdx);
byte *ptr;
AnimHeaderStruct animHeader;
Common::MemoryReadStream readS(dataPtr, ANIM_HEADER_SIZE);
loadAnimHeader(animHeader, readS);
ptr = dataPtr + ANIM_HEADER_SIZE;
int16 startFrame = 0;
int16 endFrame = animHeader.numFrames;
if (frameIndex >= 0) {
startFrame = frameIndex;
endFrame = frameIndex + 1;
ptr += frameIndex * animHeader.frameWidth * animHeader.frameHeight;
}
entry = idx < 0 ? emptyAnimSpace() : idx;
endFrame = fixAnimDataTableEndFrame(entry, startFrame, endFrame);
for (int16 i = startFrame; i < endFrame; i++, entry++) {
g_cine->_animDataTable[entry].load(ptr, ANIM_MASK, animHeader.frameWidth, animHeader.frameHeight, foundFileIdx, i, currentPartName);
ptr += animHeader.frameWidth * animHeader.frameHeight;
}
free(dataPtr);
return entry;
}
/**
* Load animation
* @param resourceName Animation filename
* @param idx Target index in animDataTable (-1 if any empty space will do)
* @param frameIndex frame of animation to load (-1 for all frames)
* @return The number of the animDataTable entry after the loaded animation (-1 if error)
*/
int loadAni(const char *resourceName, int16 idx, int16 frameIndex) {
int16 foundFileIdx = findFileInBundle(resourceName);
if (foundFileIdx < 0) {
return -1;
}
int entry = 0;
byte *dataPtr = readBundleFile(foundFileIdx);
byte *ptr;
byte transparentColor;
AnimHeaderStruct animHeader;
Common::MemoryReadStream readS(dataPtr, ANIM_HEADER_SIZE);
loadAnimHeader(animHeader, readS);
ptr = dataPtr + ANIM_HEADER_SIZE;
// HACK: If the underlying resource is really a ".SET" then use that loading routine.
// Try to detect door animations in SP11_01.ANI and SP11_02.ANI that are .SET files.
// These are on Dr. Why's island the opening and closing doors.
if (hacksEnabled && scumm_stricmp(animHeader.idString, "SET") == 0 &&
idx >= 161 && idx <= 164 && animHeader.frameHeight == 0) {
free(dataPtr);
return loadSet(resourceName, idx, frameIndex);
}
int16 startFrame = 0;
int16 endFrame = animHeader.numFrames;
if (frameIndex >= 0) {
startFrame = frameIndex;
endFrame = frameIndex + 1;
ptr += frameIndex * animHeader.frameWidth * animHeader.frameHeight;
}
transparentColor = getAnimTransparentColor(resourceName);
// TODO: Merge this special case hack into getAnimTransparentColor somehow.
// HACK: Amiga and Atari ST versions of ALPHA.ANI in Future Wars use 0 instead of 0xF for transparency.
// Fixes transparency of page number and grid position (e.g. 04 and D2) in the copy protection scene
// of Amiga and Atari ST versions of Future Wars.
if (hacksEnabled && g_cine->getGameType() == Cine::GType_FW &&
(g_cine->getPlatform() == Common::kPlatformAmiga || g_cine->getPlatform() == Common::kPlatformAtariST) &&
scumm_stricmp(resourceName, "ALPHA.ANI") == 0) {
transparentColor = 0;
}
// TODO: Merge this special case hack into getAnimTransparentColor somehow.
// HACK: Versions of TITRE.ANI with height 37 use color 0xF for transparency.
// Versions of TITRE.ANI with height 57 use color 0x0 for transparency.
// Fixes bug #3875: FW: Glitches in title display of demo (regression).
if (hacksEnabled && scumm_stricmp(resourceName, "TITRE.ANI") == 0 && animHeader.frameHeight == 37) {
transparentColor = 0xF;
}
entry = idx < 0 ? emptyAnimSpace() : idx;
endFrame = fixAnimDataTableEndFrame(entry, startFrame, endFrame);
for (int16 i = startFrame; i < endFrame; i++, entry++) {
// special case transparency handling
if (!strcmp(resourceName, "L2202.ANI")) {
transparentColor = i < 2 ? 0 : 7;
} else if (!strcmp(resourceName, "L4601.ANI")) {
transparentColor = i < 1 ? 0xE : 0;
}
g_cine->_animDataTable[entry].load(ptr, ANIM_MASKSPRITE, animHeader.frameWidth, animHeader.frameHeight, foundFileIdx, i, currentPartName, transparentColor);
ptr += animHeader.frameWidth * animHeader.frameHeight;
}
free(dataPtr);
return entry;
}
/**
* Decode 16 color image with palette
* @param[out] dest Decoded image
* @param[in] source Encoded image
* @param width Image width
* @param height Image height
*/
void convert8BBP(byte *dest, const byte *source, int16 width, int16 height) {
const byte *table = source;
byte color;
source += 16;
for (uint16 i = 0; i < width * height; i++) {
color = *(source++);
*(dest++) = table[color >> 4];
*(dest++) = table[color & 0xF];
}
}
/**
* Decode 8bit image
* @param[out] dest Decoded image
* @param[in] source Encoded image
* @param width Image width
* @param height Image height
* \attention Data in source are destroyed during decoding
*/
void convert8BBP2(byte *dest, byte *source, int16 width, int16 height) {
uint16 i, j;
int k, m;
byte color;
for (j = 0; j < (width * height) / 16; j++) {
// m = 0: even bits, m = 1: odd bits
for (m = 0; m <= 1; m++) {
for (i = 0; i < 8; i++) {
color = 0;
for (k = 14 + m; k >= 0; k -= 2) {
color |= ((*(source + k) & 0x080) >> 7);
*(source + k) <<= 1;
if (k > 0 + m)
color <<= 1;
} // end k
*(dest++) = color;
} // end i
} // end m
source += 0x10;
} // end j
}
/**
* Load image set
* @param resourceName Image set filename
* @param idx Target index in animDataTable (-1 if any empty space will do)
* @param frameIndex frame of animation to load (-1 for all frames)
* @return The number of the animDataTable entry after the loaded image set (-1 if error)
*/
int loadSet(const char *resourceName, int16 idx, int16 frameIndex) {
AnimHeader2Struct header2;
uint16 numSpriteInAnim;
int16 foundFileIdx = findFileInBundle(resourceName);
int16 entry;
byte *ptr, *startOfDataPtr, *dataPtr, *origDataPtr;
int type;
if (foundFileIdx < 0) {
return -1;
}
origDataPtr = dataPtr = readBundleFile(foundFileIdx);
assert(!memcmp(dataPtr, "SET", 3));
ptr = dataPtr + 4;
numSpriteInAnim = READ_BE_UINT16(ptr);
ptr += 2;
startOfDataPtr = ptr + numSpriteInAnim * 0x10;
entry = idx < 0 ? emptyAnimSpace() : idx;
assert(entry >= 0);
int16 startFrame = 0;
int16 endFrame = numSpriteInAnim;
if (frameIndex >= 0) {
startFrame = frameIndex;
endFrame = frameIndex + 1;
ptr += 0x10 * frameIndex;
}
endFrame = fixAnimDataTableEndFrame(entry, startFrame, endFrame);
for (int16 i = startFrame; i < endFrame; i++, entry++) {
Common::MemoryReadStream readS(ptr, 0x10);
header2.field_0 = readS.readUint32BE();
header2.width = readS.readUint16BE();
header2.height = readS.readUint16BE();
header2.type = readS.readUint16BE();
header2.field_A = readS.readUint16BE();
header2.field_C = readS.readUint16BE();
header2.field_E = readS.readUint16BE();
ptr += 0x10;
dataPtr = startOfDataPtr + header2.field_0;
if (header2.type == 1) {
type = ANIM_MASK;
} else if (header2.type == 4) {
type = ANIM_SPRITE;
} else if (header2.type == 5) {
type = ANIM_PALSPRITE;
} else {
type = ANIM_FULLSPRITE;
}
g_cine->_animDataTable[entry].load(dataPtr, type, header2.width, header2.height, foundFileIdx, i, currentPartName);
}
free(origDataPtr);
return entry;
}
/**
* Load SEQ data into animDataTable
* @param resourceName SEQ data filename
* @param idx Target index in animDataTable (-1 if any empty space will do)
* @return The number of the animDataTable entry after the loaded SEQ data (-1 if error)
*/
int loadSeq(const char *resourceName, int16 idx) {
int16 foundFileIdx = findFileInBundle(resourceName);
if (foundFileIdx < 0) {
return -1;
}
byte *dataPtr = readBundleFile(foundFileIdx);
int entry = idx < 0 ? emptyAnimSpace() : idx;
checkAnimDataTableBounds(entry);
g_cine->_animDataTable[entry].load(dataPtr + ANIM_HEADER_SIZE, ANIM_RAW, g_cine->_partBuffer[foundFileIdx].unpackedSize - 0x16, 1, foundFileIdx, 0, currentPartName);
free(dataPtr);
return entry + 1;
}
/**
* Load a resource into animDataTable
* @param resourceName Resource's filename
* @param idx Target index in animDataTable (-1 if any empty space will do)
* @return The number of the animDataTable entry after the loaded resource (-1 if error)
* @todo Implement loading of all resource types
*/
int loadResource(const char *resourceName, int16 idx, int16 frameIndex) {
int result = -1; // Return an error by default
if (g_cine->getGameType() == Cine::GType_OS &&
g_cine->getPlatform() == Common::kPlatformDOS &&
g_sound->musicType() != MT_MT32 &&
(strstr(resourceName, ".SPL") || strstr(resourceName, ".H32"))) {
char base[20];
removeExtension(base, resourceName, sizeof(base));
for (uint i = 0; i < ARRAYSIZE(resNameMapping); i++) {
if (scumm_stricmp(base, resNameMapping[i].from) == 0) {
Common::strlcpy(base, resNameMapping[i].to, sizeof(base));
break;
}
}
const char *ext = (g_sound->musicType() == MT_ADLIB) ? ".ADL" : ".HP";
Common::strlcat(base, ext, sizeof(base));
return loadResource(base, idx, frameIndex);
}
bool preferSeq = (g_cine->getGameType() == Cine::GType_OS && g_sound->musicType() == MT_MT32);
if (strstr(resourceName, ".SPL")) {
if (preferSeq)
result = loadSeq(resourceName, idx);
else
result = loadSpl(resourceName, idx);
} else if (strstr(resourceName, ".MSK")) {
result = loadMsk(resourceName, idx, frameIndex);
} else if (strstr(resourceName, ".ANI")) {
result = loadAni(resourceName, idx, frameIndex);
} else if (strstr(resourceName, ".ANM")) {
result = loadAni(resourceName, idx, frameIndex);
} else if (strstr(resourceName, ".SET")) {
result = loadSet(resourceName, idx, frameIndex);
} else if (strstr(resourceName, ".SEQ")) {
result = loadSeq(resourceName, idx);
} else if (strstr(resourceName, ".H32")) {
if (preferSeq)
result = loadSeq(resourceName, idx);
else
result = loadSpl(resourceName, idx);
} else if (strstr(resourceName, ".HP")) {
result = loadSpl(resourceName, idx);
} else if (strstr(resourceName, ".ADL")) {
result = loadSpl(resourceName, idx);
} else if (strstr(resourceName, ".AMI")) {
result = loadSpl(resourceName, idx);
} else if (strstr(resourceName, "ECHEC")) { // Echec (French) means failure
g_cine->quitGame();
} else {
error("loadResource: Cannot determine type for '%s'", resourceName);
}
return result;
}
} // End of namespace Cine

113
engines/cine/anim.h Normal file
View File

@@ -0,0 +1,113 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef CINE_ANIM_H
#define CINE_ANIM_H
#include "common/endian.h"
#include "cine/saveload.h"
namespace Cine {
// Size of AnimHeaderStruct payload in bytes
#define ANIM_HEADER_SIZE 0x16
struct AnimHeaderStruct {
char idString[4];
int16 frameWidth;
int16 frameHeight;
byte field_8;
byte field_9;
byte field_A;
byte field_B;
byte field_C;
byte field_D;
int16 numFrames;
byte field_10;
byte field_11;
byte field_12;
byte field_13;
uint16 field_14;
};
struct AnimDataEntry {
char name[9];
byte color;
};
struct AnimDataMapping {
char from[9];
char to[9];
};
#define ANIM_RAW 0 // memcpy
#define ANIM_MASK 1 // convertMask
#define ANIM_SPRITE 2 // gfxConvertSpriteToRaw
#define ANIM_MASKSPRITE 3 // gfxConvertSpriteToRaw + generateMask
#define ANIM_PALSPRITE 5 // convert8BBP
#define ANIM_FULLSPRITE 8 // convert8BBP2
class AnimData {
private:
byte *_data; ///< Image data
byte *_mask; ///< Image mask (may be NULL)
int16 _fileIdx; ///< Source file index in bundle
int16 _frameIdx; ///< Frame number in animation
char _name[10]; ///< Part filename
int _size; ///< _data/_mask size, internal only
public:
uint16 _width; ///< Image width (usually twice the real size)
uint16 _height; ///< Image height
uint16 _bpp; ///< Bit depth/type information
uint16 _var1; ///< Something related to width
int _realWidth; ///< Real image width in bytes
AnimData();
AnimData(const AnimData &src);
~AnimData();
AnimData &operator=(const AnimData &src);
int size() { return _size; }
int16 frameIndex() { return _frameIdx; }
const byte *data() const { return _data; } ///< Image data
const byte *mask() const { return _mask; } ///< Image mask (may be NULL)
byte getColor(int x, int y);
void load(byte *d, int type, uint16 w, uint16 h, int16 file, int16 frame, const char *n, byte transparent = 0);
void clear();
void save(Common::OutSaveFile &fHandle) const;
};
#define NUM_MAX_ANIMDATA 255
void freeAnimDataTable();
void freeAnimDataRange(byte startIdx, byte numIdx);
int loadResource(const char *resourceName, int16 idx = -1, int16 frameIndex = -1);
void loadResourcesFromSave(Common::SeekableReadStream &fHandle, enum CineSaveGameFormat saveGameFormat);
void generateMask(const byte *sprite, byte *mask, uint16 size, byte transparency);
} // End of namespace Cine
#endif

117
engines/cine/bg.cpp Normal file
View File

@@ -0,0 +1,117 @@
/* 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/memstream.h"
#include "common/textconsole.h"
#include "cine/cine.h"
#include "cine/various.h"
#include "cine/bg.h"
namespace Cine {
uint16 bgVar0;
byte *additionalBgTable[9];
int16 loadCtFW(const char *ctName) {
debugC(1, kCineDebugCollision, "loadCtFW(\"%s\")", ctName);
byte *ptr, *dataPtr;
int16 foundFileIdx = findFileInBundle(ctName);
if (foundFileIdx < 0) {
warning("loadCtFW: Unable to find collision data file '%s'", ctName);
return -1;
}
if (currentCtName != ctName)
Common::strlcpy(currentCtName, ctName, sizeof(currentCtName));
ptr = dataPtr = readBundleFile(foundFileIdx);
loadRelatedPalette(ctName);
assert(strstr(ctName, ".NEO"));
gfxConvertSpriteToRaw(collisionPage, ptr + 0x80, 160, 200);
free(dataPtr);
return 0;
}
int16 loadCtOS(const char *ctName) {
debugC(1, kCineDebugCollision, "loadCtOS(\"%s\")", ctName);
byte *ptr, *dataPtr;
int16 foundFileIdx = findFileInBundle(ctName);
if (foundFileIdx < 0) {
warning("loadCtOS: Unable to find collision data file '%s'", ctName);
return -1;
}
if (currentCtName != ctName)
Common::strlcpy(currentCtName, ctName, sizeof(currentCtName));
ptr = dataPtr = readBundleFile(foundFileIdx);
uint16 bpp = READ_BE_UINT16(ptr);
ptr += 2;
if (bpp == 8) {
renderer->loadCt256(ptr, ctName);
} else {
renderer->loadCt16(ptr, ctName);
}
free(dataPtr);
return 0;
}
int16 loadBg(const char *bgName) {
byte *ptr, *dataPtr;
int16 fileIdx = findFileInBundle(bgName);
if (fileIdx < 0) {
warning("loadBg(\"%s\"): Could not find background in file bundle.", bgName);
return -1;
}
checkDataDisk(-1);
ptr = dataPtr = readBundleFile(fileIdx);
uint16 bpp = READ_BE_UINT16(ptr);
ptr += 2;
if (bpp == 8) {
renderer->loadBg256(ptr, bgName);
} else {
if (g_cine->getGameType() == Cine::GType_FW) {
loadRelatedPalette(bgName);
}
renderer->loadBg16(ptr, bgName);
}
free(dataPtr);
return 0;
}
} // End of namespace Cine

34
engines/cine/bg.h Normal file
View File

@@ -0,0 +1,34 @@
/* 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 CINE_BG_H
#define CINE_BG_H
namespace Cine {
int16 loadBg(const char *bgName);
int16 loadCtFW(const char *bgName);
int16 loadCtOS(const char *bgName);
extern uint16 bgVar0;
} // End of namespace Cine
#endif

125
engines/cine/bg_list.cpp Normal file
View File

@@ -0,0 +1,125 @@
/* 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/stream.h"
#include "cine/cine.h"
#include "cine/main_loop.h"
#include "cine/object.h"
#include "cine/various.h"
#include "cine/bg_list.h"
namespace Cine {
uint32 var8;
/**
* Add masked sprite to the background
* @param objIdx Sprite description
*/
void addToBGList(int16 objIdx) {
createBgIncrustListElement(objIdx, 0);
renderer->incrustSprite(g_cine->_bgIncrustList.back());
}
/**
* Add filled sprite to the background
* @param objIdx Sprite description
*/
void addSpriteFilledToBGList(int16 objIdx) {
createBgIncrustListElement(objIdx, 1);
renderer->incrustMask(g_cine->_bgIncrustList.back());
}
void removeBgIncrustsWithBgIdx(int16 bgIdx) {
Common::List<BGIncrust>::iterator it;
for (it = g_cine->_bgIncrustList.begin(); it != g_cine->_bgIncrustList.end();) {
if (it->bgIdx == bgIdx) {
it = g_cine->_bgIncrustList.erase(it);
} else {
++it;
}
}
}
/**
* Add new element to incrust list
* @param objIdx Element description
* @param param Type of element
*/
void createBgIncrustListElement(int16 objIdx, int16 param) {
BGIncrust tmp;
tmp.unkPtr = nullptr;
tmp.objIdx = objIdx;
tmp.param = param;
tmp.x = g_cine->_objectTable[objIdx].x;
tmp.y = g_cine->_objectTable[objIdx].y;
tmp.frame = g_cine->_objectTable[objIdx].frame;
tmp.part = g_cine->_objectTable[objIdx].part & 0x0f;
tmp.bgIdx = renderer->currentBg();
g_cine->_bgIncrustList.push_back(tmp);
}
/**
* Reset var8 (probably something related to bgIncrustList)
*/
void resetBgIncrustList() {
var8 = 0;
}
/**
* Restore incrust list from savefile
* @param fHandle Savefile open for reading
*/
void loadBgIncrustFromSave(Common::SeekableReadStream &fHandle, bool hasBgIdx) {
BGIncrust tmp;
int size = fHandle.readSint16BE();
for (int i = 0; i < size; i++) {
fHandle.readUint32BE();
fHandle.readUint32BE();
tmp.unkPtr = nullptr;
tmp.objIdx = fHandle.readUint16BE();
tmp.param = fHandle.readUint16BE();
tmp.x = fHandle.readUint16BE();
tmp.y = fHandle.readUint16BE();
tmp.frame = fHandle.readUint16BE();
tmp.part = fHandle.readUint16BE();
tmp.bgIdx = (hasBgIdx ? fHandle.readUint16BE() : 0);
g_cine->_bgIncrustList.push_back(tmp);
if (tmp.param == 0) {
renderer->incrustSprite(tmp);
} else {
renderer->incrustMask(tmp);
}
}
}
} // End of namespace Cine

54
engines/cine/bg_list.h Normal file
View File

@@ -0,0 +1,54 @@
/* 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 CINE_BGLIST_H
#define CINE_BGLIST_H
#include "common/scummsys.h"
#include "common/list.h"
namespace Cine {
struct BGIncrust {
byte *unkPtr;
int16 objIdx;
int16 param;
int16 x;
int16 y;
int16 frame;
int16 part;
int16 bgIdx;
};
extern uint32 var8;
void addToBGList(int16 objIdx);
void addSpriteFilledToBGList(int16 idx);
void removeBgIncrustsWithBgIdx(int16 bgIdx);
void createBgIncrustListElement(int16 objIdx, int16 param);
void resetBgIncrustList();
void loadBgIncrustFromSave(Common::SeekableReadStream &fHandle, bool hasBgIdx = false);
} // End of namespace Cine
#endif

623
engines/cine/cine.cpp Normal file
View File

@@ -0,0 +1,623 @@
/* 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/config-manager.h"
#include "common/debug-channels.h"
#include "common/events.h"
#include "backends/keymapper/keymapper.h"
#include "engines/util.h"
#include "graphics/cursorman.h"
#include "graphics/paletteman.h"
#include "image/iff.h"
#include "cine/cine.h"
#include "cine/bg_list.h"
#include "cine/main_loop.h"
#include "cine/object.h"
#include "cine/texte.h"
#include "cine/sound.h"
#include "cine/various.h"
namespace Cine {
#ifdef USE_TTS
// The color names for the copy protection screens are images, so they are transcribed here for TTS
static const char *copyProtectionColorsFWEnglish[] = {
"White",
"Yellow",
"Red",
"Orange",
"Black",
"Blue",
"Brown",
"Green"
};
static const char *copyProtectionColorsFWFrench[] = {
"Blanc",
"Jaune",
"Rouge",
"Orange",
"Noir",
"Bleu",
"Marron",
"Vert"
};
static const char *copyProtectionColorsFWGerman[] = {
"Wei\236",
"Gelb",
"Rot",
"Orange",
"Schwarz",
"Blau",
"Braun",
"Grun"
};
static const char *copyProtectionColorsFWSpanish[] = {
"Blanco",
"Amarillo",
"Rojo",
"Naranja",
"Negro",
"Azul",
"Marr\242n",
"Verde"
};
static const char *copyProtectionColorsOSEnglish[] = {
"Black",
"White",
"Yellow",
"Orange",
"Red",
"Brown",
"Grey",
"Pink",
"Purple",
"Light Blue",
"Dark Blue",
"Light Green",
"Dark Green"
};
static const char *copyProtectionColorsOSFrench[] = {
"Noir",
"Blanc",
"Jaune",
"Orange",
"Rouge",
"Marron",
"Gris",
"Rose",
"Violet",
"Bleu Clair",
"Bleu Fonc\202",
"Vert Clair",
"Vert Fonc\202"
};
static const char *copyProtectionColorsOSGerman[] = {
"Schwarz",
"Wei\236",
"Gelb",
"Orange",
"Rot",
"Braun",
"Grau",
"Pink",
"Violett",
"Hellblau",
"Dunkelblau",
"Hellgr\201n",
"Dunkelgr\201n"
};
static const char *copyProtectionColorsOSSpanish[] = {
"Negro",
"Blanco",
"Amarillo",
"Naranja",
"Rojo",
"Marr\242n",
"Gris",
"Rosa",
"Morado",
"Azul Claro",
"Azul Oscuro",
"Verde Claro",
"Verde Oscuro"
};
static const char *copyProtectionColorsOSItalian[] = {
"Nero",
"Bianco",
"Giallo",
"Arancione",
"Rosso",
"Marrone",
"Grigio",
"Rosa",
"Viola",
"Blu Chiaro",
"Blu Scuro",
"Verde Chiaro",
"Verde Scuro"
};
static const int kNumOfFWColors = 8;
static const int kNumOfOSColors = 13;
#endif
Sound *g_sound = nullptr;
CineEngine *g_cine = nullptr;
CineEngine::CineEngine(OSystem *syst, const CINEGameDescription *gameDesc)
: Engine(syst),
_gameDescription(gameDesc),
_rnd("cine") {
// Setup mixer
syncSoundSettings();
setDebugger(new CineConsole(this));
g_cine = this;
for (int i = 0; i < NUM_FONT_CHARS; i++) {
_textHandler.fontParamTable[i].characterIdx = 0;
_textHandler.fontParamTable[i].characterWidth = 0;
}
_restartRequested = false;
_preLoad = false;
setDefaultGameSpeed();
}
CineEngine::~CineEngine() {
if (getGameType() == Cine::GType_OS) {
freeErrmessDat();
}
}
void CineEngine::syncSoundSettings() {
Engine::syncSoundSettings();
bool mute = false;
if (ConfMan.hasKey("mute"))
mute = ConfMan.getBool("mute");
// Use music volume for plain sound types (At least the AdLib player uses a plain sound type
// so previously the music and sfx volume controls didn't affect it at all).
// FIXME: Make AdLib player differentiate between playing sound effects and music and remove this.
_mixer->setVolumeForSoundType(Audio::Mixer::kPlainSoundType,
mute ? 0 : ConfMan.getInt("music_volume"));
}
Common::Error CineEngine::run() {
Graphics::ModeList modes;
modes.push_back(Graphics::Mode(320, 200));
if (g_cine->getGameType() == GType_FW && (g_cine->getFeatures() & GF_CD)) {
modes.push_back(Graphics::Mode(640, 480));
initGraphicsModes(modes);
showSplashScreen();
} else {
initGraphicsModes(modes);
}
// Initialize backend
initGraphics(320, 200);
if (g_cine->getGameType() == GType_FW && (g_cine->getFeatures() & GF_CD)) {
if (!existExtractedCDAudioFiles(19) // tracks <19 are not used
&& !isDataAndCDAudioReadFromSameCD()) {
warnMissingExtractedCDAudio();
}
}
if (getPlatform() == Common::kPlatformDOS) {
g_sound = new PCSound(_mixer, this);
} else {
// Paula chipset for Amiga and Atari versions
g_sound = new PaulaSound(_mixer, this);
}
_restartRequested = false;
Common::TextToSpeechManager *ttsMan = g_system->getTextToSpeechManager();
if (ttsMan != nullptr) {
ttsMan->setLanguage(ConfMan.get("language"));
ttsMan->enable(ConfMan.getBool("tts_enabled"));
}
switch (getLanguage()) {
case Common::EN_ANY:
case Common::EN_USA:
case Common::EN_GRB:
_ttsLanguage = kEnglish;
break;
case Common::FR_FRA:
_ttsLanguage = kFrench;
break;
case Common::DE_DEU:
_ttsLanguage = kGerman;
break;
case Common::ES_ESP:
_ttsLanguage = kSpanish;
break;
case Common::IT_ITA:
_ttsLanguage = kItalian;
break;
default:
_ttsLanguage = kEnglish;
break;
}
_copyProtectionColorScreen = false;
_copyProtectionTextScreen = false;
_saveInputMenuOpen = false;
do {
initialize();
_restartRequested = false;
CursorMan.showMouse(true);
mainLoop(BOOT_SCRIPT_INDEX);
delete renderer;
delete[] collisionPage;
delete _scriptInfo;
} while (_restartRequested);
delete g_sound;
return Common::kNoError;
}
uint32 CineEngine::getTimerDelay() const {
return (10923000 * _timerDelayMultiplier) / 1193180;
}
/**
* Modify game speed
* @param speedChange Negative values slow game down, positive values speed it up, zero does nothing
* @return Timer delay multiplier's value after the game speed change
*/
int CineEngine::modifyGameSpeed(int speedChange) {
// If we want more speed we decrement the timer delay multiplier and vice versa.
_timerDelayMultiplier = CLIP(_timerDelayMultiplier - speedChange, 1, 50);
return _timerDelayMultiplier;
}
void CineEngine::setDefaultGameSpeed() {
_timerDelayMultiplier = 12;
}
void CineEngine::initialize() {
setTotalPlayTime(0); // Reset total play time
_globalVars.reinit(NUM_MAX_VAR + 1);
// Initialize all savegames' descriptions to empty strings
memset(currentSaveName, 0, sizeof(currentSaveName));
// Resize object table to its correct size and reset all its elements
g_cine->_objectTable.resize(NUM_MAX_OBJECT);
resetObjectTable();
// Resize animation data table to its correct size and reset all its elements
g_cine->_animDataTable.resize(NUM_MAX_ANIMDATA);
freeAnimDataTable();
// Resize zone data table to its correct size and reset all its elements
g_cine->_zoneData.resize(NUM_MAX_ZONE);
Common::fill(g_cine->_zoneData.begin(), g_cine->_zoneData.end(), 0);
// Resize zone query table to its correct size and reset all its elements
g_cine->_zoneQuery.resize(NUM_MAX_ZONE);
Common::fill(g_cine->_zoneQuery.begin(), g_cine->_zoneQuery.end(), 0);
setDefaultGameSpeed();
_scriptInfo = setupOpcodes();
initLanguage(getLanguage());
if (getGameType() == Cine::GType_OS) {
renderer = new OSRenderer;
} else {
renderer = new FWRenderer;
}
renderer->initialize();
forbidBgPalReload = 0;
reloadBgPalOnNextFlip = 0;
gfxFadeOutCompleted = 0;
gfxFadeInRequested = 0;
safeControlsLastAccessedMs = 0;
lastSafeControlObjIdx = -1;
currentDisk = 1;
collisionPage = new byte[320 * 200]();
// Clear part buffer as there's nothing loaded into it yet.
// Its size will change when loading data into it with the loadPart function.
g_cine->_partBuffer.clear();
if (getGameType() == Cine::GType_OS) {
readVolCnf();
}
loadTextData("texte.dat");
if (getGameType() == Cine::GType_OS && !(getFeatures() & GF_DEMO)) {
loadPoldatDat("poldat.dat");
loadErrmessDat("errmess.dat");
}
// in case ScummVM engines can be restarted in the future
g_cine->_scriptTable.clear();
g_cine->_relTable.clear();
g_cine->_objectScripts.clear();
g_cine->_globalScripts.clear();
g_cine->_bgIncrustList.clear();
freeAnimDataTable();
g_cine->_overlayList.clear();
g_cine->_messageTable.clear();
resetObjectTable();
g_cine->_seqList.clear();
if (getGameType() == Cine::GType_OS) {
disableSystemMenu = 1;
} else {
// WORKAROUND: We do not save this variable in FW's savegames.
// Initializing this to 1, like we do it in the OS case, will
// cause the menu disabled when loading from the launcher or
// command line.
// A proper fix here would be to save this variable in FW's saves.
// Since it seems these are unversioned so far, there would be need
// to properly add versioning to them first.
//
// Adding versioning to FW saves didn't solve this problem. Setting
// disableSystemMenu according to the saved value still caused the
// action menu (EXAMINE, TAKE, INVENTORY, ...) sometimes to be
// disabled when it wasn't supposed to be disabled when
// loading from the launcher or command line.
disableSystemMenu = 0;
}
var8 = 0;
bgVar0 = 0;
var2 = var3 = var4 = lastType20OverlayBgIdx = 0;
musicIsPlaying = 0;
currentDatName[0] = 0;
_keyInputList.clear();
// Used for making sound effects work using Roland MT-32 and AdLib in
// Operation Stealth after loading a savegame. The sound effects are loaded
// in AUTO00.PRC using a combination of o2_loadAbs and o2_playSample(1, ...)
// before o1_freePartRange(0, 200). In the original game AUTO00.PRC
// was run when starting or restarting the game and one could not load a savegame
// before passing the copy protection. Thus, we try to emulate that behaviour by
// running at least part of AUTO00.PRC before loading a savegame.
//
// Confirmed that DOS and Atari ST versions do have these commands in their AUTO00.PRC files.
// Confirmed that Amiga and demo versions do not have these commands in their AUTO00.PRC files.
if (getGameType() == Cine::GType_OS && !(getFeatures() & GF_DEMO) &&
(getPlatform() == Common::kPlatformDOS || getPlatform() == Common::kPlatformAtariST)) {
loadPrc(BOOT_PRC_NAME);
Common::strcpy_s(currentPrcName, BOOT_PRC_NAME);
addScriptToGlobalScripts(BOOT_SCRIPT_INDEX);
runOnlyUntilFreePartRangeFirst200 = true;
executeGlobalScripts();
}
_preLoad = false;
if (ConfMan.hasKey("save_slot") && !_restartRequested) {
Common::Error loadError = loadGameState(ConfMan.getInt("save_slot"));
if (loadError.getCode() == Common::kNoError)
_preLoad = true;
}
if (!_preLoad) {
loadPrc(BOOT_PRC_NAME);
Common::strcpy_s(currentPrcName, BOOT_PRC_NAME);
setMouseCursor(MOUSE_CURSOR_NORMAL);
}
}
void CineEngine::showSplashScreen() {
Common::File file;
if (!file.open("sony.lbm"))
return;
Image::IFFDecoder decoder;
if (!decoder.loadStream(file))
return;
const Graphics::Surface *surface = decoder.getSurface();
if (surface->w == 640 && surface->h == 480) {
initGraphics(640, 480);
const Graphics::Palette &palette = decoder.getPalette();
g_system->getPaletteManager()->setPalette(palette.data(), 0, palette.size());
g_system->copyRectToScreen(surface->getPixels(), 640, 0, 0, 640, 480);
g_system->updateScreen();
Common::EventManager *eventMan = g_system->getEventManager();
bool done = false;
uint32 now = g_system->getMillis();
while (!done && g_system->getMillis() - now < 2000) {
Common::Keymapper *keymapper = _eventMan->getKeymapper();
keymapper->getKeymap("intro-shortcuts")->setEnabled(true);
Common::Event event;
while (eventMan->pollEvent(event)) {
if (event.type == Common::EVENT_CUSTOM_ENGINE_ACTION_START && event.customType == kActionExitSonyScreen) {
done = true;
break;
}
if (shouldQuit())
done = true;
}
keymapper->getKeymap("intro-shortcuts")->setEnabled(false);
}
}
decoder.destroy();
}
void CineEngine::sayText(const Common::String &text, Common::TextToSpeechManager::Action action) {
Common::TextToSpeechManager *ttsMan = g_system->getTextToSpeechManager();
// _previousSaid is used to prevent the TTS from looping when sayText is called inside a loop,
// for example when the cursor stays on a button. Without it when the text ends it would speak
// the same text again.
// _previousSaid is cleared when appropriate to allow for repeat requests
if (ttsMan != nullptr && ConfMan.getBool("tts_enabled") && text != _previousSaid) {
if (getLanguage() == Common::DE_DEU) {
// German translation encodes ß as 0x9e, but it's 0xe1 in codepage 850
Common::String ttsMessage = text;
ttsMessage.replace('\x9e', '\xe1');
ttsMan->say(ttsMessage, action, Common::CodePage::kDos850);
} else if (getLanguage() == Common::FR_FRA && getGameType() == GType_FW) {
// French translation for Future Wars encodes ê as 0x97, but it's 0x88 in codepage 850
Common::String ttsMessage = text;
ttsMessage.replace('\x97', '\x88');
ttsMan->say(ttsMessage, action, Common::CodePage::kDos850);
} else {
ttsMan->say(text, action, Common::CodePage::kDos850);
}
_previousSaid = text;
}
}
void CineEngine::stopTextToSpeech() {
Common::TextToSpeechManager *ttsMan = g_system->getTextToSpeechManager();
if (ttsMan != nullptr && ConfMan.getBool("tts_enabled") && ttsMan->isSpeaking()) {
ttsMan->stop();
_previousSaid.clear();
}
}
#ifdef USE_TTS
void CineEngine::mouseOverButton() {
if (!_copyProtectionTextScreen && !_copyProtectionColorScreen) {
return;
}
uint16 mouseX, mouseY, mouseButton;
getMouseData(mouseUpdateStatus, &mouseButton, &mouseX, &mouseY);
int16 objIdx = getObjectUnderCursor(mouseX, mouseY);
if (objIdx != -1) {
if (_copyProtectionTextScreen) {
// Operation Stealth has other objects than just the "Ok" button in this screen
if (getGameType() == GType_OS && _objectTable[objIdx].frame != 151) {
return;
}
sayText("Ok", Common::TextToSpeechManager::INTERRUPT);
} else if (_copyProtectionColorScreen) {
const char **colors;
if (getGameType() == GType_FW) {
// The Amiga and Atari versions use a different copy protection screen from the DOS version
// and don't have these color buttons
// The only exception is the US Amiga version, which uses the color screen
if (getPlatform() != Common::kPlatformDOS && getLanguage() != Common::EN_USA) {
return;
}
int16 index = objIdx - 150;
if (index < 0 || index >= kNumOfFWColors) {
return;
}
switch (_ttsLanguage) {
case kEnglish:
colors = copyProtectionColorsFWEnglish;
break;
case kFrench:
colors = copyProtectionColorsFWFrench;
break;
case kGerman:
colors = copyProtectionColorsFWGerman;
break;
case kSpanish:
colors = copyProtectionColorsFWSpanish;
break;
default:
colors = copyProtectionColorsFWEnglish;
break;
}
sayText(colors[index], Common::TextToSpeechManager::INTERRUPT);
} else {
int16 index = objIdx - 240;
if (index < 0 || index >= kNumOfOSColors) {
return;
}
switch (_ttsLanguage) {
case kEnglish:
colors = copyProtectionColorsOSEnglish;
break;
case kFrench:
colors = copyProtectionColorsOSFrench;
break;
case kItalian:
colors = copyProtectionColorsOSItalian;
break;
case kGerman:
colors = copyProtectionColorsOSGerman;
break;
case kSpanish:
colors = copyProtectionColorsOSSpanish;
break;
default:
colors = copyProtectionColorsOSEnglish;
break;
}
sayText(colors[index], Common::TextToSpeechManager::INTERRUPT);
}
}
} else {
_previousSaid.clear();
}
}
#endif
} // End of namespace Cine

292
engines/cine/cine.h Normal file
View File

@@ -0,0 +1,292 @@
/* 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 CINE_CINE_H
#define CINE_CINE_H
#include "common/scummsys.h"
#include "common/file.h"
#include "common/util.h"
#include "common/str.h"
#include "common/hashmap.h"
#include "common/hash-str.h"
#include "common/random.h"
#include "common/events.h"
#include "common/text-to-speech.h"
#include "engines/engine.h"
#include "cine/texte.h"
#include "cine/rel.h"
#include "cine/script.h"
#include "cine/part.h"
#include "cine/prc.h"
#include "cine/msg.h"
#include "cine/bg.h"
#include "cine/pal.h"
#include "cine/gfx.h"
#include "cine/anim.h"
#include "cine/bg_list.h"
#include "cine/various.h"
#include "cine/console.h"
#include "cine/sound.h"
#include "cine/detection.h"
//#define DUMP_SCRIPTS
/**
* This is the namespace of the Cine engine.
*
* Status of this engine:
*
* This enigne has 2 generations Cinematique evo.1 and Cinematique evo.2
* first generation is fairly complete, and second one is under development
*
* Cinematique evo.1 status:
* The engine supports Future Wars and is basically complete with support of
* all known game variants. Based on Yaz0r's engine.
*
* Cinematique evo.2 status:
* This generation supports Operation Stealth, originally developed by Yaz0r,
* for a french variant of the game which was said to be completable.
* Later the work was renewed as part of GSoC'08, by Kari Salminen, but it has not
* yet been finished. The game is not completable.
*
*
* Games using this engine:
*
* Cinematique evo.1
* - Future Wars
*
* Cinematique evo.2
* - Operation Stealth
*
*/
namespace Cine {
struct SeqListElement;
struct VolumeResource {
char name[10];
uint32 pNamesList;
int16 diskNum;
int32 sizeOfNamesList;
};
typedef Common::HashMap<Common::String, Common::Array<VolumeResource> > StringToVolumeResourceArrayHashMap;
enum CINEAction {
kActionNone,
kActionMoveUp,
kActionMoveDown,
kActionMoveLeft,
kActionMoveRight,
kActionMoveUpLeft,
kActionMoveUpRight,
kActionMoveDownLeft,
kActionMoveDownRight,
kActionGameSpeedDefault,
kActionGameSpeedSlower,
kActionGameSpeedFaster,
kActionExamine,
kActionTake,
kActionInventory,
kActionUse,
kActionActivate,
kActionSpeak,
kActionActionMenu,
kActionSystemMenu,
kActionCollisionPage,
kActionMouseLeft,
kActionMouseRight,
kActionExitSonyScreen,
kActionMenuOptionUp,
kActionMenuOptionDown
};
enum TTSLanguage {
kEnglish = 0,
kFrench = 1,
kGerman = 2,
kSpanish = 3,
kItalian = 4
};
class CineConsole;
class CineEngine : public Engine {
protected:
// Engine APIs
Common::Error run() override;
bool hasFeature(EngineFeature f) const override;
void shutdown();
bool initGame();
public:
CineEngine(OSystem *syst, const CINEGameDescription *gameDesc);
~CineEngine() override;
void syncSoundSettings() override;
bool mayHave256Colors() const;
int getGameType() const;
uint32 getFeatures() const;
Common::Language getLanguage() const;
Common::Platform getPlatform() const;
bool loadSaveDirectory();
void makeSystemMenu();
int scummVMSaveLoadDialog(bool isSave);
int modifyGameSpeed(int speedChange);
void setDefaultGameSpeed();
uint32 getTimerDelay() const;
Common::Error loadGameState(int slot) override;
Common::Error saveGameState(int slot, const Common::String &desc, bool isAutosave = false) override;
Common::String getSaveStateName(int slot) const override;
bool canLoadGameStateCurrently(Common::U32String *msg = nullptr) override;
bool canSaveGameStateCurrently(Common::U32String *msg = nullptr) override;
void sayText(const Common::String &text, Common::TextToSpeechManager::Action action);
void stopTextToSpeech();
#ifdef USE_TTS
void mouseOverButton();
#endif
const CINEGameDescription *_gameDescription;
Common::File _partFileHandle;
Common::RandomSource _rnd;
StringToVolumeResourceArrayHashMap _volumeEntriesMap;
TextHandler _textHandler;
bool _restartRequested;
Common::String _previousSaid;
TTSLanguage _ttsLanguage;
bool _copyProtectionTextScreen;
bool _copyProtectionColorScreen;
bool _saveInputMenuOpen;
private:
void initialize();
void showSplashScreen();
void resetEngine();
bool checkSaveHeaderData(const ChunkHeader& hdr);
bool loadPlainSaveFW(Common::SeekableReadStream &in, CineSaveGameFormat saveGameFormat, uint32 version);
bool loadVersionedSaveFW(Common::SeekableReadStream &in);
bool loadVersionedSaveOS(Common::SeekableReadStream &in);
bool makeLoad(const Common::String &saveName);
void writeSaveHeader(Common::OutSaveFile &out, uint32 headerId);
void makeSaveFW(Common::OutSaveFile &out);
void makeSaveOS(Common::OutSaveFile &out);
void makeSave(const Common::String &saveFileName, uint32 playtime,
Common::String desc, bool isAutosave);
void mainLoop(int bootScriptIdx);
void readVolCnf();
Common::String getTargetSaveStateName(Common::String target, int slot) const;
bool _preLoad;
int _timerDelayMultiplier;
public:
// TODO: These are pseudo-global vars
// They better belong to appropriate classes
Common::Array<AnimData> _animDataTable;
Common::List<BGIncrust> _bgIncrustList;
Common::StringArray _messageTable;
Common::Array<ObjectStruct> _objectTable;
Common::List<overlay> _overlayList;
Common::Array<PalEntry> _palArray;
Common::Array<PartBuffer> _partBuffer;
ScriptList _globalScripts;
ScriptList _objectScripts;
RawObjectScriptArray _relTable; ///< Object script bytecode table
/**
* Global variables.
* 255 of these are saved, but there's one more that's used for bypassing the copy protection.
* In CineEngine::mainLoop(int bootScriptIdx) there's this code: globalVars[VAR_BYPASS_PROTECTION] = 0;
* And as VAR_BYPASS_PROTECTION is 255 that's why we're allocating one more than we otherwise would.
*/
ScriptVars _globalVars;
RawScriptArray _scriptTable; ///< Table of script bytecode
FWScriptInfo *_scriptInfo;
Common::Array<int16> _zoneData;
Common::Array<uint16> _zoneQuery; ///< Only exists in Operation Stealth
Common::List<SeqListElement> _seqList;
Common::String _commandBuffer;
Common::Array<Common::KeyState> _keyInputList;
Common::Array<Common::CustomEventType> _actionList;
};
extern CineEngine *g_cine;
extern Sound *g_sound;
#define BOOT_PRC_NAME "AUTO00.PRC"
#define BOOT_SCRIPT_INDEX 1
#define COPY_PROT_FAIL_PRC_NAME "L201.ANI"
enum {
// Both FW and OS
VAR_MOUSE_X_POS = 249,
VAR_MOUSE_Y_POS = 250,
// FW only
VAR_MOUSE_X_MODE = 253,
VAR_MOUSE_Y_MODE = 251,
// OS only
VAR_MOUSE_X_POS_2ND = 251, // Many times used in conjunction with VAR_MOUSE_X_POS
VAR_MOUSE_Y_POS_2ND = 252, // Many times used in conjunction with VAR_MOUSE_Y_POS
VAR_BYPASS_PROTECTION = 255,
VAR_LOW_MEMORY = 0
};
enum {
MOUSE_CURSOR_NORMAL = 0,
MOUSE_CURSOR_DISK,
MOUSE_CURSOR_CROSS
};
enum {
kCineDebugScript = 1,
kCineDebugPart,
kCineDebugSound,
kCineDebugCollision,
};
enum {
kCmpEQ = (1 << 0),
kCmpGT = (1 << 1),
kCmpLT = (1 << 2)
};
} // End of namespace Cine
#endif

View 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 cine "Cinematique evo 1" yes "" "" "" "midi"

66
engines/cine/console.cpp Normal file
View File

@@ -0,0 +1,66 @@
/* 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 "cine/console.h"
#include "cine/cine.h"
namespace Cine {
bool labyrinthCheat;
bool hacksEnabled;
CineConsole::CineConsole(CineEngine *vm) : GUI::Debugger(), _vm(vm) {
assert(_vm);
registerCmd("labyrinthCheat", WRAP_METHOD(CineConsole, Cmd_LabyrinthCheat));
registerCmd("disableLabyrinthCheat", WRAP_METHOD(CineConsole, Cmd_DisableLabyrinthCheat));
registerCmd("disableHacks", WRAP_METHOD(CineConsole, Cmd_DisableHacks));
registerCmd("enableHacks", WRAP_METHOD(CineConsole, Cmd_EnableHacks));
labyrinthCheat = false;
hacksEnabled = true;
}
CineConsole::~CineConsole() {
}
// Activate Cheat during Scene 6 Labyrinth chased by Guards in Otto's Mansion
// This puzzle is hard, especially without save/load so this will aid playtesting.
bool CineConsole::Cmd_LabyrinthCheat(int argc, const char **argv) {
labyrinthCheat = true;
return true;
}
bool CineConsole::Cmd_DisableLabyrinthCheat(int argc, const char **argv) {
labyrinthCheat = false;
return true;
}
bool CineConsole::Cmd_DisableHacks(int argc, const char **argv) {
hacksEnabled = false;
return true;
}
bool CineConsole::Cmd_EnableHacks(int argc, const char **argv) {
hacksEnabled = true;
return true;
}
} // End of namespace Cine

50
engines/cine/console.h Normal file
View File

@@ -0,0 +1,50 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 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 CINE_CONSOLE_H
#define CINE_CONSOLE_H
#include "gui/debugger.h"
namespace Cine {
extern bool labyrinthCheat;
extern bool hacksEnabled;
class CineEngine;
class CineConsole : public GUI::Debugger {
public:
CineConsole(CineEngine *vm);
~CineConsole(void) override;
private:
CineEngine *_vm;
bool Cmd_LabyrinthCheat(int argc, const char **argv);
bool Cmd_DisableLabyrinthCheat(int argc, const char **argv);
bool Cmd_DisableHacks(int argc, const char **argv);
bool Cmd_EnableHacks(int argc, const char **argv);
};
} // End of namespace Cine
#endif

7
engines/cine/credits.pl Normal file
View File

@@ -0,0 +1,7 @@
begin_section("Cine");
add_person("Vincent Hamm", "yaz0r", "(retired)");
add_person("Pawe&#322; Ko&#322;odziejski", "aquadran", "");
add_person("Gregory Montoir", "cyx", "(retired)");
add_person("Kari Salminen", "Buddha^", "");
add_person("Eugene Sandulenko", "sev", "");
end_section();

View File

@@ -0,0 +1,68 @@
/* 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 "engines/advancedDetector.h"
#include "cine/detection.h"
#include "cine/cine.h"
static const PlainGameDescriptor cineGames[] = {
{"fw", "Future Wars"},
{"os", "Operation Stealth"},
{nullptr, nullptr}
};
#include "cine/detection_tables.h"
static const DebugChannelDef debugFlagList[] = {
{Cine::kCineDebugScript, "Script", "Script debug level"},
{Cine::kCineDebugPart, "Part", "Part debug level"},
{Cine::kCineDebugSound, "Sound", "Sound debug level"},
{Cine::kCineDebugCollision, "Collision", "Collision debug level"},
DEBUG_CHANNEL_END
};
class CineMetaEngineDetection : public AdvancedMetaEngineDetection<Cine::CINEGameDescription> {
public:
CineMetaEngineDetection() : AdvancedMetaEngineDetection(Cine::gameDescriptions, cineGames) {
_guiOptions = GUIO4(GUIO_NOSPEECH, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_TRANSPARENT_DIALOG_BOXES, GAMEOPTION_TTS);
}
const char *getName() const override {
return "cine";
}
const char *getEngineName() const override {
return "Cinematique evo 1";
}
const char *getOriginalCopyright() const override {
return "Cinematique evo 1 (C) Delphine Software";
}
const DebugChannelDef *getDebugChannels() const override {
return debugFlagList;
}
};
REGISTER_PLUGIN_STATIC(CINE_DETECTION, PLUGIN_TYPE_ENGINE_DETECTION, CineMetaEngineDetection);

56
engines/cine/detection.h Normal file
View File

@@ -0,0 +1,56 @@
/* 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 CINE_DETECTION_H
#define CINE_DETECTION_H
#include "engines/advancedDetector.h"
namespace Cine {
enum CineGameType {
GType_FW = 1,
GType_OS
};
enum CineGameFeatures {
GF_CD = 1 << 0,
GF_DEMO = 1 << 1,
GF_ALT_FONT = 1 << 2,
GF_CRYPTED_BOOT_PRC = 1 << 3
};
struct CINEGameDescription {
AD_GAME_DESCRIPTION_HELPERS(desc);
ADGameDescription desc;
int gameType;
uint32 features;
};
#define GAMEOPTION_ORIGINAL_SAVELOAD GUIO_GAMEOPTIONS1
#define GAMEOPTION_TRANSPARENT_DIALOG_BOXES GUIO_GAMEOPTIONS2
#define GAMEOPTION_TTS GUIO_GAMEOPTIONS3
} // End of namespace Cine
#endif // CINE_DETECTION_H

View File

@@ -0,0 +1,580 @@
/* 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/>.
*
*/
namespace Cine {
static const CINEGameDescription gameDescriptions[] = {
{
{
"fw",
"",
AD_ENTRY1("part01", "61d003202d301c29dd399acfb1354310"),
Common::EN_ANY,
Common::kPlatformDOS,
ADGF_NO_FLAGS,
GUIO0()
},
GType_FW,
0,
},
// This is a CD version of Future Wars published by Sony.
// This version has a crypted AUTO00.PRC.
{
{
"fw",
"Sony CD version",
AD_ENTRY2s("AUTO00.PRC", "4fe1e7930b38e3c63f0f2474d471bf8f", AD_NO_SIZE,
"PART01", "61d003202d301c29dd399acfb1354310", AD_NO_SIZE),
Common::EN_USA,
Common::kPlatformDOS,
ADGF_CD,
GUIO0()
},
GType_FW,
GF_CD | GF_CRYPTED_BOOT_PRC,
},
{
// This is the Future Wars CD version
// with a French translation patch (#12490).
{
"fw",
"Sony CD version with French translation patch",
AD_ENTRY2s("AUTO00.PRC", "4fe1e7930b38e3c63f0f2474d471bf8f", AD_NO_SIZE,
"PART01", "5d1acb97abe9591f9008e00d07add95a", AD_NO_SIZE),
Common::FR_FRA,
Common::kPlatformDOS,
ADGF_CD,
GUIO0()
},
GType_FW,
GF_CD | GF_CRYPTED_BOOT_PRC,
},
{
// This is the version included in the UK "Classic Collection"
{
"fw",
"",
AD_ENTRY1("part01", "91d7271155520eae6915a9dd2dac120c"),
Common::EN_ANY,
Common::kPlatformDOS,
ADGF_NO_FLAGS,
GUIO0()
},
GType_FW,
0,
},
{
{
"fw",
"",
AD_ENTRY1("part01", "f5e98fcca3fb5e7afa284c81c39d8b14"),
Common::DE_DEU,
Common::kPlatformDOS,
ADGF_NO_FLAGS,
GUIO0()
},
GType_FW,
GF_ALT_FONT,
},
{
{
"fw",
"",
AD_ENTRY1("part01", "570109f965c7f53984b98c83d86eb206"),
Common::ES_ESP,
Common::kPlatformDOS,
ADGF_NO_FLAGS,
GUIO0()
},
GType_FW,
GF_ALT_FONT,
},
{
{
"fw",
"",
AD_ENTRY1("part01", "5d1acb97abe9591f9008e00d07add95a"),
Common::FR_FRA,
Common::kPlatformDOS,
ADGF_NO_FLAGS,
GUIO0()
},
GType_FW,
0,
},
{
{
"fw",
"",
AD_ENTRY1("part01", "57afd280b598b4180fda6689fbedc4b8"),
Common::EN_ANY,
Common::kPlatformAmiga,
ADGF_NO_FLAGS,
GUIO1(GUIO_NOMIDI)
},
GType_FW,
0,
},
{ // Amiga "Interplay" labeled version
{
"fw",
"",
AD_ENTRY1("part01", "a17a5eb15200c63276d486a88263ccd0"),
Common::EN_USA,
Common::kPlatformAmiga,
ADGF_NO_FLAGS,
GUIO1(GUIO_NOMIDI)
},
GType_FW,
0,
},
{
{
"fw",
"",
AD_ENTRY1("part01", "3a87a913e0e33963a48a7f822ca0eb0e"),
Common::DE_DEU,
Common::kPlatformAmiga,
ADGF_NO_FLAGS,
GUIO1(GUIO_NOMIDI)
},
GType_FW,
GF_ALT_FONT,
},
{
{
"fw",
"",
AD_ENTRY1("part01", "5ad0007ccd5f7b3dd6b15ea7f281f9e1"),
Common::ES_ESP,
Common::kPlatformAmiga,
ADGF_NO_FLAGS,
GUIO1(GUIO_NOMIDI)
},
GType_FW,
0,
},
{
{
"fw",
"",
AD_ENTRY1("part01", "460f2da8793bc581a2d4b6fc19ccb5ae"),
Common::FR_FRA,
Common::kPlatformAmiga,
ADGF_NO_FLAGS,
GUIO1(GUIO_NOMIDI)
},
GType_FW,
0,
},
{
{
"fw",
"",
AD_ENTRY1("part01", "1c8e5207743172134409ac58860021af"),
Common::IT_ITA,
Common::kPlatformAmiga,
ADGF_NO_FLAGS,
GUIO1(GUIO_NOMIDI)
},
GType_FW,
0,
},
{
{
"fw",
"Demo",
AD_ENTRY2s("demo", "0f50767cd964e302d3af0ba2528df8c4", AD_NO_SIZE,
"demo.prc", "d2ac3a743d288359c63644ea7071edae", AD_NO_SIZE),
Common::EN_ANY,
Common::kPlatformAmiga,
ADGF_DEMO,
GUIO1(GUIO_NOMIDI)
},
GType_FW,
0,
},
{
{
"fw",
"",
AD_ENTRY1("part01", "36050db13af57e462ca1adc4df99de4e"),
Common::EN_ANY,
Common::kPlatformAtariST,
ADGF_NO_FLAGS,
GUIO1(GUIO_NOMIDI)
},
GType_FW,
0,
},
{
{
"fw",
"",
AD_ENTRY1("part01", "ef245573b7dab0d4825ceb98e37cef4d"),
Common::FR_FRA,
Common::kPlatformAtariST,
ADGF_NO_FLAGS,
GUIO1(GUIO_NOMIDI)
},
GType_FW,
0,
},
//////////////////////////////////////////////////////////////////////////////////////
// ___ _ _ ____ _ _ _ _ //
// / _ \ _ __ ___ _ __ __ _| |_(_) ___ _ __ / ___|| |_ ___ __ _| | |_| |__ //
// | | | | '_ \ / _ \ '__/ _` | __| |/ _ \| '_ \ \___ \| __/ _ \/ _` | | __| '_ \ //
// | |_| | |_) | __/ | | (_| | |_| | (_) | | | | ___) | || __/ (_| | | |_| | | | //
// \___/| .__/ \___|_| \__,_|\__|_|\___/|_| |_| |____/ \__\___|\__,_|_|\__|_| |_| //
// |_| //
//////////////////////////////////////////////////////////////////////////////////////
{
{
"os",
"256 colors",
AD_ENTRY1("procs00", "d6752e7d25924cb866b61eb7cb0c8b56"),
Common::EN_GRB,
Common::kPlatformDOS,
ADGF_NO_FLAGS,
GUIO0()
},
GType_OS,
0,
},
{
// This is a 16 color PC version (It came on three 720kB 3.5" disks).
// The protagonist is named John Glames in this version.
{
"os",
"",
AD_ENTRY1("procs1", "9629129b86979fa592c1787385bf3695"),
Common::EN_GRB,
Common::kPlatformDOS,
ADGF_NO_FLAGS,
GUIO0()
},
GType_OS,
0,
},
{
{
"os",
"",
AD_ENTRY1("procs1", "d8c3a9d05a63e4cfa801826a7063a126"),
Common::EN_USA,
Common::kPlatformDOS,
ADGF_NO_FLAGS,
GUIO0()
},
GType_OS,
0,
},
{
{
"os",
"256 colors",
AD_ENTRY1("procs00", "862a75d76fb7fffec30e52be9ad1c474"),
Common::EN_USA,
Common::kPlatformDOS,
ADGF_NO_FLAGS,
GUIO0()
},
GType_OS,
GF_CD,
},
{
{
"os",
"",
AD_ENTRY1("procs1", "39b91ae35d1297ce0a76a1a803ca1593"),
Common::DE_DEU,
Common::kPlatformDOS,
ADGF_NO_FLAGS,
GUIO0()
},
GType_OS,
0,
},
{
{
"os",
"",
AD_ENTRY1("procs1", "74c2dabd9d212525fca8875a5f6d8994"),
Common::ES_ESP,
Common::kPlatformDOS,
ADGF_NO_FLAGS,
GUIO0()
},
GType_OS,
0,
},
{
{
"os",
"256 colors",
AD_ENTRY2s("procs1", "74c2dabd9d212525fca8875a5f6d8994", AD_NO_SIZE,
"sds1", "75443ba39cdc95667e07d7118e5c151c", AD_NO_SIZE),
Common::ES_ESP,
Common::kPlatformDOS,
ADGF_NO_FLAGS,
GUIO0()
},
GType_OS,
GF_CD,
},
{
{
"os",
"256 colors",
AD_ENTRY1("procs00", "f143567f08cfd1a9b1c9a41c89eadfef"),
Common::FR_FRA,
Common::kPlatformDOS,
ADGF_NO_FLAGS,
GUIO0()
},
GType_OS,
0,
},
{ // Submitted by Kurufinwe21 in #11617 (16 color French floppy version)
{
"os",
"",
AD_ENTRY1s("procs1", "3f9edde60ccb380f716942c5b059d1d5", 14116),
Common::FR_FRA,
Common::kPlatformDOS,
ADGF_NO_FLAGS,
GUIO0()
},
GType_OS,
0,
},
{ // Submitted by laenion in #11466 (German Atari ST version)
{
"os",
"",
AD_ENTRY1s("procs1", "b67af92a92ac5fd4add55893c15df76e", 63402),
Common::DE_DEU,
Common::kPlatformAtariST,
ADGF_NO_FLAGS,
GUIO0()
},
GType_OS,
0,
},
{
{
"os",
"",
AD_ENTRY1("procs1", "da066e6b8dd93f2502c2a3755f08dc12"),
Common::IT_ITA,
Common::kPlatformDOS,
ADGF_NO_FLAGS,
GUIO0()
},
GType_OS,
0,
},
{ // Submitted by Nyarlathotep7777 in #12812 (Italian Amiga version)
{
"os",
"",
AD_ENTRY1s("procs1", "d7458be2b14d77410e6330148ca6c371", 61682),
Common::IT_ITA,
Common::kPlatformAmiga,
ADGF_NO_FLAGS,
GUIO0()
},
GType_OS,
0,
},
{
{
"os",
"Demo",
AD_ENTRY1("demo_os", "043859e4cfe3977ad95b6efd00b21c62"),
Common::EN_GRB,
Common::kPlatformDOS,
ADGF_DEMO,
GUIO0()
},
GType_OS,
GF_DEMO,
},
{
{
"os",
"",
AD_ENTRY1("procs0", "a9da5531ead0ebf9ad387fa588c0cbb0"),
Common::EN_GRB,
Common::kPlatformAmiga,
ADGF_NO_FLAGS,
GUIO1(GUIO_NOMIDI)
},
GType_OS,
0,
},
{
{
"os",
"alt",
AD_ENTRY1("procs0", "8a429ced2f4acff8a15ae125174042e8"),
Common::EN_GRB,
Common::kPlatformAmiga,
ADGF_NO_FLAGS,
GUIO1(GUIO_NOMIDI)
},
GType_OS,
0,
},
{
{
"os",
"",
AD_ENTRY1("procs0", "d5f27e33fc29c879f36f15b86ccfa58c"),
Common::EN_USA,
Common::kPlatformAmiga,
ADGF_NO_FLAGS,
GUIO1(GUIO_NOMIDI)
},
GType_OS,
0,
},
{
{
"os",
"",
AD_ENTRY1("procs0", "8b7dce249821d3a62b314399c4334347"),
Common::DE_DEU,
Common::kPlatformAmiga,
ADGF_NO_FLAGS,
GUIO1(GUIO_NOMIDI)
},
GType_OS,
0,
},
{
{
"os",
"",
AD_ENTRY1("procs0", "35fc295ddd0af9da932d256ba799a4b0"),
Common::ES_ESP,
Common::kPlatformAmiga,
ADGF_NO_FLAGS,
GUIO1(GUIO_NOMIDI)
},
GType_OS,
0,
},
{
{
"os",
"",
AD_ENTRY1("procs0", "d4ea4a97e01fa67ea066f9e785050ed2"),
Common::FR_FRA,
Common::kPlatformAmiga,
ADGF_NO_FLAGS,
GUIO1(GUIO_NOMIDI)
},
GType_OS,
0,
},
{
{
"os",
"Demo",
AD_ENTRY1("demo_os", "dfc30c6a57998a56e8980f7ea3413867"),
Common::EN_GRB,
Common::kPlatformAmiga,
ADGF_DEMO,
GUIO1(GUIO_NOMIDI)
},
GType_OS,
GF_DEMO,
},
{
{
"os",
"",
AD_ENTRY1("procs0", "1501d5ae364b2814a33ed19347c3fcae"),
Common::EN_GRB,
Common::kPlatformAtariST,
ADGF_NO_FLAGS,
GUIO1(GUIO_NOMIDI)
},
GType_OS,
0,
},
{
{
"os",
"",
AD_ENTRY1("procs0", "2148d25de3219dd4a36580ca735d0afa"),
Common::FR_FRA,
Common::kPlatformAtariST,
ADGF_NO_FLAGS,
GUIO1(GUIO_NOMIDI)
},
GType_OS,
0,
},
{ AD_TABLE_END_MARKER, 0, 0 }
};
} // End of namespace Cine

2967
engines/cine/gfx.cpp Normal file

File diff suppressed because it is too large Load Diff

346
engines/cine/gfx.h Normal file
View File

@@ -0,0 +1,346 @@
/* 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 CINE_GFX_H
#define CINE_GFX_H
#include "common/noncopyable.h"
#include "common/rect.h"
#include "common/stack.h"
#include "cine/object.h"
#include "cine/bg_list.h"
namespace Cine {
extern byte *collisionPage;
static const int kCollisionPageBgIdxAlias = 8;
enum BackBufferSource {
BEFORE_OPENING_MENU = 0,
BEFORE_TAKING_THUMBNAIL,
MAX_BACK_BUFFER_SOURCES
};
/**
* Background with palette
*/
struct palBg {
byte *bg; ///< Background data
Cine::Palette pal; ///< Background color palette
char name[15]; ///< Background filename
/** @brief Default constructor. */
palBg() : bg(NULL), pal(), name() {
// Make sure the name is empty (Maybe this is not needed?)
memset(this->name, 0, sizeof(this->name));
}
/** @brief Clears the struct (Releases allocated memory etc). */
void clear() {
// In Operation Stealth the 9th background is sometimes aliased to
// the collision page so we should take care not to double delete it
// (The collision page is deleted elsewhere).
if (this->bg != collisionPage) {
delete[] this->bg;
}
this->bg = NULL;
this->pal.clear();
memset(this->name, 0, sizeof(this->name));
}
};
class FWRenderer;
class Menu {
public:
enum Type {
kSelectionMenu,
kTextInputMenu
};
Menu(Type t) : _type(t) {}
virtual ~Menu() {}
Type getType() const { return _type; }
virtual void drawMenu(FWRenderer &r, bool top) = 0;
private:
const Type _type;
};
class SelectionMenu : public Menu {
public:
SelectionMenu(Common::Point p, int width, Common::StringArray elements);
int getElementCount() const { return _elements.size(); }
void setSelection(int selection);
void drawMenu(FWRenderer &r, bool top) override;
private:
const Common::Point _pos;
const int _width;
const Common::StringArray _elements;
int _selection;
};
class TextInputMenu : public Menu {
public:
TextInputMenu(Common::Point p, int width, const char *info);
void setInput(const char *input, int cursor);
void drawMenu(FWRenderer &r, bool top) override;
private:
const Common::Point _pos;
const int _width;
const Common::String _info;
Common::String _input;
int _cursor;
};
/**
* Future Wars renderer
*
* Screen backbuffer is not cleared between frames.
*/
class FWRenderer : public Common::NonCopyable {
// TODO: Consider getting rid of this
friend class SelectionMenu;
friend class TextInputMenu;
private:
byte * _savedBackBuffers[MAX_BACK_BUFFER_SOURCES];
byte *_background; ///< Current background
char _bgName[13]; ///< Background filename
Common::String _cmd; ///< Player command string
protected:
static const int _screenSize = 320 * 200; ///< Screen size
static const int _screenWidth = 320; ///< Screen width
static const int _screenHeight = 200; ///< Screen height
byte *_backBuffer; ///< Screen backbuffer
Cine::Palette _backupPal; ///< The backup color palette
Cine::Palette _activePal; ///< The active color palette
Common::Stack<Menu *> _menuStack; ///< All displayed menus
int _changePal; ///< Load active palette to video backend on next frame
bool _showCollisionPage; ///< Should we show the collision page instead of the back buffer? Used for debugging.
uint32 _fadeToBlackLastCalledMs;
virtual const Cine::Palette& getFadeInSourcePalette();
void fillSprite(const ObjectStruct &obj, uint8 color = 0);
void drawMaskedSprite(const ObjectStruct &obj, const byte *mask);
virtual void drawSprite(const ObjectStruct &obj);
int drawMessage(const char *str, int x, int y, int width, int color, bool draw = true);
void drawPlainBox(int x, int y, int width, int height, byte color);
byte transparentDialogBoxStartColor();
void drawTransparentBox(int x, int y, int width, int height);
void drawBorder(int x, int y, int width, int height, byte color);
void drawDoubleBorder(int x, int y, int width, int height, byte color);
virtual int drawChar(char character, int x, int y, bool draw = true);
virtual int undrawChar(char character, int x, int y);
void drawLine(int x, int y, int width, int height, byte color);
void remaskSprite(byte *mask, Common::List<overlay>::iterator it);
virtual void drawBackground();
virtual void clearBackBuffer();
virtual void removeSavedBackBuffers();
virtual void renderOverlay(const Common::List<overlay>::iterator &it);
void drawOverlays();
virtual void blit(bool useCollisionPage);
public:
uint16 _messageBg; ///< Message box background color
uint16 _cmdY; ///< Player command string position on screen
FWRenderer();
virtual ~FWRenderer();
virtual bool initialize();
/** Test if renderer is ready to draw */
virtual bool ready() { return _background != NULL; }
virtual unsigned int currentBg() { return 0; };
virtual unsigned int scrollBg() { return 0; }
virtual bool useTransparentDialogBoxes();
virtual void clear();
void drawFrame(bool wait = false);
void drawCommand();
void setCommand(Common::String cmd);
Common::String getCommand();
virtual void blit();
virtual void blitBackBuffer();
virtual void incrustMask(const BGIncrust &incrust, uint8 color = 0);
virtual void incrustSprite(const BGIncrust &incrust);
virtual bool hasSavedBackBuffer(BackBufferSource source);
/** Saves back buffer content without palette. */
virtual void saveBackBuffer(BackBufferSource source);
virtual void popSavedBackBuffer(BackBufferSource source);
/** Restores back buffer content without palette. */
virtual void restoreSavedBackBuffer(BackBufferSource source);
virtual void removeSavedBackBuffer(BackBufferSource source);
virtual int16 addBackground(const char *bgName, uint16 bgIdx);
virtual void loadBg16(const byte *bg, const char *name, unsigned int idx = 0);
virtual void loadCt16(const byte *ct, const char *name);
virtual void loadBg256(const byte *bg, const char *name, unsigned int idx = 0);
virtual void loadCt256(const byte *ct, const char *name);
virtual void selectBg(unsigned int idx);
virtual void selectScrollBg(unsigned int idx);
virtual void setScroll(unsigned int shift);
virtual uint getScroll() const;
virtual void removeBg(unsigned int idx);
virtual void saveBgNames(Common::OutSaveFile &fHandle);
virtual const char *getBgName(uint idx = 0) const;
virtual void setBlackPalette(bool updateChangePal);
virtual void setPalette();
virtual void restorePalette(Common::SeekableReadStream &fHandle, int version);
virtual void savePalette(Common::OutSaveFile &fHandle);
virtual void rotatePalette(int firstIndex, int lastIndex, int mode);
virtual void transformPalette(int first, int last, int r, int g, int b);
void pushMenu(Menu *menu);
Menu *popMenu();
void clearMenuStack();
virtual uint fadeDelayMs();
virtual uint fadeToBlackMinMs();
virtual void fadeToBlack();
virtual void fadeFromBlack();
void showCollisionPage(bool state);
void drawString(const char *string, byte param);
int getStringWidth(const char *str);
};
/**
* Operation Stealth renderer
*/
class OSRenderer : public FWRenderer {
private:
Common::Array<palBg> _bgTable; ///< Table of backgrounds loaded into renderer (Maximum is 9)
unsigned int _currentBg; ///< Current background
unsigned int _scrollBg; ///< Current scroll background
unsigned int _bgShift; ///< Background shift
protected:
void setBackground8ToCollisionPage();
const Cine::Palette& getFadeInSourcePalette() override;
void drawSprite(const ObjectStruct &obj) override;
void drawSprite(overlay *overlayPtr, const byte *spritePtr, int16 width, int16 height, byte *page, int16 x, int16 y, byte transparentColor, byte bpp);
int drawChar(char character, int x, int y, bool draw = true) override;
void drawBackground() override;
void renderOverlay(const Common::List<overlay>::iterator &it) override;
public:
OSRenderer();
~OSRenderer() override;
bool initialize() override;
/** Test if renderer is ready to draw */
bool ready() override { return _bgTable[_currentBg].bg != NULL; }
unsigned int currentBg() override { return _currentBg; };
unsigned int scrollBg() override { return _scrollBg; }
void clear() override;
void incrustMask(const BGIncrust &incrust, uint8 color = 0) override;
void incrustSprite(const BGIncrust &incrust) override;
int16 addBackground(const char *bgName, uint16 bgIdx) override;
void loadBg16(const byte *bg, const char *name, unsigned int idx = 0) override;
void loadCt16(const byte *ct, const char *name) override;
void loadBg256(const byte *bg, const char *name, unsigned int idx = 0) override;
void loadCt256(const byte *ct, const char *name) override;
void selectBg(unsigned int idx) override;
void selectScrollBg(unsigned int idx) override;
void setScroll(unsigned int shift) override;
uint getScroll() const override;
void removeBg(unsigned int idx) override;
void saveBgNames(Common::OutSaveFile &fHandle) override;
const char *getBgName(uint idx = 0) const override;
void restorePalette(Common::SeekableReadStream &fHandle, int version) override;
void savePalette(Common::OutSaveFile &fHandle) override;
void rotatePalette(int firstIndex, int lastIndex, int mode) override;
void transformPalette(int first, int last, int r, int g, int b) override;
};
void gfxDrawSprite(byte *src4, uint16 sw, uint16 sh, byte *dst4, int16 sx, int16 sy);
extern FWRenderer *renderer;
void setMouseCursor(int cursor);
void gfxCopyPage(byte *source, byte *dest);
void transformPaletteRange(byte startColor, byte numColor, int8 r, int8 g, int8 b);
void gfxFlipPage();
void gfxDrawMaskedSprite(const byte *ptr, const byte *msk, uint16 width, uint16 height, byte *page, int16 x, int16 y);
void gfxFillSprite(const byte *src4, uint16 sw, uint16 sh, byte *dst4, int16 sx, int16 sy, uint8 fillColor = 0);
void gfxUpdateSpriteMask(byte *destMask, int16 x, int16 y, int16 width, int16 height, const byte *maskPtr, int16 xm, int16 ym, int16 maskWidth, int16 maskHeight);
void gfxDrawLine(int16 x1, int16 y1, int16 x2, int16 y2, byte color, byte *page);
void gfxDrawPlainBox(int16 x1, int16 y1, int16 x2, int16 y2, byte color);
void gfxResetPage(byte *pagePtr);
int16 gfxGetBit(int16 x, int16 y, const byte *ptr, int16 width);
byte gfxGetColor(int16 x, int16 y, const byte *ptr, int16 width);
void gfxResetRawPage(byte *pageRaw);
void gfxConvertSpriteToRaw(byte *dst, const byte *src, uint16 w, uint16 h);
void gfxCopyRawPage(byte *source, byte *dest);
void gfxFlipRawPage(byte *frontBuffer);
void drawSpriteRaw(const byte *spritePtr, const byte *maskPtr, int16 width, int16 height, byte *page, int16 x, int16 y);
void gfxDrawPlainBoxRaw(int16 x1, int16 y1, int16 x2, int16 y2, byte color, byte *page);
void drawSpriteRaw2(const byte *spritePtr, byte transColor, int16 width, int16 height, byte *page, int16 x, int16 y);
void maskBgOverlay(int targetBgIdx, const byte *spritePtr, const byte *maskPtr, int16 width, int16 height, byte *page, int16 x, int16 y);
void fadeFromBlack();
void fadeToBlack();
//void gfxDrawMaskedSprite(byte *param1, byte *param2, byte *param3, byte *param4, int16 param5);
void gfxWaitVBL();
void gfxRedrawMouseCursor();
void blitScreen(byte *frontBuffer, byte *backbuffer);
void blitRawScreen(byte *frontBuffer);
void flip();
} // End of namespace Cine
#endif

571
engines/cine/main_loop.cpp Normal file
View File

@@ -0,0 +1,571 @@
/* 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/scummsys.h"
#include "common/events.h"
#include "common/system.h"
#include "cine/main_loop.h"
#include "cine/object.h"
#include "cine/various.h"
#include "cine/bg_list.h"
#include "cine/sound.h"
#include "backends/audiocd/audiocd.h"
namespace Cine {
struct MouseStatusStruct {
int left;
int right;
};
MouseStatusStruct mouseData;
uint16 mouseRight = 0;
uint16 mouseLeft = 0;
uint16 mouseUpdateStatus;
uint16 dummyU16;
static void processEvent(Common::Event &event) {
switch (event.type) {
case Common::EVENT_LBUTTONDOWN:
mouseLeft = 1;
break;
case Common::EVENT_RBUTTONDOWN:
mouseRight = 1;
break;
case Common::EVENT_MBUTTONDOWN:
mouseLeft = mouseRight = 1;
break;
case Common::EVENT_LBUTTONUP:
mouseLeft = 0;
break;
case Common::EVENT_RBUTTONUP:
mouseRight = 0;
break;
case Common::EVENT_MBUTTONUP:
mouseLeft = mouseRight = 0;
break;
case Common::EVENT_MOUSEMOVE:
#ifdef USE_TTS
g_cine->mouseOverButton();
#endif
break;
case Common::EVENT_WHEELUP:
g_cine->_actionList.push_back(Common::CustomEventType(kActionMenuOptionUp));
break;
case Common::EVENT_WHEELDOWN:
g_cine->_actionList.push_back(Common::CustomEventType(kActionMenuOptionDown));
break;
case Common::EVENT_CUSTOM_ENGINE_ACTION_START:
g_cine->_actionList.push_back(event.customType);
switch (event.customType) {
case kActionMouseLeft:
if (allowPlayerInput) {
mouseLeft = 1;
}
break;
case kActionMouseRight:
if (allowPlayerInput) {
mouseRight = 1;
}
break;
case kActionExamine:
if (allowPlayerInput) {
playerCommand = 0; // EXAMINE
makeCommandLine();
}
break;
case kActionTake:
if (allowPlayerInput) {
playerCommand = 1; // TAKE
makeCommandLine();
}
break;
case kActionInventory:
if (allowPlayerInput && !inMenu) {
playerCommand = 2; // INVENTORY
g_cine->sayText(defaultActionCommand[playerCommand], Common::TextToSpeechManager::INTERRUPT);
makeCommandLine();
}
break;
case kActionUse:
if (allowPlayerInput && !inMenu) {
playerCommand = 3; // USE
g_cine->sayText(defaultActionCommand[playerCommand], Common::TextToSpeechManager::INTERRUPT);
makeCommandLine();
}
break;
case kActionActivate:
if (allowPlayerInput) {
playerCommand = 4; // ACTIVATE
makeCommandLine();
}
break;
case kActionSpeak:
if (allowPlayerInput) {
playerCommand = 5; // SPEAK
makeCommandLine();
}
break;
case kActionActionMenu:
if (allowPlayerInput && !inMenu) {
makeActionMenu();
makeCommandLine();
}
break;
case kActionSystemMenu:
if (!inMenu) {
g_cine->makeSystemMenu();
}
break;
case kActionCollisionPage:
renderer->showCollisionPage(true);
break;
case kActionGameSpeedDefault:
g_cine->setDefaultGameSpeed();
break;
case kActionGameSpeedSlower:
g_cine->modifyGameSpeed(-1); // Slower
break;
case kActionGameSpeedFaster:
g_cine->modifyGameSpeed(+1); // Faster
break;
case kActionMoveLeft:
moveUsingKeyboard(-1, 0); // Left
break;
case kActionMoveRight:
moveUsingKeyboard(+1, 0); // Right
break;
case kActionMoveUp:
moveUsingKeyboard(0, +1); // Up
break;
case kActionMoveDown:
moveUsingKeyboard(0, -1); // Down
break;
case kActionMoveUpRight:
moveUsingKeyboard(+1, +1); // Up & Right
break;
case kActionMoveUpLeft:
moveUsingKeyboard(-1, +1); // Up & Left
break;
case kActionMoveDownLeft:
moveUsingKeyboard(-1, -1); // Down & Left
break;
case kActionMoveDownRight:
moveUsingKeyboard(+1, -1); // Down & Right
break;
default:
break;
};
break;
case Common::EVENT_CUSTOM_ENGINE_ACTION_END:
switch (event.customType) {
case kActionMouseLeft:
if (allowPlayerInput) {
mouseLeft = 0;
}
break;
case kActionMouseRight:
if (allowPlayerInput) {
mouseRight = 0;
}
break;
case kActionMoveUp:
case kActionMoveDown:
case kActionMoveLeft:
case kActionMoveRight:
case kActionMoveUpLeft:
case kActionMoveUpRight:
case kActionMoveDownLeft:
case kActionMoveDownRight:
// Stop ego movement made with keyboard when releasing a known key
moveUsingKeyboard(0, 0);
break;
case kActionCollisionPage:
renderer->showCollisionPage(false);
break;
default:
break;
}
break;
case Common::EVENT_KEYDOWN:
g_cine->_keyInputList.push_back(event.kbd);
break;
default:
break;
}
}
void manageEvents(CallSource callSource, EventTarget eventTarget, bool useMaxMouseButtonState, Common::Array<Common::Rect> rects) {
Common::EventManager *eventMan = g_system->getEventManager();
Common::Point mousePos;
uint keysPressed = g_cine->_keyInputList.size();
bool foundTarget = false;
int eventsChecked = 0;
uint16 maxMouseLeft = mouseLeft;
uint16 maxMouseRight = mouseRight;
uint32 waitStart = g_system->getMillis();
uint32 waitEnd = waitStart + g_cine->getTimerDelay();
uint32 frameEnd = waitStart + 20;
bool frameEnded = false;
bool waitEnded = false;
bool checkWaitEnd = (eventTarget == UNTIL_WAIT_ENDED);
bool updateScreen = false;
bool updateAudio = false;
do {
Common::Event event = Common::Event();
int eventsCheckedBeforePolling = eventsChecked;
while (!foundTarget && !frameEnded && (eventMan->pollEvent(event) || eventsChecked == 0)) {
processEvent(event);
eventsChecked++;
maxMouseLeft = MAX<uint16>(mouseLeft, maxMouseLeft);
maxMouseRight = MAX<uint16>(mouseRight, maxMouseRight);
bool mouseButtonDown = (mouseLeft != 0 || mouseRight != 0);
bool mouseButtonUp = !mouseButtonDown;
switch (eventTarget) {
case UNTIL_MOUSE_BUTTON_UP_DOWN_UP:
// fall through
case UNTIL_MOUSE_BUTTON_UP_DOWN:
// fall through
case UNTIL_MOUSE_BUTTON_UP:
// fall through
case UNTIL_MOUSE_BUTTON_UP_AND_WAIT_ENDED:
foundTarget = mouseButtonUp;
break;
case UNTIL_MOUSE_BUTTON_DOWN_UP:
// fall through
case UNTIL_MOUSE_BUTTON_DOWN:
foundTarget = mouseButtonDown;
break;
case UNTIL_MOUSE_BUTTON_DOWN_OR_KEY_UP_OR_DOWN_OR_IN_RECTS:
foundTarget = mouseButtonDown;
if (!g_cine->_actionList.empty()) {
Common::CustomEventType customType = g_cine->_actionList.back();
if (customType == kActionMenuOptionUp || customType == kActionMenuOptionDown) {
foundTarget = true;
}
}
mousePos = g_system->getEventManager()->getMousePos();
for (auto &r : rects) {
if (r.contains(mousePos)) {
foundTarget = true;
break;
}
}
break;
case UNTIL_MOUSE_BUTTON_DOWN_OR_KEY_INPUT:
foundTarget = mouseButtonDown || keysPressed < g_cine->_keyInputList.size();
break;
default:
break;
}
uint32 now = g_system->getMillis();
frameEnded = (now >= frameEnd);
waitEnded = (now >= waitEnd);
if (foundTarget) {
switch (eventTarget) {
case UNTIL_MOUSE_BUTTON_UP_DOWN_UP:
eventTarget = UNTIL_MOUSE_BUTTON_DOWN_UP;
foundTarget = false;
break;
case UNTIL_MOUSE_BUTTON_UP_DOWN:
eventTarget = UNTIL_MOUSE_BUTTON_DOWN;
foundTarget = false;
break;
case UNTIL_MOUSE_BUTTON_DOWN_UP:
eventTarget = UNTIL_MOUSE_BUTTON_UP;
foundTarget = false;
break;
case UNTIL_MOUSE_BUTTON_UP_AND_WAIT_ENDED:
eventTarget = UNTIL_WAIT_ENDED;
checkWaitEnd = true;
foundTarget = false;
break;
default:
break;
}
}
foundTarget |= (checkWaitEnd && waitEnded);
}
int eventsCheckedAfterPolling = eventsChecked;
bool eventQueueEmpty = (eventsCheckedBeforePolling == eventsCheckedAfterPolling);
if (eventQueueEmpty) {
uint32 now = g_system->getMillis();
frameEnded = (now >= frameEnd);
waitEnded = (now >= waitEnd);
}
if (eventTarget == UNTIL_WAIT_ENDED) {
foundTarget = waitEnded;
}
if (eventTarget == EMPTY_EVENT_QUEUE) {
foundTarget = eventQueueEmpty;
}
foundTarget |= (checkWaitEnd && waitEnded);
updateScreen = updateAudio = (foundTarget || frameEnded);
if (updateScreen) {
if (callSource != EXECUTE_PLAYER_INPUT) {
g_system->updateScreen();
} else {
// Make the command line (e.g. "EXAMINE DOOR" -> "EXAMINE BUTTON")
// responsive by updating it here.
if (allowPlayerInput && playerCommand != -1 && !mouseLeft && !mouseRight) {
// A player command is given, left and right mouse buttons are up
mousePos = eventMan->getMousePos();
playerCommandMouseLeftRightUp(mousePos.x, mousePos.y);
renderer->drawCommand();
}
renderer->blit();
}
}
if (updateAudio) {
g_system->getAudioCDManager()->update(); // For Future Wars CD version
}
if (frameEnded) {
frameEnd += 20;
}
g_system->delayMillis(10);
} while (!foundTarget && !g_cine->shouldQuit());
if (useMaxMouseButtonState) {
mouseData.left = maxMouseLeft;
mouseData.right = maxMouseRight;
} else {
mouseData.left = mouseLeft;
mouseData.right = mouseRight;
}
}
void getMouseData(uint16 param, uint16 *pButton, uint16 *pX, uint16 *pY) {
Common::Point mouse = g_system->getEventManager()->getMousePos();
*pX = mouse.x;
*pY = mouse.y;
*pButton = 0;
if (mouseData.right) {
(*pButton) |= 2;
}
if (mouseData.left) {
(*pButton) |= 1;
}
}
/** Removes elements from seqList that have their member variable var4 set to value -1. */
void purgeSeqList() {
Common::List<SeqListElement>::iterator it = g_cine->_seqList.begin();
while (it != g_cine->_seqList.end()) {
if (it->var4 == -1) {
// Erase the element and jump to the next element
it = g_cine->_seqList.erase(it);
} else {
// Let the element be and jump to the next element
++it;
}
}
}
void CineEngine::mainLoop(int bootScriptIdx) {
byte di;
if (_preLoad == false) {
resetBgIncrustList();
setTextWindow(0, 0, 20, 200);
errorVar = 0;
addScriptToGlobalScripts(bootScriptIdx);
menuVar = 0;
// gfxRedrawPage(page0c, page0, page0c, page0, -1);
// gfxWaitVBL();
// gfxRedrawMouseCursor();
inMenu = false;
allowPlayerInput = 0;
checkForPendingDataLoadSwitch = 0;
reloadBgPalOnNextFlip = 0;
forbidBgPalReload = 0;
gfxFadeOutCompleted = 0;
gfxFadeInRequested = 0;
safeControlsLastAccessedMs = 0;
lastSafeControlObjIdx = -1;
isDrawCommandEnabled = 0;
waitForPlayerClick = 0;
menuCommandLen = 0;
playerCommand = -1;
g_cine->_commandBuffer = "";
g_cine->_globalVars[VAR_MOUSE_X_POS] = 0;
g_cine->_globalVars[VAR_MOUSE_Y_POS] = 0;
if (g_cine->getGameType() == Cine::GType_OS) {
g_cine->_globalVars[VAR_MOUSE_X_POS_2ND] = 0;
g_cine->_globalVars[VAR_MOUSE_Y_POS_2ND] = 0;
g_cine->_globalVars[VAR_BYPASS_PROTECTION] = 0; // set to 1 to bypass the copy protection
g_cine->_globalVars[VAR_LOW_MEMORY] = 0; // set to 1 to disable some animations, sounds etc.
}
renderer->setBlackPalette(true); // Sets _changePal = true
newPrcName[0] = '\0';
newRelName[0] = '\0';
newObjectName[0] = '\0';
newMsgName[0] = '\0';
currentCtName[0] = '\0';
currentPartName[0] = '\0';
g_sound->stopMusic();
}
do {
// HACK: Force amount of oxygen left to maximum during Operation Stealth's first arcade sequence.
// This makes it possible to pass the arcade sequence for now.
// FIXME: Remove the hack and make the first arcade sequence normally playable.
/*
if (g_cine->getGameType() == Cine::GType_OS) {
Common::String bgName(renderer->getBgName());
// Check if the background is one of the three backgrounds
// that are only used during the first arcade sequence.
if (bgName == "28.PI1" || bgName == "29.PI1" || bgName == "30.PI1") {
static const uint oxygenObjNum = 202, maxOxygen = 264;
// Force the amount of oxygen left to the maximum.
g_cine->_objectTable[oxygenObjNum].x = maxOxygen;
}
}*/
// HACK: In Operation Stealth after the first arcade sequence jump player's position to avoid getting stuck.
// After the first arcade sequence the player comes up stairs from
// the water in Santa Paragua's downtown in front of the flower shop.
// Previously he was completely stuck after getting up the stairs.
// If the background is the one used in the flower shop scene ("21.PI1")
// and the player is at the exact location after getting up the stairs
// then we just nudge him a tiny bit away from the stairs and voila, he's free!
// Maybe the real problem behind all this is collision data related as it looks
// like there's some boundary right there near position (204, 110) which we can
// jump over by moving the character to (204, 109). The script handling the
// flower shop scene is AIRPORT.PRC's 13th script.
// FIXME: Remove the hack and solve what's really causing the problem in the first place.
if (hacksEnabled && g_cine->getGameType() == Cine::GType_OS) {
if (scumm_stricmp(renderer->getBgName(), "21.PI1") == 0 && g_cine->_objectTable[1].x == 204 && g_cine->_objectTable[1].y == 110) {
g_cine->_objectTable[1].y--; // Move the player character upward on-screen by one pixel
}
}
stopMusicAfterFadeOut();
di = executePlayerInput();
// Clear the zoneQuery table (Operation Stealth specific)
if (g_cine->getGameType() == Cine::GType_OS) {
Common::fill(g_cine->_zoneQuery.begin(), g_cine->_zoneQuery.end(), 0);
}
if (g_cine->getGameType() == Cine::GType_OS) {
processSeqList();
}
executeObjectScripts();
executeGlobalScripts();
purgeObjectScripts();
purgeGlobalScripts();
if (g_cine->getGameType() == Cine::GType_OS) {
purgeSeqList();
}
if (playerCommand == -1) {
setMouseCursor(MOUSE_CURSOR_NORMAL);
} else {
setMouseCursor(MOUSE_CURSOR_CROSS);
}
if (gfxFadeInRequested) {
gfxFadeOutCompleted = 0;
}
if (renderer->ready()) {
renderer->drawFrame(true);
}
// NOTE: In the original Future Wars and Operation Stealth messages
// were removed when running the drawOverlays function which is
// currently called from the renderer's drawFrame function.
removeMessages();
if (waitForPlayerClick) {
_messageLen <<= 3;
if (_messageLen < 800)
_messageLen = 800;
manageEvents(MAIN_LOOP_WAIT_FOR_PLAYER_CLICK, UNTIL_MOUSE_BUTTON_UP_DOWN_UP);
stopTextToSpeech();
waitForPlayerClick = 0;
}
if (checkForPendingDataLoadSwitch) {
checkForPendingDataLoad();
checkForPendingDataLoadSwitch = 0;
}
if (di) {
if ("quit"[menuCommandLen] == (char)di) {
++menuCommandLen;
if (menuCommandLen == 4) {
quitGame();
}
} else {
menuCommandLen = 0;
}
}
} while (!shouldQuit() && !_restartRequested);
hideMouse();
g_sound->stopMusic();
//if (g_cine->getGameType() == Cine::GType_OS) {
// freeUnkList();
//}
closePart();
}
} // End of namespace Cine

55
engines/cine/main_loop.h Normal file
View File

@@ -0,0 +1,55 @@
/* 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 CINE_MAINLOOP_H
#define CINE_MAINLOOP_H
namespace Cine {
enum CallSource {
EXECUTE_PLAYER_INPUT, ///< Called from executePlayerInput function.
MAIN_LOOP_WAIT_FOR_PLAYER_CLICK, ///< Called from mainLoop function's waiting for player click section.
MAKE_MENU_CHOICE, ///< Called from makeMenuChoice function.
MAKE_SYSTEM_MENU, ///< Called from makeSystemMenu function.
MAKE_TEXT_ENTRY_MENU, ///< Called from makeTextEntryMenu function.
PROCESS_INVENTORY, ///< Called from processInventory function.
WAIT_PLAYER_INPUT ///< Called from waitPlayerInput function.
};
enum EventTarget {
UNTIL_MOUSE_BUTTON_UP_DOWN_UP, ///< Wait until first mouse buttons all up, then at least one down, finally all up.
UNTIL_MOUSE_BUTTON_DOWN_UP, ///< Wait until first at least one mouse button down, finally all up.
UNTIL_MOUSE_BUTTON_UP, ///< Wait until all mouse buttons up.
UNTIL_MOUSE_BUTTON_UP_AND_WAIT_ENDED, ///< Wait until all mouse buttons up and wait period (getTimerDelay()) ended.
UNTIL_MOUSE_BUTTON_UP_DOWN, ///< Wait until first all mouse buttons up, finally at least one down.
UNTIL_MOUSE_BUTTON_DOWN, ///< Wait until at least one mouse button down.
UNTIL_MOUSE_BUTTON_DOWN_OR_KEY_UP_OR_DOWN_OR_IN_RECTS, ///< Wait until at least one mouse button down, up key pressed, down key pressed or mouse position in at least one of the given rectangles.
UNTIL_MOUSE_BUTTON_DOWN_OR_KEY_INPUT, ///< Wait until at least one mouse button down or a key pressed.
UNTIL_WAIT_ENDED, ///< Wait until wait period (getTimerDelay()) ended.
EMPTY_EVENT_QUEUE ///< Empty the event queue.
};
void mainLoop(int bootScriptIdx);
void manageEvents(CallSource callSource, EventTarget eventTarget, bool useMaxMouseButtonState = false, Common::Array<Common::Rect> rects = Common::Array<Common::Rect>());
} // End of namespace Cine
#endif

553
engines/cine/metaengine.cpp Normal file
View File

@@ -0,0 +1,553 @@
/* 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 "engines/advancedDetector.h"
#include "common/system.h"
#include "common/textconsole.h"
#include "common/translation.h"
#include "common/util.h"
#include "backends/keymapper/action.h"
#include "backends/keymapper/keymapper.h"
#include "backends/keymapper/standard-actions.h"
#include "cine/cine.h"
#include "cine/various.h"
#include "cine/detection.h"
namespace Cine {
static const ADExtraGuiOptionsMap optionsList[] = {
{
GAMEOPTION_ORIGINAL_SAVELOAD,
{
_s("Use original save/load screens"),
_s("Use the original save/load screens instead of the ScummVM ones"),
"originalsaveload",
false,
0,
0
}
},
{
GAMEOPTION_TRANSPARENT_DIALOG_BOXES,
{
_s("Use transparent dialog boxes in 16 color scenes"),
_s("Use transparent dialog boxes in 16 color scenes even if the original game version did not support them"),
"transparentdialogboxes",
false,
0,
0
}
},
#ifdef USE_TTS
{
GAMEOPTION_TTS,
{
_s("Enable Text to Speech"),
_s("Use TTS to read text in the game (if TTS is available)"),
"tts_enabled",
false,
0,
0
}
},
#endif
AD_EXTRA_GUI_OPTIONS_TERMINATOR
};
#define MAX_SAVEGAMES (ARRAYSIZE(Cine::currentSaveName))
#define SAVEGAME_NAME_LEN (sizeof(Cine::currentSaveName[0]))
#define SAVELIST_SIZE (MAX_SAVEGAMES * SAVEGAME_NAME_LEN)
bool CineEngine::mayHave256Colors() const { return getGameType() == Cine::GType_OS && getPlatform() == Common::kPlatformDOS; }
int CineEngine::getGameType() const { return _gameDescription->gameType; }
uint32 CineEngine::getFeatures() const { return _gameDescription->features; }
Common::Language CineEngine::getLanguage() const { return _gameDescription->desc.language; }
Common::Platform CineEngine::getPlatform() const { return _gameDescription->desc.platform; }
} // End of namespace Cine
class CineMetaEngine : public AdvancedMetaEngine<Cine::CINEGameDescription> {
public:
const char *getName() const override {
return "cine";
}
const ADExtraGuiOptionsMap *getAdvancedExtraGuiOptions() const override {
return Cine::optionsList;
}
Common::Error createInstance(OSystem *syst, Engine **engine, const Cine::CINEGameDescription *desc) const override;
bool hasFeature(MetaEngineFeature f) const override;
SaveStateList listSaves(const char *target) const override;
int getMaximumSaveSlot() const override;
bool removeSaveState(const char *target, int slot) const override;
Common::String getSavegameFile(int saveGameIdx, const char *target = nullptr) const override;
SaveStateDescriptor querySaveMetaInfos(const char *target, int slot) const override;
Common::KeymapArray initKeymaps(const char *target) const override;
};
bool CineMetaEngine::hasFeature(MetaEngineFeature f) const {
return
(f == kSupportsLoadingDuringStartup) ||
checkExtendedSaves(f);
}
bool Cine::CineEngine::hasFeature(EngineFeature f) const {
return
(f == kSupportsReturnToLauncher) ||
(f == kSupportsLoadingDuringRuntime) ||
(f == kSupportsSavingDuringRuntime);
}
Common::Error CineMetaEngine::createInstance(OSystem *syst, Engine **engine, const Cine::CINEGameDescription *desc) const {
*engine = new Cine::CineEngine(syst,desc);
return Common::kNoError;
}
SaveStateList CineMetaEngine::listSaves(const char *target) const {
Common::SaveFileManager *saveFileMan = g_system->getSavefileManager();
SaveStateList saveList;
Common::String pattern;
Common::String filename = target;
filename += ".dir";
Common::InSaveFile *in = saveFileMan->openForLoading(filename);
bool foundAutosave = false;
if (in) {
typedef char CommandeType[SAVEGAME_NAME_LEN];
CommandeType saveNames[MAX_SAVEGAMES];
// Initialize all savegames' descriptions to empty strings
// so that if the savegames' descriptions can only be partially read from file
// then the missing ones are correctly set to empty strings.
memset(saveNames, 0, sizeof(saveNames));
in->read(saveNames, SAVELIST_SIZE);
CommandeType saveDesc;
pattern = target;
pattern += ".#*";
Common::StringArray filenames = saveFileMan->listSavefiles(pattern);
for (const auto &file : filenames) {
// Obtain the extension part of the filename, since it corresponds to the save slot number
Common::String ext = Common::lastPathComponent(file, '.');
int slotNum = (int)ext.asUint64();
if (ext.equals(Common::String::format("%d", slotNum)) &&
slotNum >= 0 && slotNum < MAX_SAVEGAMES) {
// Copy the savegame description making sure it ends with a trailing zero
strncpy(saveDesc, saveNames[slotNum], SAVEGAME_NAME_LEN);
saveDesc[sizeof(CommandeType) - 1] = 0;
SaveStateDescriptor saveStateDesc(this, slotNum, saveDesc);
if (saveStateDesc.getDescription().empty()) {
if (slotNum == getAutosaveSlot()) {
saveStateDesc.setDescription(_("Unnamed autosave"));
} else {
saveStateDesc.setDescription(_("Unnamed savegame"));
}
}
if (slotNum == getAutosaveSlot()) {
foundAutosave = true;
}
saveList.push_back(saveStateDesc);
}
}
}
delete in;
// No saving on empty autosave slot
if (!foundAutosave) {
SaveStateDescriptor desc(this, getAutosaveSlot(), _("Empty autosave"));
saveList.push_back(desc);
}
// Sort saves based on slot number.
Common::sort(saveList.begin(), saveList.end(), SaveStateDescriptorSlotComparator());
return saveList;
}
int CineMetaEngine::getMaximumSaveSlot() const { return MAX_SAVEGAMES - 1; }
Common::String CineMetaEngine::getSavegameFile(int saveGameIdx, const char *target) const {
return Common::String::format("%s.%d", target == nullptr ? getName() : target, saveGameIdx);
}
SaveStateDescriptor CineMetaEngine::querySaveMetaInfos(const char *target, int slot) const {
if (slot < 0 || slot > getMaximumSaveSlot()) {
// HACK: Try to make SaveLoadChooserGrid::open() not use save slot
// numbers over the maximum save slot number for "New save".
SaveStateDescriptor desc;
desc.setWriteProtectedFlag(true);
return desc;
}
Common::ScopedPtr<Common::InSaveFile> f(g_system->getSavefileManager()->openForLoading(
getSavegameFile(slot, target)));
if (f) {
// Create the return descriptor
SaveStateDescriptor desc(this, slot, Common::U32String());
ExtendedSavegameHeader header;
if (readSavegameHeader(f.get(), &header, false)) {
parseSavegameHeader(&header, &desc);
desc.setThumbnail(header.thumbnail);
} else {
// Load savegame descriptions from index file
typedef char CommandeType[SAVEGAME_NAME_LEN];
CommandeType saveNames[MAX_SAVEGAMES];
memset(saveNames, 0, sizeof(saveNames));
Common::InSaveFile *in;
in = g_system->getSavefileManager()->openForLoading(Common::String::format("%s.dir", target));
if (in) {
in->read(saveNames, SAVELIST_SIZE);
delete in;
}
saveNames[slot][SAVEGAME_NAME_LEN - 1] = 0;
Common::String saveNameStr((const char *)saveNames[slot]);
desc.setDescription(saveNameStr);
}
if (desc.getDescription().empty()) {
desc.setDescription(_("Unnamed savegame"));
}
return desc;
}
// No saving on empty autosave slot
if (slot == getAutosaveSlot()) {
SaveStateDescriptor desc(this, slot, _("Empty autosave"));
desc.setAutosave(true);
return desc;
}
return SaveStateDescriptor();
}
Common::KeymapArray CineMetaEngine::initKeymaps(const char *target) const {
using namespace Common;
using namespace Cine;
Keymap *engineKeyMap = new Keymap(Keymap::kKeymapTypeGame, "cine-default", _("Default keymappings"));
Keymap *gameKeyMap = new Keymap(Keymap::kKeymapTypeGame, "game-shortcuts", _("Game keymappings"));
Keymap *mouseKeyMap = new Keymap(Keymap::kKeymapTypeGame, "mouse-shortcuts", _("Key to mouse keymappings"));
Keymap *introKeyMap = new Keymap(Keymap::kKeymapTypeGame, "intro-shortcuts", _("Exit screen keymappings"));
Action *act;
act = new Action(kStandardActionLeftClick, _("Left click"));
act->setLeftClickEvent();
act->addDefaultInputMapping("MOUSE_LEFT");
act->addDefaultInputMapping("JOY_A");
engineKeyMap->addAction(act);
act = new Action(kStandardActionRightClick, _("Right click"));
act->setRightClickEvent();
act->addDefaultInputMapping("MOUSE_RIGHT");
act->addDefaultInputMapping("JOY_B");
engineKeyMap->addAction(act);
act = new Action("SKIPSONY", _("Exit Sony intro screen"));
act->setCustomEngineActionEvent(kActionExitSonyScreen);
act->addDefaultInputMapping("ESCAPE");
act->addDefaultInputMapping("JOY_A");
introKeyMap->addAction(act);
act = new Action("MOUSELEFT", _("Select option / Click in game"));
act->setCustomEngineActionEvent(kActionMouseLeft);
act->addDefaultInputMapping("RETURN");
act->addDefaultInputMapping("KP_ENTER");
act->addDefaultInputMapping("KP5");
mouseKeyMap->addAction(act);
act = new Action("MOUSERIGHT", _("Open action menu / Close menu"));
act->setCustomEngineActionEvent(kActionMouseRight);
act->addDefaultInputMapping("ESCAPE");
mouseKeyMap->addAction(act);
act = new Action("DEFAULTSPEED", _("Default game speed"));
act->setCustomEngineActionEvent(kActionGameSpeedDefault);
act->addDefaultInputMapping("KP0");
gameKeyMap->addAction(act);
act = new Action("SLOWERSPEED", _("Slower game speed"));
act->setCustomEngineActionEvent(kActionGameSpeedSlower);
act->addDefaultInputMapping("MINUS");
act->addDefaultInputMapping("KP_MINUS");
act->allowKbdRepeats();
gameKeyMap->addAction(act);
act = new Action("FASTERSPEED", _("Faster game speed"));
act->setCustomEngineActionEvent(kActionGameSpeedFaster);
act->addDefaultInputMapping("PLUS");
act->addDefaultInputMapping("KP_PLUS");
act->addDefaultInputMapping("S+EQUALS");
act->allowKbdRepeats();
gameKeyMap->addAction(act);
act = new Action("EXAMINE", _("Examine"));
act->setCustomEngineActionEvent(kActionExamine);
act->addDefaultInputMapping("F1");
gameKeyMap->addAction(act);
act = new Action("TAKE", _("Take"));
act->setCustomEngineActionEvent(kActionTake);
act->addDefaultInputMapping("F2");
gameKeyMap->addAction(act);
act = new Action("INVENTORY", _("Inventory"));
act->setCustomEngineActionEvent(kActionInventory);
act->addDefaultInputMapping("F3");
gameKeyMap->addAction(act);
act = new Action("USE", _("Use"));
act->setCustomEngineActionEvent(kActionUse);
act->addDefaultInputMapping("F4");
gameKeyMap->addAction(act);
act = new Action("ACTIVATE", _("Activate"));
act->setCustomEngineActionEvent(kActionActivate);
act->addDefaultInputMapping("F5");
gameKeyMap->addAction(act);
act = new Action("SPEAK", _("Speak"));
act->setCustomEngineActionEvent(kActionSpeak);
act->addDefaultInputMapping("F6");
gameKeyMap->addAction(act);
act = new Action("ACTMENU", _("Action menu"));
act->setCustomEngineActionEvent(kActionActionMenu);
act->addDefaultInputMapping("F9");
act->addDefaultInputMapping("JOY_LEFT_SHOULDER");
gameKeyMap->addAction(act);
act = new Action("SYSMENU", _("System menu"));
act->setCustomEngineActionEvent(kActionSystemMenu);
act->addDefaultInputMapping("F10");
act->addDefaultInputMapping("JOY_RIGHT_SHOULDER");
gameKeyMap->addAction(act);
// I18N: Opens collision map of where the actor can freely move
act = new Action("COLLISIONPAGE", _("Show collisions"));
act->setCustomEngineActionEvent(kActionCollisionPage);
act->addDefaultInputMapping("F11");
act->addDefaultInputMapping("JOY_Y");
gameKeyMap->addAction(act);
// I18N: Move Actor to upwards direction
act = new Action("MOVEUP", _("Move up"));
act->setCustomEngineActionEvent(kActionMoveUp);
act->addDefaultInputMapping("KP8");
act->addDefaultInputMapping("JOY_UP");
gameKeyMap->addAction(act);
// I18N: Move Actor to downwards direction
act = new Action("MOVEDOWN", _("Move down"));
act->setCustomEngineActionEvent(kActionMoveDown);
act->addDefaultInputMapping("KP2");
act->addDefaultInputMapping("JOY_DOWN");
gameKeyMap->addAction(act);
// I18N: Move Actor to left direction
act = new Action("MOVELEFT", _("Move left"));
act->setCustomEngineActionEvent(kActionMoveLeft);
act->addDefaultInputMapping("KP4");
act->addDefaultInputMapping("JOY_LEFT");
gameKeyMap->addAction(act);
// I18N: Move Actor to right direction
act = new Action("MOVERIGHT", _("Move right"));
act->setCustomEngineActionEvent(kActionMoveRight);
act->addDefaultInputMapping("KP6");
act->addDefaultInputMapping("JOY_RIGHT");
gameKeyMap->addAction(act);
// I18N: Move Actor to top-left direction
act = new Action("MOVEUPLEFT", _("Move up-left"));
act->setCustomEngineActionEvent(kActionMoveUpLeft);
act->addDefaultInputMapping("KP7");
gameKeyMap->addAction(act);
// I18N: Move Actor to top-right direction
act = new Action("MOVEUPRIGHT", _("Move up-right"));
act->setCustomEngineActionEvent(kActionMoveUpRight);
act->addDefaultInputMapping("KP9");
gameKeyMap->addAction(act);
// I18N: Move Actor to bottom-left direction
act = new Action("MOVEDOWNLEFT", _("Move down-left"));
act->setCustomEngineActionEvent(kActionMoveDownLeft);
act->addDefaultInputMapping("KP1");
gameKeyMap->addAction(act);
// I18N: Move Actor to bottom-right direction
act = new Action("MOVEDOWNRIGHT", _("Move down-right"));
act->setCustomEngineActionEvent(kActionMoveDownRight);
act->addDefaultInputMapping("KP3");
gameKeyMap->addAction(act);
act = new Action("MENUUP", _("Menu option up"));
act->setCustomEngineActionEvent(kActionMenuOptionUp);
act->addDefaultInputMapping("UP");
gameKeyMap->addAction(act);
act = new Action("MENUDOWN", _("Menu option down"));
act->setCustomEngineActionEvent(kActionMenuOptionDown);
act->addDefaultInputMapping("DOWN");
gameKeyMap->addAction(act);
KeymapArray keymaps(4);
keymaps[0] = engineKeyMap;
keymaps[1] = mouseKeyMap;
keymaps[2] = gameKeyMap;
keymaps[3] = introKeyMap;
introKeyMap->setEnabled(false);
return keymaps;
}
bool CineMetaEngine::removeSaveState(const char *target, int slot) const {
if (slot < 0 || slot >= MAX_SAVEGAMES) {
return false;
}
// Load savegame descriptions from index file
typedef char CommandeType[SAVEGAME_NAME_LEN];
CommandeType saveNames[MAX_SAVEGAMES];
// Initialize all savegames' descriptions to empty strings
// so that if the savegames' descriptions can only be partially read from file
// then the missing ones are correctly set to empty strings.
memset(saveNames, 0, sizeof(saveNames));
Common::InSaveFile *in;
in = g_system->getSavefileManager()->openForLoading(Common::String::format("%s.dir", target));
if (!in)
return false;
in->read(saveNames, SAVELIST_SIZE);
delete in;
// Set description for selected slot
char slotName[SAVEGAME_NAME_LEN];
slotName[0] = 0;
Common::strlcpy(saveNames[slot], slotName, SAVEGAME_NAME_LEN);
// Update savegame descriptions
Common::String indexFile = Common::String::format("%s.dir", target);
Common::OutSaveFile *out = g_system->getSavefileManager()->openForSaving(indexFile);
if (!out) {
warning("Unable to open file %s for saving", indexFile.c_str());
return false;
}
out->write(saveNames, SAVELIST_SIZE);
delete out;
// Delete save file
Common::String saveFileName = getSavegameFile(slot, target);
return g_system->getSavefileManager()->removeSavefile(saveFileName);
}
#if PLUGIN_ENABLED_DYNAMIC(CINE)
REGISTER_PLUGIN_DYNAMIC(CINE, PLUGIN_TYPE_ENGINE, CineMetaEngine);
#else
REGISTER_PLUGIN_STATIC(CINE, PLUGIN_TYPE_ENGINE, CineMetaEngine);
#endif
namespace Cine {
Common::Error CineEngine::loadGameState(int slot) {
bool gameLoaded = makeLoad(getSaveStateName(slot));
return gameLoaded ? Common::kNoError : Common::kUnknownError;
}
Common::Error CineEngine::saveGameState(int slot, const Common::String &desc, bool isAutosave) {
if (slot < 0 || slot >= MAX_SAVEGAMES) {
return Common::kCreatingFileFailed;
}
// Load savegame descriptions from index file
loadSaveDirectory();
// Set description for selected slot making sure it ends with a trailing zero
strncpy(currentSaveName[slot], desc.c_str(), sizeof(CommandeType));
currentSaveName[slot][sizeof(CommandeType) - 1] = 0;
// Update savegame descriptions
Common::String indexFile = _targetName + ".dir";
Common::OutSaveFile *fHandle = _saveFileMan->openForSaving(indexFile);
if (!fHandle) {
warning("Unable to open file %s for saving", indexFile.c_str());
return Common::kUnknownError;
}
fHandle->write(currentSaveName, SAVELIST_SIZE);
delete fHandle;
// Save game
makeSave(getSaveStateName(slot), getTotalPlayTime() / 1000, desc, isAutosave);
checkDataDisk(-1);
return Common::kNoError;
}
Common::String CineEngine::getSaveStateName(int slot) const {
return getMetaEngine()->getSavegameFile(slot, _targetName.c_str());
}
bool CineEngine::canLoadGameStateCurrently(Common::U32String *msg) {
return (!disableSystemMenu && !inMenu);
}
bool CineEngine::canSaveGameStateCurrently(Common::U32String *msg) {
return (allowPlayerInput && !disableSystemMenu && !inMenu);
}
} // End of namespace Cine

35
engines/cine/module.mk Normal file
View File

@@ -0,0 +1,35 @@
MODULE := engines/cine
MODULE_OBJS := \
anim.o \
bg.o \
bg_list.o \
console.o \
cine.o \
gfx.o \
main_loop.o \
metaengine.o \
msg.o \
object.o \
pal.o \
part.o \
prc.o \
rel.o \
saveload.o \
script_fw.o \
script_os.o \
sound.o \
texte.o \
unpack.o \
various.o
# This module can be built as a plugin
ifeq ($(ENABLE_CINE), DYNAMIC_PLUGIN)
PLUGIN := 1
endif
# Include common rules
include $(srcdir)/rules.mk
# Detection objects
DETECT_OBJS += $(MODULE)/detection.o

79
engines/cine/msg.cpp Normal file
View File

@@ -0,0 +1,79 @@
/* 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 "cine/msg.h"
#include "cine/various.h"
namespace Cine {
int16 loadMsg(char *pMsgName) {
uint32 sourceSize;
checkDataDisk(-1);
g_cine->_messageTable.clear();
int16 foundFileIdx = findFileInBundle(pMsgName);
if (foundFileIdx < 0) {
warning("loadMsg(\"%s\"): Could not find file in bundle.", pMsgName);
return -1;
}
byte *dataPtr = readBundleFile(foundFileIdx, &sourceSize);
setMouseCursor(MOUSE_CURSOR_DISK);
uint count = READ_BE_UINT16(dataPtr);
uint messageLenPos = 2;
uint messageDataPos = messageLenPos + 2 * count;
// Read in the messages
for (uint i = 0; i < count; i++) {
// Read message's length
uint messageLen = READ_BE_UINT16(dataPtr + messageLenPos);
messageLenPos += 2;
// Store the read message.
// This code works around input data that has empty strings residing outside the input
// buffer (e.g. message indices 58-254 in BATEAU.MSG in PROCS08 in Operation Stealth).
if (messageDataPos < sourceSize) {
g_cine->_messageTable.push_back((const char *)(dataPtr + messageDataPos));
} else {
if (messageLen > 0) { // Only warn about overflowing non-empty strings
warning("loadMsg(%s): message (%d. / %d) is overflowing the input buffer. Replacing it with an empty string", pMsgName, i + 1, count);
} else {
debugC(5, kCineDebugPart, "loadMsg(%s): empty message (%d. / %d) resides outside input buffer", pMsgName, i + 1, count);
}
// Message resides outside the input buffer so we replace it with an empty string
g_cine->_messageTable.push_back("");
}
// Jump to the next message
messageDataPos += messageLen;
}
free(dataPtr);
return 0;
}
} // End of namespace Cine

35
engines/cine/msg.h Normal file
View File

@@ -0,0 +1,35 @@
/* 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 CINE_MSG_H
#define CINE_MSG_H
#include "common/str-array.h"
namespace Cine {
#define NUM_MAX_MESSAGE 255
int16 loadMsg(char *pMsgName);
} // End of namespace Cine
#endif

343
engines/cine/object.cpp Normal file
View File

@@ -0,0 +1,343 @@
/* 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/memstream.h"
#include "common/util.h"
#include "cine/cine.h"
#include "cine/object.h"
#include "cine/part.h"
#include "cine/various.h"
namespace Cine {
/** Resets all elements in the object table. */
void resetObjectTable() {
for (auto &obj : g_cine->_objectTable) {
obj.clear();
}
}
int16 loadObject(char *pObjectName) {
debug(5, "loadObject(\"%s\")", pObjectName);
uint16 numEntry;
uint16 entrySize;
uint16 i;
byte *ptr, *dataPtr;
checkDataDisk(-1);
int16 foundFileIdx = findFileInBundle(pObjectName);
if (foundFileIdx < 0) {
return -1;
}
ptr = dataPtr = readBundleFile(foundFileIdx);
setMouseCursor(MOUSE_CURSOR_DISK);
numEntry = READ_BE_UINT16(ptr); ptr += 2;
entrySize = READ_BE_UINT16(ptr); ptr += 2;
assert(numEntry <= NUM_MAX_OBJECT);
for (i = 0; i < numEntry; i++) {
bool overwrite =
(g_cine->getGameType() == Cine::GType_FW && g_cine->_objectTable[i].costume != -2) ||
(g_cine->getGameType() == Cine::GType_OS && g_cine->_objectTable[i].costume != -3);
// HACK: Fix handling of electric razor and cable in Amiga version of Operation Stealth
// when entering the Dr. Why's control room.
if (hacksEnabled && g_cine->getPlatform() == Common::kPlatformAmiga &&
g_cine->getGameType() == Cine::GType_OS && (i == 231 || i == 232) &&
scumm_stricmp(pObjectName, "SALLE59.REL") == 0) {
overwrite = false;
}
if (overwrite) {
Common::MemoryReadStream readS(ptr, entrySize);
g_cine->_objectTable[i].x = readS.readSint16BE();
g_cine->_objectTable[i].y = readS.readSint16BE();
g_cine->_objectTable[i].mask = readS.readUint16BE();
g_cine->_objectTable[i].frame = readS.readSint16BE();
g_cine->_objectTable[i].costume = readS.readSint16BE();
readS.read(g_cine->_objectTable[i].name, 20);
g_cine->_objectTable[i].part = readS.readUint16BE();
}
ptr += entrySize;
}
if (!strcmp(pObjectName, "INTRO.OBJ")) {
for (i = 0; i < 10; i++) {
g_cine->_objectTable[i].costume = 0;
}
}
free(dataPtr);
return 0;
}
/**
* Remove overlay sprite from the list
* @param objIdx Remove overlay associated with this object
* @param param Remove overlay of this type
*/
int removeOverlay(uint16 objIdx, uint16 param) {
Common::List<overlay>::iterator it;
for (it = g_cine->_overlayList.begin(); it != g_cine->_overlayList.end(); ++it) {
if (it->objIdx == objIdx && it->type == param) {
g_cine->_overlayList.erase(it);
return 1;
}
}
return 0;
}
/**
* Add new overlay sprite to the list
* @param objIdx Associate the overlay with this object
* @param type Type of new overlay
* @todo Why are x, y, width and color left uninitialized?
*/
void addOverlay(uint16 objIdx, uint16 type) {
Common::List<overlay>::iterator it;
overlay tmp;
for (it = g_cine->_overlayList.begin(); it != g_cine->_overlayList.end(); ++it) {
// This is done for both Future Wars and Operation Stealth
if (g_cine->_objectTable[it->objIdx].mask >= g_cine->_objectTable[objIdx].mask) {
break;
}
// There are additional checks in Operation Stealth's implementation
if (g_cine->getGameType() == Cine::GType_OS && (it->type == 2 || it->type == 3)) {
break;
}
}
// In Operation Stealth's implementation we might bail out early
if (g_cine->getGameType() == Cine::GType_OS && it != g_cine->_overlayList.end() && it->objIdx == objIdx && it->type == type) {
return;
}
if (g_cine->getGameType() == Cine::GType_OS && g_cine->_objectTable[it->objIdx].frame == 22 && scumm_stricmp(currentPrcName, "STARTA.PRC") == 0) {
g_cine->sayText("Washington D.C.\nCIA Headquarters", Common::TextToSpeechManager::QUEUE);
}
tmp.objIdx = objIdx;
tmp.type = type;
tmp.x = 0;
tmp.y = 0;
tmp.width = 0;
tmp.color = 0;
g_cine->_overlayList.insert(it, tmp);
}
/**
* Add new background mask overlay
* @param objIdx Associate the overlay with this object
* @param param source background index
*/
void addGfxElement(int16 objIdx, int16 param, int16 type) {
Common::List<overlay>::iterator it;
overlay tmp;
for (it = g_cine->_overlayList.begin(); it != g_cine->_overlayList.end(); ++it) {
if (g_cine->_objectTable[it->objIdx].mask >= g_cine->_objectTable[objIdx].mask || it->type == 2 || it->type == 3) {
break;
}
}
if (it != g_cine->_overlayList.end() && it->objIdx == objIdx && it->type == type && it->x == param) {
return;
}
tmp.objIdx = objIdx;
tmp.type = type;
tmp.x = param;
tmp.y = 0;
tmp.width = 0;
tmp.color = 0;
g_cine->_overlayList.insert(it, tmp);
}
/**
* Remove background mask overlay
* @param objIdx Remove overlay associated with this object
* @param param Remove overlay using this background
* @todo Check that it works
*/
void removeGfxElement(int16 objIdx, int16 param, int16 type) {
Common::List<overlay>::iterator it;
for (it = g_cine->_overlayList.begin(); it != g_cine->_overlayList.end(); ++it) {
if (it->objIdx == objIdx && it->type == type && it->x == param) {
g_cine->_overlayList.erase(it);
return;
}
}
}
void setupObject(byte objIdx, uint16 param1, uint16 param2, uint16 param3, uint16 param4) {
g_cine->_objectTable[objIdx].x = param1;
g_cine->_objectTable[objIdx].y = param2;
g_cine->_objectTable[objIdx].mask = param3;
g_cine->_objectTable[objIdx].frame = param4;
if (g_cine->getGameType() == Cine::GType_OS) {
resetGfxEntityEntry(objIdx);
} else { // Future Wars
if (removeOverlay(objIdx, 0)) {
addOverlay(objIdx, 0);
}
}
}
void subObjectParam(byte objIdx, byte paramIdx, int16 newValue) {
addObjectParam(objIdx, paramIdx, -newValue);
}
void addObjectParam(byte objIdx, byte paramIdx, int16 newValue) {
int16 currentValue = getObjectParam(objIdx, paramIdx);
modifyObjectParam(objIdx, paramIdx, currentValue + newValue);
}
void modifyObjectParam(byte objIdx, byte paramIdx, int16 newValue) {
// Operation Stealth checks object index range, Future Wars doesn't.
if (g_cine->getGameType() == Cine::GType_OS && objIdx >= NUM_MAX_OBJECT)
return;
switch (paramIdx) {
case 1:
g_cine->_objectTable[objIdx].x = newValue;
break;
case 2:
g_cine->_objectTable[objIdx].y = newValue;
break;
case 3:
g_cine->_objectTable[objIdx].mask = newValue;
if (g_cine->getGameType() == Cine::GType_OS) { // Operation Stealth specific
resetGfxEntityEntry(objIdx);
} else { // Future Wars specific
if (removeOverlay(objIdx, 0)) {
addOverlay(objIdx, 0);
}
}
break;
case 4:
g_cine->_objectTable[objIdx].frame = newValue;
break;
case 5:
// TODO: Test if this really breaks the newspaper machine on the airport in Operation Stealth.
if (g_cine->getGameType() == Cine::GType_FW && newValue == -1) {
g_cine->_objectTable[objIdx].costume = g_cine->_globalVars[0];
} else {
g_cine->_objectTable[objIdx].costume = newValue;
}
break;
case 6:
g_cine->_objectTable[objIdx].part = newValue;
break;
default:
break;
}
}
/**
* Check if at least one of the range B's endpoints is inside range A,
* not counting the starting and ending points of range A.
* Used at least by Operation Stealth's opcode 0x8D i.e. 141.
*/
bool compareRanges(uint16 aStart, uint16 aEnd, uint16 bStart, uint16 bEnd) {
return (bStart > aStart && bStart < aEnd) || (bEnd > aStart && bEnd < aEnd);
}
uint16 compareObjectParamRanges(uint16 objIdx1, uint16 xAdd1, uint16 yAdd1, uint16 maskAdd1, uint16 objIdx2, uint16 xAdd2, uint16 yAdd2, uint16 maskAdd2) {
assert(objIdx1 < NUM_MAX_OBJECT && objIdx2 < NUM_MAX_OBJECT);
const ObjectStruct &obj1 = g_cine->_objectTable[objIdx1];
const ObjectStruct &obj2 = g_cine->_objectTable[objIdx2];
if (compareRanges(obj1.x, obj1.x + xAdd1, obj2.x, obj2.x + xAdd2) &&
compareRanges(obj1.y, obj1.y + yAdd1, obj2.y, obj2.y + yAdd2) &&
compareRanges(obj1.mask, obj1.mask + maskAdd1, obj2.mask, obj2.mask + maskAdd2)) {
return kCmpEQ;
} else {
return 0;
}
}
uint16 compareObjectParam(byte objIdx, byte type, int16 value) {
uint16 compareResult = 0;
int16 objectParam = getObjectParam(objIdx, type);
if (objectParam > value) {
compareResult |= kCmpGT;
} else if (objectParam < value) {
compareResult |= kCmpLT;
} else {
compareResult |= kCmpEQ;
}
return compareResult;
}
/**
* @bug In Operation Stealth, if you try to go downstairs to the sea in the
* location between bank and hotel, getObjectParam is called with paramIdx 16
* and crashes
*/
int16 getObjectParam(uint16 objIdx, uint16 paramIdx) {
assert(objIdx <= NUM_MAX_OBJECT);
paramIdx--;
assert(paramIdx <= 5);
switch (paramIdx) {
case 0:
return g_cine->_objectTable[objIdx].x;
case 1:
return g_cine->_objectTable[objIdx].y;
case 2:
return g_cine->_objectTable[objIdx].mask;
case 3:
return g_cine->_objectTable[objIdx].frame;
case 4:
return g_cine->_objectTable[objIdx].costume;
case 5:
return g_cine->_objectTable[objIdx].part;
default:
break;
}
return 0;
}
} // End of namespace Cine

82
engines/cine/object.h Normal file
View 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 CINE_OBJECT_H
#define CINE_OBJECT_H
#include "script.h"
namespace Cine {
struct ObjectStruct {
int16 x;
int16 y;
uint16 mask;
int16 frame;
int16 costume;
char name[20];
uint16 part;
/** Sets all member variables to zero. */
void clear() {
this->x = 0;
this->y = 0;
this->mask = 0;
this->frame = 0;
this->costume = 0;
memset(this->name, 0, sizeof(this->name));
this->part = 0;
}
};
struct overlay {
uint16 objIdx;
uint16 type;
int16 x;
int16 y;
int16 width;
int16 color;
};
#define NUM_MAX_OBJECT 255
#define NUM_MAX_VAR 255
void resetObjectTable();
int16 loadObject(char *pObjectName);
void setupObject(byte objIdx, uint16 param1, uint16 param2, uint16 param3, uint16 param4);
void modifyObjectParam(byte objIdx, byte paramIdx, int16 newValue);
void addOverlay(uint16 objIdx, uint16 type);
int removeOverlay(uint16 objIdx, uint16 param);
void addGfxElement(int16 objIdx, int16 param, int16 type);
void removeGfxElement(int16 objIdx, int16 param, int16 type);
int16 getObjectParam(uint16 objIdx, uint16 paramIdx);
void addObjectParam(byte objIdx, byte paramIdx, int16 newValue);
void subObjectParam(byte objIdx, byte paramIdx, int16 newValue);
bool compareRanges(uint16 aStart, uint16 aEnd, uint16 bStart, uint16 bEnd);
uint16 compareObjectParamRanges(uint16 objIdx1, uint16 xAdd1, uint16 yAdd1, uint16 maskAdd1, uint16 objIdx2, uint16 xAdd2, uint16 yAdd2, uint16 maskAdd2);
uint16 compareObjectParam(byte objIdx, byte param1, int16 param2);
} // End of namespace Cine
#endif

423
engines/cine/pal.cpp Normal file
View File

@@ -0,0 +1,423 @@
/* 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 "cine/cine.h"
#include "cine/various.h"
#include "cine/pal.h"
#include "common/system.h" // For g_system->getPaletteManager()->setPalette
#include "common/textconsole.h"
#include "graphics/paletteman.h"
namespace Cine {
static byte paletteBuffer1[16];
static byte paletteBuffer2[16];
void loadPal(const char *fileName) {
char buffer[20];
removeExtension(buffer, fileName, sizeof(buffer));
Common::strcat_s(buffer, ".PAL");
g_cine->_palArray.clear();
Common::File palFileHandle;
if (!palFileHandle.open(buffer))
error("loadPal(): Cannot open file %s", fileName);
uint16 palEntriesCount = palFileHandle.readUint16LE();
palFileHandle.readUint16LE(); // entry size
g_cine->_palArray.resize(palEntriesCount);
for (uint i = 0; i < g_cine->_palArray.size(); ++i) {
palFileHandle.read(g_cine->_palArray[i].name, 10);
palFileHandle.read(g_cine->_palArray[i].pal1, 16);
palFileHandle.read(g_cine->_palArray[i].pal2, 16);
}
palFileHandle.close();
}
int16 findPaletteFromName(const char *fileName) {
char buffer[10];
uint16 position = 0;
uint16 i;
Common::strlcpy(buffer, fileName, sizeof(buffer));
while (position < strlen(buffer)) {
if (buffer[position] > 'a' && buffer[position] < 'z') {
buffer[position] += 'A' - 'a';
}
position++;
}
for (i = 0; i < g_cine->_palArray.size(); i++) {
if (!strcmp(buffer, g_cine->_palArray[i].name)) {
return i;
}
}
return -1;
}
void loadRelatedPalette(const char *fileName) {
char localName[16];
byte i;
int16 paletteIndex;
removeExtension(localName, fileName, sizeof(localName));
paletteIndex = findPaletteFromName(localName);
if (paletteIndex == -1) {
// generate default palette
for (i = 0; i < 16; i++) {
paletteBuffer1[i] = paletteBuffer2[i] = (i << 4) + i;
}
} else {
assert(paletteIndex < (int32)g_cine->_palArray.size());
memcpy(paletteBuffer1, g_cine->_palArray[paletteIndex].pal1, 16);
memcpy(paletteBuffer2, g_cine->_palArray[paletteIndex].pal2, 16);
}
}
namespace {
/** Is given endian type big endian? (Handles native endian type too, otherwise this would be trivial). */
bool isBigEndian(const EndianType endian) {
assert(endian == CINE_NATIVE_ENDIAN || endian == CINE_LITTLE_ENDIAN || endian == CINE_BIG_ENDIAN);
// Handle explicit little and big endian types here
if (endian != CINE_NATIVE_ENDIAN) {
return (endian == CINE_BIG_ENDIAN);
}
// Handle native endian type here
#if defined(SCUMM_BIG_ENDIAN)
return true;
#elif defined(SCUMM_LITTLE_ENDIAN)
return false;
#else
#error No endianness defined
#endif
}
/** Calculate byte position of given bit position in a multibyte variable using defined endianness. */
int bytePos(const int bitPos, const int numBytes, const bool bigEndian) {
if (bigEndian)
return (numBytes - 1) - (bitPos / 8);
else // little endian
return bitPos / 8;
}
/** Calculate the value of "base" to the power of "power". */
int power(int base, int power) {
int result = 1;
while (power--)
result *= base;
return result;
}
} // end of anonymous namespace
Palette &Palette::rotateRight(byte firstIndex, byte lastIndex) {
debug(1, "Palette::rotateRight(firstIndex: %d, lastIndex: %d)", firstIndex, lastIndex);
const Color lastColor = _colors[lastIndex];
for (uint i = lastIndex; i > firstIndex; i--)
_colors[i] = _colors[i - 1];
_colors[firstIndex] = lastColor;
return *this;
}
Palette &Palette::rotateLeft(byte firstIndex, byte lastIndex) {
debug(1, "Palette::rotateLeft(firstIndex: %d, lastIndex: %d)", firstIndex, lastIndex);
const Color firstColor = _colors[firstIndex];
for (uint i = firstIndex; i < lastIndex; i++)
_colors[i] = _colors[i + 1];
_colors[lastIndex] = firstColor;
return *this;
}
bool Palette::empty() const {
return _colors.empty();
}
uint Palette::colorCount() const {
return _colors.size();
}
byte Palette::brightness(byte colorIndex) {
return (byte) ((_colors[colorIndex].r*19 +
_colors[colorIndex].g*38 +
_colors[colorIndex].b*7) / 64);
}
bool Palette::isEqual(byte index1, byte index2) {
return _colors[index1].r == _colors[index2].r &&
_colors[index1].g == _colors[index2].g &&
_colors[index1].b == _colors[index2].b;
}
int Palette::findMinBrightnessColorIndex(uint minColorIndex) {
int minFoundBrightness = 999;
int foundColorIndex = 0;
for (uint i = minColorIndex; i < colorCount(); i++) {
byte currColorBrightness = brightness(i);
if (currColorBrightness < minFoundBrightness) {
minFoundBrightness = currColorBrightness;
foundColorIndex = i;
}
}
return (_colors.size() >= 3 && isEqual(2, foundColorIndex)) ? 0 : foundColorIndex;
}
bool Palette::ensureContrast(byte &minBrightnessColorIndex) {
minBrightnessColorIndex = findMinBrightnessColorIndex();
if (_colors.size() >= 3 && isEqual(2, minBrightnessColorIndex)) {
Color black = {0, 0, 0};
Color white = {static_cast<uint8>(_format.rMax()), static_cast<uint8>(_format.gMax()), static_cast<uint8>(_format.bMax())};
_colors[2] = white;
if (isEqual(2, minBrightnessColorIndex)) {
_colors[minBrightnessColorIndex] = black;
}
return true;
}
return false;
}
Palette &Palette::fillWithBlack() {
for (uint i = 0; i < _colors.size(); i++) {
_colors[i].r = 0;
_colors[i].g = 0;
_colors[i].b = 0;
}
return *this;
}
// TODO: Add better heuristic for checking whether the color format is valid
bool Palette::isValid() const {
// Check that the color format has been actually set and not just default constructed.
// Also check that the alpha channel is discarded.
return _format != Graphics::PixelFormat() && _format.aLoss == 8;
}
const Graphics::PixelFormat &Palette::colorFormat() const {
return _format;
}
void Palette::setGlobalOSystemPalette() const {
byte buf[256 * 3]; // Allocate space for the largest possible palette
if (g_cine->mayHave256Colors()) {
memset(buf, 0, sizeof(buf)); // Clear whole palette
}
// The color format used by OSystem's setPalette-function:
save(buf, sizeof(buf), Graphics::PixelFormat(3, 8, 8, 8, 0, 0, 8, 16, 0), CINE_LITTLE_ENDIAN);
if (renderer->useTransparentDialogBoxes() && colorCount() == 16) {
// The Amiga version of Future Wars does use the upper 16 colors for a darkened
// game palette to allow transparent dialog boxes. To support that in our code
// we do calculate that palette over here and append it to the screen palette.
for (uint i = 0; i < 16 * 3; ++i)
buf[16 * 3 + i] = buf[i] >> 1;
g_system->getPaletteManager()->setPalette(buf, 0, colorCount() * 2);
} else if (g_cine->mayHave256Colors()) {
// If 256 colors are possible then always set 256 colors
// because resources may be a combination of 16 colors and 256 colors
// and going from a 16 color screen to a 256 color screen (e.g. when leaving
// the rat maze on Dr. Why's island in Operation Stealth) may leave
// the upper 240 colors not faded out.
g_system->getPaletteManager()->setPalette(buf, 0, 256);
} else {
g_system->getPaletteManager()->setPalette(buf, 0, colorCount());
}
}
Cine::Palette::Color Palette::getColor(byte index) const {
return _colors[index];
}
uint8 Palette::getR(byte index) const {
return _colors[index].r;
}
uint8 Palette::getG(byte index) const {
return _colors[index].g;
}
uint8 Palette::getB(byte index) const {
return _colors[index].b;
}
void Palette::setColorFormat(const Graphics::PixelFormat &format) {
_format = format;
}
// a.k.a. transformPaletteRange
Palette &Palette::saturatedAddColor(Palette &output, byte firstIndex, byte lastIndex, signed r, signed g, signed b) const {
assert(firstIndex < colorCount() && lastIndex < colorCount());
assert(firstIndex < output.colorCount() && lastIndex < output.colorCount());
assert(output.colorFormat() == colorFormat());
for (uint i = firstIndex; i <= lastIndex; i++)
saturatedAddColor(output._colors[i], _colors[i], r, g, b);
return output;
}
Palette &Palette::saturatedAddColor(Palette &output, byte firstIndex, byte lastIndex, signed rSource, signed gSource, signed bSource, const Graphics::PixelFormat &sourceFormat) const {
// Convert the source color to the internal color format ensuring that no divide by zero will happen
const signed r = ((signed) _format.rMax()) * rSource / MAX<int>(sourceFormat.rMax(), 1);
const signed g = ((signed) _format.gMax()) * gSource / MAX<int>(sourceFormat.gMax(), 1);
const signed b = ((signed) _format.bMax()) * bSource / MAX<int>(sourceFormat.bMax(), 1);
return saturatedAddColor(output, firstIndex, lastIndex, r, g, b);
}
Palette &Palette::saturatedAddNormalizedGray(Palette &output, byte firstIndex, byte lastIndex, int grayDividend, int grayDenominator) const {
assert(grayDenominator != 0);
const signed r = ((signed) _format.rMax()) * grayDividend / grayDenominator;
const signed g = ((signed) _format.gMax()) * grayDividend / grayDenominator;
const signed b = ((signed) _format.bMax()) * grayDividend / grayDenominator;
return saturatedAddColor(output, firstIndex, lastIndex, r, g, b);
}
// a.k.a. transformColor
void Palette::saturatedAddColor(Color &result, const Color &baseColor, signed r, signed g, signed b) const {
result.r = CLIP<int>(baseColor.r + r, 0, _format.rMax());
result.g = CLIP<int>(baseColor.g + g, 0, _format.gMax());
result.b = CLIP<int>(baseColor.b + b, 0, _format.bMax());
}
Palette::Palette(const Graphics::PixelFormat &format, const uint numColors) : _format(format), _colors() {
_colors.resize(numColors);
fillWithBlack();
}
Palette::Palette(const Palette& other) :
_format(other._format),
_colors(other._colors) {
}
Palette& Palette::operator=(const Palette& other) {
if (this != &other) {
_format = other._format;
_colors = other._colors;
}
return *this;
}
Palette &Palette::clear() {
_format = Graphics::PixelFormat();
_colors.clear();
return *this;
}
Palette &Palette::load(const byte *buf, const uint size, const Graphics::PixelFormat &format, const uint numColors, const EndianType endian) {
assert(format.bytesPerPixel * numColors <= size); // Make sure there's enough input space
assert(format.aLoss == 8); // No alpha
assert(format.rShift / 8 == (format.rShift + MAX<int>(0, format.rBits() - 1)) / 8); // R must be inside one byte
assert(format.gShift / 8 == (format.gShift + MAX<int>(0, format.gBits() - 1)) / 8); // G must be inside one byte
assert(format.bShift / 8 == (format.bShift + MAX<int>(0, format.bBits() - 1)) / 8); // B must be inside one byte
setColorFormat(format);
_colors.clear();
_colors.resize(numColors);
const int rBytePos = bytePos(format.rShift, format.bytesPerPixel, isBigEndian(endian));
const int gBytePos = bytePos(format.gShift, format.bytesPerPixel, isBigEndian(endian));
const int bBytePos = bytePos(format.bShift, format.bytesPerPixel, isBigEndian(endian));
for (uint i = 0; i < numColors; i++) {
// format.rMax(), format.gMax(), format.bMax() are also used as masks here
_colors[i].r = (buf[i * format.bytesPerPixel + rBytePos] >> (format.rShift % 8)) & format.rMax();
_colors[i].g = (buf[i * format.bytesPerPixel + gBytePos] >> (format.gShift % 8)) & format.gMax();
_colors[i].b = (buf[i * format.bytesPerPixel + bBytePos] >> (format.bShift % 8)) & format.bMax();
}
return *this;
}
byte *Palette::save(byte *buf, const uint size, const EndianType endian) const {
return save(buf, size, colorFormat(), colorCount(), endian);
}
byte *Palette::save(byte *buf, const uint size, const Graphics::PixelFormat &format, const EndianType endian) const {
return save(buf, size, format, colorCount(), endian);
}
byte *Palette::save(byte *buf, const uint size, const Graphics::PixelFormat &format, const uint numColors, const EndianType endian, const byte firstIndex) const {
assert(format.bytesPerPixel * numColors <= size); // Make sure there's enough output space
assert(format.aLoss == 8); // No alpha
assert(format.rShift / 8 == (format.rShift + MAX<int>(0, format.rBits() - 1)) / 8); // R must be inside one byte
assert(format.gShift / 8 == (format.gShift + MAX<int>(0, format.gBits() - 1)) / 8); // G must be inside one byte
assert(format.bShift / 8 == (format.bShift + MAX<int>(0, format.bBits() - 1)) / 8); // B must be inside one byte
// Clear the part of the output palette we're going to be writing to with all black
memset(buf, 0, format.bytesPerPixel * numColors);
// Calculate original R/G/B max values
const int rOrigMax = power(2, 8 - colorFormat().rLoss) - 1;
const int gOrigMax = power(2, 8 - colorFormat().gLoss) - 1;
const int bOrigMax = power(2, 8 - colorFormat().bLoss) - 1;
// Calculate new R/G/B max values
const int rNewMax = power(2, 8 - format.rLoss) - 1;
const int gNewMax = power(2, 8 - format.gLoss) - 1;
const int bNewMax = power(2, 8 - format.bLoss) - 1;
// Calculate the byte position
const int rBytePos = bytePos(format.rShift, format.bytesPerPixel, isBigEndian(endian));
const int gBytePos = bytePos(format.gShift, format.bytesPerPixel, isBigEndian(endian));
const int bBytePos = bytePos(format.bShift, format.bytesPerPixel, isBigEndian(endian));
// Save the palette to the output in the specified format
for (uint i = firstIndex; i < firstIndex + numColors; i++) {
const uint r = (_colors[i].r * rNewMax) / (rOrigMax == 0 ? 1 : rOrigMax);
const uint g = (_colors[i].g * gNewMax) / (gOrigMax == 0 ? 1 : gOrigMax);
const uint b = (_colors[i].b * bNewMax) / (bOrigMax == 0 ? 1 : bOrigMax);
buf[i * format.bytesPerPixel + rBytePos] |= r << (format.rShift % 8);
buf[i * format.bytesPerPixel + gBytePos] |= g << (format.gShift % 8);
buf[i * format.bytesPerPixel + bBytePos] |= b << (format.bShift % 8);
}
// Return the pointer to the output palette
return buf;
}
} // End of namespace Cine

190
engines/cine/pal.h Normal file
View File

@@ -0,0 +1,190 @@
/* 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 CINE_PAL_H
#define CINE_PAL_H
#include "graphics/pixelformat.h"
namespace Cine {
/**
* Endian types. Used at least by Palette class's load and save functions.
* TODO: Move somewhere more general as this is definitely not Cine-engine specific
*
* NOTE: It seems LITTLE_ENDIAN and/or BIG_ENDIAN were defined already on some platforms so
* therefore renamed the enumerations to something not clashing by giving them "CINE_"-prefixes.
*/
enum EndianType {
CINE_NATIVE_ENDIAN,
CINE_LITTLE_ENDIAN,
CINE_BIG_ENDIAN
};
struct PalEntry {
char name[10];
byte pal1[16];
byte pal2[16];
};
void loadPal(const char *fileName);
void loadRelatedPalette(const char *fileName);
/**
* A class for handling Cine-engine's palettes.
* TODO: Test a bit more
*/
class Palette {
public:
struct Color {
uint8 r, g, b;
};
/**
* Create an initially black palette with the given color format and number of colors.
* @param format Color format
* @param numColors Number of colors
* @note For the default constructed object (i.e. no parameters given) this will hold: empty() && !isValid()
*/
Palette(const Graphics::PixelFormat &format = Graphics::PixelFormat(), const uint numColors = 0);
Palette(const Palette& other);
Palette& operator=(const Palette& other);
/**
* Clear the palette (Set color count to zero, release memory, overwrite color format with default value).
* @note This is very different from using fillWithBlack-function which fills the palette with black.
*/
Palette &clear();
/**
* Load palette from buffer with given color format, endianness and number of colors.
* @param buf Input buffer
* @param size Input buffer size in bytes
* @param format Input color format
* @param numColors Number of colors to load
* @param endian The endianness of the colors in the input buffer
*/
Palette &load(const byte *buf, const uint size, const Graphics::PixelFormat &format, const uint numColors, const EndianType endian);
/**
* Save the whole palette to buffer in original color format using defined endianness.
* @param buf Output buffer
* @param size Output buffer size in bytes
* @param endian The endian type to use
*/
byte *save(byte *buf, const uint size, const EndianType endian) const;
/**
* Save the whole palette to buffer in given color format using defined endianness.
* @param buf Output buffer
* @param size Output buffer size in bytes
* @param format Output color format
* @param endian The endian type to use
*/
byte *save(byte *buf, const uint size, const Graphics::PixelFormat &format, const EndianType endian) const;
/**
* Save (partial) palette to buffer in given color format using defined endianness.
* @param buf Output buffer
* @param size Output buffer size in bytes
* @param format Output color format
* @param numColors Number of colors to save
* @param endian The endian type to use
* @param firstIndex Starting color index (from which onwards to save the colors)
*/
byte *save(byte *buf, const uint size, const Graphics::PixelFormat &format, const uint numColors, const EndianType endian, const byte firstIndex = 0) const;
/**
* Rotate the palette in color range [firstIndex, lastIndex] to the right by one.
*/
Palette &rotateRight(byte firstIndex, byte lastIndex);
Palette &rotateLeft(byte firstIndex, byte lastIndex);
Palette &saturatedAddColor(Palette &output, byte firstIndex, byte lastIndex, signed r, signed g, signed b) const;
/**
* Saturated add an RGB color in given color format to current palette's subset and save the modified colors in the given output palette.
* @param output The output palette (Only this palette is modified)
* @param firstIndex First color index of the palette's subset (Inclusive range)
* @param lastIndex Last color index of the palette's subset (Inclusive range)
* @param rSource The red color component in the source color format
* @param gSource The green color component in the source color format
* @param bSource The blue color component in the source color format
* @param sourceFormat The source color format (i.e. the color format of the given RGB color)
* @note This function basically converts the given color to the palette's internal color format
* and adds that using the normal saturatedAddColor-function.
*/
Palette &saturatedAddColor(Palette &output, byte firstIndex, byte lastIndex, signed rSource, signed gSource, signed bSource, const Graphics::PixelFormat &sourceFormat) const;
/**
* Saturated add a normalized gray value to current palette's subset and save the modified colors in the given output palette.
* @param output The output palette (Only this palette is modified)
* @param firstIndex First color index of the palette's subset (Inclusive range)
* @param lastIndex Last color index of the palette's subset (Inclusive range)
* @param grayDividend Dividend of the normalized gray value
* @param grayDenominator Denominator of the normalized gray value
* @note The normalized gray value (i.e. in range [-1, +1]) is given as a fractional number
* (i.e. the normalized gray value is calculated by dividing grayDividend by grayDenominator).
*/
Palette &saturatedAddNormalizedGray(Palette &output, byte firstIndex, byte lastIndex, signed grayDividend, signed grayDenominator) const;
bool empty() const;
uint colorCount() const;
Palette &fillWithBlack();
/** Is the palette valid? (Mostly just checks the color format for correctness) */
bool isValid() const;
/** The original color format in which this palette was loaded. */
const Graphics::PixelFormat &colorFormat() const;
/** Sets current palette to global OSystem's palette using g_system->getPaletteManager()->setPalette. */
void setGlobalOSystemPalette() const;
/** Get the color at the given palette index. */
Color getColor(byte index) const;
/** Get the red color component of the color at the given palette index. */
uint8 getR(byte index) const;
/** Get the green color component of the color at the given palette index. */
uint8 getG(byte index) const;
/** Get the blue color component of the color at the given palette index. */
uint8 getB(byte index) const;
bool ensureContrast(byte &minBrightnessColorIndex);
bool isEqual(byte index1, byte index2);
private:
int findMinBrightnessColorIndex(uint minColorIndex = 1);
byte brightness(byte colorIndex);
void setColorFormat(const Graphics::PixelFormat &format);
void saturatedAddColor(Color &result, const Color &baseColor, signed r, signed g, signed b) const;
private:
Graphics::PixelFormat _format; ///< The used source color format
Common::Array<Color> _colors; ///< The actual palette data
};
} // End of namespace Cine
#endif

377
engines/cine/part.cpp Normal file
View File

@@ -0,0 +1,377 @@
/* 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/memstream.h"
#include "common/textconsole.h"
#include "cine/cine.h"
#include "cine/unpack.h"
#include "cine/various.h"
namespace Cine {
void loadPart(const char *partName) {
g_cine->_partBuffer.clear();
g_cine->_partFileHandle.close();
checkDataDisk(-1);
if (!g_cine->_partFileHandle.open(partName))
error("loadPart(): Cannot open file %s", partName);
setMouseCursor(MOUSE_CURSOR_DISK);
uint16 numElementInPart = g_cine->_partFileHandle.readUint16BE();
g_cine->_partBuffer.resize(numElementInPart);
g_cine->_partFileHandle.readUint16BE(); // entry size
if (currentPartName != partName)
Common::strlcpy(currentPartName, partName, sizeof(currentPartName));
for (uint16 i = 0; i < g_cine->_partBuffer.size(); i++) {
g_cine->_partFileHandle.read(g_cine->_partBuffer[i].partName, 14);
g_cine->_partBuffer[i].offset = g_cine->_partFileHandle.readUint32BE();
g_cine->_partBuffer[i].packedSize = g_cine->_partFileHandle.readUint32BE();
g_cine->_partBuffer[i].unpackedSize = g_cine->_partFileHandle.readUint32BE();
g_cine->_partFileHandle.readUint32BE(); // unused
}
if (g_cine->getGameType() == Cine::GType_FW && g_cine->getPlatform() == Common::kPlatformDOS && strcmp(partName, "BASESON.SND") != 0)
loadPal(partName);
}
void closePart() {
// TODO
}
static Common::String fixVolCnfFileName(const uint8 *src, uint len) {
assert(len == 11 || len == 13);
// Copy source to a temporary buffer and force a trailing zero for string manipulation
char tmp[14];
memcpy(tmp, src, len);
tmp[len] = 0;
Common::String result;
if (len == 11) {
// Filenames of length 11 have no separation of the extension and the basename
// so that's why we have to convert them first. There's no trailing zero in them
// either and they're always of the full length 11 with padding spaces. Extension
// can be always found at offset 8 onwards.
//
// Examples of filename mappings:
// "AEROPORTMSG" -> "AEROPORT.MSG"
// "MITRAILLHP " -> "MITRAILL.HP" (Notice the trailing space after the extension)
// "BOND10 " -> "BOND10"
// "GIRL SET" -> "GIRL.SET"
// Replace all space characters with zeroes
for (uint i = 0; i < len; i++)
if (tmp[i] == ' ')
tmp[i] = 0;
// Extract the filename's extension
Common::String extension(tmp + 8);
tmp[8] = 0; // Force separation of extension and basename
Common::String basename(tmp);
if (extension.empty()) {
result = basename;
} else {
result = basename + "." + extension;
}
} else {
// Filenames of length 13 are okay as they are, no need for conversion
result = Common::String(tmp);
}
return result;
}
void CineEngine::readVolCnf() {
Common::File f;
if (!f.open("vol.cnf")) {
error("Unable to open 'vol.cnf'");
}
uint32 unpackedSize, packedSize;
char hdr[8];
f.read(hdr, 8);
bool compressed = (memcmp(hdr, "ABASECP", 7) == 0);
if (compressed) {
unpackedSize = f.readUint32BE();
packedSize = f.readUint32BE();
} else {
f.seek(0);
unpackedSize = packedSize = f.size();
}
uint8 *buf = new uint8[unpackedSize];
uint8 *packedBuf = new uint8[packedSize];
f.read(packedBuf, packedSize);
CineUnpacker cineUnpacker;
if (!cineUnpacker.unpack(packedBuf, packedSize, buf, unpackedSize)) {
error("Error while unpacking 'vol.cnf' data");
}
delete[] packedBuf;
Common::Array<VolumeResource> volumeResourceFiles;
uint8 *p = buf;
int resourceFilesCount = READ_BE_INT16(p); p += 2;
int entrySize = READ_BE_INT16(p); p += 2;
for (int i = 0; i < resourceFilesCount; ++i) {
Common::MemoryReadStream readS(p, 0x14);
VolumeResource res;
readS.read(res.name, sizeof(res.name));
res.name[sizeof(res.name) - 1] = 0;
res.pNamesList = readS.readUint32BE();
res.diskNum = readS.readSint16BE();
res.sizeOfNamesList = readS.readUint32BE();
volumeResourceFiles.push_back(res);
p += entrySize;
}
// Check file name blocks' sizes
bool fileNameLenMod11, fileNameLenMod13;
fileNameLenMod11 = fileNameLenMod13 = true;
for (int i = 0; i < resourceFilesCount; ++i) {
int size = READ_BE_UINT32(p); p += 4;
fileNameLenMod11 &= ((size % 11) == 0);
fileNameLenMod13 &= ((size % 13) == 0);
p += size;
}
// Make sure at least one of the candidates for file name length fits the data
assert(fileNameLenMod11 || fileNameLenMod13);
// File name length used to be deduced from the fact whether the file
// was compressed or not. Compressed files used file name length 11,
// uncompressed files used file name length 13. It worked almost always,
// but not with the game entry that's detected as the Operation Stealth's
// US Amiga release. It uses a compressed 'vol.cnf' file but still uses
// file names of length 13. So we try to deduce the file name length from
// the data in the 'vol.cnf' file.
int fileNameLength;
if (fileNameLenMod11 != fileNameLenMod13) {
// All file name blocks' sizes were divisible by either 11 or 13, but not with both.
fileNameLength = (fileNameLenMod11 ? 11 : 13);
} else {
warning("Couldn't deduce file name length from data in 'vol.cnf', using a backup deduction scheme");
// Here we use the former file name length detection method
// if we couldn't deduce the file name length from the data.
fileNameLength = (compressed ? 11 : 13);
}
p = buf + 4 + resourceFilesCount * entrySize;
for (int i = 0; i < resourceFilesCount; ++i) {
const VolumeResource& volRes = volumeResourceFiles[i];
int count = READ_BE_UINT32(p) / fileNameLength; p += 4;
while (count--) {
Common::String volumeEntryName = fixVolCnfFileName(p, fileNameLength);
if (!_volumeEntriesMap.contains(volumeEntryName)) {
_volumeEntriesMap.setVal(volumeEntryName, Common::Array<VolumeResource>());
}
_volumeEntriesMap.find(volumeEntryName)->_value.push_back(volRes);
debugC(5, kCineDebugPart, "Added volume entry name '%s', disk number %d, resource file '%s'", volumeEntryName.c_str(), volRes.diskNum, volRes.name);
p += fileNameLength;
}
}
delete[] buf;
}
int16 findFileInBundle(const char *fileName) {
// HACK: Fix underwater background palette by reading it from correct file
if (hacksEnabled && g_cine->getGameType() == Cine::GType_OS &&
scumm_stricmp(currentPrcName, "SOUSMAR2.PRC") == 0 &&
g_cine->_volumeEntriesMap.contains(fileName)) {
Common::Array<VolumeResource> volRes = g_cine->_volumeEntriesMap.find(fileName)->_value;
if (volRes.size() == 2 && scumm_stricmp(volRes[0].name, "rsc12") == 0 &&
scumm_stricmp(volRes[1].name, "rsc08") == 0 &&
(scumm_stricmp(fileName, "39.PI1") == 0 ||
scumm_stricmp(fileName, "SP39_11.SET") == 0 ||
scumm_stricmp(fileName, "SP39_12.SET") == 0)) {
debugC(5, kCineDebugPart, "Reading underwater background and fish from file rsc12 for the original (broken) palette.");
loadPart("rsc08");
}
}
if (g_cine->getGameType() == Cine::GType_OS) {
for (uint i = 0; i < g_cine->_partBuffer.size(); i++) {
if (!scumm_stricmp(fileName, g_cine->_partBuffer[i].partName)) {
return i;
}
}
StringToVolumeResourceArrayHashMap::const_iterator it = g_cine->_volumeEntriesMap.find(fileName);
if (it == g_cine->_volumeEntriesMap.end()) {
warning("Unable to find part file for filename '%s'", fileName);
return -1;
}
// Prefer current disk's resource file
Common::Array<VolumeResource> volRes = it->_value;
VolumeResource match = volRes[0];
for (uint i = 0; i < volRes.size(); i++) {
if (volRes[i].diskNum == currentDisk) {
match = volRes[i];
break;
}
}
checkDataDisk(match.diskNum);
loadPart(match.name);
}
for (uint i = 0; i < g_cine->_partBuffer.size(); i++) {
if (!scumm_stricmp(fileName, g_cine->_partBuffer[i].partName)) {
return i;
}
}
return -1;
}
void readFromPart(int16 idx, byte *dataPtr, uint32 maxSize) {
assert(maxSize >= g_cine->_partBuffer[idx].packedSize);
setMouseCursor(MOUSE_CURSOR_DISK);
g_cine->_partFileHandle.seek(g_cine->_partBuffer[idx].offset, SEEK_SET);
g_cine->_partFileHandle.read(dataPtr, g_cine->_partBuffer[idx].packedSize);
}
byte *readBundleFile(int16 foundFileIdx, uint32 *size) {
assert(foundFileIdx >= 0 && foundFileIdx < (int32)g_cine->_partBuffer.size());
bool error = false;
byte *dataPtr = (byte *)calloc(g_cine->_partBuffer[foundFileIdx].unpackedSize, 1);
byte *packedData = (byte *)calloc(g_cine->_partBuffer[foundFileIdx].packedSize, 1);
assert(dataPtr && packedData);
readFromPart(foundFileIdx, packedData, g_cine->_partBuffer[foundFileIdx].packedSize);
CineUnpacker cineUnpacker;
error = !cineUnpacker.unpack(packedData, g_cine->_partBuffer[foundFileIdx].packedSize, dataPtr, g_cine->_partBuffer[foundFileIdx].unpackedSize);
free(packedData);
if (error) {
warning("Error unpacking '%s' from bundle file '%s'", g_cine->_partBuffer[foundFileIdx].partName, currentPartName);
}
// Set the size variable if a pointer to it has been given
if (size != nullptr) {
*size = g_cine->_partBuffer[foundFileIdx].unpackedSize;
}
return dataPtr;
}
byte *readBundleSoundFileOS(const char *entryName, uint32 *size) {
int16 index = findFileInBundle(entryName);
if (index == -1) {
return nullptr;
}
return readBundleFile(index, size);
}
byte *readBundleSoundFileFW(const char *entryName, uint32 *size) {
int16 index;
byte *data = nullptr;
char previousPartName[15] = "";
if (g_cine->getGameType() == Cine::GType_FW) {
Common::strcpy_s(previousPartName, currentPartName);
loadPart("BASESON.SND");
}
index = findFileInBundle((const char *)entryName);
if (index != -1) {
data = readBundleFile(index);
if (size) {
*size = g_cine->_partBuffer[index].unpackedSize;
}
}
if (g_cine->getGameType() == Cine::GType_FW) {
loadPart(previousPartName);
}
return data;
}
byte *readBundleSoundFile(const char *entryName, uint32 *size) {
if (g_cine->getGameType() == Cine::GType_FW) {
return readBundleSoundFileFW(entryName, size);
} else {
return readBundleSoundFileOS(entryName, size);
}
}
/** Rotate byte value to the left by n bits */
byte rolByte(byte value, uint n) {
n %= 8;
return (byte)((value << n) | (value >> (8 - n)));
}
byte *readFile(const char *filename, bool crypted) {
Common::File in;
in.open(filename);
if (!in.isOpen())
error("readFile(): Cannot open file %s", filename);
uint32 size = in.size();
byte *dataPtr = (byte *)malloc(size);
in.read(dataPtr, size);
// The Sony published CD version of Future Wars has its
// AUTO00.PRC file's bytes rotated to the right by one.
// So we decode the so called crypting by rotating all
// the bytes to the left by one.
if (crypted) {
for (uint index = 0; index < size; index++) {
dataPtr[index] = rolByte(dataPtr[index], 1);
}
}
return dataPtr;
}
void checkDataDisk(int16 diskNum) {
if (diskNum != -1) {
currentDisk = diskNum;
}
}
void dumpBundle(const char *fileName) {
char tmpPart[15];
Common::strcpy_s(tmpPart, currentPartName);
loadPart(fileName);
for (uint i = 0; i < g_cine->_partBuffer.size(); i++) {
byte *data = readBundleFile(i);
debug(0, "%s", g_cine->_partBuffer[i].partName);
Common::DumpFile out;
if (out.open(Common::Path("dumps/").appendInPlace(g_cine->_partBuffer[i].partName))) {
out.write(data, g_cine->_partBuffer[i].unpackedSize);
out.close();
}
free(data);
}
loadPart(tmpPart);
}
} // End of namespace Cine

53
engines/cine/part.h Normal file
View File

@@ -0,0 +1,53 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef CINE_PART_H
#define CINE_PART_H
namespace Cine {
struct PartBuffer {
char partName[14];
uint32 offset;
uint32 packedSize;
uint32 unpackedSize;
};
#define NUM_MAX_PARTDATA 255
void loadPart(const char *partName);
void closePart();
int16 findFileInBundle(const char *fileName);
void readFromPart(int16 idx, byte *dataPtr, uint32 maxSize);
byte *readBundleFile(int16 foundFileIdx, uint32 *size = NULL);
byte *readBundleSoundFile(const char *entryName, uint32 *size = 0);
byte *readFile(const char *filename, bool crypted = false);
void checkDataDisk(int16 param);
void dumpBundle(const char *filename);
} // End of namespace Cine
#endif

114
engines/cine/prc.cpp Normal file
View File

@@ -0,0 +1,114 @@
/* 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/config-manager.h"
#include "common/system.h" // for g_system->getEventManager()
#include "cine/cine.h"
#include "cine/various.h"
namespace Cine {
//char currentPrcName[20];
/**
* @todo Is script size of 0 valid?
* @todo Fix script dump code
* @return Was the loading successful?
*/
bool loadPrc(const char *pPrcName) {
byte i;
uint16 numScripts;
byte *scriptPtr, *dataPtr;
assert(pPrcName);
g_cine->_globalScripts.clear();
g_cine->_scriptTable.clear();
// This is copy protection. Used to hang the machine
if (!scumm_stricmp(pPrcName, COPY_PROT_FAIL_PRC_NAME)) {
Common::Event event;
event.type = Common::EVENT_RETURN_TO_LAUNCHER;
g_system->getEventManager()->pushEvent(event);
return false;
}
checkDataDisk(-1);
if ((g_cine->getGameType() == Cine::GType_FW) &&
(!scumm_stricmp(pPrcName, BOOT_PRC_NAME) || !scumm_stricmp(pPrcName, "demo.prc"))) {
scriptPtr = dataPtr = readFile(pPrcName, (g_cine->getFeatures() & GF_CRYPTED_BOOT_PRC) != 0);
} else {
checkDataDisk(-1);
scriptPtr = dataPtr = readBundleFile(findFileInBundle(pPrcName));
}
assert(scriptPtr);
setMouseCursor(MOUSE_CURSOR_DISK);
numScripts = READ_BE_UINT16(scriptPtr);
scriptPtr += 2;
assert(numScripts <= NUM_MAX_SCRIPT);
for (i = 0; i < numScripts; i++) {
RawScriptPtr tmp(new RawScript(READ_BE_UINT16(scriptPtr)));
scriptPtr += 2;
assert(tmp);
g_cine->_scriptTable.push_back(tmp);
}
for (i = 0; i < numScripts; i++) {
uint16 size = g_cine->_scriptTable[i]->_size;
// TODO: delete the test?
if (size) {
g_cine->_scriptTable[i]->setData(*g_cine->_scriptInfo, scriptPtr);
scriptPtr += size;
}
}
free(dataPtr);
#ifdef DUMP_SCRIPTS
{
uint16 s;
char buffer[256];
for (s = 0; s < numScripts; s++) {
if (g_cine->_scriptTable[s]->_size) {
Common::sprintf_s(buffer, "%s_%03d.txt", pPrcName, s);
decompileScript((const byte *)g_cine->_scriptTable[s]->getString(0), g_cine->_scriptTable[s]->_size, s);
dumpScript(buffer);
}
}
}
#endif
return true;
}
} // End of namespace Cine

31
engines/cine/prc.h Normal file
View File

@@ -0,0 +1,31 @@
/* 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 CINE_PRC_H
#define CINE_PRC_H
namespace Cine {
bool loadPrc(const char *pPrcName);
} // End of namespace Cine
#endif

90
engines/cine/rel.cpp Normal file
View File

@@ -0,0 +1,90 @@
/* 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 "cine/cine.h"
#include "cine/various.h"
namespace Cine {
/**
* @todo Is script size of 0 valid?
* @todo Fix script dump code
*/
void loadRel(char *pRelName) {
uint16 numEntry;
uint16 i;
uint16 size, p1, p2, p3;
byte *ptr, *dataPtr;
checkDataDisk(-1);
g_cine->_objectScripts.clear();
g_cine->_relTable.clear();
ptr = dataPtr = readBundleFile(findFileInBundle(pRelName));
setMouseCursor(MOUSE_CURSOR_DISK);
numEntry = READ_BE_UINT16(ptr); ptr += 2;
for (i = 0; i < numEntry; i++) {
size = READ_BE_UINT16(ptr); ptr += 2;
p1 = READ_BE_UINT16(ptr); ptr += 2;
p2 = READ_BE_UINT16(ptr); ptr += 2;
p3 = READ_BE_UINT16(ptr); ptr += 2;
RawObjectScriptPtr tmp(new RawObjectScript(size, p1, p2, p3));
assert(tmp);
g_cine->_relTable.push_back(tmp);
}
for (i = 0; i < numEntry; i++) {
size = g_cine->_relTable[i]->_size;
// TODO: delete the test?
if (size) {
g_cine->_relTable[i]->setData(*g_cine->_scriptInfo, ptr);
ptr += size;
}
}
free(dataPtr);
#ifdef DUMP_SCRIPTS
{
uint16 s;
char buffer[256];
for (s = 0; s < numEntry; s++) {
if (g_cine->_relTable[s]->_size) {
Common::sprintf_s(buffer, "%s_%03d.txt", pRelName, s);
decompileScript((const byte *)g_cine->_relTable[s]->getString(0), g_cine->_relTable[s]->_size, s);
dumpScript(buffer);
}
}
}
#endif
}
} // End of namespace Cine

32
engines/cine/rel.h Normal file
View File

@@ -0,0 +1,32 @@
/* 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 CINE_REL_H
#define CINE_REL_H
#include "cine/script.h"
namespace Cine {
void loadRel(char *pRelName);
} // End of namespace Cine
#endif

1102
engines/cine/saveload.cpp Normal file

File diff suppressed because it is too large Load Diff

104
engines/cine/saveload.h Normal file
View File

@@ -0,0 +1,104 @@
/* 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 CINE_SAVELOAD_H
#define CINE_SAVELOAD_H
#include "common/endian.h"
namespace Cine {
extern int16 currentDisk;
/**
* Cine engine's save game formats.
* Enumeration entries (Excluding the one used as an error)
* are sorted according to age (i.e. top one is oldest, last one newest etc).
*
* ANIMSIZE_UNKNOWN:
* - Animation data entry size is unknown (Used as an error).
*
* ANIMSIZE_23:
* - Animation data entry size is 23 bytes.
* - Used at least by 0.11.0 and 0.11.1 releases of ScummVM.
* - Introduced in revision 21772, stopped using in revision 31444.
*
* ANIMSIZE_30_PTRS_BROKEN:
* - Animation data entry size is 30 bytes.
* - Data and mask pointers in the saved structs are always NULL.
* - Introduced in revision 31453, stopped using in revision 32073.
*
* ANIMSIZE_30_PTRS_INTACT:
* - Animation data entry size is 30 bytes.
* - Data and mask pointers in the saved structs are intact,
* so you can test them for equality or inequality with NULL
* but don't try using them for anything else, it won't work.
* - Introduced in revision 31444, got broken in revision 31453,
* got fixed in revision 32073 and used after that.
*
* TEMP_OS_FORMAT:
* - Temporary Operation Stealth savegame format.
* - NOT backward compatible and NOT to be supported in the future.
* This format should ONLY be used during development and abandoned
* later in favor of a better format!
*/
enum CineSaveGameFormat {
ANIMSIZE_UNKNOWN,
ANIMSIZE_23,
ANIMSIZE_30_PTRS_BROKEN,
ANIMSIZE_30_PTRS_INTACT,
TEMP_OS_FORMAT,
VERSIONED_FW_FORMAT,
VERSIONED_OS_FORMAT
};
/** Identifier for the temporary Operation Stealth savegame format. */
static const uint32 TEMP_OS_FORMAT_ID = MKTAG('T', 'E', 'M', 'P');
/** Identifiers for versioned Future Wars and Operation Stealth savegame formats. */
static const uint32 VERSIONED_FW_FORMAT_ID = MKTAG('C', '1', 'F', 'W');
static const uint32 VERSIONED_OS_FORMAT_ID = MKTAG('C', '2', 'O', 'S');
/** The current version number of versioned Future Wars and Operation Stealth savegame formats.
Version 4: First version used. Added disableSystemMenu to Future Wars savegame format.
*/
static const uint32 CURRENT_SAVE_VER = 4;
/** The last version number of temporary Operation Stealth's savegame format.
Version 0: Color count was not saved, was assumed to be 256. BGIncrust.bgIdx does not exist, _currentBg was used.
Version 1: Saving of real color count was added but still 256 colors were always saved.
Version 2: BGIncrust.bgIdx was added.
Version 3: Saving real values for current music name, music playing status, current background index,
scroll background index and background scrolling was added.
*/
static const uint32 LAST_TEMP_OS_SAVE_VER = 3;
/** Chunk header used by the temporary Operation Stealth savegame format. */
struct ChunkHeader {
uint32 id; ///< Identifier (e.g. MKTAG('T','E','M','P'))
uint32 version; ///< Version number
uint32 size; ///< Size of the chunk after this header in bytes
};
} // End of namespace Cine
#endif

392
engines/cine/script.h Normal file
View File

@@ -0,0 +1,392 @@
/* 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 CINE_SCRIPT_H
#define CINE_SCRIPT_H
#include "common/savefile.h"
#include "common/array.h"
#include "common/list.h"
#include "common/ptr.h"
namespace Cine {
#define SCRIPT_STACK_SIZE 50
#define LOCAL_VARS_SIZE 50
/**
* Fixed size array of script variables.
*
* Array size can be set in constructors, once the instance is created,
* it cannot be changed directly.
*/
class FWScript;
typedef int (FWScript::*OpFunc)();
struct Opcode {
OpFunc proc;
const char *args;
};
/**
* Fixed size array for script variables
*/
class ScriptVars {
private:
unsigned int _size; ///< Size of array
int16 *_vars; ///< Variable values
public:
// Explicit to prevent var=0 instead of var[i]=0 typos.
explicit ScriptVars(unsigned int len = 50);
ScriptVars(Common::SeekableReadStream &fHandle, unsigned int len = 50);
ScriptVars(const ScriptVars &src);
~ScriptVars();
void reinit(unsigned int len);
ScriptVars &operator=(const ScriptVars &src);
int16 &operator[](unsigned int idx);
int16 operator[](unsigned int idx) const;
void save(Common::OutSaveFile &fHandle) const;
void save(Common::OutSaveFile &fHandle, unsigned int len) const;
void load(Common::SeekableReadStream &fHandle);
void load(Common::SeekableReadStream &fHandle, unsigned int len);
void reset();
};
class FWScriptInfo;
/**
* Script bytecode and initial labels, ScriptStruct replacement.
*
* _data is one byte longer to make sure strings in bytecode are properly
* terminated
*/
class RawScript {
private:
byte *_data; ///< Script bytecode
ScriptVars _labels; ///< Initial script labels
protected:
void computeLabels(const FWScriptInfo &info);
int getNextLabel(const FWScriptInfo &info, int offset) const;
public:
uint16 _size; ///< Bytecode length
explicit RawScript(uint16 size);
RawScript(const FWScriptInfo &info, const byte *data, uint16 size);
RawScript(const RawScript &src);
~RawScript();
RawScript &operator=(const RawScript &src);
void setData(const FWScriptInfo &info, const byte *data);
const ScriptVars &labels() const;
byte getByte(unsigned int pos) const;
uint16 getWord(unsigned int pos) const;
const char *getString(unsigned int pos) const;
uint16 getLabel(const FWScriptInfo &info, byte index, uint16 offset) const;
};
/**
* Object script class, RelObjectScript replacement
*
* Script parameters are not used, this class is required by different
* script initialization of object scripts
*/
class RawObjectScript : public RawScript {
public:
int16 _runCount; ///< How many times the script was used
uint16 _param1; ///< Additional parameter not used at the moment
uint16 _param2; ///< Additional parameter not used at the moment
uint16 _param3; ///< Additional parameter not used at the moment
RawObjectScript(uint16 size, uint16 p1, uint16 p2, uint16 p3);
RawObjectScript(const FWScriptInfo &info, const byte *data, uint16 size, uint16 p1, uint16 p2, uint16 p3);
/**
* Run the script one more time.
* @return Run count before incrementation
*/
int16 run() { return _runCount++; }
};
/**
* Future Wars script, prcLinkedListStruct replacement.
* @todo Rewrite _globalVars initialization
*/
class FWScript {
private:
const RawScript &_script; ///< Script bytecode reference
uint16 _pos; ///< Current position in script
uint16 _line; ///< Current opcode index in bytecode for debugging
uint16 _compare; ///< Last compare result
ScriptVars _labels; ///< Current script labels
ScriptVars _localVars; ///< Local script variables
ScriptVars &_globalVars; ///< Global variables reference
FWScriptInfo *_info; ///< Script info
protected:
static const Opcode *_opcodeTable;
static unsigned int _numOpcodes;
int o1_modifyObjectParam();
int o1_getObjectParam();
int o1_addObjectParam();
int o1_subObjectParam();
int o1_mulObjectParam();
int o1_divObjectParam();
int o1_compareObjectParam();
int o1_setupObject();
int o1_checkCollision();
int o1_loadVar();
int o1_addVar();
int o1_subVar();
int o1_mulVar();
int o1_divVar();
int o1_compareVar();
int o1_modifyObjectParam2();
int o1_loadMask0();
int o1_unloadMask0();
int o1_addToBgList();
int o1_loadMask1();
int o1_unloadMask1();
int o1_loadMask4();
int o1_unloadMask4();
int o1_addSpriteFilledToBgList();
int o1_clearBgIncrustList();
int o1_label();
int o1_goto();
int o1_gotoIfSup();
int o1_gotoIfSupEqu();
int o1_gotoIfInf();
int o1_gotoIfInfEqu();
int o1_gotoIfEqu();
int o1_gotoIfDiff();
int o1_removeLabel();
int o1_loop();
int o1_startGlobalScript();
int o1_endGlobalScript();
int o1_loadAnim();
int o1_loadBg();
int o1_loadCt();
int o1_loadPart();
int o1_closePart();
int o1_loadNewPrcName();
int o1_requestCheckPendingDataLoad();
int o1_blitAndFade();
int o1_fadeToBlack();
int o1_transformPaletteRange();
int o1_setDefaultMenuBgColor();
int o1_palRotate();
int o1_break();
int o1_endScript();
int o1_message();
int o1_loadGlobalVar();
int o1_compareGlobalVar();
int o1_declareFunctionName();
int o1_freePartRange();
int o1_unloadAllMasks();
int o1_setScreenDimensions();
int o1_displayBackground();
int o1_initializeZoneData();
int o1_setZoneDataEntry();
int o1_getZoneDataEntry();
int o1_setPlayerCommandPosY();
int o1_allowPlayerInput();
int o1_disallowPlayerInput();
int o1_changeDataDisk();
int o1_loadMusic();
int o1_playMusic();
int o1_fadeOutMusic();
int o1_stopSample();
int o1_op71();
int o1_op72();
int o1_op73();
int o1_playSample();
int o1_playSampleSwapped();
int o1_disableSystemMenu();
int o1_loadMask5();
int o1_unloadMask5();
// pointers to member functions in C++ suck...
int o2_loadCt();
int o2_loadPart();
int o2_addSeqListElement();
int o2_removeSeq();
int o2_playSample();
int o2_playSampleAlt();
int o2_clearSeqList();
int o2_modifySeqListElement();
int o2_isSeqRunning();
int o2_gotoIfSupNearest();
int o2_gotoIfSupEquNearest();
int o2_gotoIfInfNearest();
int o2_gotoIfInfEquNearest();
int o2_gotoIfEquNearest();
int o2_gotoIfDiffNearest();
int o2_startObjectScript();
int o2_stopObjectScript();
int o2_op8D();
int o2_addBackground();
int o2_removeBackground();
int o2_loadAbs();
int o2_loadBg();
int o2_wasZoneChecked();
int o2_op9B();
int o2_op9C();
int o2_useBgScroll();
int o2_setAdditionalBgVScroll();
int o2_op9F();
int o2_addGfxElementType20();
int o2_removeGfxElementType20();
int o2_addGfxElementType21();
int o2_removeGfxElementType21();
int o2_loadMask22();
int o2_unloadMask22();
byte getNextByte();
uint16 getNextWord();
const char *getNextString();
void load(const ScriptVars &labels, const ScriptVars &local, uint16 compare, uint16 pos);
FWScript(const RawScript &script, int16 index, FWScriptInfo *info);
FWScript(RawObjectScript &script, int16 index, FWScriptInfo *info);
FWScript(const FWScript &src, FWScriptInfo *info);
public:
int16 _index; ///< Index in script table
static void setupTable();
FWScript(const RawScript &script, int16 index);
// FWScript(const RawObjectScript &script, int16 index);
FWScript(const FWScript &src);
~FWScript();
int execute();
void save(Common::OutSaveFile &fHandle) const;
friend class FWScriptInfo;
// workaround for bug in g++ which prevents protected member functions
// of FWScript from being used in OSScript::_opcodeTable[]
// initialization ("error: protected within this context")
friend class OSScript;
};
/**
* Operation Stealth script, prcLinkedListStruct replacement
*/
class OSScript : public FWScript {
private:
static const Opcode *_opcodeTable;
static unsigned int _numOpcodes;
protected:
void load(const ScriptVars &labels, const ScriptVars &local, uint16 compare, uint16 pos);
public:
static void setupTable();
OSScript(const RawScript &script, int16 index);
OSScript(RawObjectScript &script, int16 index);
OSScript(const OSScript &src);
friend class OSScriptInfo;
};
/**
* Future Wars script factory and info
*/
class FWScriptInfo {
protected:
virtual OpFunc opcodeHandler(byte opcode) const;
public:
virtual ~FWScriptInfo() {}
virtual const char *opcodeInfo(byte opcode) const;
virtual FWScript *create(const RawScript &script, int16 index) const;
virtual FWScript *create(const RawObjectScript &script, int16 index) const;
virtual FWScript *create(const RawScript &script, int16 index, const ScriptVars &labels, const ScriptVars &local, uint16 compare, uint16 pos) const;
virtual FWScript *create(const RawObjectScript &script, int16 index, const ScriptVars &labels, const ScriptVars &local, uint16 compare, uint16 pos) const;
friend class FWScript;
};
/**
* Operation Stealth script factory and info
*/
class OSScriptInfo : public FWScriptInfo {
protected:
OpFunc opcodeHandler(byte opcode) const override;
public:
~OSScriptInfo() override {}
const char *opcodeInfo(byte opcode) const override;
FWScript *create(const RawScript &script, int16 index) const override;
FWScript *create(const RawObjectScript &script, int16 index) const override;
FWScript *create(const RawScript &script, int16 index, const ScriptVars &labels, const ScriptVars &local, uint16 compare, uint16 pos) const override;
FWScript *create(const RawObjectScript &script, int16 index, const ScriptVars &labels, const ScriptVars &local, uint16 compare, uint16 pos) const override;
friend class FWScript;
};
typedef Common::SharedPtr<FWScript> ScriptPtr;
typedef Common::SharedPtr<RawScript> RawScriptPtr;
typedef Common::SharedPtr<RawObjectScript> RawObjectScriptPtr;
typedef Common::List<ScriptPtr> ScriptList;
typedef Common::Array<RawScriptPtr> RawScriptArray;
typedef Common::Array<RawObjectScriptPtr> RawObjectScriptArray;
#define NUM_MAX_SCRIPT 50
FWScriptInfo *setupOpcodes();
void decompileScript(const byte *scriptPtr, uint16 scriptSize, uint16 scriptIdx);
void dumpScript(char *dumpName);
#define OP_loadPart 0x3F
#define OP_loadNewPrcName 0x41
#define OP_requestCheckPendingDataLoad 0x42
#define OP_endScript 0x50
void addScriptToGlobalScripts(uint16 idx);
int16 checkCollision(int16 objIdx, int16 x, int16 y, int16 numZones, int16 zoneIdx);
void runObjectScript(int16 entryIdx);
void executeObjectScripts();
void executeGlobalScripts();
void purgeObjectScripts();
void purgeGlobalScripts();
} // End of namespace Cine
#endif

3250
engines/cine/script_fw.cpp Normal file

File diff suppressed because it is too large Load Diff

881
engines/cine/script_os.cpp Normal file
View File

@@ -0,0 +1,881 @@
/* 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/>.
*
*/
/** \file
* Operation Stealth script interpreter file
*/
#include "common/endian.h"
#include "common/textconsole.h"
#include "cine/cine.h"
#include "cine/bg_list.h"
#include "cine/object.h"
#include "cine/sound.h"
#include "cine/various.h"
#include "cine/script.h"
namespace Cine {
const Opcode *OSScript::_opcodeTable = nullptr;
unsigned int OSScript::_numOpcodes = 0;
void OSScript::setupTable() {
static const Opcode opcodeTable[] = {
/* 00 */
{ &FWScript::o1_modifyObjectParam, "bbw" },
{ &FWScript::o1_getObjectParam, "bbb" },
{ &FWScript::o1_addObjectParam, "bbw" },
{ &FWScript::o1_subObjectParam, "bbw" },
/* 04 */
{ &FWScript::o1_mulObjectParam, "bbw" },
{ &FWScript::o1_divObjectParam, "bbw" },
{ &FWScript::o1_compareObjectParam, "bbw" },
{ &FWScript::o1_setupObject, "bwwww" },
/* 08 */
{ &FWScript::o1_checkCollision, "bwwww" },
{ &FWScript::o1_loadVar, "bc" },
{ &FWScript::o1_addVar, "bc" },
{ &FWScript::o1_subVar, "bc" },
/* 0C */
{ &FWScript::o1_mulVar, "bc" },
{ &FWScript::o1_divVar, "bc" },
{ &FWScript::o1_compareVar, "bc" },
{ &FWScript::o1_modifyObjectParam2, "bbb" },
/* 10 */
{ nullptr, nullptr },
{ nullptr, nullptr },
{ nullptr, nullptr },
{ &FWScript::o1_loadMask0, "b" },
/* 14 */
{ &FWScript::o1_unloadMask0, "b" },
{ &FWScript::o1_addToBgList, "b" },
{ &FWScript::o1_loadMask1, "b" },
{ &FWScript::o1_unloadMask1, "b" },
/* 18 */
{ &FWScript::o1_loadMask4, "b" },
{ &FWScript::o1_unloadMask4, "b" },
{ &FWScript::o1_addSpriteFilledToBgList, "b" },
{ &FWScript::o1_clearBgIncrustList, "" },
/* 1C */
{ nullptr, nullptr },
{ &FWScript::o1_label, "l" },
{ &FWScript::o1_goto, "b" },
{ &FWScript::o1_gotoIfSup, "b" },
/* 20 */
{ &FWScript::o1_gotoIfSupEqu, "b" },
{ &FWScript::o1_gotoIfInf, "b" },
{ &FWScript::o1_gotoIfInfEqu, "b" },
{ &FWScript::o1_gotoIfEqu, "b" },
/* 24 */
{ &FWScript::o1_gotoIfDiff, "b" },
{ &FWScript::o1_removeLabel, "b" },
{ &FWScript::o1_loop, "bb" },
{ nullptr, nullptr },
/* 28 */
{ nullptr, nullptr },
{ nullptr, nullptr },
{ nullptr, nullptr },
{ nullptr, nullptr },
/* 2C */
{ nullptr, nullptr },
{ nullptr, nullptr },
{ nullptr, nullptr },
{ nullptr, nullptr },
/* 30 */
{ nullptr, nullptr },
{ &FWScript::o1_startGlobalScript, "b" },
{ &FWScript::o1_endGlobalScript, "b" },
{ nullptr, nullptr },
/* 34 */
{ nullptr, nullptr },
{ nullptr, nullptr },
{ nullptr, nullptr },
{ nullptr, nullptr },
/* 38 */
{ nullptr, nullptr },
{ nullptr, nullptr },
{ nullptr, nullptr },
{ &FWScript::o1_loadAnim, "s" },
/* 3C */
{ &FWScript::o1_loadBg, "s" },
{ &FWScript::o2_loadCt, "s" },
{ nullptr, nullptr },
{ &FWScript::o2_loadPart, "s" },
/* 40 */
{ &FWScript::o1_closePart, "" },
{ &FWScript::o1_loadNewPrcName, "bs" },
{ &FWScript::o1_requestCheckPendingDataLoad, "" },
{ nullptr, nullptr },
/* 44 */
{ nullptr, nullptr },
{ &FWScript::o1_blitAndFade, "" },
{ &FWScript::o1_fadeToBlack, "" },
{ &FWScript::o1_transformPaletteRange, "bbwww" },
/* 48 */
{ nullptr, nullptr },
{ &FWScript::o1_setDefaultMenuBgColor, "b" },
{ &FWScript::o1_palRotate, "bbb" },
{ nullptr, nullptr },
/* 4C */
{ nullptr, nullptr },
{ nullptr, nullptr },
{ nullptr, nullptr },
{ &FWScript::o1_break, "" },
/* 50 */
{ &FWScript::o1_endScript, "x" },
{ &FWScript::o1_message, "bwwww" },
{ &FWScript::o1_loadGlobalVar, "bc" },
{ &FWScript::o1_compareGlobalVar, "bc" },
/* 54 */
{ nullptr, nullptr },
{ nullptr, nullptr },
{ nullptr, nullptr },
{ nullptr, nullptr },
/* 58 */
{ nullptr, nullptr },
{ &FWScript::o1_declareFunctionName, "s" },
{ &FWScript::o1_freePartRange, "bb" },
{ &FWScript::o1_unloadAllMasks, "" },
/* 5C */
{ nullptr, nullptr },
{ nullptr, nullptr },
{ nullptr, nullptr },
{ nullptr, nullptr },
/* 60 */
{ nullptr, nullptr },
{ nullptr, nullptr },
{ nullptr, nullptr },
{ &FWScript::o1_setScreenDimensions, "wwww" },
/* 64 */
{ &FWScript::o1_displayBackground, "" },
{ &FWScript::o1_initializeZoneData, "" },
{ &FWScript::o1_setZoneDataEntry, "bw" },
{ &FWScript::o1_getZoneDataEntry, "bb" },
/* 68 */
{ &FWScript::o1_setPlayerCommandPosY, "b" },
{ &FWScript::o1_allowPlayerInput, "" },
{ &FWScript::o1_disallowPlayerInput, "" },
{ &FWScript::o1_changeDataDisk, "b" }, /* Same as opcodes 0x95 and 0xA9. */
/* 6C */
{ nullptr, nullptr },
{ &FWScript::o1_loadMusic, "s" },
{ &FWScript::o1_playMusic, "" },
{ &FWScript::o1_fadeOutMusic, "" },
/* 70 */
{ &FWScript::o1_stopSample, "" },
{ &FWScript::o1_op71, "bw" }, /* TODO: Name this opcode properly. */
{ &FWScript::o1_op72, "wbw" }, /* Same as opcode 0x73. TODO: Name this opcode properly. */
{ &FWScript::o1_op72, "wbw" }, /* Same as opcode 0x72. */
/* 74 */
{ nullptr, nullptr },
{ nullptr, nullptr },
{ nullptr, nullptr },
{ &FWScript::o2_playSample, "bbwbww" },
/* 78 */
{ &FWScript::o2_playSampleAlt, "bbwbww" },
{ &FWScript::o1_disableSystemMenu, "b" },
{ &FWScript::o1_loadMask5, "b" },
{ &FWScript::o1_unloadMask5, "b" }, /* Last opcode used by Future Wars. */
/* 7C */
{ nullptr, nullptr },
{ nullptr, nullptr },
{ nullptr, nullptr },
{ &FWScript::o2_addSeqListElement, "bbbbwww" },
/* 80 */
{ &FWScript::o2_removeSeq, "bb" },
{ &FWScript::o2_clearSeqList, "" },
{ &FWScript::o2_modifySeqListElement, "bbwwb" },
{ &FWScript::o2_isSeqRunning, "bb" },
/* 84 */
{ &FWScript::o2_gotoIfSupNearest, "b" },
{ &FWScript::o2_gotoIfSupEquNearest, "b" },
{ &FWScript::o2_gotoIfInfNearest, "b" },
{ &FWScript::o2_gotoIfInfEquNearest, "b" },
/* 88 */
{ &FWScript::o2_gotoIfEquNearest, "b" },
{ &FWScript::o2_gotoIfDiffNearest, "b" },
{ nullptr, nullptr },
{ &FWScript::o2_startObjectScript, "b" },
/* 8C */
{ &FWScript::o2_stopObjectScript, "b" },
{ &FWScript::o2_op8D, "wwwwwwww" }, /* TODO: Name this opcode properly. */
{ &FWScript::o2_addBackground, "bs" },
{ &FWScript::o2_removeBackground, "b" },
/* 90 */
{ &FWScript::o2_loadAbs, "bs" },
{ &FWScript::o2_loadBg, "b" },
{ nullptr, nullptr },
{ nullptr, nullptr },
/* 94 */
{ nullptr, nullptr },
{ &FWScript::o1_changeDataDisk, "b" }, /* Same as opcodes 0x6B and 0xA9. */
{ nullptr, nullptr },
{ nullptr, nullptr },
/* 98 */
{ nullptr, nullptr },
{ nullptr, nullptr },
{ &FWScript::o2_wasZoneChecked, "b" },
{ &FWScript::o2_op9B, "wwwwwwww" }, /* TODO: Name this opcode properly. */
/* 9C */
{ &FWScript::o2_op9C, "wwww" }, /* TODO: Name this opcode properly. */
{ &FWScript::o2_useBgScroll, "b" },
{ &FWScript::o2_setAdditionalBgVScroll, "c" },
{ &FWScript::o2_op9F, "ww" }, /* TODO: Name this opcode properly. */
/* A0 */
{ &FWScript::o2_addGfxElementType20, "ww" }, /* TODO: Name this opcode properly. */
{ &FWScript::o2_removeGfxElementType20, "ww" }, /* TODO: Name this opcode properly. */
{ &FWScript::o2_addGfxElementType21, "ww" }, /* TODO: Name this opcode properly. */
{ &FWScript::o2_removeGfxElementType21, "ww" }, /* TODO: Name this opcode properly. */
/* A4 */
{ &FWScript::o2_loadMask22, "b" }, /* TODO: Name this opcode properly. */
{ &FWScript::o2_unloadMask22, "b" }, /* TODO: Name this opcode properly. */
{ nullptr, nullptr },
{ nullptr, nullptr },
/* A8 */
{ nullptr, nullptr },
{ &FWScript::o1_changeDataDisk, "b" } /* Same as opcodes 0x6B and 0x95. */
};
OSScript::_opcodeTable = (const Opcode *)opcodeTable;
OSScript::_numOpcodes = ARRAYSIZE(opcodeTable);
}
/**
* Contructor for global scripts
* @param script Script bytecode reference
* @param idx Script bytecode index
*/
OSScript::OSScript(const RawScript &script, int16 idx) :
FWScript(script, idx, new OSScriptInfo) {}
/**
* Constructor for object scripts
* @param script Script bytecode reference
* @param idx Script bytecode index
*/
OSScript::OSScript(RawObjectScript &script, int16 idx) :
FWScript(script, idx, new OSScriptInfo) {}
/**
* Copy constructor
*/
OSScript::OSScript(const OSScript &src) : FWScript(src, new OSScriptInfo) {}
/**
* Restore script state from savefile
* @param labels Restored script labels
* @param local Restored local script variables
* @param compare Restored last comparison result
* @param pos Restored script position
*/
void OSScript::load(const ScriptVars &labels, const ScriptVars &local, uint16 compare, uint16 pos) {
FWScript::load(labels, local, compare, pos);
}
/**
* Get opcode info string
* @param opcode Opcode to look for in opcode table
*/
const char *OSScriptInfo::opcodeInfo(byte opcode) const {
if (opcode == 0 || opcode > OSScript::_numOpcodes) {
return nullptr;
}
if (!OSScript::_opcodeTable[opcode - 1].args) {
warning("Undefined opcode 0x%02X in OSScriptInfo::opcodeInfo", opcode - 1);
return nullptr;
}
return OSScript::_opcodeTable[opcode - 1].args;
}
/**
* Get opcode handler pointer
* @param opcode Opcode to look for in opcode table
*/
OpFunc OSScriptInfo::opcodeHandler(byte opcode) const {
if (opcode == 0 || opcode > OSScript::_numOpcodes) {
return nullptr;
}
if (!OSScript::_opcodeTable[opcode - 1].proc) {
warning("Undefined opcode 0x%02X in OSScriptInfo::opcodeHandler", opcode - 1);
return nullptr;
}
return OSScript::_opcodeTable[opcode - 1].proc;
}
/**
* Create new OSScript instance
* @param script Script bytecode
* @param index Bytecode index
*/
FWScript *OSScriptInfo::create(const RawScript &script, int16 index) const {
return new OSScript(script, index);
}
/**
* Create new OSScript instance
* @param script Object script bytecode
* @param index Bytecode index
*/
FWScript *OSScriptInfo::create(const RawObjectScript &script, int16 index) const {
return new OSScript(script, index);
}
/**
* Load saved OSScript instance
* @param script Script bytecode
* @param index Bytecode index
* @param local Local variables
* @param labels Script labels
* @param compare Last compare result
* @param pos Position in script
*/
FWScript *OSScriptInfo::create(const RawScript &script, int16 index, const ScriptVars &labels, const ScriptVars &local, uint16 compare, uint16 pos) const {
OSScript *tmp = new OSScript(script, index);
assert(tmp);
tmp->load(labels, local, compare, pos);
return tmp;
}
/**
* Load saved OSScript instance
* @param script Object script bytecode
* @param index Bytecode index
* @param local Local variables
* @param labels Script labels
* @param compare Last compare result
* @param pos Position in script
*/
FWScript *OSScriptInfo::create(const RawObjectScript &script, int16 index, const ScriptVars &labels, const ScriptVars &local, uint16 compare, uint16 pos) const {
OSScript *tmp = new OSScript(script, index);
assert(tmp);
tmp->load(labels, local, compare, pos);
return tmp;
}
// ------------------------------------------------------------------------
// OPERATION STEALTH opcodes
// ------------------------------------------------------------------------
/** Load collision table data */
int FWScript::o2_loadCt() {
const char *param = getNextString();
debugC(5, kCineDebugScript, "Line: %d: loadCt(\"%s\")", _line, param);
loadCtOS(param);
removeBgIncrustsWithBgIdx(kCollisionPageBgIdxAlias);
return 0;
}
int FWScript::o2_loadPart() {
const char *param = getNextString();
debugC(5, kCineDebugScript, "Line: %d: loadPart(\"%s\")", _line, param);
return 0;
}
int FWScript::o2_playSample() {
if (g_cine->getPlatform() == Common::kPlatformAmiga || g_cine->getPlatform() == Common::kPlatformAtariST) {
// no-op in these versions
getNextByte();
getNextByte();
getNextWord();
getNextByte();
getNextWord();
getNextWord();
return 0;
}
debugC(5, kCineDebugScript, "Line: %d: o2_playSample()", _line);
byte mode = getNextByte();
byte channel = getNextByte();
int16 param3 = getNextWord();
int16 param4 = getNextByte();
int16 param5 = getNextWord();
uint16 size = getNextWord();
if (mode == 2) {
switch (param4) {
case 0:
param4 = param5;
break;
case 1:
param4 = _localVars[param5];
break;
case 2:
param4 = _globalVars[param5];
break;
}
}
g_sound->playSound(mode, channel, param3, param4, param5, size);
return 0;
}
int FWScript::o2_playSampleAlt() {
byte num = getNextByte();
byte channel = getNextByte();
uint16 frequency = getNextWord();
/* byte param4 = */getNextByte();
/* uint16 param5 = */getNextWord();
uint16 size = getNextWord();
if (size == 0xFFFF) {
size = g_cine->_animDataTable[num]._width * g_cine->_animDataTable[num]._height;
}
if (g_cine->_animDataTable[num].data()) {
if (g_cine->getPlatform() == Common::kPlatformDOS) {
// if speaker output is available, play sound on it
// if it's another device, don't play anything
// TODO: implement this, it's used in the introduction for example
// on each letter displayed
} else {
g_sound->playSound(channel, frequency, g_cine->_animDataTable[num].data(), size, 0, 0, 63, 0);
}
}
return 0;
}
int FWScript::o2_addSeqListElement() {
byte param1 = getNextByte();
byte param2 = getNextByte();
byte param3 = getNextByte();
byte param4 = getNextByte();
uint16 param5 = getNextWord();
uint16 param6 = getNextWord();
uint16 param7 = getNextWord();
debugC(5, kCineDebugScript, "Line: %d: addSeqListElement(%d,%d,%d,%d,%d,%d,%d)", _line, param1, param2, param3, param4, param5, param6, param7);
addSeqListElement(param1, 0, param2, param3, param4, param5, param6, 0, param7);
return 0;
}
int FWScript::o2_removeSeq() {
byte a = getNextByte();
byte b = getNextByte();
debugC(5, kCineDebugScript, "Line: %d: removeSeq(%d,%d)", _line, a, b);
removeSeq(a, 0, b);
return 0;
}
/**
* @note According to the scripts' opcode usage comparison this opcode isn't used at all.
*/
int FWScript::o2_clearSeqList() {
debugC(5, kCineDebugScript, "Line: %d: clearSeqList()", _line);
g_cine->_seqList.clear();
return 0;
}
int FWScript::o2_modifySeqListElement() {
byte a = getNextByte();
byte b = getNextByte();
uint16 c = getNextWord();
uint16 d = getNextWord();
byte e = getNextByte();
debugC(5, kCineDebugScript, "Line: %d: o2_modifySeqListElement(%d,%d,%d,%d,%d)", _line, a, b, c, d, e);
modifySeqListElement(a, 0, b, c, d, e);
return 0;
}
/**
* @todo Check whether this opcode's name is backwards (i.e. should it be o2_isSeqNotRunning?)
*/
int FWScript::o2_isSeqRunning() {
byte a = getNextByte();
byte b = getNextByte();
debugC(5, kCineDebugScript, "Line: %d: o2_isSeqRunning(%d,%d)", _line, a, b);
if (isSeqRunning(a, 0, b)) {
_compare = 1;
} else {
_compare = 0;
}
return 0;
}
/**
* @todo The assert may produce false positives and requires testing
*/
int FWScript::o2_gotoIfSupNearest() {
byte labelIdx = getNextByte();
if (_compare == kCmpGT) {
assert(_labels[labelIdx] != -1);
debugC(5, kCineDebugScript, "Line: %d: if(>) goto nearest %d (true)", _line, labelIdx);
_pos = _script.getLabel(*_info, labelIdx, _pos);
} else {
debugC(5, kCineDebugScript, "Line: %d: if(>) goto nearest %d (false)", _line, labelIdx);
}
return 0;
}
/**
* @todo The assert may produce false positives and requires testing
*/
int FWScript::o2_gotoIfSupEquNearest() {
byte labelIdx = getNextByte();
if (_compare & (kCmpGT | kCmpEQ)) {
assert(_labels[labelIdx] != -1);
debugC(5, kCineDebugScript, "Line: %d: if(>=) goto nearest %d (true)", _line, labelIdx);
_pos = _script.getLabel(*_info, labelIdx, _pos);
} else {
debugC(5, kCineDebugScript, "Line: %d: if(>=) goto nearest %d (false)", _line, labelIdx);
}
return 0;
}
/**
* @todo The assert may produce false positives and requires testing
*/
int FWScript::o2_gotoIfInfNearest() {
byte labelIdx = getNextByte();
if (_compare == kCmpLT) {
assert(_labels[labelIdx] != -1);
debugC(5, kCineDebugScript, "Line: %d: if(<) goto nearest %d (true)", _line, labelIdx);
_pos = _script.getLabel(*_info, labelIdx, _pos);
} else {
debugC(5, kCineDebugScript, "Line: %d: if(<) goto nearest %d (false)", _line, labelIdx);
}
return 0;
}
/**
* @todo The assert may produce false positives and requires testing
*/
int FWScript::o2_gotoIfInfEquNearest() {
byte labelIdx = getNextByte();
if (_compare & (kCmpLT | kCmpEQ)) {
assert(_labels[labelIdx] != -1);
debugC(5, kCineDebugScript, "Line: %d: if(<=) goto nearest %d (true)", _line, labelIdx);
_pos = _script.getLabel(*_info, labelIdx, _pos);
} else {
debugC(5, kCineDebugScript, "Line: %d: if(<=) goto nearest %d (false)", _line, labelIdx);
}
return 0;
}
/**
* @todo The assert may produce false positives and requires testing
*/
int FWScript::o2_gotoIfEquNearest() {
byte labelIdx = getNextByte();
if (_compare == kCmpEQ) {
assert(_labels[labelIdx] != -1);
debugC(5, kCineDebugScript, "Line: %d: if(==) goto nearest %d (true)", _line, labelIdx);
_pos = _script.getLabel(*_info, labelIdx, _pos);
} else {
debugC(5, kCineDebugScript, "Line: %d: if(==) goto nearest %d (false)", _line, labelIdx);
}
return 0;
}
/**
* @todo The assert may produce false positives and requires testing
*/
int FWScript::o2_gotoIfDiffNearest() {
byte labelIdx = getNextByte();
if (_compare != kCmpEQ) {
assert(_labels[labelIdx] != -1);
debugC(5, kCineDebugScript, "Line: %d: if(!=) goto nearest %d (true)", _line, labelIdx);
_pos = _script.getLabel(*_info, labelIdx, _pos);
} else {
debugC(5, kCineDebugScript, "Line: %d: if(!=) goto nearest %d (false)", _line, labelIdx);
}
return 0;
}
int FWScript::o2_startObjectScript() {
byte param = getNextByte();
debugC(5, kCineDebugScript, "Line: %d: startObjectScript(%d)", _line, param);
runObjectScript(param);
return 0;
}
int FWScript::o2_stopObjectScript() {
byte param = getNextByte();
debugC(5, kCineDebugScript, "Line: %d: stopObjectScript(%d)", _line, param);
for (auto &script : g_cine->_objectScripts) {
if (script->_index == param) {
script->_index = -1;
}
}
return 0;
}
int FWScript::o2_op8D() {
uint16 objIdx1 = getNextWord();
uint16 xAdd1 = getNextWord();
uint16 yAdd1 = getNextWord();
uint16 maskAdd1 = getNextWord();
uint16 objIdx2 = getNextWord();
uint16 xAdd2 = getNextWord();
uint16 yAdd2 = getNextWord();
uint16 maskAdd2 = getNextWord();
debugC(5, kCineDebugScript, "Line: %d: o2_op8D(%d, %d, %d, %d, %d, %d, %d, %d)", _line, objIdx1, xAdd1, yAdd1, maskAdd1, objIdx2, xAdd2, yAdd2, maskAdd2);
_compare = compareObjectParamRanges(objIdx1, xAdd1, yAdd1, maskAdd1, objIdx2, xAdd2, yAdd2, maskAdd2);
return 0;
}
int FWScript::o2_addBackground() {
byte param1 = getNextByte();
const char *param2 = getNextString();
debugC(5, kCineDebugScript, "Line: %d: addBackground(%s,%d)", _line, param2, param1);
renderer->addBackground(param2, param1);
removeBgIncrustsWithBgIdx(param1);
return 0;
}
int FWScript::o2_removeBackground() {
byte param = getNextByte();
assert(param);
debugC(5, kCineDebugScript, "Line: %d: removeBackground(%d)", _line, param);
renderer->removeBg(param);
removeBgIncrustsWithBgIdx(param);
return 0;
}
int FWScript::o2_loadAbs() {
byte param1 = getNextByte();
const char *param2 = getNextString();
debugC(5, kCineDebugScript, "Line: %d: loadABS(%d,%s)", _line, param1, param2);
// Load the resource to an absolute position
if (loadResource(param2, param1) == -1) { // Check if the loading failed
// WORKAROUND: In the 256 color PC version of Operation Stealth when
// walking out of the airport in Santa Paragua to the street the
// player character should be seen as a grey silhuette while walking
// behind the glass. But actually the player character is completely
// invisible when walking behind the glass because the animation files
// used are wrongly loaded. In AIRPORT.PRC's 6th script there are
// calls loadAbs("JOHN01.ANI", 73) and loadAbs("JOHN02.ANI", 37) to
// load the animations involved but no such files are found with the
// game. Corresponding SET-files are found though. As it worked and
// looked fine when I tried loading them instead of the missing ANI
// files I'm doing so here. NOTE: At least the German Amiga version
// of Operation Stealth seems to have all the files involved
// (JOHN01.ANI, JOHN02.ANI, JOHN01.SET and JOHN02.SET).
if (scumm_stricmp(param2, "JOHN01.ANI") == 0 && param1 == 73) {
loadResource("JOHN01.SET", param1);
} else if (scumm_stricmp(param2, "JOHN02.ANI") == 0 && param1 == 37) {
loadResource("JOHN02.SET", param1);
}
}
return 0;
}
int FWScript::o2_loadBg() {
byte param = getNextByte();
debugC(5, kCineDebugScript, "Line: %d: useBg(%d)", _line, param);
if (param <= 8) {
renderer->selectBg(param);
}
return 0;
}
int FWScript::o2_wasZoneChecked() {
byte param = getNextByte();
_compare = (param < NUM_MAX_ZONE && g_cine->_zoneQuery[param]) ? 1 : 0;
debugC(5, kCineDebugScript, "Line: %d: o2_wasZoneChecked(%d)", _line, param);
return 0;
}
/**
* @todo Implement this instruction
* @note According to the scripts' opcode usage comparison this opcode isn't used at all.
* @note In Operation Stealth 16 color DOS version this calculates temporary values and discards them.
*/
int FWScript::o2_op9B() {
uint16 a = getNextWord();
uint16 b = getNextWord();
uint16 c = getNextWord();
uint16 d = getNextWord();
uint16 e = getNextWord();
uint16 f = getNextWord();
uint16 g = getNextWord();
uint16 h = getNextWord();
warning("STUB: o2_op9B(%x, %x, %x, %x, %x, %x, %x, %x)", a, b, c, d, e, f, g, h);
return 0;
}
/**
* @todo Implement this instruction
* @note According to the scripts' opcode usage comparison this opcode isn't used at all.
* @note In Operation Stealth 16 color DOS version this calculates temporary values and discards them.
*/
int FWScript::o2_op9C() {
uint16 a = getNextWord();
uint16 b = getNextWord();
uint16 c = getNextWord();
uint16 d = getNextWord();
warning("STUB: o2_op9C(%x, %x, %x, %x)", a, b, c, d);
return 0;
}
int FWScript::o2_useBgScroll() {
byte param = getNextByte();
assert(param < 9);
debugC(5, kCineDebugScript, "Line: %d: useBgScroll(%d)", _line, param);
if (param <= 8) {
renderer->selectScrollBg(param);
}
return 0;
}
int FWScript::o2_setAdditionalBgVScroll() {
uint16 mouseX, mouseY;
unsigned int scroll = renderer->getScroll();
byte param1 = getNextByte();
if (param1) {
byte param2 = getNextByte();
switch (param1) {
case 1:
debugC(5, kCineDebugScript, "Line: %d: additionalBgVScroll = var[%d]", _line, param2);
scroll = _localVars[param2];
break;
case 2:
debugC(5, kCineDebugScript, "Line: %d: additionalBgVScroll = globalVar[%d]", _line, param2);
scroll = _globalVars[param2];
break;
case 3:
debugC(5, kCineDebugScript, "Line: %d: additionalBgVScroll = mouseX", _line);
getMouseData(mouseUpdateStatus, &dummyU16, &mouseX, &mouseY);
scroll = mouseX;
break;
case 4:
debugC(5, kCineDebugScript, "Line: %d: additionalBgVScroll = mouseY", _line);
getMouseData(mouseUpdateStatus, &dummyU16, &mouseX, &mouseY);
scroll = mouseY;
break;
case 5:
debugC(5, kCineDebugScript, "Line: %d: additionalBgVScroll = rand() %% %d", _line, param2);
scroll = ((param2 == 0) ? 0 : g_cine->_rnd.getRandomNumber(param2 - 1));
break;
}
} else {
uint16 param2 = getNextWord();
debugC(5, kCineDebugScript, "Line: %d: additionalBgVScroll = %d", _line, param2);
scroll = param2;
}
renderer->setScroll(scroll);
return 0;
}
/**
* @todo Implement this instruction
* @note According to the scripts' opcode usage comparison this opcode isn't used at all.
* @note In Operation Stealth 16 color DOS version this calculates temporary values and discards them.
*/
int FWScript::o2_op9F() {
warning("o2_op9F()");
/* uint16 param1 = */getNextWord();
/* uint16 param2 = */getNextWord();
return 0;
}
int FWScript::o2_addGfxElementType20() {
uint16 param1 = getNextWord();
uint16 param2 = getNextWord();
debugC(5, kCineDebugScript, "Line: %d: o2_addGfxElementType20(%d,%d)", _line, param1, param2);
addGfxElement(param1, param2, 20);
return 0;
}
int FWScript::o2_removeGfxElementType20() {
uint16 idx = getNextWord();
uint16 param = getNextWord();
debugC(5, kCineDebugScript, "Line: %d: o2_removeGfxElementType20(%d,%d)", _line, idx, param);
removeGfxElement(idx, param, 20);
return 0;
}
int FWScript::o2_addGfxElementType21() {
uint16 a = getNextWord();
uint16 b = getNextWord();
debugC(5, kCineDebugScript, "Line: %d: o2_addGfxElementType21(%d,%d)", _line, a, b);
addGfxElement(a, b, 21);
return 0;
}
int FWScript::o2_removeGfxElementType21() {
uint16 a = getNextWord();
uint16 b = getNextWord();
debugC(5, kCineDebugScript, "Line: %d: o2_removeGfxElementType21(%d,%d)", _line, a, b);
removeGfxElement(a, b, 21);
return 0;
}
int FWScript::o2_loadMask22() {
byte param = getNextByte();
debugC(5, kCineDebugScript, "Line: %d: addOverlay22(%d)", _line, param);
addOverlay(param, 22);
return 0;
}
int FWScript::o2_unloadMask22() {
byte param = getNextByte();
debugC(5, kCineDebugScript, "Line: %d: removeOverlay22(%d)", _line, param);
removeOverlay(param, 22);
return 0;
}
} // End of namespace Cine

1574
engines/cine/sound.cpp Normal file

File diff suppressed because it is too large Load Diff

145
engines/cine/sound.h Normal file
View File

@@ -0,0 +1,145 @@
/* 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 CINE_SOUND_H_
#define CINE_SOUND_H_
#include "common/util.h"
#include "common/mutex.h"
#include "audio/mixer.h"
#include "audio/mididrv.h"
namespace Audio {
class AudioStream;
}
namespace Cine {
class CineEngine;
class Sound {
public:
Sound(Audio::Mixer *mixer, CineEngine *vm) : _mixer(mixer), _vm(vm), _musicType(MT_INVALID) {}
virtual ~Sound() {}
virtual MusicType musicType();
virtual void loadMusic(const char *name) = 0;
virtual void playMusic() = 0;
virtual void stopMusic() = 0;
virtual void fadeOutMusic() = 0;
virtual void playSound(int mode, int channel, int param3, int param4, int param5, int size) = 0;
virtual void playSound(int channel, int frequency, const uint8 *data, int size, int volumeStep, int stepCount, int volume, int repeat) = 0;
virtual void stopSound(int channel) = 0;
virtual void setBgMusic(int num) = 0;
protected:
Audio::Mixer *_mixer;
CineEngine *_vm;
MusicType _musicType;
};
class PCSoundDriver;
class PCSoundFxPlayer;
class PCSound : public Sound {
public:
PCSound(Audio::Mixer *mixer, CineEngine *vm);
~PCSound() override;
void loadMusic(const char *name) override;
void playMusic() override;
void stopMusic() override;
void fadeOutMusic() override;
void playSound(int mode, int channel, int param3, int param4, int param5, int size) override;
void playSound(int channel, int frequency, const uint8 *data, int size, int volumeStep, int stepCount, int volume, int repeat) override;
void stopSound(int channel) override;
void setBgMusic(int num) override;
protected:
PCSoundDriver *_soundDriver;
PCSoundFxPlayer *_player;
uint8 _currentMusic, _currentMusicStatus, _currentBgSlot;
};
class PaulaSound : public Sound {
public:
PaulaSound(Audio::Mixer *mixer, CineEngine *vm);
~PaulaSound() override;
void loadMusic(const char *name) override;
void playMusic() override;
void stopMusic() override;
void fadeOutMusic() override;
void playSound(int mode, int channel, int param3, int param4, int param5, int size) override;
void playSound(int channel, int frequency, const uint8 *data, int size, int volumeStep, int stepCount, int volume, int repeat) override;
void stopSound(int channel) override;
void setBgMusic(int num) override;
enum {
PAULA_FREQ = 3579545,
NUM_CHANNELS = 4
};
protected:
struct SfxChannel {
Audio::SoundHandle handle;
int volume;
int volumeStep;
int curStep;
int stepCount;
void initialize(int vol, int volStep, int stepCnt) {
volume = vol;
volumeStep = volStep;
curStep = stepCount = stepCnt;
}
};
SfxChannel _channelsTable[NUM_CHANNELS];
static const int _channelBalance[NUM_CHANNELS];
Common::Mutex _sfxMutex;
int _sfxTimer;
static void sfxTimerProc(void *param);
void sfxTimerCallback();
Common::Mutex _musicMutex;
int _musicTimer;
int _musicFadeTimer;
static void musicTimerProc(void *param);
void musicTimerCallback();
Audio::SoundHandle _moduleHandle;
Audio::AudioStream *_moduleStream;
};
extern Sound *g_sound;
} // End of namespace Cine
#endif /* CINE_SOUND_H_ */

863
engines/cine/texte.cpp Normal file
View File

@@ -0,0 +1,863 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "common/file.h"
#include "common/textconsole.h"
#include "cine/cine.h"
#include "cine/various.h"
namespace Cine {
bool allocatedFailureMessages = false;
const char *const *failureMessages;
const CommandeType *defaultActionCommand;
const CommandeType *systemMenu;
const CommandeType *confirmMenu;
const char *const *otherMessages;
const char *defaultCommandPreposition;
const char *const *commandPrepositionTable;
/**
* Loads font data from the given file.
* The number of characters used in the font varies between game versions:
* 78 (Most PC, Amiga and Atari ST versions of Future Wars, but also Operation Stealth's Amiga demo),
* 85 (All observed versions of German Future Wars (Amiga and PC), possibly Spanish Future Wars too),
* 90 (Most PC, Amiga and Atari ST versions of Operation Stealth),
* 93 (All observed versions of German Operation Stealth (Amiga and PC)).
*/
void loadTextData(const char *filename) {
Common::File fileHandle;
assert(filename);
if (!fileHandle.open(filename))
error("loadTextData(): Cannot open file %s", filename);
static const uint headerSize = 2 + 2; // The entry size (16-bit) and entry count (16-bit).
const uint entrySize = fileHandle.readUint16BE(); // Observed values: 8.
const uint entryCount = fileHandle.readUint16BE(); // Observed values: 624, 680, 720, 744.
const uint fontDataSize = entryCount * entrySize; // Observed values: 4992, 5440, 5760, 5952.
const uint numChars = entryCount / entrySize; // Observed values: 78, 85, 90, 93.
const uint bytesPerChar = fontDataSize / numChars; // Observed values: 64.
static const uint bytesPerRow = FONT_WIDTH / 2; // The input font data is 4-bit so it takes only half the space
if (headerSize + fontDataSize != (uint)fileHandle.size()) {
warning("loadTextData: file '%s' (entrySize = %d, entryCount = %d) is of incorrect size %d", filename, entrySize, entryCount, (int)fileHandle.size());
}
Common::Array<byte> source;
source.resize(fontDataSize);
fileHandle.read(source.begin(), fontDataSize);
if (g_cine->getGameType() == Cine::GType_FW) {
loadRelatedPalette(filename);
}
for (uint i = 0; i < numChars; i++) {
gfxConvertSpriteToRaw(g_cine->_textHandler.textTable[i][FONT_DATA], &source[i * bytesPerChar], bytesPerRow, FONT_HEIGHT);
generateMask(g_cine->_textHandler.textTable[i][FONT_DATA], g_cine->_textHandler.textTable[i][FONT_MASK], FONT_WIDTH * FONT_HEIGHT, 0);
}
fileHandle.close();
}
static const CharacterEntry fontParamTable_standard[NUM_FONT_CHARS] = {
{ 0, 0}, { 0, 0}, { 0, 0}, { 0, 0}, { 0, 0}, { 0, 0}, { 0, 0}, { 0, 0},
{ 0, 0}, { 0, 0}, { 0, 0}, { 0, 0}, { 0, 0}, { 0, 0}, { 0, 0}, { 0, 0},
{ 0, 0}, { 0, 0}, { 0, 0}, { 0, 0}, { 0, 0}, { 0, 0}, { 0, 0}, { 0, 0},
{ 0, 0}, { 0, 0}, { 0, 0}, { 0, 0}, { 0, 0}, { 0, 0}, { 0, 0}, { 0, 0},
{ 0, 0}, {63, 1}, {69, 5}, { 0, 0}, { 0, 0}, { 0, 0}, { 0, 0}, {68, 3},
{64, 3}, {65, 3}, { 0, 0}, { 0, 0}, {62, 2}, {74, 6}, {66, 1}, {67, 6},
{52, 6}, {53, 6}, {54, 6}, {55, 6}, {56, 6}, {57, 6}, {58, 6}, {59, 6},
{60, 6}, {61, 6}, {76, 3}, { 0, 0}, { 0, 0}, { 0, 0}, { 0, 0}, {75, 6},
{ 0, 0}, { 0, 6}, //a
{ 1, 6}, { 2, 6}, { 3, 6}, { 4, 6}, { 5, 6}, { 6, 6},
{ 7, 6}, { 8, 3}, { 9, 6}, {10, 6}, {11, 6}, {12, 7}, {13, 6}, {14, 6},
{15, 6}, {16, 6}, {17, 6}, {18, 6}, {19, 6}, {20, 6}, {21, 6}, {22, 7},
{23, 6}, {24, 6}, {25, 6}, { 0, 0}, { 0, 0}, { 0, 0}, { 0, 0}, { 0, 0},
{ 0, 0}, {26, 6}, //a
{27, 6}, {28, 5}, {29, 6}, {30, 6}, {31, 5}, {32, 6},
{33, 6}, {34, 4}, {35, 4}, {36, 5}, {37, 3}, {38, 7}, {39, 6}, {40, 6},
{41, 6}, {42, 6}, {43, 6}, {44, 6}, {45, 6}, {46, 6}, {47, 6}, {48, 7},
{49, 6}, {50, 6}, {51, 6}, { 0, 0}, { 0, 0}, { 0, 0}, { 0, 0}, { 0, 0},
{ 0, 0}, { 0, 0}, {70, 6}, { 0, 0}, { 0, 0}, {72, 6}, { 0, 0}, {73, 5},
{77, 6}, { 0, 0}, {71, 6}, { 0, 0}, { 0, 0}, { 0, 0}, { 0, 0}, { 0, 0},
{ 0, 0}, { 0, 0}, { 0, 0}, { 0, 0}, { 0, 0}, { 0, 0}, { 0, 0}, {77, 6},
{ 0, 0}, { 0, 0}, { 0, 0}, { 0, 0}, { 0, 0}, { 0, 0}, { 0, 0}, { 0, 0},
{ 0, 0}, { 0, 0}, { 0, 0}, { 0, 0}, { 0, 0}, { 0, 0}, { 0, 0}, { 0, 0},
{ 0, 0}, { 0, 0}, { 0, 0}, { 0, 0}, { 0, 0}, { 0, 0}, { 0, 0}, { 0, 0},
{ 0, 0}, { 0, 0}, { 0, 0}, { 0, 0}, { 0, 0}, { 0, 0}, { 0, 0}, { 0, 0},
{ 0, 0}, { 0, 0}, { 0, 0}, { 0, 0}, { 0, 0}, { 0, 0}, { 0, 0}, { 0, 0},
{ 0, 0}, { 0, 0}, { 0, 0}, { 0, 0}, { 0, 0}, { 0, 0}, { 0, 0}, { 0, 0},
{ 0, 0}, { 0, 0}, { 0, 0}, { 0, 0}, { 0, 0}, { 0, 0}, { 0, 0}, { 0, 0},
{ 0, 0}, { 0, 0}, { 0, 0}, { 0, 0}, { 0, 0}, { 0, 0}, { 0, 0}, { 0, 0},
{ 0, 0}, { 0, 0}, { 0, 0}, { 0, 0}, { 0, 0}, { 0, 0}, { 0, 0}, { 0, 0},
{ 0, 0}, { 0, 0}, { 0, 0}, { 0, 0}, { 0, 0}, { 0, 0}, { 0, 0}, { 0, 0},
{ 0, 0}, { 0, 0}, { 0, 0}, { 0, 0}, { 0, 0}, { 0, 0}, { 0, 0}, { 0, 0},
{ 0, 0}, { 0, 0}, { 0, 0}, { 0, 0}, { 0, 0}, { 0, 0}, { 0, 0}, { 0, 0},
{ 0, 0}, { 0, 0}, { 0, 0}, { 0, 0}, { 0, 0}, { 0, 0}, { 0, 0}, { 0, 0}
};
static const CharacterEntry fontParamTable_alt[NUM_FONT_CHARS] = {
{ 0, 0}, { 0, 0}, { 0, 0}, { 0, 0}, { 0, 0}, { 0, 0}, { 0, 0}, { 0, 0},
{ 0, 0}, { 0, 0}, { 0, 0}, { 0, 0}, { 0, 0}, { 0, 0}, { 0, 0}, { 0, 0},
{ 0, 0}, { 0, 0}, { 0, 0}, { 0, 0}, { 0, 0}, { 0, 0}, { 0, 0}, { 0, 0},
{ 0, 0}, { 0, 0}, { 0, 0}, { 0, 0}, { 0, 0}, { 0, 0}, { 0, 0}, { 0, 0},
{ 0, 0}, {63, 1}, {69, 5}, { 0, 0}, { 0, 0}, { 0, 0}, { 0, 0}, {68, 3},
{64, 3}, {65, 3}, { 0, 0}, { 0, 0}, {62, 2}, {74, 6}, {66, 1}, {67, 6},
{52, 6}, {53, 6}, {54, 6}, {55, 6}, {56, 6}, {57, 6}, {58, 6}, {59, 6},
{60, 6}, {61, 6}, {76, 3}, { 0, 0}, { 0, 0}, { 0, 0}, { 0, 0}, {75, 6},
{ 0, 0}, { 0, 6}, { 1, 6}, { 2, 6}, { 3, 6}, { 4, 6}, { 5, 6}, { 6, 6},
{ 7, 6}, { 8, 3}, { 9, 6}, {10, 6}, {11, 6}, {12, 7}, {13, 6}, {14, 6},
{15, 6}, {16, 6}, {17, 6}, {18, 6}, {19, 6}, {20, 6}, {21, 6}, {22, 7},
{23, 6}, {24, 6}, {25, 6}, { 0, 0}, { 0, 0}, { 0, 0}, { 0, 0}, { 0, 0},
{ 0, 0}, {26, 6}, {27, 6}, {28, 5}, {29, 6}, {30, 6}, {31, 5}, {32, 6},
{33, 6}, {34, 4}, {35, 4}, {36, 5}, {37, 3}, {38, 7}, {39, 6}, {40, 6},
{41, 6}, {42, 6}, {43, 6}, {44, 6}, {45, 6}, {46, 6}, {47, 6}, {48, 7},
{49, 6}, {50, 6}, {51, 6}, { 0, 0}, { 0, 0}, { 0, 0}, { 0, 0}, { 0, 0},
{ 0, 0}, {82, 6}, {70, 6}, { 0, 0}, {78, 6}, {72, 6}, { 0, 0}, {73, 5},
{77, 6}, {79, 6}, {71, 6}, {80, 4}, { 0, 0}, { 0, 0}, {78, 6}, { 0, 0},
{ 0, 0}, { 0, 0}, { 0, 0}, { 0, 0}, {81, 6}, { 0, 0}, { 0, 0}, {77, 6},
{83, 6}, {81, 6}, {82, 6}, { 0, 0}, { 0, 0}, { 0, 0}, {84, 6}, { 0, 0},
{ 0, 0}, { 0, 0}, { 0, 0}, { 0, 0}, { 0, 0}, { 0, 0}, { 0, 0}, { 0, 0},
{ 0, 0}, { 0, 0}, { 0, 0}, { 0, 0}, { 0, 0}, { 0, 0}, { 0, 0}, { 0, 0},
{ 0, 0}, { 0, 0}, { 0, 0}, { 0, 0}, { 0, 0}, { 0, 0}, { 0, 0}, { 0, 0},
{ 0, 0}, { 0, 0}, { 0, 0}, { 0, 0}, { 0, 0}, { 0, 0}, { 0, 0}, { 0, 0},
{ 0, 0}, { 0, 0}, { 0, 0}, { 0, 0}, { 0, 0}, { 0, 0}, { 0, 0}, { 0, 0},
{ 0, 0}, { 0, 0}, { 0, 0}, { 0, 0}, { 0, 0}, { 0, 0}, { 0, 0}, { 0, 0},
{ 0, 0}, { 0, 0}, { 0, 0}, { 0, 0}, { 0, 0}, { 0, 0}, { 0, 0}, { 0, 0},
{ 0, 0}, { 0, 0}, { 0, 0}, { 0, 0}, { 0, 0}, { 0, 0}, { 0, 0}, { 0, 0},
{ 0, 0}, {84, 6}, { 0, 0}, { 0, 0}, { 0, 0}, { 0, 0}, { 0, 0}, { 0, 0},
{ 0, 0}, { 0, 0}, { 0, 0}, { 0, 0}, { 0, 0}, { 0, 0}, { 0, 0}, { 0, 0},
{ 0, 0}, { 0, 0}, { 0, 0}, { 0, 0}, { 0, 0}, { 0, 0}, { 0, 0}, { 0, 0},
{ 0, 0}, { 0, 0}, { 0, 0}, { 0, 0}, { 0, 0}, { 0, 0}, { 0, 0}, { 0, 0}
};
void initLanguage(Common::Language lang) {
static const char *const failureMessages_EN[] = {
// EXAMINE
"I don't see anything unusual.",
"There's nothing of interest here.",
"This isn't particularly interesting.",
"You won't find anything.",
// TAKE
"I can't take that.",
"I find it difficult.",
"I don't see what I am supposed to take.",
"I have difficulty in following you.",
// INVENTORY
"There's no point.",
"You have better things to do.",
"Come on, don't let's waste any time.",
"That doesn't seem to me to be a good idea.",
// USE
"I don't see why I should do that.",
"It's had no effect whatsoever.",
"It won't produce any results.",
"Try and find something else.",
// OPERATE
"It doesn't work.",
"Let suppose you are trying and don't let's mention it again.",
"Nothing happens.",
"You have better things to do.",
// SPEAK
"No answer.",
"More action , less talking !",
"I'd be very surprised if you got an answer",
"A wall of silence ..."
};
/**
* British English error messages for Operation Stealth.
*/
static const char *const failureMessages_OS_EN[] = {
// EXAMINE
"You haven't noticed anything special.",
"Nothing interesting.",
"Nothing to say about it.",
"You find nothing.",
// TAKE
"Let's forget about it.",
"I could if I wanted, but I don't.",
"There are so many things to take and you want this one!",
"No need.",
// INVENTORY
"",
"",
"",
"",
// USE
"That won't do anything.",
"Nothing's happening.",
"If you have anything else like this, go ahead and finish it.",
"It's like you did nothing.",
// OPERATE
"You do it, nothing happens.",
"Why don't you try it and we won't talk about it anymore.",
"Nothing's happening.",
"No result.",
// SPEAK
"You speak with him. No answer.",
"More action and less talk.",
"My name is GLAMES... JOHN GLAMES.",
"A wall of silence..."
};
/**
* American English error messages for Operation Stealth.
*/
static const char *const failureMessages_OS_US[] = {
// EXAMINE
"You haven't noticed anything special.",
"Nothing interesting.",
"Nothing to say about it.",
"You find nothing.",
// TAKE
"Let's forget about it.",
"I could if I wanted, but I don't.",
"There are so many things to take and you want this one!",
"No need.",
// INVENTORY
"",
"",
"",
"",
// USE
"That won't do anything.",
"Nothing's happening.",
"If you have anything else like this, go ahead and finish it.",
"It's like you did nothing.",
// OPERATE
"You do it, nothing happens.",
"Absolutely not! It would be a waste of my valuable time.",
"Nothing's happening.",
"No result.",
// SPEAK
"You speak with him. No answer.",
"More action and less talk.",
"My name is BOND... JAMES BOND. ",
"A wall of silence..."
};
static const CommandeType defaultActionCommand_EN[] = {
"EXAMINE",
"TAKE",
"INVENTORY",
"USE",
"OPERATE",
"SPEAK",
"NOACTION"
};
static const char *const commandPrepositionTable_EN[] = {
"", // EXAMINE
"", // TAKE
"", // INVENTORY
"on", // USE
"", // OPERATE
"to", // SPEAK
"" // NOACTION
};
static const CommandeType systemMenu_EN[] = {
"Pause",
"Restart Game",
"Quit",
"Backup Drive is A:",
"Restore game",
"Save game"
};
static const char *const otherMessages_EN[] = {
"This backup doesn't exist ...",
"Could not create save file ...",
"PAUSE",
"Loading | %s",
"Loading canceled ...",
"No backup in the drive...",
"Please enter the backup name"
};
static const CommandeType confirmMenu_EN[] = {
"Ok, go ahead ...",
"Absolutely Not!"
};
static const char *const failureMessages_FR[] = {
// EXAMINER
"Je ne vois rien de special.",
"Il n'y a rien d'int\x82ressant.",
"Cela pr\x82sente peu d'int\x82r\x88ts.",
"Vous ne trouvez rien.",
// PRENDRE
"Je ne peux pas prendre cela.",
"Cela me semble difficile",
"Je ne vois pas ce qu'il y a \x85 prendre",
"j'ai du mal \x85 vous suivre.",
// INVENTAIRE
"C'est inutile",
"Vous avez mieux \x85 faire",
"Allons, ne perdons pas de temps",
"\x87""a ne me semble pas \x88tre une bonne id\x82""e",
// UTILISER
"Je ne vois pas pourquoi je ferais cela.",
"C'est absolument sans effets",
"Cela n'amenerait \x85 rien",
"Essayez de trouver autre chose.",
// ACTIONNER
"Ca ne marche pas",
"Supposons que vous essayez et n'en parlons plus.",
"Rien n'y fait.",
"Vous avez mieux \x85 faire.",
// PARLER
"Vous lui parlez . Sans r\x82ponse.",
"Plus d'actes et moins de Paroles !",
"Je serais bien surpris si vous obteniez une r\x82ponse.",
"Un mur de silence ..."
};
/**
* French error messages for Operation Stealth.
*/
static const char *const failureMessages_OS_FR[] = {
// EXAMINER
"Vous ne remarquez rien de sp\x82""cial.",
"Rien d'int\x82ressant.",
"Peu de choses \x85 dire l\x85-dessus.",
"Vous ne trouvez rien.",
// PRENDRE
"N'en parlons plus...",
"Je pourrais si je le voulais! MAIS JE NE VEUX PAS!",
"Il y a des tas de choses \x85 prendre et vous voulez celle l\x85!",
"C'est inutile!",
// INVENTAIRE
"",
"",
"",
"",
// UTILISER
"C'est absolument sans effet.",
"Il ne se passe rien.",
"Si vous en avez d'autres comme \x87""a, allez-y qu'on en finisse.",
"C'est comme si vous n'aviez rien fait!",
// ACTIONNER
"OK! vous l'actionnez. Il ne se passe rien.",
"Supposons que vous essayiez et n'en parlons plus.",
"Rien n'y fait.",
"Aucun r\x82sultat.",
// PARLER
"Vous lui parlez . Pas de r\x82ponse.",
"Plus d'actes et moins de Paroles !",
"Mon nom est GLAMES... JOHN GLAMES.",
"Un mur de silence ..."
};
static const CommandeType defaultActionCommand_FR[] = {
"EXAMINER",
"PRENDRE",
"INVENTAIRE",
"UTILISER",
"ACTIONNER",
"PARLER",
"NOACTION"
};
static const char *const commandPrepositionTable_FR[] = {
"", // EXAMINER
"", // PRENDRE
"", // INVENTAIRE
"sur", // UTILISER
"", // ACTIONNER
"a", // PARLER
"" // NOACTION
};
static const CommandeType systemMenu_FR[] = {
"Pause",
"Nouvelle partie",
"Quitter",
"Lecteur de Svg. A:",
"Charger une partie",
"Sauver la partie"
};
static const CommandeType confirmMenu_FR[] = {
"Ok , Vas-y ...",
"Surtout Pas !"
};
static const char *const otherMessages_FR[] = {
"Cette sauvegarde n'existe pas ...",
"Could not create save file ...", //
"PAUSE",
"Sauvegarde de | %s",
("Sauvegarde Annul\x82""e ..."),
"Aucune sauvegarde dans le lecteur ...",
"Veuillez entrer le Nom de la Sauvegarde ."
};
static const char *const failureMessages_ES[] = {
// EXAMINE
"No veo nada especial",
"No hay nada interesante",
"No tiene mucho interes",
"No encuentras nada",
// TAKE
"No puedo coger eso",
"Eso parece dificil",
"No veo nada mas para coger",
"No he debido entenderte",
// INVENTORY
"Es inutil",
"Tienes algo mejor que hacer",
"Vamos. No perdamos tiempo",
"Esa no es una buena idea",
// USE
"No veo porque hacerlo",
"No ha tenido efecto",
"Eso no arreglara nada",
"Intenta encontrar otra cosa",
// OPERATE
"Eso no funciona",
"Suponfamos que pruebas y no hablamos mas",
"Nada ha pasado",
"Tienes cosas mejores que hacer",
// SPEAK
"Le hablas. Sin respuesta.",
"Menos hablar y mas trabajar",
"Me sorprenderia si tuvieras otra repuesta",
"Un muro de silencio ..."
};
static const CommandeType defaultActionCommand_ES[] = {
"EXAMINAR",
"COGER",
"INVENTARIO",
"USAR",
"ACCIONAR",
"HABLAR",
"NOACTION"
};
static const char *const commandPrepositionTable_ES[] = {
"", // EXAMINAR
"", // COGER
"", // INVENTARIO
"donde", // USAR
"", // ACCIONAR
"a", // HABLAR
"" // NOACTION
};
static const CommandeType systemMenu_ES[] = {
"Pause",
"Nueva partida",
"Abandonar",
"Unidad grabar. A:",
"Cargar una partida",
"Salvar la partida"
};
static const CommandeType confirmMenu_ES[] = {
"Ok , Vas a ...",
"Nade de nada !"
};
static const char *const otherMessages_ES[] = {
"Esta granacion no existe",
"Could not create save file ...", //
"PAUSE",
"Gabacion de| %s",
"Rrabacion anulada",
"No hay partidas grabadas en este disco...",
"Teclea el nombre de la partida grabada"
};
static const char *const failureMessages_DE[] = {
// EXAMINE
"Ich sehe nichts Besonderes",
"Es gibt hier nichts Interessantes",
"Das ist nicht besonders interessant",
"Sie werden nichts finden",
// TAKE
"Ich Kann das nicht nehmen",
"Das finde ich schwierig'",
"Ich wei\x9e nicht, was ich nehmen soll",
"Ich kann Ihnen nicht folgen",
// INVENTORY
"Das bringt nichts",
"Sie haben wirklich was Besseres zu tun",
"Los, wir sollten keine Zeit verschwenden",
"Das scheint mir eine gute Idee zu sein",
// USE
"Ich wei\x9e nicht, warum ich das tun soll",
"Es hat so oder so nichts begracht",
"Davon haben wir nichts",
"Versuchen Sie, etwas anderes zu finden",
// OPERATE
"Es geht nicht",
("Sagen wir, das war ein Versuch, und reden wir nicht mehr dr\x81""ber"),
"Nichts passiert",
"Sie haben wirklich was Besseres zu tun",
// SPEAK
"Sie sprechen m it ihm. Keine Antwort",
"Nicht reden, sondern handeln!",
"Wenn Sie eine Antwork bek\x84men, w\x81rde es mich sehr wundern",
"Eine Wand des Schweigens..."
};
/**
* German error messages for Operation Stealth.
*/
static const char *const failureMessages_OS_DE[] = {
// EXAMINE
"Ich sehe nichts Besonderes",
"Es gibt hier nichts Interessantes",
"Das ist nicht besonders interessant",
"Sie werden nichts finden",
// TAKE
"Ich Kann das nicht nehmen",
"Das finde ich schwierig",
"Ich wei\x9e nicht, was ich nehmen soll",
"Ich kann Ihnen nicht folgen",
// INVENTORY
"Das bringt nichts",
"Sie haben wirklich was Besseres zu tun",
"Los, wir sollten keine Zeit verschwenden",
"Das scheint mir eine gute Idee zu sein",
// USE
"Ich wei\x9e nicht, warum ich das tun soll",
"Es hat so oder so nichts begracht",
"Davon haben wir nichts",
"Versuchen Sie, etwas anderes zu finden",
// OPERATE
"Es geht nicht",
"Nichts passiert",
"Nichts passiert",
"Sie haben wirklich was Besseres zu tun",
// SPEAK
"Sie sprechen mit ihm. Keine Antwort",
"Nicht reden, sondern handeln!",
"Wenn Sie eine Antwork bek\x84men, w\x81rde es mich sehr wundern",
"Eine Wand des Schweigens..."
};
static const CommandeType defaultActionCommand_DE[] = {
("Pr\x81""fe"), // FIXME? The third letter should be Latin Small Letter U with diaeresis
"Nimm",
"Bestand",
"Benutze",
"Bet\x84tige", // FIXME? The fourth letter should be Latin Small Letter A with diaeresis
"Sprich",
"NOACTION"
};
static const char *const commandPrepositionTable_DE[] = {
"", // Prufe
"", // Nimm
"", // Bestand
"gegen", // Benutze
"", // Betatige
"a", // Sprich
"" // NOACTION
};
static const CommandeType systemMenu_DE[] = {
"Pause",
"Spiel Neu Starten",
"Lassen",
"Backuplaufwerk A:",
"Spiel Laden",
"Spiel Speichern"
};
static const CommandeType confirmMenu_DE[] = {
"Gut, Weitermachen",
"Absolut Nicht!"
};
static const char *const otherMessages_DE[] = {
"Diese Sicherungskopie gibt es nicht",
"Could not create save file ...", //
"PAUSE",
("Er L\x84""dt | %s"),
"Ladevorgang Abgebrochen...",
"Kein Backup im Laufwerk...",
"Geben Sie den Namen|der Sicherungsdiskette ein"
};
static const char *const failureMessages_IT[] = {
// EXAMINE
"Non vedo nula di speciale",
"Non c'\x8a niente di interessante",
"E' di poco interesse",
"Non trovate nulla",
// TAKE
"Non poso prendere quello",
"Quello mi sembra difficile",
"Non vedo cosa ci sia da prendere",
"Faccio fatica a seguirvi",
// INVENTORY
"E' inutile",
"Avete di meglio da fare",
"Allora, no perdiamo tempo",
"Non mi pare che sia una buona idea",
// USE
"Non vedo perch\x82 dovrei farlo",
"E' assolutamente privo di effetti",
"Cio non portera a nulla",
"Provate a trovare qualcosa d'altro",
// OPERATE
"Non funziona",
"Supponiamo che voi proviate e non ne parliamo piu",
"Niente di fatto",
"Avete di meglio da fare",
// SPEAK
"Gli parlate. Senza risposta",
"Piu fatti e meno parole",
"Sarei sorpreso se voi otterreste una risposta",
"Un muro di silenzio ..."
};
static const CommandeType defaultActionCommand_IT[] = {
"ESAMINARE",
"PRENDERE",
"INVENTARIO",
"UTILIZZARE",
"AZIONARE",
"PARLARE",
"NOACTION"
};
static const char *const commandPrepositionTable_IT[] = {
"", // ESAMINARE
"", // PRENDERE
"", // INVENTARIO
"su", // UTILIZZARE
"", // AZIONARE
"a", // PARLARE
"" // NOACTION
};
static const CommandeType systemMenu_IT[] = {
"Pausa",
"Parte nuova",
"Quit",
"Drive di svg. A:",
"Caricare una parte",
"Salvare una parte"
};
static const CommandeType confirmMenu_IT[] = {
"Ok, vacci ...",
"Supratutto non!"
};
static const char *const otherMessages_IT[] = {
"Questo salvataggio non esiste...",
"Could not create save file ...", //
"PAUSE",
"Caricamento di| %s",
"Caricamento annullato...",
"Nessun salvataggio su questo disco...",
"Vogliate accedere con il nome del salvataggio"
};
switch (lang) {
case Common::FR_FRA:
if (g_cine->getGameType() == Cine::GType_OS) {
setFailureMessages(failureMessages_OS_FR, false);
} else {
setFailureMessages(failureMessages_FR, false);
}
defaultActionCommand = defaultActionCommand_FR;
systemMenu = systemMenu_FR;
confirmMenu = confirmMenu_FR;
otherMessages = otherMessages_FR;
defaultCommandPreposition = commandPrepositionTable_FR[3];
commandPrepositionTable = commandPrepositionTable_FR;
break;
case Common::ES_ESP:
setFailureMessages(failureMessages_ES, false);
defaultActionCommand = defaultActionCommand_ES;
systemMenu = systemMenu_ES;
confirmMenu = confirmMenu_ES;
otherMessages = otherMessages_ES;
defaultCommandPreposition = commandPrepositionTable_ES[3];
commandPrepositionTable = commandPrepositionTable_ES;
break;
case Common::DE_DEU:
if (g_cine->getGameType() == Cine::GType_OS) {
setFailureMessages(failureMessages_OS_DE, false);
} else {
setFailureMessages(failureMessages_DE, false);
}
defaultActionCommand = defaultActionCommand_DE;
systemMenu = systemMenu_DE;
confirmMenu = confirmMenu_DE;
otherMessages = otherMessages_DE;
defaultCommandPreposition = commandPrepositionTable_DE[3];
commandPrepositionTable = commandPrepositionTable_DE;
break;
case Common::IT_ITA:
setFailureMessages(failureMessages_IT, false);
defaultActionCommand = defaultActionCommand_IT;
systemMenu = systemMenu_IT;
confirmMenu = confirmMenu_IT;
otherMessages = otherMessages_IT;
defaultCommandPreposition = commandPrepositionTable_IT[3];
commandPrepositionTable = commandPrepositionTable_IT;
break;
default:
if (g_cine->getGameType() == Cine::GType_OS) {
if (lang == Common::EN_USA) {
setFailureMessages(failureMessages_OS_US, false);
} else {
setFailureMessages(failureMessages_OS_EN, false);
}
} else {
setFailureMessages(failureMessages_EN, false);
}
defaultActionCommand = defaultActionCommand_EN;
systemMenu = systemMenu_EN;
confirmMenu = confirmMenu_EN;
otherMessages = otherMessages_EN;
defaultCommandPreposition = commandPrepositionTable_EN[3];
commandPrepositionTable = commandPrepositionTable_EN;
break;
}
if (g_cine->getFeatures() & GF_ALT_FONT) {
// Copy alternative font parameter table to the current font parameter table
Common::copy(fontParamTable_alt, fontParamTable_alt + NUM_FONT_CHARS, g_cine->_textHandler.fontParamTable);
} else {
// Copy standard font parameter to the current font parameter table
Common::copy(fontParamTable_standard, fontParamTable_standard + NUM_FONT_CHARS, g_cine->_textHandler.fontParamTable);
}
}
void loadErrmessDat(const char *fname) {
Common::File in;
in.open(fname);
if (in.isOpen()) {
if (allocatedFailureMessages) {
freeErrmessDat();
}
char **ptr = (char **)malloc(sizeof(char *) * 6 * 4 + 60 * 6 * 4);
for (int i = 0; i < 6 * 4; i++) {
ptr[i] = (char *)ptr + (sizeof(char *) * 6 * 4) + 60 * i;
in.read(ptr[i], 60);
}
setFailureMessages(const_cast<const char *const *>(ptr), true);
in.close();
} else {
warning("Cannot read error messages from '%s'. Using default values. Error messages may be incorrect!", fname);
}
}
void setFailureMessages(const char *const *messages, bool allocated) {
if (allocatedFailureMessages) {
freeErrmessDat();
}
failureMessages = messages;
allocatedFailureMessages = allocated;
}
void freeErrmessDat() {
if (allocatedFailureMessages) {
free(const_cast<const char **>(failureMessages));
}
failureMessages = nullptr;
allocatedFailureMessages = false;
}
void loadPoldatDat(const char *fname) {
Common::File in;
in.open(fname);
if (in.isOpen()) {
for (int i = 0; i < NUM_FONT_CHARS; i++) {
g_cine->_textHandler.fontParamTable[i].characterIdx = in.readByte();
g_cine->_textHandler.fontParamTable[i].characterWidth = in.readByte();
}
in.close();
} else {
error("Cannot open file %s for reading", fname);
}
}
/**
* Fit a substring of text into one line of fixed width text box
* @param str Text to fit
* @param maxWidth Text box width
* @param[out] words Number of words that fit
* @param[out] width Total width of nonblank characters that fit
* @return Length of substring which fits
*/
int fitLine(const char *str, int maxWidth, int &words, int &width) {
int i, bkpWords = 0, bkpWidth = 0, bkpLen = 0;
int charWidth = 0, fullWidth = 0;
words = 0;
width = 0;
for (i = 0; str[i]; i++) {
if (str[i] == 0x7C) {
i++;
break;
} else if (str[i] == ' ') {
charWidth = 5;
bkpWords = words++;
bkpWidth = width;
bkpLen = i + 1;
} else {
charWidth = g_cine->_textHandler.fontParamTable[(unsigned char)str[i]].characterWidth + 1;
width += charWidth;
}
if (!charWidth) {
continue;
}
if (fullWidth + charWidth < maxWidth) {
fullWidth += charWidth;
} else if (fullWidth) {
words = bkpWords;
width = bkpWidth;
i = bkpLen;
break;
}
}
return i;
}
} // End of namespace Cine

70
engines/cine/texte.h Normal file
View File

@@ -0,0 +1,70 @@
/* 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 CINE_TEXTE_H
#define CINE_TEXTE_H
#include "common/scummsys.h"
namespace Cine {
typedef char CommandeType[20];
// Number of characters in a font
#define NUM_FONT_CHARS 256
#define FONT_WIDTH 16
#define FONT_HEIGHT 8
// Used for choosing between font's data and font's mask
#define FONT_DATA 0
#define FONT_MASK 1
struct CharacterEntry {
byte characterIdx;
byte characterWidth;
};
struct TextHandler {
byte textTable[NUM_FONT_CHARS][2][FONT_WIDTH *FONT_HEIGHT];
CharacterEntry fontParamTable[NUM_FONT_CHARS];
};
extern const char *const *failureMessages;
extern const CommandeType *defaultActionCommand;
extern const CommandeType *systemMenu;
extern const CommandeType *confirmMenu;
extern const char *const *otherMessages;
extern const char *defaultCommandPreposition;
extern const char *const *commandPrepositionTable;
void loadTextData(const char *filename);
void loadErrmessDat(const char *fname);
void setFailureMessages(const char *const *messages, bool allocated);
void freeErrmessDat();
void loadPoldatDat(const char *fname);
int fitLine(const char *ptr, int maxWidth, int &words, int &width);
} // End of namespace Cine
#endif

153
engines/cine/unpack.cpp Normal file
View File

@@ -0,0 +1,153 @@
/* 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 "cine/unpack.h"
namespace Cine {
uint32 CineUnpacker::readSource() {
if (_src < _srcBegin || _src + 4 > _srcEnd) {
_error = true;
return 0; // The source pointer is out of bounds, returning a default value
}
uint32 value = READ_BE_UINT32(_src);
_src -= 4;
return value;
}
uint CineUnpacker::rcr(bool inputCarry) {
uint outputCarry = (_chunk32b & 1);
_chunk32b >>= 1;
if (inputCarry) {
_chunk32b |= 0x80000000;
}
return outputCarry;
}
uint CineUnpacker::nextBit() {
uint carry = rcr(false);
// Normally if the chunk becomes zero then the carry is one as
// the end of chunk marker is always the last to be shifted out.
if (_chunk32b == 0) {
_chunk32b = readSource();
_crc ^= _chunk32b;
carry = rcr(true); // Put the end of chunk marker in the most significant bit
}
return carry;
}
uint CineUnpacker::getBits(uint numBits) {
uint c = 0;
while (numBits--) {
c <<= 1;
c |= nextBit();
}
return c;
}
void CineUnpacker::unpackRawBytes(uint numBytes) {
if (_dst >= _dstEnd || _dst - numBytes + 1 < _dstBegin) {
_error = true;
return; // Destination pointer is out of bounds for this operation
}
while (numBytes--) {
*_dst = (byte)getBits(8);
--_dst;
}
}
void CineUnpacker::copyRelocatedBytes(uint offset, uint numBytes) {
if (_dst + offset >= _dstEnd || _dst - numBytes + 1 < _dstBegin) {
_error = true;
return; // Destination pointer is out of bounds for this operation
}
while (numBytes--) {
*_dst = *(_dst + offset);
--_dst;
}
}
bool CineUnpacker::unpack(const byte *src, uint srcLen, byte *dst, uint dstLen) {
// Initialize variables used for detecting errors during unpacking
_error = false;
_srcBegin = src;
_srcEnd = src + srcLen;
_dstBegin = dst;
_dstEnd = dst + dstLen;
// Handle already unpacked data here
if (srcLen == dstLen) {
// Source length is same as destination length so the source
// data is already unpacked. Let's just copy it then.
memcpy(dst, src, srcLen);
return true;
}
// Initialize other variables
_src = _srcBegin + srcLen - 4;
uint32 unpackedLength = readSource(); // Unpacked length in bytes
_dst = _dstBegin + unpackedLength - 1;
_crc = readSource();
_chunk32b = readSource();
_crc ^= _chunk32b;
while (_dst >= _dstBegin && !_error) {
/*
Bits => Action:
0 0 => unpackRawBytes(3 bits + 1) i.e. unpackRawBytes(1..8)
1 1 1 => unpackRawBytes(8 bits + 9) i.e. unpackRawBytes(9..264)
0 1 => copyRelocatedBytes(8 bits, 2) i.e. copyRelocatedBytes(0..255, 2)
1 0 0 => copyRelocatedBytes(9 bits, 3) i.e. copyRelocatedBytes(0..511, 3)
1 0 1 => copyRelocatedBytes(10 bits, 4) i.e. copyRelocatedBytes(0..1023, 4)
1 1 0 => copyRelocatedBytes(12 bits, 8 bits + 1) i.e. copyRelocatedBytes(0..4095, 1..256)
*/
if (!nextBit()) { // 0...
if (!nextBit()) { // 0 0
uint numBytes = getBits(3) + 1;
unpackRawBytes(numBytes);
} else { // 0 1
uint numBytes = 2;
uint offset = getBits(8);
copyRelocatedBytes(offset, numBytes);
}
} else { // 1...
uint c = getBits(2);
if (c == 3) { // 1 1 1
uint numBytes = getBits(8) + 9;
unpackRawBytes(numBytes);
} else if (c < 2) { // 1 0 x
uint numBytes = c + 3;
uint offset = getBits(c + 9);
copyRelocatedBytes(offset, numBytes);
} else { // 1 1 0
uint numBytes = getBits(8) + 1;
uint offset = getBits(12);
copyRelocatedBytes(offset, numBytes);
}
}
}
return !_error && (_crc == 0);
}
} // End of namespace Cine

113
engines/cine/unpack.h Normal file
View File

@@ -0,0 +1,113 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef CINE_UNPACK_H
#define CINE_UNPACK_H
#include "common/scummsys.h"
namespace Cine {
/**
* A LZ77 style decompressor for Delphine's data files
* used in at least Future Wars and Operation Stealth.
* @note Works backwards in the source and destination buffers.
* @warning Having the source and destination in the same buffer when unpacking can cause errors!
*/
class CineUnpacker {
public:
/**
* Unpacks packed data from the source buffer to the destination buffer.
* @note You may call this on already unpacked data but then source length must be equal to destination length.
* @warning The source and destination should not point to the same buffer. If they do, errors may occur!
* @param src Pointer to the source buffer.
* @param srcLen Length of the source buffer.
* @param dst Pointer to the destination buffer.
* @param dstLen Length of the destination buffer.
* @return True if no errors were detected in the source data and unpacking was successful, otherwise false.
*/
bool unpack(const byte *src, uint srcLen, byte *dst, uint dstLen);
private:
/**
* Reads an unsigned big endian 32-bit integer from the source stream and goes backwards 4 bytes.
* @return If the operation is valid, an unsigned big endian 32-bit integer read from the source stream.
* @return If the operation is invalid, zero.
* @note Sets internal error state if the read operation would be out of source bounds.
*/
uint32 readSource();
/**
* Shifts the current internal 32-bit chunk to the right by one.
* Puts input carry into internal chunk's topmost (i.e. leftmost) bit.
* @return The least significant bit that was shifted out from the chunk.
*/
uint rcr(bool inputCarry);
/**
* Get the next bit from the source stream.
* @note Changes the bit position in the source stream.
* @return The next bit from the source stream.
*/
uint nextBit();
/**
* Get bits from the source stream.
* @note Changes the bit position in the source stream.
* @param numBits Number of bits to read from the source stream.
* @return Integer value consisting of the bits read from the source stream (In range [0, (2 ** numBits) - 1]).
* @return Later the bit was read from the source, the less significant it is in the return value.
*/
uint getBits(uint numBits);
/**
* Copy raw bytes from the input stream and write them to the destination stream.
* This is used when no adequately long match is found in the sliding window.
* @note Sets internal error state if the operation would be out of bounds.
* @param numBytes Amount of bytes to copy from the input stream
*/
void unpackRawBytes(uint numBytes);
/**
* Copy bytes from the sliding window in the destination buffer.
* This is used when a match of two bytes or longer is found.
* @note Sets internal error state if the operation would be out of bounds.
* @param offset Offset in the sliding window
* @param numBytes Amount of bytes to copy
*/
void copyRelocatedBytes(uint offset, uint numBytes);
private:
uint32 _crc; ///< Error-detecting code (This should be zero after successful unpacking)
uint32 _chunk32b; ///< The current internal 32-bit chunk of source data
byte *_dst; ///< Pointer to the current position in the destination buffer
const byte *_src; ///< Pointer to the current position in the source buffer
// These are used for detecting errors (e.g. out of bounds issues) during unpacking
bool _error; ///< Did an error occur during unpacking?
const byte *_srcBegin; ///< Source buffer's beginning
const byte *_srcEnd; ///< Source buffer's end
byte *_dstBegin; ///< Destination buffer's beginning
byte *_dstEnd; ///< Destination buffer's end
};
} // End of namespace Cine
#endif

2022
engines/cine/various.cpp Normal file

File diff suppressed because it is too large Load Diff

163
engines/cine/various.h Normal file
View File

@@ -0,0 +1,163 @@
/* 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 CINE_VARIOUS_H
#define CINE_VARIOUS_H
#include "common/file.h"
#include "common/keyboard.h"
#include "cine/cine.h"
namespace Cine {
#define kMaxSavegames 100
#define kMaxOrigUiSavegames 20 // 20 fit on screen using original save/load interface
// Maximum size of the command buffer including the trailing zero
#define kMaxCommandBufferSize 80
void initLanguage(Common::Language lang);
int16 makeMenuChoice(const CommandeType commandList[], uint16 height, uint16 X, uint16 Y, uint16 width, int minY = 0, bool recheckValue = false, bool allowEmpty = false);
void makeCommandLine();
void makeFWCommandLine();
void makeOSCommandLine();
void makeActionMenu();
void waitPlayerInput();
void setTextWindow(uint16 param1, uint16 param2, uint16 param3, uint16 param4);
extern int16 disableSystemMenu;
extern bool inMenu;
extern bool runOnlyUntilFreePartRangeFirst200;
extern CommandeType currentSaveName[kMaxSavegames];
struct SeqListElement {
int16 var4;
uint16 objIdx; ///< Is this really unsigned?
int16 var8;
int16 frame;
int16 varC;
int16 varE;
int16 var10;
int16 var12;
int16 var14;
int16 var16;
int16 var18;
int16 var1A;
int16 var1C;
int16 var1E;
};
extern uint16 var2;
extern uint16 var3;
extern uint16 var4;
extern uint16 lastType20OverlayBgIdx;
extern uint16 reloadBgPalOnNextFlip;
extern uint16 forbidBgPalReload;
extern uint16 gfxFadeOutCompleted;
extern uint16 gfxFadeInRequested;
extern uint32 safeControlsLastAccessedMs; ///< Time in milliseconds when safe controls were last accessed.
extern int16 lastSafeControlObjIdx; ///< Object index of the last safe control accessed.
extern int16 commandVar1;
extern int16 commandVar2;
extern int16 commandVar3[4];
extern char currentDatName[30];
extern uint16 musicIsPlaying;
extern uint16 errorVar;
extern byte menuVar;
extern uint16 allowPlayerInput;
extern uint16 checkForPendingDataLoadSwitch;
extern uint16 isDrawCommandEnabled;
extern uint16 waitForPlayerClick;
extern uint16 menuCommandLen;
extern bool _paletteNeedUpdate;
extern uint16 _messageLen;
extern int16 playerCommand;
extern char currentPrcName[20];
extern char currentRelName[20];
extern char currentObjectName[20];
extern char currentMsgName[20];
extern char newPrcName[20];
extern char newRelName[20];
extern char newObjectName[20];
extern char newMsgName[20];
extern char currentCtName[15];
extern char currentPartName[15];
void stopSample();
void stopMusicAfterFadeOut();
void playerCommandMouseLeftRightUp(uint16 mouseX, uint16 mouseY);
uint16 executePlayerInput();
void drawOverlays();
extern uint16 mouseUpdateStatus;
extern uint16 dummyU16;
void getMouseData(uint16 param, uint16 *pButton, uint16 *pX, uint16 *pY);
uint16 processKeyboard(uint16 param);
void mainLoopSub6();
void checkForPendingDataLoad();
void hideMouse();
void removeExtension(char *dest, const char *source, size_t sz);
struct SelectedObjStruct {
int16 idx;
int16 param;
};
#define NUM_MAX_ZONE 16
void addMessage(byte param1, int16 param2, int16 param3, int16 param4, int16 param5);
void removeMessages();
void removeSeq(uint16 param1, uint16 param2, uint16 param3);
bool isSeqRunning(uint16 param1, uint16 param2, uint16 param3);
void addSeqListElement(uint16 objIdx, int16 param1, int16 param2, int16 frame, int16 param4, int16 param5, int16 param6, int16 param7, int16 param8);
void modifySeqListElement(uint16 objIdx, int16 var4Test, int16 param1, int16 param2, int16 param3, int16 param4);
void processSeqList();
void resetGfxEntityEntry(uint16 objIdx);
bool makeTextEntryMenu(const char *caption, char *string, int strLen, int y);
void moveUsingKeyboard(int x, int y);
int16 getObjectUnderCursor(uint16 x, uint16 y);
} // End of namespace Cine
#endif

184
engines/cine/xref.txt Normal file
View File

@@ -0,0 +1,184 @@
script_fw.cpp:
setupOpcodes() - replaced with FWScript/OSScript class members
getNextByte() - replaced with RawScript/FWScript class members
getNextWord() - replaced with RawScript/FWScript class members
getNextString() - replaced with RawScript/FWScript class members
computeScriptStackSub() - replaced with RawScript::getNextLabel()
computeScriptStack() - replaced with RawScript::computeLabels()
computeScriptStackFromScript() - replaced with RawScript::getLabel()
executeScript() - replaced with FWScript::execute()
endScript0() - removed (obsoleted by new executeList0() implementation)
endScript1() - removed (obsoleted by new executeList1() implementation)
o1_modifyObjectParam() - replaced with FWScript::o1_modifyObjectParam()
o1_getObjectParam() - replaced with FWScript::o1_getObjectParam()
o1_addObjectParam() - replaced with FWScript::o1_addObjectParam()
o1_subObjectParam() - replaced with FWScript::o1_subObjectParam()
o1_add2ObjectParam() - replaced with FWScript::o1_add2ObjectParam()
o1_sub2ObjectParam() - replaced with FWScript::o1_sub2ObjectParam()
o1_compareObjectParam() - replaced with FWScript::o1_compareObjectParam()
o1_setupObject() - replaced with FWScript::o1_setupObject()
o1_checkCollision() - replaced with FWScript::o1_checkCollision()
o1_loadVar() - replaced with FWScript::o1_loadVar()
o1_addVar() - replaced with FWScript::o1_addVar()
o1_subVar() - replaced with FWScript::o1_subVar()
o1_mulVar() - replaced with FWScript::o1_mulVar()
o1_divVar() - replaced with FWScript::o1_divVar()
o1_compareVar() - replaced with FWScript::o1_compareVar()
o1_modifyObjectParam2() - replaced with FWScript::o1_modifyObjectParam2()
o1_loadMask0() - replaced with FWScript::o1_loadMask0()
o1_unloadMask0() - replaced with FWScript::o1_unloadMask0()
o1_addToBgList() - replaced with FWScript::o1_addToBgList()
o1_loadMask1() - replaced with FWScript::o1_loadMask1()
o1_unloadMask1() - replaced with FWScript::o1_unloadMask1()
o1_loadMask4() - replaced with FWScript::o1_loadMask4()
o1_unloadMask4() - replaced with FWScript::o1_unloadMask4()
o1_addSpriteFilledToBgList() - replaced with FWScript::o1_addSpriteFilledToBgList()
o1_op1B() - replaced with FWScript::o1_op1B()
o1_label() - replaced with FWScript::o1_label()
o1_goto() - replaced with FWScript::o1_goto()
o1_gotoIfSup() - replaced with FWScript::o1_gotoIfSup()
o1_gotoIfSupEqu() - replaced with FWScript::o1_gotoIfSupEqu()
o1_gotoIfInf() - replaced with FWScript::o1_gotoIfInf()
o1_gotoIfInfEqu() - replaced with FWScript::o1_gotoIfInfEqu()
o1_gotoIfEqu() - replaced with FWScript::o1_gotoIfEqu()
o1_gotoIfDiff() - replaced with FWScript::o1_gotoIfDiff()
o1_removeLabel() - replaced with FWScript::o1_removeLabel()
o1_loop() - replaced with FWScript::o1_loop()
o1_startGlobalScript() - replaced with FWScript::o1_startGlobalScript()
o1_endGlobalScript() - replaced with FWScript::o1_endGlobalScript()
o1_loadAnim() - replaced with FWScript::o1_loadAnim()
o1_loadBg() - replaced with FWScript::o1_loadBg()
o1_loadCt() - replaced with FWScript::o1_loadCt()
o1_loadPart() - replaced with FWScript::o1_loadPart()
o1_closePart() - replaced with FWScript::o1_closePart()
o1_loadNewPrcName() - replaced with FWScript::o1_loadNewPrcName()
o1_requestCheckPendingDataLoad() - replaced with FWScript::o1_requestCheckPendingDataLoad()
o1_blitAndFade() - replaced with FWScript::o1_blitAndFade()
o1_fadeToBlack() - replaced with FWScript::o1_fadeToBlack()
o1_transformPaletteRange() - replaced with FWScript::o1_transformPaletteRange()
o1_setDefaultMenuColor2() - replaced with FWScript::o1_setDefaultMenuColor2()
o1_palRotate() - replaced with FWScript::o1_palRotate()
o1_break() - replaced with FWScript::o1_break()
o1_endScript() - replaced with FWScript::o1_endScript()
o1_message() - replaced with FWScript::o1_message()
o1_loadGlobalVar() - replaced with FWScript::o1_loadGlobalVar()
o1_compareGlobalVar() - replaced with FWScript::o1_compareGlobalVar()
o1_declareFunctionName() - replaced with FWScript::o1_declareFunctionName()
o1_freePartRange() - replaced with FWScript::o1_freePartRange()
o1_unloadAllMasks() - replaced with FWScript::o1_unloadAllMasks()
o1_setScreenDimensions() - replaced with FWScript::o1_setScreenDimensions()
o1_displayBackground() - replaced with FWScript::o1_displayBackground()
o1_initializeZoneData() - replaced with FWScript::o1_initializeZoneData()
o1_setZoneDataEntry() - replaced with FWScript::o1_setZoneDataEntry()
o1_getZoneDataEntry() - replaced with FWScript::o1_getZoneDataEntry()
o1_setDefaultMenuColor() - replaced with FWScript::o1_setDefaultMenuColor()
o1_allowPlayerInput() - replaced with FWScript::o1_allowPlayerInput()
o1_disallowPlayerInput() - replaced with FWScript::o1_disallowPlayerInput()
o1_changeDataDisk() - replaced with FWScript::o1_changeDataDisk()
o1_loadMusic() - replaced with FWScript::o1_loadMusic()
o1_playMusic() - replaced with FWScript::o1_playMusic()
o1_fadeOutMusic() - replaced with FWScript::o1_fadeOutMusic()
o1_stopSample() - replaced with FWScript::o1_stopSample()
o1_op71() - replaced with FWScript::o1_op71()
o1_op72() - replaced with FWScript::o1_op72()
o1_op73() - replaced with FWScript::o1_op73()
o1_playSample() - replaced with FWScript::o1_playSample()
o1_playSample() - replaced with FWScript::o1_playSample()
o1_disableSystemMenu() - replaced with FWScript::o1_disableSystemMenu()
o1_loadMask5() - replaced with FWScript::o1_loadMask5()
o1_unloadMask5() - replaced with FWScript::o1_unloadMask5()
palRotate() - modified and moved to pal.cpp
script_os.cpp:
o2_loadPart() - replaced with FWScript::o2_loadPart()
o2_addSeqListElement() - replaced with FWScript::o2_addSeqListElement()
o2_removeSeq() - replaced with FWScript::o2_removeSeq()
o2_playSample() - replaced with FWScript::o2_playSample()
o2_playSampleAlt() - replaced with FWScript::o2_playSampleAlt()
o2_op81() - replaced with FWScript::o2_op81()
o2_op82() - replaced with FWScript::o2_op82()
o2_isSeqRunning() - replaced with FWScript::o2_isSeqRunning()
o2_gotoIfSupNearest() - replaced with FWScript::o2_gotoIfSupNearest()
o2_gotoIfSupEquNearest() - replaced with FWScript::o2_gotoIfSupEquNearest()
o2_gotoIfInfNearest() - replaced with FWScript::o2_gotoIfInfNearest()
o2_gotoIfInfEquNearest() - replaced with FWScript::o2_gotoIfInfEquNearest()
o2_gotoIfEquNearest() - replaced with FWScript::o2_gotoIfEquNearest()
o2_gotoIfDiffNearest() - replaced with FWScript::o2_gotoIfDiffNearest()
o2_startObjectScript() - replaced with FWScript::o2_startObjectScript()
o2_stopObjectScript() - replaced with FWScript::o2_stopObjectScript()
o2_op8D() - replaced with FWScript::o2_op8D()
o2_addBackground() - replaced with FWScript::o2_addBackground()
o2_removeBackground() - replaced with FWScript::o2_removeBackground()
o2_loadAbs() - replaced with FWScript::o2_loadAbs()
o2_loadBg() - replaced with FWScript::o2_loadBg()
o2_wasZoneChecked() - replaced with FWScript::o2_wasZoneChecked()
o2_op9B() - replaced with FWScript::o2_op9B()
o2_op9C() - replaced with FWScript::o2_op9C()
o2_useBgScroll() - replaced with FWScript::o2_useBgScroll()
o2_setAdditionalBgVScroll() - replaced with FWScript::o2_setAdditionalBgVScroll()
o2_op9F() - replaced with FWScript::o2_op9F()
o2_addGfxElementA0() - replaced with FWScript::o2_addGfxElementA0()
o2_opA1() - replaced with FWScript::o2_opA1()
o2_opA2() - replaced with FWScript::o2_opA2()
o2_opA3() - replaced with FWScript::o2_opA3()
o2_loadMask22() - replaced with FWScript::o2_loadMask22()
o2_unloadMask22() - replaced with FWScript::o2_unloadMask22()
prc.cpp:
resetGlobalScriptsHead() - removed (obsoleted by Common::List)
freePrcLinkedList() - removed (obsoleted by Common::List::clear())
rel.cpp:
resetObjectScriptHead() - removed (obsoleted by Common::List)
releaseObjectScripts() - removed (obsoleted by Common::List::clear())
various.cpp:
setupScriptList() - removed (obsoleted by new makeLoad() and
loadScriptFromSave() implementation)
drawChar() - removed (obsoleted by FWRenderer::drawChar())
makeTextEntry() - removed (obsoleted by FWRenderer::drawMenu())
drawMenuBox() - removed (obsoleted by FWRenderer::drawCommand())
backupOverlayPage() - removed (obsoleted by FWRenderer::drawBackground())
drawMessage() - removed (obsoleted by FWRenderer::drawMessage())
drawDialogueMessage() - removed (obsoleted by FWRenderer::renderOverlay())
drawFailureMessage() - removed (obsoleted by FWRenderer::renderOverlay())
drawOverlays() - removed (obsoleted by FWRenderer::drawOverlays())
resetSeqList() - removed (obsoleted by Common::List)
anim.cpp:
freeAnimData() - replaced with animData::clear()
allocFrame() - replaced with animData::load()
reserveFrame() - replaced with animData::load()
bg_list.cpp:
reincrustAllBg() - removed (obsoleted by new loadResourcesFromSave() and
loadBgIncrustFromSave() implementation)
freeBgIncrustList() - removed (obsoleted by Common::List::clear())
object.cpp:
unloadAllMasks() - removed (obsoleted by Common::List::clear())
resetMessageHead() - removed (obsoleted by Common::List)
freeOverlay() - removed (duplicate of removeOverlay)
removeOverlayElement() - renamed to removeOverlay
loadOverlayElement() - renamed to addOverlay
gfx.cpp:
gfxInit() - removed (obsoleted by FWRenderer)
gfxDestroy() - removed (obsoleted by FWRenderer)
transformColor() - moved to pal.cpp
transformPaletteRange() - modified and moved to pal.cpp
gfxCopyRawPage() - removed (obsoleted by FWRenderer)
gfxFlipRawPage() - removed (obsoleted by FWRenderer::blit() and
FWRenderer::refreshPalette())
fadeToBlack() - removed (obsoleted by FWRenderer::fadeToBlack())
blitRawScreen() - removed (obsoleted by FWRenderer)
flip() - removed (obsoleted by FWRenderer::reloadPalette())
bg.cpp:
loadCt() - split into loadCtFW() and loadCtOS()
loadBgHigh() - removed (obsoleted by OSRenderer::loadBg256())
texte.cpp:
computeMessageLength() - replaced with fitLine()