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

2
engines/vcruise/POTFILES Normal file
View File

@@ -0,0 +1,2 @@
engines/vcruise/metaengine.cpp
engines/vcruise/vcruise.cpp

View File

@@ -0,0 +1,103 @@
/* 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 "vcruise/ad2044_items.h"
namespace VCruise {
const AD2044ItemInfo g_ad2044ItemInfos[kNumAD2044Items] = {
{0, 0, false, 0}, // 0
{0, 0, false, 0}, // 1
{0, 0, false, 0}, // 2
{0, 0, false, 0}, // 3
{0, 0, false, 0}, // 4
{0, 0, false, 0}, // 5
{0, 0, false, 0}, // 6
{0, 0, false, 0}, // 7
{0, 0, false, 0}, // 8
{0, 0, false, 0}, // 9
{0, 0, false, 0}, // 10
{0, 0, false, 0}, // 11
{0, 0, false, 0}, // 12
{0, 0, false, 0}, // 13
{0, 0, false, 0}, // 14
{0, 0, false, 0}, // 15
{0, 0, false, 0}, // 16
{0, 0, false, 0}, // 17
{0x3D70D2EC, 0x98382A38, true, 0x128}, // 18 spoon
{0, 0, false, 0}, // 19
{0, 0, false, 0}, // 20
{0, 0, false, 0}, // 21
{0, 0, false, 0}, // 22
{0, 0, false, 0}, // 23
{0, 0, true, 0x134}, // 24 cigarettes (filled)
{0, 0, false, 0}, // 25
{0, 0, false, 0}, // 26
{0, 0, true, 0x137}, // 27 matches
{0, 0, false, 0}, // 28
{0, 0, false, 0}, // 29
{0, 0, false, 0}, // 30
{0, 0, false, 0}, // 31
{0, 0, false, 0}, // 32
{0, 0, false, 0}, // 33
{0, 0, false, 0}, // 34
{0, 0, false, 0}, // 35
{0, 0, false, 0}, // 36
{0, 0, false, 0}, // 37
{0, 0, false, 0}, // 38
{0, 0, false, 0}, // 39
{0, 0, false, 0}, // 40
{0, 0, false, 0}, // 41
{0, 0, false, 0}, // 42
{0, 0, false, 0}, // 43
{0, 0, false, 0}, // 44
{0, 0, false, 0}, // 45
{0, 0, false, 0}, // 46
{0, 0, false, 0}, // 47
{0, 0, false, 0}, // 48
{0, 0, false, 0}, // 49
{0, 0, false, 0}, // 50
{0, 0, false, 0}, // 51
{0, 0, false, 0}, // 52
{0, 0, false, 0}, // 53
{0x83d54448, 0x839911EF, 0, 0}, // 54 goaler (sic)
{0, 0, false, 0}, // 55
{0, 0, false, 0}, // 56
{0, 0, false, 0}, // 57
{0, 0, false, 0}, // 58
{0, 0, false, 0}, // 59
{0, 0, false, 0x170}, // 60 mirror
{0, 0, false, 0}, // 61
{0, 0, false, 0}, // 62
{0, 0, false, 0}, // 63
{0, 0, false, 0}, // 64
{0, 0, false, 0}, // 65
{0, 0, false, 0}, // 66
{0, 0, false, 0}, // 67
{0, 0, true, 0x178}, // 68 cigarette
{0, 0, true, 0x179}, // 69 cigarette (lit)
{0, 0, false, 0}, // 70
{0, 0, false, 0}, // 71
{0, 0, false, 0}, // 72
{0, 0, true, 0x183}, // 73 cigarettes (1 missing)
};
} // End of namespace VCruise

View File

@@ -0,0 +1,43 @@
/* 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 VCRUISE_AD2044_ITEMS_H
#define VCRUISE_AD2044_ITEMS_H
#include "common/scummsys.h"
namespace VCruise {
struct AD2044ItemInfo {
uint32 enNameCRC;
uint32 plNameCRC;
bool canBeExamined;
uint16 scriptItemID;
};
static const uint kNumAD2044Items = 74;
extern const AD2044ItemInfo g_ad2044ItemInfos[kNumAD2044Items];
} // End of namespace VCruise
#endif

View File

@@ -0,0 +1,61 @@
/* 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 "vcruise/ad2044_ui.h"
namespace VCruise {
namespace AD2044Interface {
Common::Rect getRectForUI(AD2044InterfaceRectID rectID) {
switch (rectID) {
case AD2044InterfaceRectID::ActiveItemRender:
return Common::Rect(512, 150, 588, 217);
case AD2044InterfaceRectID::ExamineButton:
return Common::Rect(495, 248, 595, 318);
case AD2044InterfaceRectID::InventoryRender0:
return Common::Rect(24, 394, 100, 461);
case AD2044InterfaceRectID::InventoryRender1:
return Common::Rect(119, 395, 195, 462);
case AD2044InterfaceRectID::InventoryRender2:
return Common::Rect(209, 393, 285, 460);
case AD2044InterfaceRectID::InventoryRender3:
return Common::Rect(302, 393, 378, 460);
case AD2044InterfaceRectID::InventoryRender4:
return Common::Rect(393, 394, 469, 461);
case AD2044InterfaceRectID::InventoryRender5:
return Common::Rect(481, 393, 557, 460);
default:
return Common::Rect();
}
}
Common::Rect getFirstInvSlotRect() {
return Common::Rect(21, 392, 96, 460);
}
uint getInvSlotSpacing() {
return 92;
}
} // End of namespace AD2044Interface
} // End of namespace VCruise

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 VCRUISE_AD2044_UI_H
#define VCRUISE_AD2044_UI_H
#include "common/rect.h"
namespace VCruise {
enum class AD2044InterfaceRectID {
ActiveItemRender,
ExamineButton,
InventoryRender0,
InventoryRender1,
InventoryRender2,
InventoryRender3,
InventoryRender4,
InventoryRender5,
};
namespace AD2044Interface {
Common::Rect getRectForUI(AD2044InterfaceRectID rectID);
Common::Rect getFirstInvSlotRect();
uint getInvSlotSpacing();
} // End of namespace AD2044Interface
} // End of namespace VCruise
#endif

View File

@@ -0,0 +1,95 @@
/* 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 "vcruise/audio_player.h"
namespace VCruise {
AudioPlayer::AudioPlayer(Audio::Mixer *mixer, const Common::SharedPtr<Audio::AudioStream> &baseStream, Audio::Mixer::SoundType soundType)
: _exhausted(false), _isPlaying(false), _mixer(mixer), _baseStream(baseStream), _soundType(soundType) {
}
AudioPlayer::~AudioPlayer() {
stop();
}
int AudioPlayer::readBuffer(int16 *buffer, const int numSamplesTimesChannelCount) {
Common::StackLock lock(_mutex);
int samplesRead = 0;
if (_exhausted)
return 0;
samplesRead = _baseStream->readBuffer(buffer, numSamplesTimesChannelCount);
if (samplesRead != numSamplesTimesChannelCount)
_exhausted = true;
return samplesRead;
}
bool AudioPlayer::isStereo() const {
return _baseStream->isStereo();
}
int AudioPlayer::getRate() const {
return _baseStream->getRate();
}
bool AudioPlayer::endOfData() const {
return _exhausted;
}
void AudioPlayer::play(byte volume, int8 balance) {
if (!_isPlaying) {
_isPlaying = true;
_exhausted = false;
_mixer->playStream(_soundType, &_handle, this, -1, volume, balance, DisposeAfterUse::NO);
}
}
void AudioPlayer::setVolume(byte volume) {
_mixer->setChannelVolume(_handle, volume);
}
void AudioPlayer::setBalance(int8 balance) {
_mixer->setChannelBalance(_handle, balance);
}
void AudioPlayer::setVolumeAndBalance(byte volume, int8 balance) {
if (_isPlaying) {
Common::StackLock lock(_mixer->mutex());
_mixer->setChannelVolume(_handle, volume);
_mixer->setChannelBalance(_handle, balance);
}
}
void AudioPlayer::stop() {
if (_isPlaying) {
_mixer->stopHandle(_handle);
_isPlaying = false;
}
_exhausted = true;
}
} // End of namespace VCruise

View File

@@ -0,0 +1,67 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef VCRUISE_AUDIO_PLAYER_H
#define VCRUISE_AUDIO_PLAYER_H
#include "common/mutex.h"
#include "audio/audiostream.h"
#include "audio/mixer.h"
namespace VCruise {
struct AudioMetadata;
class CachedAudio;
class AudioPlayer : public Audio::AudioStream {
public:
AudioPlayer(Audio::Mixer *mixer, const Common::SharedPtr<Audio::AudioStream> &baseStream, Audio::Mixer::SoundType soundType);
~AudioPlayer();
int readBuffer(int16 *buffer, const int numSamples) override;
bool isStereo() const override;
int getRate() const override;
bool endOfData() const override;
void play(byte volume, int8 balance);
void stop();
void setVolume(byte volume);
void setBalance(int8 balance);
void setVolumeAndBalance(byte volume, int8 balance);
private:
Common::Mutex _mutex;
Audio::SoundHandle _handle;
//bool _isLooping;
bool _isPlaying;
bool _exhausted;
Audio::Mixer *_mixer;
Common::SharedPtr<Audio::AudioStream> _baseStream;
Audio::Mixer::SoundType _soundType;
};
} // End of namespace VCruise
#endif

View File

@@ -0,0 +1,715 @@
/* 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/algorithm.h"
#include "vcruise/circuitpuzzle.h"
namespace VCruise {
namespace CircuitPuzzleTables {
// These are hard-coded into the Schizm executable (they're 32-bit in it), figured these out by hand
// from tracing screen captures and feeding through a boundary finder script.
static const int16 g_barriersHorizontal1[100] = {
166, 6, 191, 62,
244, 7, 267, 62,
320, 7, 341, 62,
394, 8, 416, 63,
469, 8, 492, 62,
164, 80, 191, 134,
244, 80, 265, 130,
320, 80, 336, 131,
394, 80, 415, 130,
468, 80, 492, 130,
163, 152, 191, 199,
245, 152, 263, 199,
320, 152, 340, 199,
395, 152, 415, 199,
469, 152, 493, 199,
166, 221, 187, 270,
245, 221, 266, 270,
320, 220, 341, 271,
394, 221, 414, 271,
469, 221, 492, 271,
166, 290, 187, 343,
244, 290, 263, 343,
320, 290, 341, 343,
395, 289, 415, 343,
469, 291, 494, 346,
};
static const int16 g_barriersVertical1[64] = {
187, 64, 240, 84,
266, 63, 316, 84,
340, 64, 392, 83,
416, 64, 470, 84,
186, 135, 240, 151,
266, 135, 315, 152,
341, 134, 393, 151,
416, 135, 479, 152,
187, 204, 239, 221,
266, 203, 314, 221,
341, 204, 391, 220,
416, 204, 470, 220,
187, 273, 239, 293,
266, 271, 314, 292,
342, 271, 391, 292,
416, 272, 471, 292,
};
static const int16 g_barriersHorizontal2[100] = {
160, 8, 185, 62,
239, 7, 260, 63,
309, 8, 332, 63,
388, 8, 407, 63,
455, 8, 485, 63,
162, 79, 184, 130,
241, 80, 260, 131,
313, 80, 332, 131,
387, 82, 408, 131,
460, 82, 484, 131,
162, 153, 184, 200,
238, 153, 258, 200,
312, 153, 333, 201,
386, 153, 408, 201,
459, 153, 485, 200,
161, 220, 183, 270,
237, 220, 259, 270,
313, 221, 332, 270,
384, 220, 408, 269,
459, 219, 485, 267,
161, 289, 183, 343,
238, 288, 259, 350,
312, 289, 332, 341,
383, 291, 408, 342,
460, 290, 484, 341,
};
static const int16 g_barriersVertical2[64] = {
186, 63, 239, 84,
263, 64, 313, 84,
337, 64, 389, 84,
411, 64, 464, 85,
184, 135, 238, 151,
259, 136, 311, 151,
336, 135, 388, 152,
410, 136, 465, 152,
184, 203, 238, 220,
263, 204, 311, 220,
338, 203, 388, 219,
408, 203, 464, 219,
185, 272, 237, 291,
260, 272, 313, 291,
337, 271, 388, 290,
411, 271, 463, 289,
};
static const int16 g_linksHorizontal1[100] = {
136, 24, 206, 38,
216, 24, 284, 38,
294, 24, 363, 38,
374, 24, 442, 38,
452, 24, 520, 38,
136, 97, 205, 110,
215, 97, 284, 110,
294, 98, 363, 110,
373, 98, 442, 110,
451, 98, 520, 110,
137, 170, 206, 182,
216, 170, 284, 182,
294, 170, 363, 182,
373, 170, 442, 182,
452, 170, 520, 182,
137, 242, 204, 255,
216, 242, 284, 255,
295, 242, 363, 254,
374, 242, 441, 254,
452, 242, 520, 254,
137, 315, 204, 328,
216, 315, 284, 328,
295, 315, 362, 327,
374, 315, 441, 327,
452, 315, 520, 327,
};
static const int16 g_linksVertical1[64] = {
205, 36, 217, 98,
284, 36, 295, 99,
362, 36, 374, 99,
441, 37, 452, 99,
205, 109, 217, 171,
284, 109, 295, 171,
363, 109, 374, 171,
441, 109, 452, 170,
205, 181, 217, 244,
284, 181, 295, 243,
362, 181, 374, 243,
441, 181, 452, 243,
205, 254, 217, 316,
284, 254, 295, 316,
362, 254, 374, 316,
441, 254, 453, 316,
};
static const int16 g_linksHorizontal2[100] = {
135, 26, 206, 39,
214, 27, 284, 39,
292, 27, 362, 40,
370, 28, 439, 41,
447, 29, 515, 40,
135, 98, 206, 111,
214, 99, 284, 111,
292, 99, 362, 111,
370, 99, 439, 112,
447, 100, 515, 112,
135, 170, 206, 182,
214, 170, 284, 182,
293, 170, 362, 182,
370, 170, 438, 182,
447, 170, 514, 182,
135, 241, 206, 255,
214, 241, 285, 254,
293, 241, 362, 254,
370, 241, 438, 253,
447, 241, 515, 253,
135, 314, 206, 327,
214, 314, 284, 326,
292, 313, 362, 325,
370, 313, 438, 325,
447, 312, 515, 324,
};
static const int16 g_linksVertical2[64] = {
204, 37, 216, 100,
283, 38, 294, 100,
360, 39, 372, 100,
437, 39, 449, 102,
204, 109, 216, 171,
283, 110, 294, 171,
360, 110, 371, 171,
437, 111, 448, 171,
204, 181, 216, 242,
283, 181, 294, 242,
360, 181, 371, 242,
436, 181, 448, 242,
204, 253, 216, 315,
283, 252, 294, 315,
360, 252, 372, 314,
436, 251, 448, 314,
};
} // End of namespace CircuitPuzzleTables
struct CircuitPuzzleAIEvaluator {
CircuitPuzzleAIEvaluator();
static const uint kMaxMovesToReach = CircuitPuzzle::kBoardWidth * CircuitPuzzle::kBoardHeight * 2;
uint stepsToReach[CircuitPuzzle::kBoardWidth][CircuitPuzzle::kBoardHeight];
};
class CircuitPuzzleVisitedSet {
public:
CircuitPuzzleVisitedSet();
void set(const Common::Point &coord);
bool get(const Common::Point &coord) const;
void clear();
private:
uint32 _bits;
};
CircuitPuzzleVisitedSet::CircuitPuzzleVisitedSet() : _bits(0) {
}
void CircuitPuzzleVisitedSet::set(const Common::Point &coord) {
int bit = coord.y * static_cast<int>(CircuitPuzzle::kBoardWidth) + coord.x;
_bits |= (1u << bit);
}
bool CircuitPuzzleVisitedSet::get(const Common::Point &coord) const {
int bit = coord.y * static_cast<int>(CircuitPuzzle::kBoardWidth) + coord.x;
return (_bits & (1u << bit)) != 0;
}
void CircuitPuzzleVisitedSet::clear() {
_bits = 0;
}
CircuitPuzzleAIEvaluator::CircuitPuzzleAIEvaluator() {
for (uint x = 0; x < CircuitPuzzle::kBoardWidth; x++)
for (uint y = 0; y < CircuitPuzzle::kBoardHeight; y++)
stepsToReach[x][y] = kMaxMovesToReach;
}
CircuitPuzzle::Action::Action() : _direction(kCellDirectionDown) {
}
CircuitPuzzle::CircuitPuzzle(int layout) : _havePreviousAction(false) {
_startPoint = Common::Point(0, 0);
_goalPoint = Common::Point(kBoardWidth - 1, 0);
const int16 *linksHoriz = nullptr;
const int16 *linksVert = nullptr;
const int16 *barriersHoriz = nullptr;
const int16 *barriersVert = nullptr;
if (layout == 1) {
linksHoriz = CircuitPuzzleTables::g_linksHorizontal1;
linksVert = CircuitPuzzleTables::g_linksVertical1;
barriersHoriz = CircuitPuzzleTables::g_barriersHorizontal1;
barriersVert = CircuitPuzzleTables::g_barriersVertical1;
} else if (layout == 2) {
linksHoriz = CircuitPuzzleTables::g_linksHorizontal2;
linksVert = CircuitPuzzleTables::g_linksVertical2;
barriersHoriz = CircuitPuzzleTables::g_barriersHorizontal2;
barriersVert = CircuitPuzzleTables::g_barriersVertical2;
} else
error("Unknown circuit screen layout");
// Pre-connect the side rails
for (uint i = 0; i < (kBoardHeight - 1u); i++) {
*getConnectionState(Common::Point(0, i), KDirectionDown) = kLinkStateConnected;
*getConnectionState(Common::Point(kBoardWidth - 1, i), KDirectionDown) = kLinkStateConnected;
}
// Block edge points
for (uint i = 0; i < kBoardWidth; i++)
_cells[i][kBoardHeight - 1]._downLink = kLinkStateBlocked;
for (uint i = 0; i < kBoardHeight; i++)
_cells[kBoardWidth - 1][i]._rightLink = kLinkStateBlocked;
// Barriers are traced from pixel matches, but links are traced from the highlight boxes.
// Since the highlight boxes are (1,1) larger than the clipping box of the animation, and because
// the coordinates are inclusive, we need to add (1,1) to barrier sizes, but not link sizes, since the
// inclusive (+1,+1) cancels out from the oversize (-1,-1) adjustment.
// Resolve horizontal links and barriers
for (uint y = 0; y < kBoardHeight; y++) {
for (uint x = 0; x < (kBoardWidth - 1u); x++) {
uint rectDataOffset = (x + y * (kBoardWidth - 1u)) * 4u;
CellRectSpec &rectSpec = _cellRectSpecs[x][y];
rectSpec._rightBarrierRect = Common::Rect(barriersHoriz[rectDataOffset + 0], barriersHoriz[rectDataOffset + 1], barriersHoriz[rectDataOffset + 2] + 1, barriersHoriz[rectDataOffset + 3] + 1);
rectSpec._rightLinkRect = Common::Rect(linksHoriz[rectDataOffset + 0], linksHoriz[rectDataOffset + 1], linksHoriz[rectDataOffset + 2], linksHoriz[rectDataOffset + 3]);
}
}
// Resolve vertical links and barriers. Skip the first and last column.
for (uint y = 0; y < (kBoardHeight - 1u); y++) {
for (uint x = 1; x < (kBoardWidth - 1u); x++) {
uint rectDataOffset = ((x - 1) + y * (kBoardWidth - 2u)) * 4u;
CellRectSpec &rectSpec = _cellRectSpecs[x][y];
rectSpec._downBarrierRect = Common::Rect(barriersVert[rectDataOffset + 0], barriersVert[rectDataOffset + 1], barriersVert[rectDataOffset + 2] + 1, barriersVert[rectDataOffset + 3] + 1);
rectSpec._downLinkRect = Common::Rect(linksVert[rectDataOffset + 0], linksVert[rectDataOffset + 1], linksVert[rectDataOffset + 2], linksVert[rectDataOffset + 3]);
}
}
}
bool CircuitPuzzle::executeAIAction(Common::RandomSource &randomSource, Common::Point &outCoord, CellDirection &outBlockDirection) {
// Don't know exactly what algorithm Schizm uses, we use something that approximates the original
// pretty well most of the time:
// - Identify all connection paths that are tied for the fewest number of new connections required to win.
// - Enumerate all open connections on those paths.
// - If the previous move blocked a horizontal connection (i.e. with a vertical barrier), prioritize
// connections on the same X coordinate as that block.
// - Block a random connection from the candidates.
//
// There seem to be times that Schizm doesn't do this. In particular, Schizm will (rarely) fail to block
// a connection even if it's the only connection that will immediately win the puzzle for the player.
//
// It also doesn't prioritize making moves that will immediately win for the AI.
CircuitPuzzleAIEvaluator evaluator;
computeStepsToReach(evaluator);
uint stepsToReachGoal = evaluator.stepsToReach[_goalPoint.x][_goalPoint.y];
if (stepsToReachGoal == 0 || stepsToReachGoal == CircuitPuzzleAIEvaluator::kMaxMovesToReach)
return false;
const uint kMaxLinks = kBoardWidth * kBoardHeight * 2;
Action potentialBlocks[kMaxLinks];
uint numPotentialBlocks = 0;
Common::Point pointsList1[kMaxLinks];
Common::Point pointsList2[kMaxLinks];
Common::Point *pointsToFloodFill = pointsList1;
Common::Point *pointsToProspect = pointsList2;
uint numPointsToFloodFill = 1;
uint numPointsToProspect = 0;
pointsToFloodFill[0] = _goalPoint;
CircuitPuzzleVisitedSet visitedSet;
uint prospectLevel = stepsToReachGoal;
while (prospectLevel > 0) {
floodFillLinks(pointsToFloodFill, numPointsToFloodFill, visitedSet);
for (uint i = 0; i < numPointsToFloodFill; i++) {
const Common::Point &pt = pointsToFloodFill[i];
for (uint dir = 0; dir < kDirectionCount; dir++) {
const LinkState *linkState = getConnectionState(pt, static_cast<Direction>(dir));
if (linkState && (*linkState) == kLinkStateOpen) {
Common::Point connectedPoint = getConnectedPoint(pt, static_cast<Direction>(dir));
if (!visitedSet.get(connectedPoint)) {
visitedSet.set(connectedPoint);
if (evaluator.stepsToReach[connectedPoint.x][connectedPoint.y] + 1u == prospectLevel) {
// This point is on the shortest path
Action action;
switch (dir) {
case kDirectionUp:
action._point = connectedPoint;
action._direction = kCellDirectionDown;
break;
case KDirectionDown:
action._point = pt;
action._direction = kCellDirectionDown;
break;
case kDirectionLeft:
action._point = connectedPoint;
action._direction = kCellDirectionRight;
break;
case kDirectionRight:
action._point = pt;
action._direction = kCellDirectionRight;
break;
default:
error("Internal error: Bad direction");
return false;
}
potentialBlocks[numPotentialBlocks] = action;
numPotentialBlocks++;
pointsToProspect[numPointsToProspect] = connectedPoint;
numPointsToProspect++;
}
}
}
}
}
Common::Point *tempList = pointsToFloodFill;
pointsToFloodFill = pointsToProspect;
pointsToProspect = tempList;
numPointsToFloodFill = numPointsToProspect;
numPointsToProspect = 0;
prospectLevel--;
}
if (numPotentialBlocks == 0)
return false;
// All potential blocks are now on the shortest path.
// Try to mimic some of the AI behavior of Schizm to form wall advances.
// The highest-priority move is one that runs parallel to the previous move.
uint selectedBlock = 0;
if (numPotentialBlocks > 1) {
uint blockQualities[kMaxLinks];
for (uint i = 0; i < numPotentialBlocks; i++)
blockQualities[i] = 0;
uint highestQuality = 0;
if (_havePreviousAction) {
for (uint i = 0; i < numPotentialBlocks; i++) {
uint quality = 0;
const Action &pblock = potentialBlocks[i];
// We don't want to favor horizontal walls because otherwise that triggers are degenerate behavior where the player can run a wall
// directly across and the AI will keeps inserting horizontal walls parallel to the player action.
bool isWallBlock = false;
if (_previousAction._direction == kCellDirectionRight && pblock._direction == kCellDirectionRight && _previousAction._point.x == pblock._point.x)
isWallBlock = true;
#if 0
else if (_previousAction._direction == kCellDirectionDown && pblock._direction == kCellDirectionDown && _previousAction._point.y == pblock._point.y)
isWallBlock = true;
#endif
// If this forms a vertical wall, it's quality 2
if (isWallBlock)
quality = 2;
else {
// If this forms a corner, it's quality 1 (disabled, this seems less accurate)
#if 0
if (_previousAction._direction != pblock._direction) {
Common::Point prevAdjacent = _previousAction._point;
if (_previousAction._direction == kCellDirectionRight)
prevAdjacent.x++;
else if (_previousAction._direction == kCellDirectionDown)
prevAdjacent.y++;
Common::Point pblockAdjacent = pblock._point;
if (pblock._direction == kCellDirectionRight)
pblockAdjacent.x++;
else if (pblock._direction == kCellDirectionDown)
pblockAdjacent.y++;
if (prevAdjacent == pblock._point || prevAdjacent == pblockAdjacent || _previousAction._point == pblock._point || _previousAction._point == pblockAdjacent)
quality = 1;
}
#endif
}
blockQualities[i] = quality;
if (quality > highestQuality)
highestQuality = quality;
}
}
uint blocksInHighestQuality[kMaxLinks];
uint numBlocksInHighestQuality = 0;
for (uint i = 0; i < numPotentialBlocks; i++) {
if (blockQualities[i] == highestQuality) {
blocksInHighestQuality[numBlocksInHighestQuality] = i;
numBlocksInHighestQuality++;
}
}
if (numBlocksInHighestQuality == 1)
selectedBlock = blocksInHighestQuality[0];
else {
assert(numBlocksInHighestQuality > 1);
selectedBlock = blocksInHighestQuality[randomSource.getRandomNumber(numBlocksInHighestQuality - 1)];
}
}
const Action &pblock = potentialBlocks[selectedBlock];
outCoord = pblock._point;
outBlockDirection = pblock._direction;
if (pblock._direction == kCellDirectionDown)
_cells[pblock._point.x][pblock._point.y]._downLink = kLinkStateBlocked;
if (pblock._direction == kCellDirectionRight)
_cells[pblock._point.x][pblock._point.y]._rightLink = kLinkStateBlocked;
_havePreviousAction = true;
_previousAction = pblock;
return true;
}
void CircuitPuzzle::addLink(const Common::Point &coord, CellDirection direction) {
validateCoord(coord);
CellState &cell = _cells[coord.x][coord.y];
LinkState *linkState = nullptr;
if (direction == kCellDirectionDown)
linkState = &cell._downLink;
else if (direction == kCellDirectionRight)
linkState = &cell._rightLink;
if (linkState == nullptr || (*linkState) != kLinkStateOpen)
error("Internal error: Circuit link state was invalid");
*linkState = kLinkStateConnected;
}
CircuitPuzzle::Conclusion CircuitPuzzle::checkConclusion() const {
CircuitPuzzleAIEvaluator evaluator;
computeStepsToReach(evaluator);
uint stepsToReachGoal = evaluator.stepsToReach[_goalPoint.x][_goalPoint.y];
if (stepsToReachGoal == 0)
return kConclusionPlayerWon;
if (stepsToReachGoal == CircuitPuzzleAIEvaluator::kMaxMovesToReach)
return kConclusionPlayerLost;
return kConclusionNone;
}
const CircuitPuzzle::CellRectSpec *CircuitPuzzle::getCellRectSpec(const Common::Point &coord) const {
validateCoord(coord);
return &_cellRectSpecs[coord.x][coord.y];
}
bool CircuitPuzzle::isCellDownLinkOpen(const Common::Point &coord) const {
validateCoord(coord);
return _cells[coord.x][coord.y]._downLink == kLinkStateOpen;
}
bool CircuitPuzzle::isCellRightLinkOpen(const Common::Point &coord) const {
validateCoord(coord);
return _cells[coord.x][coord.y]._rightLink == kLinkStateOpen;
}
CircuitPuzzle::CellState::CellState() : _downLink(kLinkStateOpen), _rightLink(kLinkStateOpen) {
}
Common::Point CircuitPuzzle::getConnectedPoint(const Common::Point &coord, Direction direction) {
switch (direction) {
case kDirectionUp:
return Common::Point(coord.x, coord.y - 1);
case KDirectionDown:
return Common::Point(coord.x, coord.y + 1);
case kDirectionLeft:
return Common::Point(coord.x - 1, coord.y);
case kDirectionRight:
return Common::Point(coord.x + 1, coord.y);
default:
return coord;
};
}
CircuitPuzzle::LinkState *CircuitPuzzle::getConnectionState(const Common::Point &coord, Direction direction) {
if (!isPositionValid(coord))
return nullptr;
switch (direction) {
case kDirectionUp:
if (coord.y == 0)
return nullptr;
return &_cells[coord.x][coord.y - 1]._downLink;
case KDirectionDown:
if (coord.y == static_cast<int>(kBoardHeight - 1))
return nullptr;
return &_cells[coord.x][coord.y]._downLink;
case kDirectionLeft:
if (coord.x <= 0)
return nullptr;
return &_cells[coord.x - 1][coord.y]._rightLink;
case kDirectionRight:
if (coord.x == static_cast<int>(kBoardWidth - 1))
return nullptr;
return &_cells[coord.x][coord.y]._rightLink;
default:
return nullptr;
};
}
const CircuitPuzzle::LinkState *CircuitPuzzle::getConnectionState(const Common::Point &coord, Direction direction) const {
return const_cast<CircuitPuzzle *>(this)->getConnectionState(coord, direction);
}
bool CircuitPuzzle::isPositionValid(const Common::Point &coord) {
if (coord.x < 0 || coord.y < 0 || coord.x >= static_cast<int>(kBoardWidth) || coord.y >= static_cast<int>(kBoardHeight))
return false;
return true;
}
void CircuitPuzzle::computeStepsToReach(CircuitPuzzleAIEvaluator &evaluator) const {
const uint kMaxLinks = kBoardWidth * kBoardHeight * 2;
Common::Point pointsList1[kMaxLinks];
Common::Point pointsList2[kMaxLinks];
Common::Point *pointsToFloodFill = pointsList1;
Common::Point *pointsToProspect = pointsList2;
uint numPointsToFloodFill = 1;
uint numPointsToProspect = 0;
uint floodFillValue = 0;
pointsToFloodFill[0] = _startPoint;
for (uint x = 0; x < kBoardWidth; x++)
for (uint y = 0; y < kBoardHeight; y++)
evaluator.stepsToReach[x][y] = CircuitPuzzleAIEvaluator::kMaxMovesToReach;
CircuitPuzzleVisitedSet visitedSet;
while (numPointsToFloodFill > 0) {
floodFillLinks(pointsToFloodFill, numPointsToFloodFill, visitedSet);
for (uint i = 0; i < numPointsToFloodFill; i++) {
const Common::Point &pt = pointsToFloodFill[i];
evaluator.stepsToReach[pt.x][pt.y] = floodFillValue;
for (uint dir = 0; dir < kDirectionCount; dir++) {
const LinkState *linkState = getConnectionState(pt, static_cast<Direction>(dir));
if (linkState && (*linkState) == kLinkStateOpen) {
Common::Point connectedPoint = getConnectedPoint(pt, static_cast<Direction>(dir));
if (!visitedSet.get(connectedPoint)) {
visitedSet.set(connectedPoint);
pointsToProspect[numPointsToProspect] = connectedPoint;
numPointsToProspect++;
}
}
}
}
Common::Point *tempList = pointsToFloodFill;
pointsToFloodFill = pointsToProspect;
pointsToProspect = tempList;
numPointsToFloodFill = numPointsToProspect;
numPointsToProspect = 0;
floodFillValue++;
}
}
void CircuitPuzzle::floodFillLinks(Common::Point *pointsList, uint &listSize, CircuitPuzzleVisitedSet &visitedSet) const {
for (uint i = 0; i < listSize; i++) {
const Common::Point &pt = pointsList[i];
visitedSet.set(pt);
for (uint dir = 0; dir < kDirectionCount; dir++) {
const LinkState *linkState = getConnectionState(pt, static_cast<Direction>(dir));
if (linkState && (*linkState) == kLinkStateConnected) {
Common::Point connectedPoint = getConnectedPoint(pt, static_cast<Direction>(dir));
if (!visitedSet.get(connectedPoint)) {
pointsList[listSize] = connectedPoint;
listSize++;
}
}
}
}
}
void CircuitPuzzle::validateCoord(const Common::Point &coord) {
assert(coord.x >= 0 && coord.y >= 0 && coord.x < static_cast<int>(kBoardWidth) && coord.y < static_cast<int>(kBoardHeight));
}
} // End of namespace VCruise

View File

@@ -0,0 +1,127 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 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 VCRUISE_CIRCUITPUZZLE_H
#define VCRUISE_CIRCUITPUZZLE_H
#include "common/array.h"
#include "common/random.h"
#include "common/rect.h"
namespace Common {
class RandomSource;
} // End of namespace Common
namespace VCruise {
struct CircuitPuzzleAIEvaluator;
class CircuitPuzzleVisitedSet;
class CircuitPuzzle {
public:
explicit CircuitPuzzle(int layout);
static const uint kBoardWidth = 6;
static const uint kBoardHeight = 5;
enum CellDirection {
kCellDirectionRight,
kCellDirectionDown,
};
enum Conclusion {
kConclusionNone,
kConclusionPlayerWon,
kConclusionPlayerLost,
};
struct CellRectSpec {
Common::Rect _rightLinkRect;
Common::Rect _downLinkRect;
Common::Rect _rightBarrierRect;
Common::Rect _downBarrierRect;
};
// Returns true if the AI can act, if it can then the actions are produced
bool executeAIAction(Common::RandomSource &randomSource, Common::Point &outCoord, CellDirection &outBlockDirection);
void addLink(const Common::Point &coord, CellDirection direction);
Conclusion checkConclusion() const;
const CellRectSpec *getCellRectSpec(const Common::Point &coord) const;
bool isCellDownLinkOpen(const Common::Point &coord) const;
bool isCellRightLinkOpen(const Common::Point &coord) const;
private:
enum LinkState {
kLinkStateOpen,
kLinkStateConnected,
kLinkStateBlocked,
};
enum Direction {
kDirectionUp,
KDirectionDown,
kDirectionLeft,
kDirectionRight,
kDirectionCount,
};
struct CellState {
CellState();
LinkState _downLink;
LinkState _rightLink;
};
struct Action {
Action();
Common::Point _point;
CellDirection _direction;
};
static Common::Point getConnectedPoint(const Common::Point &coord, Direction direction);
LinkState *getConnectionState(const Common::Point &coord, Direction direction);
const LinkState *getConnectionState(const Common::Point &coord, Direction direction) const;
static bool isPositionValid(const Common::Point &coord);
void computeStepsToReach(CircuitPuzzleAIEvaluator &evaluator) const;
void floodFillLinks(Common::Point *pointsList, uint &listSize, CircuitPuzzleVisitedSet &visitedSet) const;
static void validateCoord(const Common::Point &coord);
CellState _cells[kBoardWidth][kBoardHeight];
CellRectSpec _cellRectSpecs[kBoardWidth][kBoardHeight];
Common::Point _startPoint;
Common::Point _goalPoint;
bool _havePreviousAction;
Action _previousAction;
};
} // End of namespace VCruise
#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 vcruise "V-Cruise" yes "" "" "16bit highres" "midi"

View File

@@ -0,0 +1,3 @@
begin_section("V-Cruise");
add_person("Eric Lasota", "OneEightHundred", "");
end_section();

View File

@@ -0,0 +1,123 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "base/plugins.h"
#include "common/config-manager.h"
#include "common/language.h"
#include "engines/advancedDetector.h"
#include "vcruise/detection.h"
static const PlainGameDescriptor g_vcruiseGames[] = {
{"ad2044", "A.D. 2044"},
{"reah", "Reah: Face the Unknown"},
{"schizm", "Schizm: Mysterious Journey"},
{nullptr, nullptr}
};
static const char *const g_vcruiseDirectoryGlobs[] = {
"Sfx",
"Log",
"Waves-12",
"Waves-22",
"WAVE-01",
nullptr
};
#include "vcruise/detection_tables.h"
class VCruiseMetaEngineDetection : public AdvancedMetaEngineDetection<VCruise::VCruiseGameDescription> {
public:
VCruiseMetaEngineDetection() : AdvancedMetaEngineDetection(VCruise::gameDescriptions, g_vcruiseGames) {
_guiOptions = GUIO5(GAMEOPTION_FAST_ANIMATIONS, GAMEOPTION_INCREASE_DRAG_DISTANCE, GAMEOPTION_LAUNCH_DEBUG, GAMEOPTION_SKIP_MENU, GAMEOPTION_PRELOAD_SOUNDS);
_maxScanDepth = 3;
_directoryGlobs = g_vcruiseDirectoryGlobs;
_flags = kADFlagCanPlayUnknownVariants;
}
const char *getName() const override {
return "vcruise";
}
const char *getEngineName() const override {
return "V-Cruise";
}
const char *getOriginalCopyright() const override {
return "V-Cruise (C) LK Avalon";
}
DetectedGame toDetectedGame(const ADDetectedGame &adGame, ADDetectedGameExtraInfo *extraInfo) const override {
DetectedGame game = AdvancedMetaEngineDetection::toDetectedGame(adGame, extraInfo);
VCruise::VCruiseGameID gameID = reinterpret_cast<const VCruise::VCruiseGameDescription *>(adGame.desc)->gameID;
if ((adGame.desc->flags & VCruise::VCRUISE_GF_FORCE_LANGUAGE) == 0) {
if (gameID == VCruise::GID_AD2044) {
game.appendGUIOptions(Common::getGameGUIOptionsDescriptionLanguage(Common::EN_ANY));
game.appendGUIOptions(Common::getGameGUIOptionsDescriptionLanguage(Common::PL_POL));
} else if (gameID == VCruise::GID_REAH) {
game.appendGUIOptions(Common::getGameGUIOptionsDescriptionLanguage(Common::EN_ANY));
game.appendGUIOptions(Common::getGameGUIOptionsDescriptionLanguage(Common::NL_NLD));
game.appendGUIOptions(Common::getGameGUIOptionsDescriptionLanguage(Common::FR_FRA));
game.appendGUIOptions(Common::getGameGUIOptionsDescriptionLanguage(Common::IT_ITA));
game.appendGUIOptions(Common::getGameGUIOptionsDescriptionLanguage(Common::DE_DEU));
game.appendGUIOptions(Common::getGameGUIOptionsDescriptionLanguage(Common::PL_POL));
game.appendGUIOptions(Common::getGameGUIOptionsDescriptionLanguage(Common::ES_ESP));
} else if (gameID == VCruise::GID_SCHIZM) {
game.appendGUIOptions(Common::getGameGUIOptionsDescriptionLanguage(Common::EN_USA));
game.appendGUIOptions(Common::getGameGUIOptionsDescriptionLanguage(Common::EN_GRB));
game.appendGUIOptions(Common::getGameGUIOptionsDescriptionLanguage(Common::NL_NLD));
game.appendGUIOptions(Common::getGameGUIOptionsDescriptionLanguage(Common::FR_FRA));
game.appendGUIOptions(Common::getGameGUIOptionsDescriptionLanguage(Common::IT_ITA));
game.appendGUIOptions(Common::getGameGUIOptionsDescriptionLanguage(Common::DE_DEU));
game.appendGUIOptions(Common::getGameGUIOptionsDescriptionLanguage(Common::PL_POL));
game.appendGUIOptions(Common::getGameGUIOptionsDescriptionLanguage(Common::ES_ESP));
game.appendGUIOptions(Common::getGameGUIOptionsDescriptionLanguage(Common::EL_GRC));
game.appendGUIOptions(Common::getGameGUIOptionsDescriptionLanguage(Common::RU_RUS));
if (adGame.desc->flags & VCruise::VCRUISE_GF_BUL_LANGUAGE)
game.appendGUIOptions(Common::getGameGUIOptionsDescriptionLanguage(Common::BG_BUL));
if (adGame.desc->flags & VCruise::VCRUISE_GF_TWN_LANGUAGE)
game.appendGUIOptions(Common::getGameGUIOptionsDescriptionLanguage(Common::ZH_TWN));
if (adGame.desc->flags & VCruise::VCRUISE_GF_JPN_LANGUAGE)
game.appendGUIOptions(Common::getGameGUIOptionsDescriptionLanguage(Common::JA_JPN));
if (adGame.desc->flags & VCruise::VCRUISE_GF_HUN_LANGUAGE)
game.appendGUIOptions(Common::getGameGUIOptionsDescriptionLanguage(Common::HU_HUN));
if (adGame.desc->flags & VCruise::VCRUISE_GF_CHN_LANGUAGE)
game.appendGUIOptions(Common::getGameGUIOptionsDescriptionLanguage(Common::ZH_CHN));
if (adGame.desc->flags & VCruise::VCRUISE_GF_CZE_LANGUAGE)
game.appendGUIOptions(Common::getGameGUIOptionsDescriptionLanguage(Common::CS_CZE));
}
}
return game;
}
};
REGISTER_PLUGIN_STATIC(VCRUISE_DETECTION, PLUGIN_TYPE_ENGINE_DETECTION, VCruiseMetaEngineDetection);

View File

@@ -0,0 +1,83 @@
/* 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 VCRUISE_DETECTION_H
#define VCRUISE_DETECTION_H
#include "engines/advancedDetector.h"
namespace VCruise {
enum VCruiseGameID {
GID_UNKNOWN = 0,
GID_REAH = 1,
GID_SCHIZM = 2,
GID_AD2044 = 3,
};
enum VCruiseGameFlag {
VCRUISE_GF_WANT_MP3 = (1 << 0),
VCRUISE_GF_WANT_OGG_VORBIS = (1 << 1),
VCRUISE_GF_NEED_JPEG = (1 << 2),
VCRUISE_GF_GENTEE_PACKAGE = (1 << 3),
VCRUISE_GF_USE_SETUP_EXE = (1 << 4),
VCRUISE_GF_BUL_LANGUAGE = (1 << 6),
VCRUISE_GF_TWN_LANGUAGE = (1 << 7),
VCRUISE_GF_JPN_LANGUAGE = (1 << 8),
VCRUISE_GF_HUN_LANGUAGE = (1 << 9),
VCRUISE_GF_CHN_LANGUAGE = (1 << 10),
VCRUISE_GF_CZE_LANGUAGE = (1 << 11),
VCRUISE_GF_FORCE_LANGUAGE = (1 << 12),
VCRUISE_GF_WANT_MIDI = (1 << 13),
VCRUISE_GF_STEAM_LANGUAGES = VCRUISE_GF_BUL_LANGUAGE | VCRUISE_GF_TWN_LANGUAGE | VCRUISE_GF_JPN_LANGUAGE
| VCRUISE_GF_HUN_LANGUAGE | VCRUISE_GF_CHN_LANGUAGE | VCRUISE_GF_CZE_LANGUAGE,
};
struct VCruiseGameDescription {
AD_GAME_DESCRIPTION_HELPERS(desc);
ADGameDescription desc;
VCruiseGameID gameID;
// Specifying the language in the ADGameDescription causes AD to fail to detect the game as a match,
// so we have to specify it as UNK_LANG and specify the default language here.
Common::Language defaultLanguage;
};
#define GAMEOPTION_LAUNCH_DEBUG GUIO_GAMEOPTIONS1
#define GAMEOPTION_FAST_ANIMATIONS GUIO_GAMEOPTIONS2
#define GAMEOPTION_SKIP_MENU GUIO_GAMEOPTIONS3
#define GAMEOPTION_INCREASE_DRAG_DISTANCE GUIO_GAMEOPTIONS4
#define GAMEOPTION_USE_4BIT_GRAPHICS GUIO_GAMEOPTIONS5
#define GAMEOPTION_PRELOAD_SOUNDS GUIO_GAMEOPTIONS6
#define GAMEOPTION_FAST_VIDEO_DECODER GUIO_GAMEOPTIONS7
} // End of namespace VCruise
#endif // VCRUISE_DETECTION_H

View File

@@ -0,0 +1,539 @@
/* 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 VCRUISE_DETECTION_TABLES_H
#define VCRUISE_DETECTION_TABLES_H
#include "engines/advancedDetector.h"
#include "vcruise/detection.h"
namespace VCruise {
static const VCruiseGameDescription gameDescriptions[] = {
{ // A.D. 2044, GOG English digital version
{
"ad2044",
"English Digital",
AD_ENTRY2s("ad2044.exe", "0ab1e3f8b3a17a5b18bb5ee356face25", 327168,
"00010001.wav", "d385bb2f1b10ea8c13bbb2948794c9f6", 74950),
Common::UNK_LANG,
Common::kPlatformWindows,
VCRUISE_GF_WANT_MP3 | ADGF_UNSTABLE,
GUIO0()
},
GID_AD2044,
Common::EN_ANY,
},
{ // Reah: Face the Unknown, English DVD version
{
"reah",
"English DVD",
AD_ENTRY2s("Reah.exe", "69aa9832338db3f40f616386e54857e8", 304128,
"0170_b.wav", "5b705300b4fee3ceb821a1b55884a722", 129248),
Common::UNK_LANG,
Common::kPlatformWindows,
VCRUISE_GF_WANT_MP3,
GUIO0()
},
GID_REAH,
Common::EN_ANY,
},
{ // Reah: Face the Unknown, English digital (GOG) version
{
"reah",
"English Digital",
AD_ENTRY2s("Reah.exe", "60ec19c53f1323cc7f0314f98d396283", 304128,
"0170_b.wav", "5b705300b4fee3ceb821a1b55884a722", 129248),
Common::UNK_LANG,
Common::kPlatformWindows,
VCRUISE_GF_WANT_MP3,
GUIO0()
},
GID_REAH,
Common::EN_ANY,
},
{ // Reah: Face the Unknown, English 6 CD Version
{
"reah",
"English CD",
AD_ENTRY2s("Reah.exe", "77bc7f7819cdd443f52b193529138c87", 305664,
"0170_b.wav", "5b705300b4fee3ceb821a1b55884a722", 129248),
Common::UNK_LANG,
Common::kPlatformWindows,
ADGF_NO_FLAGS,
GUIO0()
},
GID_REAH,
Common::EN_ANY,
},
{ // Reah: Face the Unknown, English 6 CD Version (Project Two Interactive variation)
{
"reah",
"English CD",
AD_ENTRY2s("Reah.exe", "77bc7f7819cdd443f52b193529138c87", 305664,
"0170_b.wav", "36c0bf57ab5a748ef6699a159195b3ae", 124356),
Common::UNK_LANG,
Common::kPlatformWindows,
ADGF_NO_FLAGS,
GUIO0()
},
GID_REAH,
Common::EN_ANY,
},
{ // Reah: Face the Unknown, German 6 CD Version
{
"reah",
"German CD",
AD_ENTRY2s("Reah.exe", "be29f9f9fc9a454488f9d2fb68e26326", 305664,
"0170_b.wav", "5f84af02d1193ce9c47fe0dc821f0613", 121220),
Common::UNK_LANG,
Common::kPlatformWindows,
ADGF_NO_FLAGS,
GUIO0()
},
GID_REAH,
Common::DE_DEU,
},
{ // Reah: Face the Unknown, Russian 6 CD Version
{
"reah",
"Russian CD",
AD_ENTRY3s("Reah.exe", "c44224a888035c14e876cbc45519faca", 305664,
"0170_b.wav", "4632023ed0bab3fc800abfa5ef65ceaf", 119850,
"Speech01.txt", "734478c94944eab9c954c612c70efb9a", 72694),
Common::RU_RUS,
Common::kPlatformWindows,
VCRUISE_GF_FORCE_LANGUAGE,
GUIO0()
},
GID_REAH,
Common::RU_RUS,
},
{ // Reah: Face the Unknown, English digital (GOG) version + German VO community patch
{
"reah",
"English Digital + German Community Patch",
AD_ENTRY2s("Reah.exe", "60ec19c53f1323cc7f0314f98d396283", 304128,
"0170_b.wav", "5f84af02d1193ce9c47fe0dc821f0613", 121220),
Common::UNK_LANG,
Common::kPlatformWindows,
VCRUISE_GF_WANT_MP3,
GUIO0()
},
GID_REAH,
Common::EN_ANY,
},
{ // Reah: Face the Unknown, English digital (GOG) version + Russian VO/subtitles community patch
{
"reah",
"English Digital + Russian Community Patch",
AD_ENTRY3s("Reah.exe", "60ec19c53f1323cc7f0314f98d396283", 304128,
"0170_b.wav", "4632023ed0bab3fc800abfa5ef65ceaf", 119850,
"Speech01.txt", "a4eaace1299de1d70805532fc0643a77", 72689),
Common::RU_RUS,
Common::kPlatformWindows,
VCRUISE_GF_WANT_MP3 | VCRUISE_GF_FORCE_LANGUAGE,
GUIO0()
},
GID_REAH,
Common::RU_RUS,
},
{ // Reah: Face the Unknown, Polish demo
{
"reah",
"Polish Demo",
AD_ENTRY1s("Reah.exe", "4667d7e3d886f01ec28040a9022b1b56", 281600),
Common::UNK_LANG,
Common::kPlatformWindows,
ADGF_NO_FLAGS,
GUIO0()
},
GID_REAH,
Common::PL_POL,
},
{ // Schizm: Mysterious Journey, English CD Version
{
"schizm",
"English CD",
AD_ENTRY2s("Schizm.exe", "24bb1831a53b3969d9d1a9302740de4a", 368640,
"0001_a.wav", "374d93abc3422840623acc618ecb2b1e", 1553784),
Common::UNK_LANG,
Common::kPlatformWindows,
ADGF_CD | VCRUISE_GF_WANT_OGG_VORBIS | VCRUISE_GF_NEED_JPEG,
GUIO1(GAMEOPTION_FAST_VIDEO_DECODER)
},
GID_SCHIZM,
Common::EN_GRB,
},
{ // Schizm: Mysterious Journey, German DVD Version (installed)
{
"schizm",
"German DVD",
AD_ENTRY2s("Schizm.exe", "3d63307697c72f3fd6cafb378f61ca2b", 364544,
"0001_a.wav", "f2b7eccfb1e9af0282b541c5eac66cc7", 1613240),
Common::UNK_LANG,
Common::kPlatformWindows,
VCRUISE_GF_WANT_OGG_VORBIS | VCRUISE_GF_NEED_JPEG,
GUIO1(GAMEOPTION_FAST_VIDEO_DECODER)
},
GID_SCHIZM,
Common::DE_DEU,
},
{ // Schizm: Mysterious Journey, English DVD Version (NL release)
{
"schizm",
"English DVD",
AD_ENTRY1s("disk1.pak", "41bd7514a7d783c555f3783c9417bf9e", 272405273),
Common::UNK_LANG,
Common::kPlatformWindows,
VCRUISE_GF_WANT_OGG_VORBIS | VCRUISE_GF_NEED_JPEG | VCRUISE_GF_GENTEE_PACKAGE,
GUIO1(GAMEOPTION_FAST_VIDEO_DECODER)
},
GID_SCHIZM,
Common::EN_GRB,
},
{ // Schizm: Mysterious Journey, English DVD Version, 11-language
{
"schizm",
"English DVD",
AD_ENTRY2s("setup.pak", "eaaed2f6655342b4c320bdeb6f5ccfb9", 272655597,
"setup.exe", "62f2ed1b1a6a4ed3e3298c7d6852a495", 63234),
Common::UNK_LANG,
Common::kPlatformWindows,
VCRUISE_GF_WANT_OGG_VORBIS | VCRUISE_GF_NEED_JPEG | VCRUISE_GF_GENTEE_PACKAGE | VCRUISE_GF_BUL_LANGUAGE,
GUIO1(GAMEOPTION_FAST_VIDEO_DECODER)
},
GID_SCHIZM,
Common::EN_GRB,
},
{ // Schizm: Mysterious Journey, Japanese DVD Version, 12-language
{
"schizm",
"Japanese DVD",
AD_ENTRY2s("setup.pak", "eaaed2f6655342b4c320bdeb6f5ccfb9", 272655597,
"setup.exe", "62f2ed1b1a6a4ed3e3298c7d6852a495", 66646),
Common::UNK_LANG,
Common::kPlatformWindows,
VCRUISE_GF_WANT_OGG_VORBIS | VCRUISE_GF_NEED_JPEG | VCRUISE_GF_GENTEE_PACKAGE | VCRUISE_GF_USE_SETUP_EXE
| VCRUISE_GF_BUL_LANGUAGE | VCRUISE_GF_JPN_LANGUAGE,
GUIO1(GAMEOPTION_FAST_VIDEO_DECODER)
},
GID_SCHIZM,
Common::JA_JPN,
},
{ // Schizm: Mysterious Journey, English DVD Version, unknown variant
{
"schizm",
"English DVD",
AD_ENTRY1s("setup.pak", "964e386b187752d53b69f9c55c4f6e6b", 274948185),
Common::UNK_LANG,
Common::kPlatformWindows,
VCRUISE_GF_WANT_OGG_VORBIS | VCRUISE_GF_NEED_JPEG | VCRUISE_GF_GENTEE_PACKAGE,
GUIO1(GAMEOPTION_FAST_VIDEO_DECODER)
},
GID_SCHIZM,
Common::EN_GRB,
},
{ // Schizm: Mysterious Journey, Polish DVD Version
{
"schizm",
"Polish DVD",
AD_ENTRY1s("disk1.pak", "a3453878ad86d012b483a82e04276667", 272507257),
Common::UNK_LANG,
Common::kPlatformWindows,
VCRUISE_GF_WANT_OGG_VORBIS | VCRUISE_GF_NEED_JPEG | VCRUISE_GF_GENTEE_PACKAGE,
GUIO1(GAMEOPTION_FAST_VIDEO_DECODER)
},
GID_SCHIZM,
Common::PL_POL,
},
{ // Schizm: Mysterious Journey, German DVD Version (Project 3 Interactive variation)
{
"schizm",
"German DVD",
AD_ENTRY1s("disk1.pak", "dcb27eb3d8a0029c551df5f779af36fc", 274285596),
Common::UNK_LANG,
Common::kPlatformWindows,
VCRUISE_GF_WANT_OGG_VORBIS | VCRUISE_GF_NEED_JPEG | VCRUISE_GF_GENTEE_PACKAGE,
GUIO1(GAMEOPTION_FAST_VIDEO_DECODER)
},
GID_SCHIZM,
Common::DE_DEU,
},
{ // Schizm: Mysterious Journey, German DVD Version (Project 3 Interactive + Brightstar Interactive variation)
{
"schizm",
"German DVD",
AD_ENTRY1s("disk1.pak", "2f964852baf18f90e884d6873b1bad23", 272798001),
Common::UNK_LANG,
Common::kPlatformWindows,
VCRUISE_GF_WANT_OGG_VORBIS | VCRUISE_GF_NEED_JPEG | VCRUISE_GF_GENTEE_PACKAGE,
GUIO1(GAMEOPTION_FAST_VIDEO_DECODER)
},
GID_SCHIZM,
Common::DE_DEU,
},
{ // Schizm: Mysterious Journey, English digital 10-language (GOG) version
{
"schizm",
"English Digital",
AD_ENTRY2s("Schizm.exe", "296edd26d951c3bdc4d303c4c88b27cd", 364544,
"0001_a.wav", "374d93abc3422840623acc618ecb2b1e", 1553784),
Common::UNK_LANG,
Common::kPlatformWindows,
VCRUISE_GF_WANT_OGG_VORBIS | VCRUISE_GF_NEED_JPEG,
GUIO1(GAMEOPTION_FAST_VIDEO_DECODER)
},
GID_SCHIZM,
Common::EN_GRB,
},
{ // Schizm: Mysterious Journey, German digital 10-language (GOG) version
{
"schizm",
"German Digital",
AD_ENTRY2s("Schizm.exe", "296edd26d951c3bdc4d303c4c88b27cd", 364544,
"0001_a.wav", "f2b7eccfb1e9af0282b541c5eac66cc7", 1613240),
Common::UNK_LANG,
Common::kPlatformWindows,
VCRUISE_GF_WANT_OGG_VORBIS | VCRUISE_GF_NEED_JPEG,
GUIO1(GAMEOPTION_FAST_VIDEO_DECODER)
},
GID_SCHIZM,
Common::DE_DEU,
},
{ // Schizm: Mysterious Journey, German digital 10-language (GOG) version
{
"schizm",
"Spanish Digital",
AD_ENTRY2s("Schizm.exe", "296edd26d951c3bdc4d303c4c88b27cd", 364544,
"0001_a.wav", "7a398f17e847a46de629a09fa6178b00", 1409560),
Common::UNK_LANG,
Common::kPlatformWindows,
VCRUISE_GF_WANT_OGG_VORBIS | VCRUISE_GF_NEED_JPEG,
GUIO1(GAMEOPTION_FAST_VIDEO_DECODER)
},
GID_SCHIZM,
Common::ES_ESP,
},
{ // Schizm: Mysterious Journey, French digital 10-language (GOG) version
{
"schizm",
"French Digital",
AD_ENTRY2s("Schizm.exe", "296edd26d951c3bdc4d303c4c88b27cd", 364544,
"0001_a.wav", "cd46a5df85a879bf293871b5911abcc3", 1305470),
Common::UNK_LANG,
Common::kPlatformWindows,
VCRUISE_GF_WANT_OGG_VORBIS | VCRUISE_GF_NEED_JPEG,
GUIO1(GAMEOPTION_FAST_VIDEO_DECODER)
},
GID_SCHIZM,
Common::FR_FRA,
},
{ // Schizm: Mysterious Journey, Hungarian digital 10-language (GOG) version
{
"schizm",
"Hungarian Digital",
AD_ENTRY2s("Schizm.exe", "296edd26d951c3bdc4d303c4c88b27cd", 364544,
"0001_a.wav", "9838f59fce21ba3a1eecc3d84c5be8b1", 1502674),
Common::UNK_LANG,
Common::kPlatformWindows,
VCRUISE_GF_WANT_OGG_VORBIS | VCRUISE_GF_NEED_JPEG,
GUIO1(GAMEOPTION_FAST_VIDEO_DECODER)
},
GID_SCHIZM,
Common::HU_HUN,
},
{ // Schizm: Mysterious Journey, Italian digital 10-language (GOG) version
{
"schizm",
"Italian Digital",
AD_ENTRY2s("Schizm.exe", "296edd26d951c3bdc4d303c4c88b27cd", 364544,
"0001_a.wav", "6d8e4b97710b858525d9965ef1e627ab", 1280506),
Common::UNK_LANG,
Common::kPlatformWindows,
VCRUISE_GF_WANT_OGG_VORBIS | VCRUISE_GF_NEED_JPEG,
GUIO1(GAMEOPTION_FAST_VIDEO_DECODER)
},
GID_SCHIZM,
Common::IT_ITA,
},
{ // Schizm: Mysterious Journey, Polish digital 10-language (GOG) version
{
"schizm",
"Polish Digital",
AD_ENTRY2s("Schizm.exe", "296edd26d951c3bdc4d303c4c88b27cd", 364544,
"0001_a.wav", "4fc0053f66657e416b69419106d238a5", 1592106),
Common::UNK_LANG,
Common::kPlatformWindows,
VCRUISE_GF_WANT_OGG_VORBIS | VCRUISE_GF_NEED_JPEG,
GUIO1(GAMEOPTION_FAST_VIDEO_DECODER)
},
GID_SCHIZM,
Common::PL_POL,
},
{ // Schizm: Mysterious Journey, Russian digital 10-language (GOG) version
{
"schizm",
"Russian Digital",
AD_ENTRY2s("Schizm.exe", "296edd26d951c3bdc4d303c4c88b27cd", 364544,
"0001_a.wav", "ba8fc041a92f1afceee35bdbb84eb2cd", 1427794),
Common::UNK_LANG,
Common::kPlatformWindows,
VCRUISE_GF_WANT_OGG_VORBIS | VCRUISE_GF_NEED_JPEG,
GUIO1(GAMEOPTION_FAST_VIDEO_DECODER)
},
GID_SCHIZM,
Common::RU_RUS,
},
// Steam (16-language) versions
{ // Schizm: Mysterious Journey, English digital 16-language (Steam) version
{
"schizm",
"English Digital",
AD_ENTRY3s("Schizm.exe", "8b77c96b51f0c1d676d7a9ff29f06250", 381240,
"0001_a.wav", "374d93abc3422840623acc618ecb2b1e", 1553784,
"Speech10.txt", "88fdaab90be33bca88db423e1acda8c3", 63697),
Common::UNK_LANG,
Common::kPlatformWindows,
VCRUISE_GF_WANT_OGG_VORBIS | VCRUISE_GF_NEED_JPEG | VCRUISE_GF_STEAM_LANGUAGES,
GUIO1(GAMEOPTION_FAST_VIDEO_DECODER)
},
GID_SCHIZM,
Common::EN_GRB,
},
{ // Schizm: Mysterious Journey, German digital 16-language (Steam) version
{
"schizm",
"German Digital",
AD_ENTRY3s("Schizm.exe", "8b77c96b51f0c1d676d7a9ff29f06250", 381240,
"0001_a.wav", "f2b7eccfb1e9af0282b541c5eac66cc7", 1613240,
"Speech10.txt", "88fdaab90be33bca88db423e1acda8c3", 63697),
Common::UNK_LANG,
Common::kPlatformWindows,
VCRUISE_GF_WANT_OGG_VORBIS | VCRUISE_GF_NEED_JPEG | VCRUISE_GF_STEAM_LANGUAGES,
GUIO1(GAMEOPTION_FAST_VIDEO_DECODER)
},
GID_SCHIZM,
Common::DE_DEU,
},
{ // Schizm: Mysterious Journey, Spanish digital 16-language (Steam) version
{
"schizm",
"Spanish Digital",
AD_ENTRY3s("Schizm.exe", "8b77c96b51f0c1d676d7a9ff29f06250", 381240,
"0001_a.wav", "7a398f17e847a46de629a09fa6178b00", 1409560,
"Speech10.txt", "88fdaab90be33bca88db423e1acda8c3", 63697),
Common::UNK_LANG,
Common::kPlatformWindows,
VCRUISE_GF_WANT_OGG_VORBIS | VCRUISE_GF_NEED_JPEG | VCRUISE_GF_STEAM_LANGUAGES,
GUIO1(GAMEOPTION_FAST_VIDEO_DECODER)
},
GID_SCHIZM,
Common::ES_ESP,
},
{ // Schizm: Mysterious Journey, French digital 16-language (Steam) version
{
"schizm",
"French Digital",
AD_ENTRY3s("Schizm.exe", "8b77c96b51f0c1d676d7a9ff29f06250", 381240,
"0001_a.wav", "cd46a5df85a879bf293871b5911abcc3", 1305470,
"Speech10.txt", "88fdaab90be33bca88db423e1acda8c3", 63697),
Common::UNK_LANG,
Common::kPlatformWindows,
VCRUISE_GF_WANT_OGG_VORBIS | VCRUISE_GF_NEED_JPEG | VCRUISE_GF_STEAM_LANGUAGES,
GUIO1(GAMEOPTION_FAST_VIDEO_DECODER)
},
GID_SCHIZM,
Common::FR_FRA,
},
{ // Schizm: Mysterious Journey, Hungarian digital 16-language (Steam) version
{
"schizm",
"Hungarian Digital",
AD_ENTRY3s("Schizm.exe", "8b77c96b51f0c1d676d7a9ff29f06250", 381240,
"0001_a.wav", "9838f59fce21ba3a1eecc3d84c5be8b1", 1502674,
"Speech10.txt", "88fdaab90be33bca88db423e1acda8c3", 63697),
Common::UNK_LANG,
Common::kPlatformWindows,
VCRUISE_GF_WANT_OGG_VORBIS | VCRUISE_GF_NEED_JPEG | VCRUISE_GF_STEAM_LANGUAGES,
GUIO1(GAMEOPTION_FAST_VIDEO_DECODER)
},
GID_SCHIZM,
Common::HU_HUN,
},
{ // Schizm: Mysterious Journey, Italian digital 16-language (Steam) version
{
"schizm",
"Italian Digital",
AD_ENTRY3s("Schizm.exe", "8b77c96b51f0c1d676d7a9ff29f06250", 381240,
"0001_a.wav", "6d8e4b97710b858525d9965ef1e627ab", 1280506,
"Speech10.txt", "88fdaab90be33bca88db423e1acda8c3", 63697),
Common::UNK_LANG,
Common::kPlatformWindows,
VCRUISE_GF_WANT_OGG_VORBIS | VCRUISE_GF_NEED_JPEG | VCRUISE_GF_STEAM_LANGUAGES,
GUIO1(GAMEOPTION_FAST_VIDEO_DECODER)
},
GID_SCHIZM,
Common::IT_ITA,
},
{ // Schizm: Mysterious Journey, Polish digital 16-language (Steam) version
{
"schizm",
"Polish Digital",
AD_ENTRY3s("Schizm.exe", "8b77c96b51f0c1d676d7a9ff29f06250", 381240,
"0001_a.wav", "4fc0053f66657e416b69419106d238a5", 1592106,
"Speech10.txt", "88fdaab90be33bca88db423e1acda8c3", 63697),
Common::UNK_LANG,
Common::kPlatformWindows,
VCRUISE_GF_WANT_OGG_VORBIS | VCRUISE_GF_NEED_JPEG | VCRUISE_GF_STEAM_LANGUAGES,
GUIO1(GAMEOPTION_FAST_VIDEO_DECODER)
},
GID_SCHIZM,
Common::PL_POL,
},
{ // Schizm: Mysterious Journey, Russian digital 16-language (Steam) version
{
"schizm",
"Russian Digital",
AD_ENTRY3s("Schizm.exe", "8b77c96b51f0c1d676d7a9ff29f06250", 381240,
"0001_a.wav", "ba8fc041a92f1afceee35bdbb84eb2cd", 1427794,
"Speech10.txt", "88fdaab90be33bca88db423e1acda8c3", 63697),
Common::UNK_LANG,
Common::kPlatformWindows,
VCRUISE_GF_WANT_OGG_VORBIS | VCRUISE_GF_NEED_JPEG | VCRUISE_GF_STEAM_LANGUAGES,
GUIO1(GAMEOPTION_FAST_VIDEO_DECODER)
},
GID_SCHIZM,
Common::RU_RUS,
},
{ AD_TABLE_END_MARKER, GID_UNKNOWN, Common::UNK_LANG }
};
} // End of namespace VCruise
#endif

1197
engines/vcruise/menu.cpp Normal file

File diff suppressed because it is too large Load Diff

95
engines/vcruise/menu.h Normal file
View File

@@ -0,0 +1,95 @@
/* 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 VCRUISE_MENU_H
#define VCRUISE_MENU_H
#include "common/array.h"
#include "common/ptr.h"
namespace Graphics {
class Font;
struct Surface;
class ManagedSurface;
} // End of namespace Graphics
namespace Common {
struct Rect;
} // End of namespace Common
namespace VCruise {
struct OSEvent;
class MenuPage;
class Runtime;
class MenuInterface {
public:
virtual ~MenuInterface();
virtual void commitRect(const Common::Rect &rect) const = 0;
virtual bool popOSEvent(OSEvent &evt) const = 0;
virtual Graphics::ManagedSurface *getUIGraphic(uint index) const = 0;
virtual Graphics::ManagedSurface *getMenuSurface() const = 0;
virtual bool hasDefaultSave() const = 0;
virtual bool hasAnySave() const = 0;
virtual bool isInGame() const = 0;
virtual Common::Point getMouseCoordinate() const = 0;
virtual void restartGame() const = 0;
virtual void goToCredits() const = 0;
virtual void changeMenu(MenuPage *newPage) const = 0;
virtual void quitGame() const = 0;
virtual void quitToMenu() const = 0;
virtual bool canSave() const = 0;
virtual bool reloadFromCheckpoint() const = 0;
virtual void setMusicMute(bool muted) const = 0;
virtual void drawLabel(Graphics::ManagedSurface *surface, const Common::String &labelID, const Common::Rect &contentRect) const = 0;
};
class MenuPage {
public:
MenuPage();
virtual ~MenuPage();
void init(const MenuInterface *menuInterface);
virtual void start();
virtual bool run();
virtual void onSettingsChanged();
protected:
const MenuInterface *_menuInterface;
};
MenuPage *createMenuMain(bool isSchizm);
MenuPage *createMenuHelp(bool isSchizm);
MenuPage *createMenuSound(bool isSchizm);
MenuPage *createMenuQuit(bool isSchizm);
MenuPage *createMenuPause(bool isSchizm);
} // End of namespace VCruise
#endif

View File

@@ -0,0 +1,246 @@
/* 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/translation.h"
#include "backends/keymapper/action.h"
#include "backends/keymapper/keymapper.h"
#include "backends/keymapper/standard-actions.h"
#include "engines/advancedDetector.h"
#include "vcruise/vcruise.h"
namespace Graphics {
struct Surface;
} // End of namespace Graphics
namespace VCruise {
static const ADExtraGuiOptionsMap optionsList[] = {
{
GAMEOPTION_INCREASE_DRAG_DISTANCE,
{
_s("Improved click sensitivity"),
_s("Allows the mouse to be moved a short distance without cancelling click interactions. If this is off, any mouse movement while the mouse button is held cancels a click interaction."),
"vcruise_increase_drag_distance",
true,
0,
0
}
},
{
GAMEOPTION_FAST_ANIMATIONS,
{
_s("Faster animations"),
_s("Speeds up animations."),
"vcruise_fast_animations",
false,
0,
0
}
},
{
GAMEOPTION_LAUNCH_DEBUG,
{
_s("Start with debugger"),
_s("Starts with the debugger dashboard active."),
"vcruise_debug",
false,
0,
0
}
},
{
GAMEOPTION_SKIP_MENU,
{
_s("Skip main menu"),
_s("Starts a new game upon launching instead of going to the main menu."),
"vcruise_skip_menu",
false,
0,
0
}
},
{
GAMEOPTION_USE_4BIT_GRAPHICS,
{
_s("Use 16-color graphics"),
_s("Uses 16-color graphics."),
"vcruise_use_4bit",
false,
0,
0
}
},
{
GAMEOPTION_PRELOAD_SOUNDS,
{
_s("Preload sounds"),
_s("Preload sounds. May improve performance on slow hard drives."),
"vcruise_preload_sounds",
false,
0,
0
}
},
{
GAMEOPTION_FAST_VIDEO_DECODER,
{
_s("Faster video decoder (lower quality)"),
_s("Reduce video decoding CPU usage at the cost of quality."),
"vcruise_fast_video_decoder",
false,
0,
0
}
},
AD_EXTRA_GUI_OPTIONS_TERMINATOR
};
} // End of namespace VCruise
class VCruiseMetaEngine : public AdvancedMetaEngine<VCruise::VCruiseGameDescription> {
public:
const char *getName() const override {
return "vcruise";
}
const ADExtraGuiOptionsMap *getAdvancedExtraGuiOptions() const override {
return VCruise::optionsList;
}
bool hasFeature(MetaEngineFeature f) const override;
Common::Error createInstance(OSystem *syst, Engine **engine, const VCruise::VCruiseGameDescription *desc) const override;
Common::Array<Common::Keymap *> initKeymaps(const char *target) const override;
};
bool VCruiseMetaEngine::hasFeature(MetaEngineFeature f) const {
switch (f) {
case kSupportsLoadingDuringStartup:
return true;
default:
break;
}
return checkExtendedSaves(f);
}
Common::Error VCruiseMetaEngine::createInstance(OSystem *syst, Engine **engine, const VCruise::VCruiseGameDescription *desc) const {
*engine = new VCruise::VCruiseEngine(syst, reinterpret_cast<const VCruise::VCruiseGameDescription *>(desc));
return Common::kNoError;
}
Common::Array<Common::Keymap *> VCruiseMetaEngine::initKeymaps(const char *target) const {
Common::Keymap *keymap = new Common::Keymap(Common::Keymap::kKeymapTypeGame, "vcruise", "V-Cruise");
Common::Action *act;
act = new Common::Action(Common::kStandardActionLeftClick, _("Left click"));
act->setLeftClickEvent();
act->addDefaultInputMapping("MOUSE_LEFT");
act->addDefaultInputMapping("JOY_A");
keymap->addAction(act);
act = new Common::Action("VCRUISE_ESCAPE", _("Escape"));
act->setCustomEngineActionEvent(VCruise::kKeymappedEventEscape);
act->addDefaultInputMapping("ESCAPE");
act->addDefaultInputMapping("JOY_Y");
keymap->addAction(act);
act = new Common::Action("VCRUISE_HELP", _("Display help screen"));
act->setCustomEngineActionEvent(VCruise::kKeymappedEventHelp);
act->addDefaultInputMapping("F1");
keymap->addAction(act);
act = new Common::Action("VCRUISE_SAVE_GAME", _("Save game"));
act->setCustomEngineActionEvent(VCruise::kKeymappedEventSaveGame);
act->addDefaultInputMapping("F2");
keymap->addAction(act);
act = new Common::Action("VCRUISE_LOAD_GAME", _("Load game"));
act->setCustomEngineActionEvent(VCruise::kKeymappedEventLoadGame);
act->addDefaultInputMapping("F3");
keymap->addAction(act);
act = new Common::Action("VCRUISE_SOUND_SETTINGS", _("Open sound settings"));
act->setCustomEngineActionEvent(VCruise::kKeymappedEventSoundSettings);
act->addDefaultInputMapping("F4");
keymap->addAction(act);
act = new Common::Action("VCRUISE_QUIT", _("Quit game"));
act->setCustomEngineActionEvent(VCruise::kKeymappedEventQuit);
act->addDefaultInputMapping("F10");
keymap->addAction(act);
act = new Common::Action("VCRUISE_PAUSE", _("Pause game"));
act->setCustomEngineActionEvent(VCruise::kKeymappedEventPause);
act->addDefaultInputMapping("SPACE");
keymap->addAction(act);
act = new Common::Action("VCRUISE_MUSIC_TOGGLE", _("Toggle music"));
act->setCustomEngineActionEvent(VCruise::kKeymappedEventMusicToggle);
act->addDefaultInputMapping("F5");
keymap->addAction(act);
act = new Common::Action("VCRUISE_SOUND_TOGGLE", _("Toggle sound effects"));
act->setCustomEngineActionEvent(VCruise::kKeymappedEventSoundToggle);
act->addDefaultInputMapping("F6");
keymap->addAction(act);
act = new Common::Action("VCRUISE_MUSIC_VOLUME_DOWN", _("Music volume down"));
act->setCustomEngineActionEvent(VCruise::kKeymappedEventMusicVolumeDown);
act->addDefaultInputMapping("F7");
keymap->addAction(act);
act = new Common::Action("VCRUISE_MUSIC_VOLUME_UP", _("Music volume up"));
act->setCustomEngineActionEvent(VCruise::kKeymappedEventMusicVolumeUp);
act->addDefaultInputMapping("F8");
keymap->addAction(act);
act = new Common::Action("VCRUISE_SOUND_EFFECTS_VOLUME_DOWN", _("Sound effect volume down"));
act->setCustomEngineActionEvent(VCruise::kKeymappedEventSoundVolumeUp);
act->addDefaultInputMapping("F11");
keymap->addAction(act);
act = new Common::Action("VCRUISE_SOUND_EFFECTS_VOLUME_UP", _("Sound effect volume up"));
act->setCustomEngineActionEvent(VCruise::kKeymappedEventSoundVolumeDown);
act->addDefaultInputMapping("F12");
keymap->addAction(act);
act = new Common::Action("VCRUISE_SKIP_ANIMATION", _("Skip current animation"));
act->setCustomEngineActionEvent(VCruise::kKeymappedEventSkipAnimation);
keymap->addAction(act);
act = new Common::Action("VCRUISE_PUT_ITEM", _("Cycle item in scene (debug cheat)"));
act->setCustomEngineActionEvent(VCruise::kKeymappedEventPutItem);
keymap->addAction(act);
return Common::Keymap::arrayOf(keymap);
}
#if PLUGIN_ENABLED_DYNAMIC(VCRUISE)
REGISTER_PLUGIN_DYNAMIC(VCRUISE, PLUGIN_TYPE_ENGINE, VCruiseMetaEngine);
#else
REGISTER_PLUGIN_STATIC(VCRUISE, PLUGIN_TYPE_ENGINE, VCruiseMetaEngine);
#endif

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 "audio/mididrv.h"
#include "audio/midiparser_smf.h"
#include "vcruise/midi_player.h"
namespace VCruise {
MidiPlayer::MidiPlayer(MidiDriver *midiDrv, Common::Array<byte> &&musicData, int volume)
: _midiDrv(midiDrv), _data(Common::move(musicData)) {
_parser.reset(MidiParser::createParser_SMF());
if (_data.size() > 0u && _parser->loadMusic(&_data[0], _data.size())) {
_parser->setTrack(0);
_parser->setMidiDriver(_midiDrv);
_parser->startPlaying();
_parser->property(MidiParser::mpAutoLoop, 1);
_parser->setTimerRate(_midiDrv->getBaseTempo());
setVolume(volume);
} else
_parser.reset();
}
MidiPlayer::~MidiPlayer() {
if (_parser)
_parser->stopPlaying();
}
void MidiPlayer::setVolume(int volume) {
if (!_parser)
return;
if (volume > 100)
volume = 100;
else if (volume < 0)
volume = 0;
volume = 51;
//uint32 effectiveValue = static_cast<uint32>(floor(sqrt(sqrt(volume)) * 5180.76));
uint32 effectiveValue = static_cast<uint32>(volume * 0x3fff / 100);
if (effectiveValue > 0x3fffu)
effectiveValue = 0x3fffu;
byte masterVolMessage[6] = {
0x7f, 0x00, 0x04, 0x01, static_cast<byte>(effectiveValue & 0x7f), static_cast<byte>((effectiveValue >> 7) & 0x7f)
};
_midiDrv->sysEx(masterVolMessage, 6);
}
void MidiPlayer::onMidiTimer() {
if (_parser)
_parser->onTimer();
}
} // End of namespace VCruise

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 VCRUISE_MIDI_PLAYER_H
#define VCRUISE_MIDI_PLAYER_H
#include "common/array.h"
#include "common/mutex.h"
#include "common/ptr.h"
class MidiDriver;
class MidiParser;
namespace Common {
class SeekableReadStream;
} // End of namespace Common
namespace VCruise {
class MidiPlayer {
public:
MidiPlayer(MidiDriver *midiDrv, Common::Array<byte> &&musicData, int volume);
~MidiPlayer();
void setVolume(int volume);
void onMidiTimer();
private:
MidiDriver *_midiDrv;
Common::SharedPtr<MidiParser> _parser;
Common::Array<byte> _data;
};
} // End of namespace VCruise
#endif

28
engines/vcruise/module.mk Normal file
View File

@@ -0,0 +1,28 @@
MODULE := engines/vcruise
MODULE_OBJS = \
ad2044_items.o \
ad2044_ui.o \
audio_player.o \
circuitpuzzle.o \
midi_player.o \
metaengine.o \
menu.o \
runtime.o \
runtime_scriptexec.o \
sampleloop.o \
script.o \
textparser.o \
vcruise.o
# This module can be built as a plugin
ifeq ($(ENABLE_VCRUISE), DYNAMIC_PLUGIN)
PLUGIN := 1
endif
# Include common rules
include $(srcdir)/rules.mk
# Detection objects
DETECT_OBJS += $(MODULE)/detection.o

7391
engines/vcruise/runtime.cpp Normal file

File diff suppressed because it is too large Load Diff

1555
engines/vcruise/runtime.h Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,362 @@
/* 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/stream.h"
#include "vcruise/sampleloop.h"
namespace VCruise {
SampleLoop::SampleLoop() : identifier(0), type(0), start(0), end(0), fraction(0), playCount(0) {
}
bool SampleLoop::read(Common::ReadStream &stream, uint &availableBytes) {
if (availableBytes < 24)
return false;
byte bytes[24];
uint32 bytesRead = stream.read(bytes, 24);
availableBytes -= bytesRead;
if (bytesRead != 24)
return false;
this->identifier = READ_LE_UINT32(bytes + 0);
this->type = READ_LE_UINT32(bytes + 4);
this->start = READ_LE_UINT32(bytes + 8);
this->end = READ_LE_UINT32(bytes + 12);
this->fraction = READ_LE_UINT32(bytes + 16);
this->playCount = READ_LE_UINT32(bytes + 20);
return true;
}
SampleChunk::SampleChunk() : manufacturer(0), product(0), samplePeriod(0), midiUnityNote(0), midiPitchFraction(0), smpteFormat(0), smpteOffset(0) {
}
bool SampleChunk::read(Common::ReadStream &stream, uint &availableBytes) {
if (availableBytes < 36)
return false;
byte bytes[36];
uint32 bytesRead = stream.read(bytes, 36);
availableBytes -= bytesRead;
if (bytesRead != 36)
return false;
this->manufacturer = READ_LE_UINT32(bytes + 0);
this->product = READ_LE_UINT32(bytes + 4);
this->samplePeriod = READ_LE_UINT32(bytes + 8);
this->midiUnityNote = READ_LE_UINT32(bytes + 12);
this->midiPitchFraction = READ_LE_UINT32(bytes + 16);
this->smpteFormat = READ_LE_UINT32(bytes + 20);
this->smpteOffset = READ_LE_UINT32(bytes + 24);
uint32 numSampleLoops = READ_LE_UINT32(bytes + 28);
uint32 sizeOfSamplerData = READ_LE_UINT32(bytes + 32);
loops.resize(numSampleLoops);
samplerData.resize(sizeOfSamplerData);
for (uint32 i = 0; i < numSampleLoops; i++)
if (!loops[i].read(stream, availableBytes))
return false;
if (sizeOfSamplerData > 0) {
if (availableBytes < sizeOfSamplerData)
return false;
bytesRead = stream.read(&samplerData[0], sizeOfSamplerData);
availableBytes -= bytesRead;
if (bytesRead != sizeOfSamplerData)
return false;
}
return true;
}
Common::SharedPtr<SoundLoopInfo> SoundLoopInfo::readFromWaveFile(Common::SeekableReadStream &stream) {
if (!stream.seek(0))
return nullptr;
int64 waveSize64 = stream.size();
if (waveSize64 > static_cast<int64>(0xffffffffu))
return nullptr;
uint availableBytes = waveSize64;
if (availableBytes < 8)
return nullptr;
byte riffHeader[8];
if (stream.read(riffHeader, 8) != 8)
return nullptr;
availableBytes -= 8;
if (READ_LE_UINT32(riffHeader + 0) != 0x46464952)
return nullptr;
uint riffDataSize = READ_LE_UINT32(riffHeader + 4);
if (riffDataSize > availableBytes)
return nullptr;
availableBytes = riffDataSize;
if (availableBytes < 4)
return nullptr;
byte waveHeader[4];
if (stream.read(waveHeader, 4) != 4)
return nullptr;
availableBytes -= 4;
if (READ_LE_UINT32(waveHeader + 0) != 0x45564157)
return nullptr;
while (availableBytes > 0) {
if (availableBytes < 8)
return nullptr;
byte chunkHeader[8];
if (stream.read(chunkHeader, 8) != 8)
return nullptr;
availableBytes -= 8;
uint32 chunkType = READ_LE_UINT32(chunkHeader + 0);
uint32 chunkSize = READ_LE_UINT32(chunkHeader + 4);
if (chunkSize > availableBytes)
return nullptr;
if (chunkType == 0x6c706d73) {
Common::SharedPtr<SoundLoopInfo> sndLoop(new SoundLoopInfo());
uint chunkAvailableBytes = chunkSize;
if (!sndLoop->_sampleChunk.read(stream, chunkAvailableBytes))
return nullptr;
if (sndLoop->_sampleChunk.loops.size() == 0)
return nullptr;
return sndLoop;
}
if (!stream.seek(chunkSize, SEEK_CUR))
return nullptr;
availableBytes -= chunkSize;
}
return nullptr;
}
SampleLoopAudioStream::LoopRange::LoopRange() : _startSampleInclusive(0), _endSampleExclusive(0), _playCount(0) {
}
SampleLoopAudioStream::SampleLoopAudioStream(Audio::SeekableAudioStream *baseStream, const SoundLoopInfo *loopInfo)
: _baseStream(baseStream), _terminated(false), _ignoreLoops(false), _currentSampleOffset(0), _currentLoop(-1), _currentLoopIteration(0), _streamFrames(0), _streamSamples(0) {
_streamFrames = baseStream->getLength().convertToFramerate(baseStream->getRate()).totalNumberOfFrames();
_streamSamples = _streamFrames;
if (_baseStream->isStereo())
_streamSamples *= 2;
if (loopInfo) {
_loopRanges.resize(loopInfo->_sampleChunk.loops.size());
for (uint i = 0; i < _loopRanges.size(); i++) {
const SampleLoop &inLoop = loopInfo->_sampleChunk.loops[i];
LoopRange &outLoop = _loopRanges[i];
outLoop._startSampleInclusive = inLoop.start;
outLoop._endSampleExclusive = inLoop.end;
outLoop._playCount = inLoop.playCount;
}
} else {
_loopRanges.resize(1);
_loopRanges[0]._startSampleInclusive = 0;
_loopRanges[0]._endSampleExclusive = _streamFrames;
}
for (LoopRange &range : _loopRanges) {
range._restartTimestamp = Audio::Timestamp(0, range._startSampleInclusive, baseStream->getRate());
if (range._startSampleInclusive > static_cast<uint>(_streamFrames))
range._startSampleInclusive = _streamFrames;
if (range._endSampleExclusive > static_cast<uint>(_streamFrames))
range._endSampleExclusive = _streamFrames;
if (range._endSampleExclusive < range._startSampleInclusive)
range._endSampleExclusive = range._startSampleInclusive;
if (_baseStream->isStereo()) {
range._startSampleInclusive *= 2;
range._endSampleExclusive *= 2;
}
}
for (uint i = 0; i < _loopRanges.size(); ) {
const LoopRange &thisRange = _loopRanges[i];
bool isValid = true;
if (thisRange._endSampleExclusive == thisRange._startSampleInclusive)
isValid = false;
if (i > 0) {
const LoopRange &prevRange = _loopRanges[i - 1];
if (thisRange._startSampleInclusive < prevRange._endSampleExclusive)
isValid = false;
}
if (isValid)
i++;
else
_loopRanges.remove_at(i);
}
}
SampleLoopAudioStream::~SampleLoopAudioStream() {
}
void SampleLoopAudioStream::stopLooping() {
_mutex.lock();
_ignoreLoops = true;
_mutex.unlock();
}
int SampleLoopAudioStream::readBuffer(int16 *buffer, int numSamples) {
bool ignoreLoops = false;
_mutex.lock();
ignoreLoops = _ignoreLoops;
_mutex.unlock();
int totalSamplesRead = 0;
for (;;) {
if (_terminated || numSamples == 0)
return totalSamplesRead;
int consecutiveSamplesAvailable = 0;
bool terminateIfReadCompletes = false;
if (ignoreLoops) {
consecutiveSamplesAvailable = _streamSamples - _currentSampleOffset;
terminateIfReadCompletes = true;
} else if (_currentLoop < 0) {
// Not currently in a loop
int samplesUntilLoop = -1;
uint scanLoopIndex = 0;
for (const LoopRange &loopRange : _loopRanges) {
if (static_cast<int>(loopRange._startSampleInclusive) >= _currentSampleOffset) {
samplesUntilLoop = loopRange._startSampleInclusive - _currentSampleOffset;
break;
} else
scanLoopIndex++;
}
if (samplesUntilLoop < 0) {
// Past the end of the last loop
consecutiveSamplesAvailable = _streamSamples - _currentSampleOffset;
terminateIfReadCompletes = true;
} else if (samplesUntilLoop == 0) {
// At the start of a loop
_currentLoop = scanLoopIndex;
_currentLoopIteration = 0;
continue;
} else {
// Before a loop
consecutiveSamplesAvailable = samplesUntilLoop;
}
} else {
// In a loop
const LoopRange &loopRange = _loopRanges[_currentLoop];
int samplesAvailable = loopRange._endSampleExclusive - static_cast<int>(_currentSampleOffset);
if (samplesAvailable == 0) {
// At the end of the loop
if (loopRange._playCount > 0) {
if (_currentLoopIteration == loopRange._playCount) {
// Exit loop
_currentLoop = -1;
continue;
} else
_currentLoopIteration++;
}
if (!_baseStream->seek(loopRange._restartTimestamp)) {
_terminated = true;
return totalSamplesRead;
}
_currentSampleOffset = loopRange._startSampleInclusive;
continue;
} else {
// Inside of a loop
consecutiveSamplesAvailable = samplesAvailable;
}
}
if (consecutiveSamplesAvailable == 0)
_terminated = true;
else {
int samplesDesired = numSamples;
if (samplesDesired > consecutiveSamplesAvailable)
samplesDesired = consecutiveSamplesAvailable;
int samplesRead = _baseStream->readBuffer(buffer, samplesDesired);
if (samplesRead > 0)
totalSamplesRead += samplesRead;
if (samplesRead != samplesDesired)
_terminated = true;
else {
_currentSampleOffset += samplesRead;
buffer += samplesRead;
numSamples -= samplesRead;
if (samplesRead == consecutiveSamplesAvailable && terminateIfReadCompletes)
_terminated = true;
}
}
}
}
bool SampleLoopAudioStream::isStereo() const {
return _baseStream->isStereo();
}
int SampleLoopAudioStream::getRate() const {
return _baseStream->getRate();
}
bool SampleLoopAudioStream::endOfData() const {
return _terminated;
}
} // namespace VCruise

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/>.
*
*/
#ifndef VCRUISE_SAMPLELOOP_H
#define VCRUISE_SAMPLELOOP_H
#include "common/ptr.h"
#include "common/mutex.h"
#include "audio/audiostream.h"
namespace Common {
class ReadStream;
} // End of namespace Common
namespace VCruise {
struct SampleLoop {
SampleLoop();
bool read(Common::ReadStream &stream, uint &availableBytes);
uint32 identifier;
uint32 type;
uint32 start;
uint32 end;
uint32 fraction;
uint32 playCount;
};
struct SampleChunk {
SampleChunk();
bool read(Common::ReadStream &stream, uint &availableBytes);
uint32 manufacturer;
uint32 product;
uint32 samplePeriod;
uint32 midiUnityNote;
uint32 midiPitchFraction;
uint32 smpteFormat;
uint32 smpteOffset;
// uint32 numSampleLoops;
// uint32 sizeOfSamplerData;
Common::Array<SampleLoop> loops;
Common::Array<byte> samplerData;
};
struct SoundLoopInfo {
SampleChunk _sampleChunk;
static Common::SharedPtr<SoundLoopInfo> readFromWaveFile(Common::SeekableReadStream &stream);
};
class SampleLoopAudioStream : public Audio::AudioStream {
public:
SampleLoopAudioStream(Audio::SeekableAudioStream *baseStream, const SoundLoopInfo *loopInfo);
virtual ~SampleLoopAudioStream();
void stopLooping();
int readBuffer(int16 *buffer, const int numSamples) override;
bool isStereo() const override;
int getRate() const override;
bool endOfData() const override;
private:
struct LoopRange {
LoopRange();
Audio::Timestamp _restartTimestamp;
uint _startSampleInclusive;
uint _endSampleExclusive;
uint _playCount;
};
Common::Mutex _mutex;
int _currentSampleOffset;
int _currentLoop;
uint _currentLoopIteration;
int _streamFrames;
int _streamSamples;
bool _terminated;
bool _ignoreLoops;
Common::Array<LoopRange> _loopRanges;
Audio::SeekableAudioStream *_baseStream;
};
} // End of namespace VCruise
#endif

1715
engines/vcruise/script.cpp Normal file

File diff suppressed because it is too large Load Diff

323
engines/vcruise/script.h Normal file
View File

@@ -0,0 +1,323 @@
/* 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 VCRUISE_SCRIPT_H
#define VCRUISE_SCRIPT_H
#include "common/array.h"
#include "common/hashmap.h"
#include "common/path.h"
#include "common/ptr.h"
#include "common/types.h"
namespace Common {
class ReadStream;
} // End of namespace Common
namespace VCruise {
struct ScreenScriptSet;
struct RoomScriptSet;
struct ScriptSet;
struct ITextPreprocessor;
namespace ScriptOps {
enum ScriptOp {
kInvalid,
kNumber,
kRotate,
kAngle,
kAngleGGet,
kSpeed,
kSAnimL,
kChangeL,
kAnimR,
kAnimF,
kAnimN,
kAnimG,
kAnimS,
kAnim,
kStatic,
kVarLoad,
kVarStore,
kVarAddAndStore,
kVarGlobalLoad,
kVarGlobalStore,
kItemCheck,
kItemHaveSpace,
kItemRemove,
kItemHighlightSet,
kItemAdd,
kItemClear,
kSetCursor,
kSetRoom,
kLMB,
kLMB1,
kSoundS1,
kSoundS2,
kSoundS3,
kSoundL1,
kSoundL2,
kSoundL3,
k3DSoundS2,
k3DSoundL2,
k3DSoundL3,
kStopAL,
kRange,
kAddXSound,
kClrXSound,
kStopSndLA,
kStopSndLO,
kMusic,
kMusicVolRamp,
kParm0,
kParm1,
kParm2,
kParm3,
kParmG,
kSParmX,
kSAnimX,
kVolumeDn2,
kVolumeDn3,
kVolumeDn4,
kVolumeUp3,
kRandom,
kDrop,
kDup,
kSwap,
kSay1,
kSay2,
kSay3,
kSay3Get,
kSetTimer,
kGetTimer,
kDelay,
kLoSet,
kLoGet,
kHiSet,
kHiGet,
kNot,
kAnd,
kOr,
kSub,
kAdd,
kNegate,
kCmpEq,
kCmpLt,
kCmpGt,
kBitLoad,
kBitSet0,
kBitSet1,
kDisc1,
kDisc2,
kDisc3,
kGoto,
kEscOn,
kEscOff,
kEscGet,
kBackStart,
kSaveAs,
kSave0,
kExit,
kAllowSaves,
kAnimName,
kValueName,
kVarName,
kSoundName,
kCursorName,
kDubbing,
kCheckValue, // Check if stack top is equal to arg. If it is, pop the argument, otherwise leave it on the stack and skip the next instruction.
kJump, // Offset instruction index by arg.
// Schizm ops
kCallFunction,
kMusicStop,
kMusicPlayScore,
kScoreAlways,
kScoreNormal,
kSndPlay,
kSndPlayEx,
kSndPlay3D,
kSndPlaying,
kSndWait,
kSndHalt,
kSndToBack,
kSndStop,
kSndStopAll,
kSndAddRandom,
kSndClearRandom,
kVolumeAdd,
kVolumeChange,
kAnimVolume,
kAnimChange,
kScreenName,
kExtractByte,
kInsertByte,
kString,
kCmpNE,
kCmpLE,
kCmpGE,
kReturn,
kSpeech,
kSpeechEx,
kSpeechTest,
kSay,
kRandomInclusive,
kHeroOut,
kHeroGetPos,
kHeroSetPos,
kHeroGet,
kGarbage,
kGetRoom,
kBitAnd,
kBitOr,
kAngleGet,
kIsCDVersion,
kIsDVDVersion,
kDisc,
kHidePanel,
kRotateUpdate,
kMul,
kDiv,
kMod,
kGetDigit,
kPuzzleInit,
kPuzzleCanPress,
kPuzzleDoMove1,
kPuzzleDoMove2,
kPuzzleDone,
kPuzzleWhoWon,
kFn,
kItemHighlightSetTrue,
// AD2044 ops
kAnimT,
kAnimForward,
kAnimReverse,
kAnimKForward,
kNoUpdate,
kNoClear,
kSay1_AD2044,
kSay2_AD2044,
kSay1Rnd,
kM,
kEM,
kSE,
kSDot,
kE,
kDot,
kSound,
kISound,
kUSound,
kSay2K,
kSay3K,
kRGet,
kRSet,
kEndRSet,
kStop,
kNumOps,
};
} // End of namespace ScriptOps
struct Instruction {
Instruction();
explicit Instruction(ScriptOps::ScriptOp paramOp);
Instruction(ScriptOps::ScriptOp paramOp, int32 paramArg);
ScriptOps::ScriptOp op;
int32 arg;
};
struct Script {
Common::Array<Instruction> instrs;
};
typedef Common::HashMap<uint, Common::SharedPtr<Script> > ScriptMap_t;
typedef Common::HashMap<uint, Common::SharedPtr<ScreenScriptSet> > ScreenScriptSetMap_t;
typedef Common::HashMap<uint, Common::SharedPtr<RoomScriptSet> > RoomScriptSetMap_t;
typedef Common::HashMap<Common::String, uint> ScreenNameMap_t;
struct ScreenScriptSet {
Common::SharedPtr<Script> entryScript;
ScriptMap_t interactionScripts;
};
struct RoomScriptSet {
ScreenScriptSetMap_t screenScripts;
ScreenNameMap_t screenNames;
};
struct ScriptSet {
ScriptSet();
RoomScriptSetMap_t roomScripts;
Common::Array<Common::SharedPtr<Script> > functions;
Common::Array<Common::String> functionNames;
Common::Array<Common::String> strings;
};
struct FunctionDef {
Common::String fnName;
Common::SharedPtr<Script> func;
};
// Global state is required for Schizm because its preprocessor defines exist across files.
// For example, volPortWaves is set in Room01 but used in Room03 and Room20
struct IScriptCompilerGlobalState {
virtual ~IScriptCompilerGlobalState();
virtual void define(const Common::String &key, uint roomNumber, int32 value) = 0;
virtual bool getDefine(const Common::String &str, uint &outRoomNumber, int32 &outValue) const = 0;
virtual uint getFunctionIndex(const Common::String &fnName) = 0;
virtual void setFunction(uint fnIndex, const Common::SharedPtr<Script> &fn) = 0;
virtual uint getNumFunctions() const = 0;
virtual void dumpFunctionNames(Common::Array<Common::String> &fnNames) const = 0;
virtual Common::SharedPtr<Script> getFunction(uint fnIndex) const = 0;
};
Common::SharedPtr<IScriptCompilerGlobalState> createScriptCompilerGlobalState();
Common::SharedPtr<ScriptSet> compileReahLogicFile(Common::ReadStream &stream, uint streamSize, const Common::Path &blamePath);
Common::SharedPtr<ScriptSet> compileAD2044LogicFile(Common::ReadStream &stream, uint streamSize, const Common::Path &blamePath);
void compileSchizmLogicFile(ScriptSet &scriptSet, uint loadAsRoom, uint fileRoom, Common::ReadStream &stream, uint streamSize, const Common::Path &blamePath, IScriptCompilerGlobalState *gs);
bool checkSchizmLogicForDuplicatedRoom(Common::ReadStream &stream, uint streamSize);
void optimizeScriptSet(ScriptSet &scriptSet);
}
#endif

View File

@@ -0,0 +1,306 @@
/* 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/path.h"
#include "common/stream.h"
#include "common/textconsole.h"
#include "vcruise/textparser.h"
namespace VCruise {
TextParserState::TextParserState() : _lineNum(1), _col(1), _prevWasCR(false), _isParsingComment(false) {
}
TextParser::TextParser(Common::ReadStream *stream) : _stream(stream), _returnedBufferPos(kReturnedBufferSize) {
memset(_returnedBuffer, 0, kReturnedBufferSize);
}
bool TextParser::readOneChar(char &outC, TextParserState &outState) {
if (_returnedBufferPos == kReturnedBufferSize) {
if (_stream->eos())
return false;
}
char c = 0;
if (_returnedBufferPos != kReturnedBufferSize) {
c = _returnedBuffer[_returnedBufferPos++];
} else {
if (!_stream->read(&c, 1))
return false;
}
TextParserState prevState = _state;
if (c == '\r') {
_state._lineNum++;
_state._col = 1;
_state._isParsingComment = false;
_state._prevWasCR = true;
} else if (c == '\n') {
if (!_state._prevWasCR) {
_state._lineNum++;
_state._col = 1;
}
_state._prevWasCR = false;
} else {
_state._col++;
_state._prevWasCR = false;
if (c == ';')
_state._isParsingComment = true;
}
outC = c;
outState = prevState;
return true;
}
bool TextParser::skipWhitespaceAndComments(char &outC, TextParserState &outState) {
char c = 0;
TextParserState firstCharState;
while (readOneChar(c, firstCharState)) {
if (isWhitespace(c))
continue;
if (_state._isParsingComment)
continue;
outC = c;
outState = firstCharState;
return true;
}
return false;
}
bool TextParser::isDelimiter(char c) {
if (c == ',' || c == '=' || c == '[' || c == ']')
return true;
return false;
}
bool TextParser::isCompoundDelimiter(char c1, char c2) {
if (c2 == '=' && (c1 == '=' || c1 == '<' || c1 == '>' || c1 == '!'))
return true;
return false;
}
bool TextParser::isWhitespace(char c) {
return (c == ' ') || ((c & 0xe0) == 0);
}
void TextParser::requeue(const char *chars, uint numChars, const TextParserState &state) {
_state = state;
assert(_returnedBufferPos >= numChars);
_returnedBufferPos -= numChars;
memcpy(_returnedBuffer + _returnedBufferPos, chars, numChars);
}
void TextParser::requeue(const Common::String &str, const TextParserState &state) {
requeue(str.c_str(), str.size(), state);
}
void TextParser::expectToken(Common::String &outToken, const Common::Path &blamePath) {
TextParserState state;
expectTokenInternal(outToken, blamePath, state);
}
void TextParser::expectShort(int16 &outInt, const Common::Path &blamePath) {
int i;
expectInt(i, blamePath);
outInt = static_cast<int16>(i);
}
void TextParser::expectInt(int &outInt, const Common::Path &blamePath) {
Common::String token;
TextParserState state;
expectTokenInternal(token, blamePath, state);
if (!sscanf(token.c_str(), "%i", &outInt))
error("Parsing error in '%s' at line %i col %i: Integer was malformed", blamePath.toString(Common::Path::kNativeSeparator).c_str(), static_cast<int>(state._lineNum), static_cast<int>(state._col));
}
void TextParser::expectUInt(uint &outUInt, const Common::Path &blamePath) {
Common::String token;
TextParserState state;
expectTokenInternal(token, blamePath, state);
if (!sscanf(token.c_str(), "%u", &outUInt))
error("Parsing error in '%s' at line %i col %i: Unsigned integer was malformed", blamePath.toString(Common::Path::kNativeSeparator).c_str(), static_cast<int>(state._lineNum), static_cast<int>(state._col));
}
void TextParser::expectLine(Common::String &outToken, const Common::Path &blamePath, bool continueToNextLine) {
outToken.clear();
char c = 0;
TextParserState state;
bool isSkippingWhitespace = true;
uint nonWhitespaceLength = 0;
while (readOneChar(c, state)) {
if (c == '\r' || c == '\n' || _state._isParsingComment) {
requeue(&c, 1, state);
if (continueToNextLine)
skipToEOL();
break;
}
bool cIsWhitespace = isWhitespace(c);
if (isSkippingWhitespace) {
if (cIsWhitespace)
continue;
isSkippingWhitespace = false;
}
outToken += c;
if (!cIsWhitespace)
nonWhitespaceLength = outToken.size();
}
if (nonWhitespaceLength != outToken.size())
outToken = outToken.substr(0, nonWhitespaceLength);
}
void TextParser::expect(const char *str, const Common::Path &blamePath) {
Common::String token;
TextParserState state;
expectTokenInternal(token, blamePath, state);
if (token != str)
error("Parsing error in '%s' at line %i col %i: Expected token '%s' but found '%s'", blamePath.toString(Common::Path::kNativeSeparator).c_str(), static_cast<int>(state._lineNum), static_cast<int>(state._col), str, token.c_str());
}
void TextParser::skipToEOL() {
char c = 0;
TextParserState state;
while (readOneChar(c, state)) {
if (c == '\n')
return;
if (c == '\r') {
if (readOneChar(c, state)) {
if (c != '\n')
requeue(&c, 1, state);
return;
}
}
}
}
bool TextParser::checkEOL() {
char c = 0;
TextParserState state;
for (;;) {
if (!readOneChar(c, state))
return true;
if (_state._isParsingComment || c == '\n' || c == '\r') {
// EOL or comment
requeue(&c, 1, state);
return true;
}
if (!isWhitespace(c)) {
// Non-whitespace
requeue(&c, 1, state);
return false;
}
}
}
void TextParser::expectTokenInternal(Common::String &outToken, const Common::Path &blamePath, TextParserState &outState) {
if (!parseToken(outToken, outState))
error("Parsing error in '%s' unexpected end of file", blamePath.toString(Common::Path::kNativeSeparator).c_str());
}
bool TextParser::parseToken(Common::String &outString, TextParserState &outState) {
outString.clear();
char c = 0;
TextParserState state;
if (!skipWhitespaceAndComments(c, state))
return false;
outState = state;
outString += c;
if (c == '\"') {
while (readOneChar(c, state)) {
if (c == '\n' || c == '\r') {
requeue(&c, 1, state);
return true;
}
outString += c;
if (c == '\"')
return true;
}
return true;
}
if (isDelimiter(c)) {
char firstC = c;
if (readOneChar(c, state)) {
if (isCompoundDelimiter(firstC, c))
outString += c;
else
requeue(&c, 1, state);
}
return true;
}
while (readOneChar(c, state)) {
if (isWhitespace(c) || _state._isParsingComment) {
requeue(&c, 1, state);
break;
}
if (outString.size() == 1 && isCompoundDelimiter(outString[0], c)) {
outString += c;
break;
}
if (isDelimiter(c)) {
requeue(&c, 1, state);
break;
}
outString += c;
}
return true;
}
} // End of namespace VCruise

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/>.
*
*/
#ifndef VCRUISE_TEXTPARSER_H
#define VCRUISE_TEXTPARSER_H
#include "common/hashmap.h"
#include "common/hash-str.h"
#include "common/str.h"
#include "common/memstream.h"
namespace Common {
class Path;
class ReadStream;
} // End of namespace Common
namespace VCruise {
struct TextParserState {
TextParserState();
uint _lineNum;
uint _col;
bool _prevWasCR;
bool _isParsingComment;
};
class TextParser {
public:
explicit TextParser(Common::ReadStream *stream);
bool parseToken(Common::String &outString, TextParserState &outState);
bool readOneChar(char &outC, TextParserState &outState);
bool skipWhitespaceAndComments(char &outC, TextParserState &outState);
void requeue(const char *chars, uint numChars, const TextParserState &state);
void requeue(const Common::String &str, const TextParserState &state);
void expectToken(Common::String &outToken, const Common::Path &blamePath);
void expectShort(int16 &outInt, const Common::Path &blamePath);
void expectInt(int &outInt, const Common::Path &blamePath);
void expectUInt(uint &outUInt, const Common::Path &blamePath);
void expectLine(Common::String &outToken, const Common::Path &blamePath, bool continueToNextLine);
void expect(const char *str, const Common::Path &blamePath);
void skipToEOL();
bool checkEOL();
private:
void expectTokenInternal(Common::String &outToken, const Common::Path &blamePath, TextParserState &outState);
static bool isDelimiter(char c);
static bool isCompoundDelimiter(char c1, char c2);
static bool isWhitespace(char c);
TextParserState _state;
Common::ReadStream *_stream;
static const uint kReturnedBufferSize = 8;
char _returnedBuffer[kReturnedBufferSize];
uint _returnedBufferPos;
};
} // End of namespace VCruise
#endif

497
engines/vcruise/vcruise.cpp Normal file
View File

@@ -0,0 +1,497 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "engines/util.h"
#include "common/compression/gentee_installer.h"
#include "common/config-manager.h"
#include "common/events.h"
#include "common/file.h"
#include "common/stream.h"
#include "common/savefile.h"
#include "common/system.h"
#include "common/algorithm.h"
#include "common/translation.h"
#include "audio/mididrv.h"
#include "audio/mixer.h"
#include "gui/message.h"
#include "vcruise/runtime.h"
#include "vcruise/vcruise.h"
namespace VCruise {
VCruiseEngine::VCruiseEngine(OSystem *syst, const VCruiseGameDescription *gameDesc) : Engine(syst), _gameDescription(gameDesc) {
const Common::FSNode gameDataDir(ConfMan.getPath("path"));
}
VCruiseEngine::~VCruiseEngine() {
}
void VCruiseEngine::handleEvents() {
Common::Event evt;
Common::EventManager *eventMan = _system->getEventManager();
while (eventMan->pollEvent(evt)) {
switch (evt.type) {
case Common::EVENT_LBUTTONDOWN:
_runtime->onLButtonDown(evt.mouse.x, evt.mouse.y);
break;
case Common::EVENT_LBUTTONUP:
_runtime->onLButtonUp(evt.mouse.x, evt.mouse.y);
break;
case Common::EVENT_MOUSEMOVE:
_runtime->onMouseMove(evt.mouse.x, evt.mouse.y);
break;
case Common::EVENT_CUSTOM_ENGINE_ACTION_START:
_runtime->onKeymappedEvent(static_cast<VCruise::KeymappedEvent>(evt.customType));
break;
default:
break;
}
}
}
void VCruiseEngine::staticHandleMidiTimer(void *refCon) {
static_cast<VCruiseEngine *>(refCon)->handleMidiTimer();
}
void VCruiseEngine::handleMidiTimer() {
_runtime->onMidiTimer();
}
Common::Error VCruiseEngine::run() {
#if !defined(USE_JPEG)
if (_gameDescription->desc.flags & VCRUISE_GF_NEED_JPEG) {
return Common::Error(Common::kUnknownError, _s("This game requires JPEG support, which was not compiled in."));
}
#endif
#if !defined(USE_OGG) || !defined(USE_VORBIS)
if (_gameDescription->desc.flags & VCRUISE_GF_WANT_OGG_VORBIS) {
GUI::MessageDialog dialog(
_("Music for this game requires Ogg Vorbis support, which was not compiled in.\n"
"The game will still play, but will not have any music."));
dialog.runModal();
}
#endif
#if !defined(USE_MAD)
if (_gameDescription->desc.flags & VCRUISE_GF_WANT_MP3) {
GUI::MessageDialog dialog(
_("Music for this game requires MP3 support, which was not compiled in.\n"
"The game will still play, but will not have any music."));
dialog.runModal();
}
#endif
Common::ScopedPtr<MidiDriver> midiDrv;
if (_gameDescription->gameID == GID_AD2044) {
MidiDriver::DeviceHandle midiDevHdl = MidiDriver::detectDevice(MDT_MIDI | MDT_ADLIB | MDT_PREFER_GM);
if (midiDevHdl) {
midiDrv.reset(MidiDriver::createMidi(midiDevHdl));
if (midiDrv->open() != 0)
midiDrv.reset();
}
}
if (_gameDescription->desc.flags & VCRUISE_GF_GENTEE_PACKAGE) {
Common::File *f = new Common::File();
if (!f->open(_gameDescription->desc.filesDescriptions[0].fileName))
error("Couldn't open installer package '%s'", _gameDescription->desc.filesDescriptions[0].fileName);
Common::Archive *installerPackageArchive = Common::createGenteeInstallerArchive(f, "#setuppath#/", false, true);
if (!installerPackageArchive)
error("Couldn't load installer package '%s'", _gameDescription->desc.filesDescriptions[0].fileName);
SearchMan.add("VCruiseInstallerPackage", installerPackageArchive);
}
if (_gameDescription->desc.flags & VCRUISE_GF_USE_SETUP_EXE) {
Common::File *f = new Common::File();
if (!f->open(_gameDescription->desc.filesDescriptions[1].fileName))
error("Couldn't open installer package '%s'", _gameDescription->desc.filesDescriptions[1].fileName);
Common::Archive *setupPackageArchive = Common::createGenteeInstallerArchive(f, "#setuppath#/", true, true);
if (!setupPackageArchive)
error("Couldn't load installer package '%s'", _gameDescription->desc.filesDescriptions[1].fileName);
SearchMan.add("VCruiseSetupPackage", setupPackageArchive);
}
syncSoundSettings();
// Figure out screen layout
Common::Point size;
if (_gameDescription->gameID == GID_AD2044) {
size = Common::Point(640, 480);
Common::Point traySize = Common::Point(640, 97);
Common::Point menuBarSize = Common::Point(188, 102);
Common::Point videoTL = Common::Point(20, 21);
Common::Point videoSize = Common::Point(432, 307);
_menuBarRect = Common::Rect(size.x - menuBarSize.x, 0, size.x, menuBarSize.y);
_videoRect = Common::Rect(videoTL, videoTL + videoSize);
_trayRect = Common::Rect(0, size.y - traySize.y, size.x, size.y);
_subtitleRect = Common::Rect(_videoRect.left, _videoRect.bottom, _videoRect.right, _trayRect.top);
} else {
Common::Point videoSize;
Common::Point traySize;
Common::Point menuBarSize;
if (_gameDescription->gameID == GID_REAH) {
videoSize = Common::Point(608, 348);
menuBarSize = Common::Point(640, 44);
traySize = Common::Point(640, 88);
} else if (_gameDescription->gameID == GID_SCHIZM) {
videoSize = Common::Point(640, 360);
menuBarSize = Common::Point(640, 32);
traySize = Common::Point(640, 88);
} else {
error("Unknown game");
}
size.x = videoSize.x;
if (menuBarSize.x > size.x)
size.x = menuBarSize.x;
if (traySize.x > size.x)
size.x = traySize.x;
size.y = videoSize.y + menuBarSize.y + traySize.y;
Common::Point menuTL = Common::Point((size.x - menuBarSize.x) / 2, 0);
Common::Point videoTL = Common::Point((size.x - videoSize.x) / 2, menuTL.y + menuBarSize.y);
Common::Point trayTL = Common::Point((size.x - traySize.x) / 2, videoTL.y + videoSize.y);
_menuBarRect = Common::Rect(menuTL.x, menuTL.y, menuTL.x + menuBarSize.x, menuTL.y + menuBarSize.y);
_videoRect = Common::Rect(videoTL.x, videoTL.y, videoTL.x + videoSize.x, videoTL.y + videoSize.y);
_trayRect = Common::Rect(trayTL.x, trayTL.y, trayTL.x + traySize.x, trayTL.y + traySize.y);
}
// TODO: Optionally support CLUT8 for AD 2044
initGraphics(size.x, size.y, nullptr);
if (_system->getScreenFormat().isCLUT8())
return Common::kUnsupportedColorMode;
_system->fillScreen(0);
_runtime.reset(new Runtime(_system, _mixer, midiDrv.get(), _rootFSNode, _gameDescription->gameID, _gameDescription->defaultLanguage));
_runtime->initSections(_videoRect, _menuBarRect, _trayRect, _subtitleRect, Common::Rect(640, 480), _system->getScreenFormat());
if (midiDrv)
midiDrv->setTimerCallback(this, VCruiseEngine::staticHandleMidiTimer);
const char *exeName = _gameDescription->desc.filesDescriptions[0].fileName;
if (_gameDescription->desc.flags & VCRUISE_GF_GENTEE_PACKAGE)
exeName = "Schizm.exe";
_runtime->loadCursors(exeName);
if (ConfMan.getBool("vcruise_debug")) {
_runtime->setDebugMode(true);
}
if (ConfMan.getBool("vcruise_fast_animations")) {
_runtime->setFastAnimationMode(true);
}
if (ConfMan.getBool("vcruise_preload_sounds")) {
_runtime->setPreloadSounds(true);
}
if (ConfMan.getBool("vcruise_use_4bit")) {
_runtime->setLowQualityGraphicsMode(true);
}
if (ConfMan.hasKey("save_slot")) {
int saveSlot = ConfMan.getInt("save_slot");
if (saveSlot >= 0) {
(void)_runtime->bootGame(false);
Common::Error err = loadGameState(saveSlot);
if (err.getCode() != Common::kNoError)
return err;
}
}
// Run the game
while (!shouldQuit()) {
handleEvents();
if (!_runtime->runFrame())
break;
_runtime->drawFrame();
_system->delayMillis(5);
}
if (midiDrv)
midiDrv->setTimerCallback(nullptr, nullptr);
_runtime.reset();
if (midiDrv)
midiDrv->close();
if (_gameDescription->desc.flags & VCRUISE_GF_GENTEE_PACKAGE)
SearchMan.remove("VCruiseInstallerPackage");
// Flush any settings changes made in-game
ConfMan.flushToDisk();
return Common::kNoError;
}
void VCruiseEngine::pauseEngineIntern(bool pause) {
Engine::pauseEngineIntern(pause);
}
bool VCruiseEngine::hasFeature(EngineFeature f) const {
switch (f) {
case kSupportsReturnToLauncher:
case kSupportsSavingDuringRuntime:
case kSupportsLoadingDuringRuntime:
return true;
default:
return false;
};
}
void VCruiseEngine::syncSoundSettings() {
// Sync the engine with the config manager
int soundVolumeMusic = ConfMan.getInt("music_volume");
int soundVolumeSFX = ConfMan.getInt("sfx_volume");
int soundVolumeSpeech = ConfMan.getInt("speech_volume");
bool mute = false;
if (ConfMan.hasKey("mute"))
mute = ConfMan.getBool("mute");
// We need to handle the speech mute separately here. This is because the
// engine code should be able to rely on all speech sounds muted when the
// user specified subtitles only mode, which results in "speech_mute" to
// be set to "true". The global mute setting has precedence over the
// speech mute setting though.
bool speechMute = mute;
if (!speechMute)
speechMute = ConfMan.getBool("speech_mute");
bool muteSound = ConfMan.getBool("vcruise_mute_sound");
if (ConfMan.hasKey("vcruise_mute_sound"))
muteSound = ConfMan.getBool("vcruise_mute_sound");
// We don't mute music here because Schizm has a special behavior that bypasses music mute when using one
// of the ships to transition zones.
_mixer->muteSoundType(Audio::Mixer::kPlainSoundType, mute || muteSound);
_mixer->muteSoundType(Audio::Mixer::kMusicSoundType, mute);
_mixer->muteSoundType(Audio::Mixer::kSFXSoundType, mute || muteSound);
_mixer->muteSoundType(Audio::Mixer::kSpeechSoundType, speechMute || muteSound);
_mixer->setVolumeForSoundType(Audio::Mixer::kPlainSoundType, Audio::Mixer::kMaxMixerVolume);
_mixer->setVolumeForSoundType(Audio::Mixer::kMusicSoundType, soundVolumeMusic);
_mixer->setVolumeForSoundType(Audio::Mixer::kSFXSoundType, soundVolumeSFX);
_mixer->setVolumeForSoundType(Audio::Mixer::kSpeechSoundType, soundVolumeSpeech);
}
Common::Error VCruiseEngine::saveGameStream(Common::WriteStream *stream, bool isAutosave) {
_runtime->saveGame(stream);
if (stream->err())
return Common::Error(Common::kWritingFailed);
return Common::Error(Common::kNoError);
}
Common::Error VCruiseEngine::loadGameStream(Common::SeekableReadStream *stream) {
LoadGameOutcome loadGameOutcome = _runtime->loadGame(stream);
switch (loadGameOutcome) {
case kLoadGameOutcomeSucceeded:
return Common::Error(Common::kNoError);
case kLoadGameOutcomeSaveDataCorrupted: {
GUI::MessageDialog dialog(_("Failed to load save, the save data appears to be damaged."));
dialog.runModal();
}
return Common::Error(Common::kReadingFailed);
case kLoadGameOutcomeMissingVersion: {
GUI::MessageDialog dialog(_("Failed to read version information from save file."));
dialog.runModal();
}
return Common::Error(Common::kReadingFailed);
case kLoadGameOutcomeInvalidVersion: {
GUI::MessageDialog dialog(_("Failed to load save, the save file doesn't contain valid version information."));
dialog.runModal();
}
return Common::Error(Common::kReadingFailed);
case kLoadGameOutcomeSaveIsTooNew: {
GUI::MessageDialog dialog(_("Saved game was created with a newer version of ScummVM. Unable to load."));
dialog.runModal();
}
return Common::Error(Common::kReadingFailed);
case kLoadGameOutcomeSaveIsTooOld: {
GUI::MessageDialog dialog(_("Saved game was created with an earlier, incompatible version of ScummVM. Unable to load."));
dialog.runModal();
}
return Common::Error(Common::kReadingFailed);
default: {
GUI::MessageDialog dialog(_("An unknown error occurred while attempting to load the saved game."));
dialog.runModal();
}
return Common::Error(Common::kReadingFailed);
};
return Common::Error(Common::kNoError);
}
bool VCruiseEngine::canSaveAutosaveCurrently() {
return _runtime->canSave(false);
}
bool VCruiseEngine::canSaveGameStateCurrently(Common::U32String *msg) {
return _runtime->canSave(false);
}
bool VCruiseEngine::canLoadGameStateCurrently(Common::U32String *msg) {
return _runtime->canLoad();
}
void VCruiseEngine::initializePath(const Common::FSNode &gamePath) {
Engine::initializePath(gamePath);
const char *gameSubPath = nullptr;
if (_gameDescription->desc.flags & VCRUISE_GF_GENTEE_PACKAGE) {
if (_gameDescription->gameID == GID_SCHIZM)
gameSubPath = "Schizm";
}
if (gameSubPath) {
const int kNumCasePasses = 3;
for (int casePass = 0; casePass < kNumCasePasses; casePass++) {
Common::String subPathStr(gameSubPath);
if (casePass == 1)
subPathStr.toUppercase();
else if (casePass == 2)
subPathStr.toLowercase();
Common::FSNode gameSubDir = gamePath.getChild(subPathStr);
if (gameSubDir.isDirectory()) {
SearchMan.addDirectory("VCruiseGameDir", gameSubDir, 0, 3);
break;
}
if (casePass != kNumCasePasses - 1)
warning("Expected to find subpath '%s' in the game directory but couldn't find it", gameSubPath);
}
}
_rootFSNode = gamePath;
}
bool VCruiseEngine::hasDefaultSave() {
return hasAnySave();
}
bool VCruiseEngine::hasAnySave() {
Common::StringArray saveFiles = getSaveFileManager()->listSavefiles(_targetName + ".*");
return saveFiles.size() > 0;
}
Common::Error VCruiseEngine::loadMostRecentSave() {
Common::SaveFileManager *sfm = getSaveFileManager();
Common::StringArray saveFiles = sfm->listSavefiles(_targetName + ".*");
uint64 highestDateTime = 0;
uint32 highestPlayTime = 0;
Common::String highestSaveFileName;
for (const Common::String &saveFileName : saveFiles) {
Common::InSaveFile *saveFile = sfm->openForLoading(saveFileName);
if (!saveFile) {
warning("Couldn't load save file '%s' to determine recency", saveFileName.c_str());
continue;
}
ExtendedSavegameHeader header;
bool loadedHeader = getMetaEngine()->readSavegameHeader(saveFile, &header, true);
if (!loadedHeader) {
warning("Couldn't parse header from '%s'", saveFileName.c_str());
continue;
}
uint8 day = 0;
uint8 month = 0;
uint16 year = 0;
uint8 hour = 0;
uint8 minute = 0;
MetaEngine::decodeSavegameDate(&header, year, month, day);
MetaEngine::decodeSavegameTime(&header, hour, minute);
uint64 dateTime = static_cast<uint64>(year) << 32;
dateTime |= static_cast<uint64>(month) << 24;
dateTime |= static_cast<uint64>(day) << 16;
dateTime |= static_cast<uint64>(hour) << 8;
dateTime |= minute;
uint32 playTime = header.playtime;
if (dateTime > highestDateTime || (dateTime == highestDateTime && playTime > highestPlayTime)) {
highestSaveFileName = saveFileName;
highestDateTime = dateTime;
highestPlayTime = playTime;
}
}
if (highestSaveFileName.empty()) {
warning("Couldn't find any valid saves to load");
return Common::Error(Common::kReadingFailed);
}
Common::String slotStr = highestSaveFileName.substr(_targetName.size() + 1);
int slot = 0;
if (sscanf(slotStr.c_str(), "%i", &slot) != 1) {
warning("Couldn't parse save slot ID from %s", highestSaveFileName.c_str());
return Common::Error(Common::kReadingFailed);
}
return loadGameState(slot);
}
} // End of namespace VCruise

90
engines/vcruise/vcruise.h 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/>.
*
*/
#ifndef VCRUISE_H
#define VCRUISE_H
#include "engines/engine.h"
#include "common/rect.h"
#include "vcruise/runtime.h"
#include "vcruise/detection.h"
/**
* This is the namespace of the V-Cruise engine.
*
* Status of this engine: ???
*
* Games using this engine:
* - Reah: Face the Unknown
* - Schizm: Mysterious Journey
*/
namespace VCruise {
class VCruiseEngine : public ::Engine {
protected:
// Engine APIs
Common::Error run() override;
public:
VCruiseEngine(OSystem *syst, const VCruiseGameDescription *gameDesc);
~VCruiseEngine() override;
bool hasFeature(EngineFeature f) const override;
void syncSoundSettings() override;
const VCruiseGameDescription *_gameDescription;
Common::Error saveGameStream(Common::WriteStream *stream, bool isAutosave) override;
Common::Error loadGameStream(Common::SeekableReadStream *stream) override;
bool canSaveAutosaveCurrently() override;
bool canSaveGameStateCurrently(Common::U32String *msg = nullptr) override;
bool canLoadGameStateCurrently(Common::U32String *msg = nullptr) override;
void initializePath(const Common::FSNode &gamePath) override;
bool hasDefaultSave();
bool hasAnySave();
Common::Error loadMostRecentSave();
protected:
void pauseEngineIntern(bool pause) override;
private:
void handleEvents();
static void staticHandleMidiTimer(void *refCon);
void handleMidiTimer();
Common::Rect _videoRect;
Common::Rect _menuBarRect;
Common::Rect _trayRect;
Common::Rect _subtitleRect;
Common::FSNode _rootFSNode;
Common::SharedPtr<Runtime> _runtime;
};
} // End of namespace VCruise
#endif /* VCRUISE_H */