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

4
engines/sword1/POTFILES Normal file
View File

@@ -0,0 +1,4 @@
engines/sword1/animation.cpp
engines/sword1/control.cpp
engines/sword1/logic.cpp
engines/sword1/metaengine.cpp

View File

@@ -0,0 +1,581 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "common/file.h"
#include "common/events.h"
#include "common/keyboard.h"
#include "common/textconsole.h"
#include "common/translation.h"
#include "sword1/sword1.h"
#include "sword1/animation.h"
#include "sword1/text.h"
#include "sword1/resman.h"
#include "common/str.h"
#include "common/system.h"
#include "graphics/paletteman.h"
#include "graphics/surface.h"
#include "gui/message.h"
#ifdef USE_MPEG2
#include "video/avi_decoder.h"
#endif
#include "video/dxa_decoder.h"
#include "video/psx_decoder.h"
#include "video/smk_decoder.h"
#include "engines/util.h"
namespace Sword1 {
static const char *const sequenceList[20] = {
"ferrari", // 0 CD2 ferrari running down fitz in sc19
"ladder", // 1 CD2 george walking down ladder to dig sc24->sc$
"steps", // 2 CD2 george walking down steps sc23->sc24
"sewer", // 3 CD1 george entering sewer sc2->sc6
"intro", // 4 CD1 intro sequence ->sc1
"river", // 5 CD1 george being thrown into river by flap & g$
"truck", // 6 CD2 truck arriving at bull's head sc45->sc53/4
"grave", // 7 BOTH george's grave in scotland, from sc73 + from sc38 $
"montfcon", // 8 CD2 monfaucon clue in ireland dig, sc25
"tapestry", // 9 CD2 tapestry room beyond spain well, sc61
"ireland", // 10 CD2 ireland establishing shot europe_map->sc19
"finale", // 11 CD2 grand finale at very end, from sc73
"history", // 12 CD1 George's history lesson from Nico, in sc10
"spanish", // 13 CD2 establishing shot for 1st visit to Spain, europe_m$
"well", // 14 CD2 first time being lowered down well in Spai$
"candle", // 15 CD2 Candle burning down in Spain mausoleum sc59
"geodrop", // 16 CD2 from sc54, George jumping down onto truck
"vulture", // 17 CD2 from sc54, vultures circling George's dead body
"enddemo", // 18 --- for end of single CD demo
"credits", // 19 CD2 credits, to follow "finale" sequence
};
// This is the list of the names of the PlayStation videos
// TODO: fight.str, flashy.str,
static const char *const sequenceListPSX[20] = {
"e_ferr1",
"ladder1",
"steps1",
"sewer1",
"e_intro1",
"river1",
"truck1",
"grave1",
"montfcn1",
"tapesty1",
"ireland1",
"e_fin1",
"e_hist1",
"spanish1",
"well1",
"candle1",
"geodrop1",
"vulture1",
"", // demo video not present
"" // credits are not a video
};
///////////////////////////////////////////////////////////////////////////////
// Basic movie player
///////////////////////////////////////////////////////////////////////////////
MoviePlayer::MoviePlayer(SwordEngine *vm, Text *textMan, ResMan *resMan, Sound *sound, OSystem *system, Video::VideoDecoder *decoder, DecoderType decoderType)
: _vm(vm), _textMan(textMan), _resMan(resMan), _sound(sound), _system(system), _textX(0), _textY(0), _textWidth(0), _textHeight(0), _textColor(1), _modeChange(false) {
_decoderType = decoderType;
_decoder = decoder;
_c1Color = _c2Color = _c3Color = _c4Color = 255;
_black = 0;
}
MoviePlayer::~MoviePlayer() {
delete _decoder;
}
/**
* Plays an animated cutscene.
* @param id the id of the file
*/
Common::Error MoviePlayer::load(uint32 id) {
Common::Path filename;
if (SwordEngine::_systemVars.showText) {
Common::File f;
filename = Common::Path(Common::String::format("%s.txt", sequenceList[id]));
if (f.open(filename)) {
Common::String line;
int lineNo = 0;
int lastEnd = -1;
_movieTexts.clear();
while (!f.eos() && !f.err()) {
line = f.readLine();
lineNo++;
if (line.empty() || line[0] == '#') {
continue;
}
const char *ptr = line.c_str();
// TODO: Better error handling
int startFrame = strtoul(ptr, const_cast<char **>(&ptr), 10);
int endFrame = strtoul(ptr, const_cast<char **>(&ptr), 10);
while (*ptr && Common::isSpace(*ptr))
ptr++;
if (startFrame > endFrame) {
warning("%s:%d: startFrame (%d) > endFrame (%d)", filename.toString().c_str(), lineNo, startFrame, endFrame);
continue;
}
if (startFrame <= lastEnd) {
warning("%s:%d startFrame (%d) <= lastEnd (%d)", filename.toString().c_str(), lineNo, startFrame, lastEnd);
continue;
}
int color = 0;
if (*ptr == '@') {
++ptr;
color = strtoul(ptr, const_cast<char **>(&ptr), 10);
while (*ptr && Common::isSpace(*ptr))
ptr++;
}
_movieTexts.push_back(MovieText(startFrame, endFrame, ptr, color));
lastEnd = endFrame;
}
}
}
switch (_decoderType) {
case kVideoDecoderDXA:
filename = Common::Path(Common::String::format("%s.dxa", sequenceList[id]));
break;
case kVideoDecoderSMK:
filename = Common::Path(Common::String::format("%s.smk", sequenceList[id]));
break;
case kVideoDecoderPSX:
filename = Common::Path(Common::String::format("%s.str", (_vm->_systemVars.isDemo && id == 4) ? "intro" : sequenceListPSX[id]));
break;
case kVideoDecoderMP2:
filename = Common::Path(Common::String::format("%s.mp2", sequenceList[id]));
break;
default:
break;
}
if (!_decoder->loadFile(filename)) {
return Common::Error(Common::kPathDoesNotExist, filename.toString());
}
// Need to switch to true color for PSX/MP2 videos
if (!_decoder->getPixelFormat().isCLUT8()) {
if (!_decoder->setOutputPixelFormats(g_system->getSupportedFormats()))
return Common::kUnsupportedColorMode;
Graphics::PixelFormat format = _decoder->getPixelFormat();
initGraphics(g_system->getWidth(), g_system->getHeight(), &format);
if (g_system->getScreenFormat() != format) {
return Common::kUnsupportedColorMode;
} else {
_modeChange = true;
}
}
// For DXA/MP2, also add the external sound file
if (_decoderType == kVideoDecoderDXA || _decoderType == kVideoDecoderMP2)
_decoder->addStreamFileTrack(sequenceList[id]);
_decoder->start();
return Common::kNoError;
}
void MoviePlayer::play() {
_textX = 0;
_textY = 0;
playVideo();
_textMan->releaseText(2, false);
_movieTexts.clear();
// It's tempting to call _screen->fullRefresh() here to restore the old
// palette. However, that causes glitches with DXA movies, where the
// previous location would be momentarily drawn, before switching to
// the new one. Work around this by setting the palette to black.
byte pal[3 * 256];
memset(pal, 0, sizeof(pal));
_system->getPaletteManager()->setPalette(pal, 0, 256);
}
void MoviePlayer::performPostProcessing(byte *screen) {
// TODO: We don't support displaying these in true color yet,
// nor using the PSX fonts to display subtitles.
if (_vm->isPsx() || _modeChange)
return;
if (!_movieTexts.empty()) {
if (_decoder->getCurFrame() == _movieTexts.front()._startFrame) {
_textMan->makeTextSprite(2, (const uint8 *)_movieTexts.front()._text.c_str(), 600, LETTER_COL);
FrameHeader *frame = _textMan->giveSpriteData(2);
_textWidth = _resMan->toUint16(frame->width);
_textHeight = _resMan->toUint16(frame->height);
_textX = 320 - _textWidth / 2;
_textY = 420 - _textHeight;
_textColor = _movieTexts.front()._color;
}
if (_decoder->getCurFrame() == _movieTexts.front()._endFrame) {
_textMan->releaseText(2, false);
_movieTexts.pop_front();
}
}
byte *src, *dst;
int x, y;
if (_textMan->giveSpriteData(2)) {
src = (byte *)_textMan->giveSpriteData(2) + sizeof(FrameHeader);
dst = screen + _textY * SCREEN_WIDTH + _textX * 1;
for (y = 0; y < _textHeight; y++) {
for (x = 0; x < _textWidth; x++) {
switch (src[x]) {
case BORDER_COL:
dst[x] = getBlackColor();
break;
case LETTER_COL:
dst[x] = findTextColor();
break;
default:
break;
}
}
src += _textWidth;
dst += SCREEN_WIDTH;
}
} else if (_textX && _textY) {
// If the frame doesn't cover the entire screen, we have to
// erase the subtitles manually.
int frameWidth = _decoder->getWidth();
int frameHeight = _decoder->getHeight();
int frameX = (_system->getWidth() - frameWidth) / 2;
int frameY = (_system->getHeight() - frameHeight) / 2;
dst = screen + _textY * _system->getWidth();
for (y = 0; y < _textHeight; y++) {
if (_textY + y < frameY || _textY + y >= frameY + frameHeight) {
memset(dst + _textX, getBlackColor(), _textWidth);
} else {
if (frameX > _textX)
memset(dst + _textX, getBlackColor(), frameX - _textX);
if (frameX + frameWidth < _textX + _textWidth)
memset(dst + frameX + frameWidth, getBlackColor(), _textX + _textWidth - (frameX + frameWidth));
}
dst += _system->getWidth();
}
_textX = 0;
_textY = 0;
}
}
bool MoviePlayer::playVideo() {
bool skipped = false;
uint16 x = (g_system->getWidth() - _decoder->getWidth()) / 2;
uint16 y = (g_system->getHeight() - _decoder->getHeight()) / 2;
while (!_vm->shouldQuit() && !_decoder->endOfVideo() && !skipped) {
if (_decoder->needsUpdate()) {
const Graphics::Surface *frame = _decoder->decodeNextFrame();
if (frame) {
if (_decoderType == kVideoDecoderPSX)
drawFramePSX(frame);
else
_vm->_system->copyRectToScreen(frame->getPixels(), frame->pitch, x, y, frame->w, frame->h);
}
_sound->setCrossFadeIncrement();
_sound->updateMusicStreaming();
if (_decoder->hasDirtyPalette()) {
_vm->_system->getPaletteManager()->setPalette(_decoder->getPalette(), 0, 256);
if (!_movieTexts.empty()) {
// Look for the best color indexes to use to display the subtitles
uint32 minWeight = 0xFFFFFFFF;
uint32 weight;
float c1Weight = 1e+30f;
float c2Weight = 1e+30f;
float c3Weight = 1e+30f;
float c4Weight = 1e+30f;
byte r, g, b;
float h, s, v, hd, hsvWeight;
const byte *palette = _decoder->getPalette();
// Color comparaison for the subtitles colors is done in HSL
// C1 color is used for George and is almost white (R = 248, G = 252, B = 248)
const float h1 = 0.333333f, s1 = 0.02f, v1 = 0.99f;
// C2 color is used for George as a narrator and is grey (R = 184, G = 188, B = 184)
const float h2 = 0.333333f, s2 = 0.02f, v2 = 0.74f;
// C3 color is used for Nicole and is rose (R = 200, G = 120, B = 184)
const float h3 = 0.866667f, s3 = 0.4f, v3 = 0.78f;
// C4 color is used for Maguire and is blue (R = 80, G = 152, B = 184)
const float h4 = 0.55f, s4 = 0.57f, v4 = 0.72f;
for (int i = 0; i < 256; i++) {
r = *palette++;
g = *palette++;
b = *palette++;
weight = 3 * r * r + 6 * g * g + 2 * b * b;
if (weight <= minWeight) {
minWeight = weight;
_black = i;
}
convertColor(r, g, b, h, s, v);
// C1 color
// It is almost achromatic (very low saturation) so the hue as litle impact on the color.
// Therefore use a low weight on hue and high weight on saturation.
hd = h - h1;
hd += hd < -0.5f ? 1.0f : hd > 0.5f ? -1.0f : 0.0f;
hsvWeight = 1.0f * hd * hd + 4.0f * (s - s1) * (s - s1) + 3.0f * (v - v1) * (v - v1);
if (hsvWeight <= c1Weight) {
c1Weight = hsvWeight;
_c1Color = i;
}
// C2 color
// Also an almost achromatic color so use the same weights as for C1 color.
hd = h - h2;
hd += hd < -0.5f ? 1.0f : hd > 0.5f ? -1.0f : 0.0f;
hsvWeight = 1.0f * hd * hd + 4.0f * (s - s2) * (s - s2) + 3.0f * (v - v2) * (v - v2);
if (hsvWeight <= c2Weight) {
c2Weight = hsvWeight;
_c2Color = i;
}
// C3 color
// A light rose. Use a high weight on the hue to get a rose.
// The color is a bit gray and the saturation has not much impact so use a low weight.
hd = h - h3;
hd += hd < -0.5f ? 1.0f : hd > 0.5f ? -1.0f : 0.0f;
hsvWeight = 4.0f * hd * hd + 1.0f * (s - s3) * (s - s3) + 2.0f * (v - v3) * (v - v3);
if (hsvWeight <= c3Weight) {
c3Weight = hsvWeight;
_c3Color = i;
}
// C4 color
// Blue. Use a hight weight on the hue to get a blue.
// The color is darker and more saturated than C3 and the saturation has more impact.
hd = h - h4;
hd += hd < -0.5f ? 1.0f : hd > 0.5f ? -1.0f : 0.0f;
hsvWeight = 5.0f * hd * hd + 3.0f * (s - s4) * (s - s4) + 2.0f * (v - v4) * (v - v4);
if (hsvWeight <= c4Weight) {
c4Weight = hsvWeight;
_c4Color = i;
}
}
}
}
Graphics::Surface *screen = _vm->_system->lockScreen();
performPostProcessing((byte *)screen->getPixels());
_vm->_system->unlockScreen();
_vm->_system->updateScreen();
}
Common::Event event;
while (_vm->_system->getEventManager()->pollEvent(event))
if ((event.type == Common::EVENT_CUSTOM_ENGINE_ACTION_START && event.customType == kActionEscape) || event.type == Common::EVENT_LBUTTONUP)
skipped = true;
_vm->_system->delayMillis(10);
}
// Need to jump back to paletted color
if (_modeChange) {
initGraphics(g_system->getWidth(), g_system->getHeight());
_modeChange = false;
}
return !_vm->shouldQuit() && !skipped;
}
uint32 MoviePlayer::getBlackColor() {
return (_modeChange) ? g_system->getScreenFormat().RGBToColor(0x00, 0x00, 0x00) : _black;
}
uint32 MoviePlayer::findTextColor() {
if (_modeChange) {
// We're in true color mode, so return the actual colors
switch (_textColor) {
case 1:
return g_system->getScreenFormat().RGBToColor(248, 252, 248);
case 2:
return g_system->getScreenFormat().RGBToColor(184, 188, 184);
case 3:
return g_system->getScreenFormat().RGBToColor(200, 120, 184);
case 4:
return g_system->getScreenFormat().RGBToColor(80, 152, 184);
default:
break;
}
return g_system->getScreenFormat().RGBToColor(0xFF, 0xFF, 0xFF);
}
switch (_textColor) {
case 1:
return _c1Color;
case 2:
return _c2Color;
case 3:
return _c3Color;
case 4:
return _c4Color;
default:
break;
}
return _c1Color;
}
void MoviePlayer::convertColor(byte r, byte g, byte b, float &h, float &s, float &v) {
float varR = r / 255.0f;
float varG = g / 255.0f;
float varB = b / 255.0f;
float min = MIN(varR, MIN(varG, varB));
float max = MAX(varR, MAX(varG, varB));
v = max;
float d = max - min;
s = max == 0.0f ? 0.0f : d / max;
if (min == max) {
h = 0.0f; // achromatic
} else {
if (max == varR)
h = (varG - varB) / d + (varG < varB ? 6.0f : 0.0f);
else if (max == varG)
h = (varB - varR) / d + 2.0f;
else
h = (varR - varG) / d + 4.0f;
h /= 6.0f;
}
}
void MoviePlayer::drawFramePSX(const Graphics::Surface *frame) {
// The PSX videos have half resolution
Graphics::Surface scaledFrame;
scaledFrame.create(frame->w, frame->h * 2, frame->format);
for (int y = 0; y < scaledFrame.h; y++)
memcpy(scaledFrame.getBasePtr(0, y), frame->getBasePtr(0, y / 2), scaledFrame.w * scaledFrame.format.bytesPerPixel);
uint16 x = (g_system->getWidth() - scaledFrame.w) / 2;
uint16 y = (g_system->getHeight() - scaledFrame.h) / 2;
_vm->_system->copyRectToScreen(scaledFrame.getPixels(), scaledFrame.pitch, x, y, scaledFrame.w, scaledFrame.h);
scaledFrame.free();
}
///////////////////////////////////////////////////////////////////////////////
// Factory function for creating the appropriate cutscene player
///////////////////////////////////////////////////////////////////////////////
MoviePlayer *makeMoviePlayer(uint32 id, SwordEngine *vm, Text *textMan, ResMan *resMan, Sound *sound, OSystem *system) {
Common::Path filename;
// For the PSX version, we'll try the PlayStation stream files
if (vm->isPsx()) {
// The demo uses the normal file names for the intro cutscene
filename = ((vm->_systemVars.isDemo && id == 4) ? Common::Path("intro.str") : Common::Path(Common::String(sequenceListPSX[id]) + ".str"));
if (Common::File::exists(filename)) {
// All BS1 PSX videos run the videos at 2x speed
Video::VideoDecoder *psxDecoder = new Video::PSXStreamDecoder(Video::PSXStreamDecoder::kCD2x);
return new MoviePlayer(vm, textMan, resMan, sound, system, psxDecoder, kVideoDecoderPSX);
}
}
filename = Common::Path(Common::String::format("%s.smk", sequenceList[id]));
if (Common::File::exists(filename)) {
Video::SmackerDecoder *smkDecoder = new Video::SmackerDecoder();
return new MoviePlayer(vm, textMan, resMan, sound, system, smkDecoder, kVideoDecoderSMK);
}
filename = Common::Path(Common::String::format("%s.dxa", sequenceList[id]));
if (Common::File::exists(filename)) {
Video::VideoDecoder *dxaDecoder = new Video::DXADecoder();
return new MoviePlayer(vm, textMan, resMan, sound, system, dxaDecoder, kVideoDecoderDXA);
}
// Old MPEG2 cutscenes
filename = Common::Path(Common::String::format("%s.mp2", sequenceList[id]));
if (Common::File::exists(filename)) {
#ifdef USE_MPEG2
// HACK: Old ScummVM builds ignored the AVI frame rate field and forced the video
// to be played back at 12fps.
Video::VideoDecoder *aviDecoder = new Video::AVIDecoder(12);
return new MoviePlayer(vm, textMan, resMan, sound, system, aviDecoder, kVideoDecoderMP2);
#else
GUI::MessageDialog dialog(_("MPEG-2 cutscenes found but ScummVM has been built without MPEG-2 support"));
dialog.runModal();
return 0;
#endif
}
if (!vm->isPsx() || scumm_stricmp(sequenceList[id], "enddemo") != 0) {
Common::U32String buf = Common::U32String::format(_("Cutscene '%s' not found"), sequenceList[id]);
GUI::MessageDialog dialog(buf);
dialog.runModal();
}
return 0;
}
} // End of namespace Sword1

View File

@@ -0,0 +1,97 @@
/* 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 SWORD1_ANIMATION_H
#define SWORD1_ANIMATION_H
#include "common/list.h"
#include "sword1/screen.h"
#include "sword1/sound.h"
namespace Graphics {
struct Surface;
}
namespace Video {
class VideoDecoder;
}
namespace Sword1 {
enum DecoderType {
kVideoDecoderDXA = 0,
kVideoDecoderSMK = 1,
kVideoDecoderPSX = 2,
kVideoDecoderMP2 = 3
};
class MovieText {
public:
uint16 _startFrame;
uint16 _endFrame;
uint16 _color;
Common::String _text;
MovieText(int startFrame, int endFrame, const Common::String &text, int color) {
_startFrame = startFrame;
_endFrame = endFrame;
_text = text;
_color = color;
}
};
class MoviePlayer {
public:
MoviePlayer(SwordEngine *vm, Text *textMan, ResMan *resMan, Sound *sound, OSystem *system, Video::VideoDecoder *decoder, DecoderType decoderType);
virtual ~MoviePlayer();
Common::Error load(uint32 id);
void play();
protected:
SwordEngine *_vm;
Text *_textMan;
ResMan *_resMan;
Sound *_sound;
OSystem *_system;
Common::List<MovieText> _movieTexts;
int _textX, _textY, _textWidth, _textHeight;
int _textColor;
uint32 _black;
uint32 _c1Color, _c2Color, _c3Color, _c4Color;
DecoderType _decoderType;
bool _modeChange;
Video::VideoDecoder *_decoder;
bool playVideo();
void performPostProcessing(byte *screen);
void drawFramePSX(const Graphics::Surface *frame);
uint32 getBlackColor();
uint32 findTextColor();
void convertColor(byte r, byte g, byte b, float &h, float &s, float &v);
};
MoviePlayer *makeMoviePlayer(uint32 id, SwordEngine *vm, Text *textMan, ResMan *resMan, Sound *sound, OSystem *system);
} // End of namespace Sword1
#endif

View File

@@ -0,0 +1,49 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef SWORD1_COLLISION_H
#define SWORD1_COLLISION_H
/*#include "objectman.h"
namespace Sword1 {
class Logic;
class Collision {
public:
Collision(ObjectMan *pObjMan, Logic *pLogic);
~Collision();
void checkCollisions();
void fnBumpOff();
void fnBumpOn();
private:
int32 getIntersect(int32 x0, int32 y0, int32 x1, int32 y1, int32 x2, int32 y2, int32 x3, int32 y3);
int noCol;
ObjectMan *_objMan;
Logic *_logic; // for CFN_preset_script
};
} // End of namespace Sword1
*/
// maybe it's better to make this part of Router
#endif // BSCOLLISION_H

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 sword1 "Broken Sword" yes "" "" "highres" "mpeg2"

View File

@@ -0,0 +1,57 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "sword1/console.h"
#include "sword1/sword1.h"
#include "sword1/sound.h"
#include "common/config-manager.h"
#include "common/str.h"
namespace Sword1 {
SwordConsole::SwordConsole(SwordEngine *vm) : GUI::Debugger(), _vm(vm) {
assert(_vm);
if (_vm->isMac())
registerCmd("speechEndianness", WRAP_METHOD(SwordConsole, Cmd_SpeechEndianness));
}
SwordConsole::~SwordConsole() {
}
bool SwordConsole::Cmd_SpeechEndianness(int argc, const char **argv) {
if (argc == 1) {
debugPrintf("Using %s speech\n", _vm->_sound->_bigEndianSpeech ? "be" : "le");
return true;
}
if (argc == 2) {
if (scumm_stricmp(argv[1], "le") == 0) {
_vm->_sound->_bigEndianSpeech = false;
return false;
} else if (scumm_stricmp(argv[1], "be") == 0) {
_vm->_sound->_bigEndianSpeech = true;
return false;
}
}
debugPrintf("Usage: %s [le | be]\n", argv[0]);
return true;
}
} // End of namespace Sword

43
engines/sword1/console.h Normal file
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 SWORD1_CONSOLE_H
#define SWORD1_CONSOLE_H
#include "gui/debugger.h"
namespace Sword1 {
class SwordEngine;
class SwordConsole : public GUI::Debugger {
public:
SwordConsole(SwordEngine *vm);
~SwordConsole(void) override;
private:
SwordEngine *_vm;
bool Cmd_SpeechEndianness(int argc, const char **argv);
};
} // End of namespace Sword1
#endif

3744
engines/sword1/control.cpp Normal file

File diff suppressed because it is too large Load Diff

276
engines/sword1/control.h Normal file
View File

@@ -0,0 +1,276 @@
/* 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 SWORD1_CONTROL_H
#define SWORD1_CONTROL_H
#include "common/scummsys.h"
#include "common/events.h"
#include "common/str-array.h"
#include "sword1/sworddefs.h"
class OSystem;
namespace Common {
class SaveFileManager;
class MemoryWriteStreamDynamic;
}
namespace Sword1 {
enum SNRStatus {
SNR_BLANK = 0,
SNR_MAINPANEL,
SNR_SAVE,
SNR_RESTORE,
SNR_RESTART,
SNR_QUIT,
SNR_SPEED,
SNR_VOLUME,
SNR_SUBTITLES,
SNR_DONE,
SNR_DRIVEFULL = 99
};
enum SaveGameFlags {
SGF_DONE = 0,
SGF_SAVE,
SGF_RESTORE,
SGF_RESTART,
SGF_QUIT
};
enum PsxComponents {
PSX_PANEL = 0,
PSX_DEATHPANEL,
PSX_CONFIRM,
PSX_BUTTON,
PSX_TEXT,
PSX_SLAB,
PSX_SCROLL
};
class SwordEngine;
class ObjectMan;
class ResMan;
class Mouse;
class Sound;
class Screen;
class Logic;
#define SAVEGAME_HEADER MKTAG('B','S','_','1')
#define SAVEGAME_VERSION 2
#define FIRSTFONTCHAR ' '
#define LASTFONTCHAR (32 + 137)
#define CR 13
#define LF 10
#define ESCAPE 27
#define BACKSPACE 8
#define CONTROL_NOTHING_DONE 0
#define CONTROL_GAME_RESTORED 1
#define CONTROL_RESTART_GAME 2
#define VD1X 139
#define VD2X 273
#define VD3X 404
#define VDY 94
#define SCROLL1X 311
#define SCROLL1Y 124
#define SCROLL2X 311
#define SCROLL2Y 188
#define SAVEBUTTONS 14
#define MAXSAVEGAMES 1000
#define OVERLAP 3
#define SP_OVERLAP 2
#define TEXTBUTTONID 7
#define PSX_CREDITS_SPACING (-3)
#define PSX_CREDITS_MIDDLE 450
#define PSX_CREDITS_OFFSET 150
#define PSX_NUM_CREDITS 14
struct Button {
int32 x1;
int32 y1;
int32 x2;
int32 y2;
};
class Control {
public:
Control(SwordEngine *vm, Common::SaveFileManager *saveFileMan, ResMan *pResMan, ObjectMan *pObjMan, OSystem *system, Mouse *pMouse, Sound *pSound, Screen *pScreen, Logic *pLogic);
void getPlayerOptions();
void askForCdMessage(uint32 needCD, bool incorrectCDPhase);
void doRestore();
bool savegamesExist();
void saveGameToFile(uint8 slot);
bool restoreGameFromFile(uint8 slot);
bool restoreGame();
void checkForOldSaveGames();
bool isPanelShown();
const uint8 *getPauseString();
void psxEndCredits();
void setSaveDescription(int slot, const char *desc) {
Common::strcpy_s((char *)_fileDescriptions[slot], sizeof(_fileDescriptions[slot]), desc);
}
private:
void saveRestoreScreen();
void renderSlab(int32 start, int32 i);
void renderSlabs();
void renderText(const uint8 *str, int32 x, int32 y, bool useSpeechFont = false);
void renderRedText(const uint8 *str, int32 x, int32 y);
void renderTexts();
int32 getTextLength(const uint8 *str, bool useSpeechFont = false);
void putButton(int32 x, int32 y, int32 index);
void putSpriteButton(Sprite *spr, int32 x, int32 y, int32 index);
void putTextButton(int32 index);
int32 getCurrentButton(const Button b[]);
void initialiseConfirmation(const uint8 *title);
int32 implementConfirmation();
void removeConfirmation();
void volUp(int32 i, int32 j);
void volDown(int32 i, int32 j);
void renderVolumeLight(int32 i);
void renderVolumeDisc(int32 i, int32 j);
void initialiseVolume();
void implementVolume();
void removeVolume();
void renderScrolls();
void initialiseSpeed();
void implementSpeed();
void removeSpeed();
int16 readFileDescriptions();
void setEditDescription(int32 line);
bool driveSpaceAvailable();
bool attemptSave();
bool saveGame();
void editDescription();
void restoreSelected();
void uneditDescription();
void initialiseSave();
void implementSave();
void removeSave();
void initialiseRestore();
void implementRestore();
void removeRestore();
void initialiseControlPanel();
void implementControlPanel();
void removeControlPanel();
void initialiseResources();
void releaseResources();
uint8 *decompressPsxGfx(uint8 *src, FrameHeader *f);
void drawPsxComponent(int componentType, uint8 *src, uint8 *dst, FrameHeader *f);
bool convertSaveGame(uint8 slot, char *desc);
void delay(uint32 msecs);
bool gameVersionIsAkella();
bool gameVersionIsMediaHouse();
bool loadCustomStrings(const char *filename);
int displayMessage(MSVC_PRINTF const char *message, ...) GCC_PRINTF(2, 3);
// PSX Credits functions
int32 getCreditsFontHeight(uint8 *font);
int32 getCreditsStringLength(uint8 *str, uint8 *font);
void renderCreditsTextSprite(uint8 *data, uint8 *dst, int16 x, int16 y, int16 width, int16 height);
void createCreditsTextSprite(uint8 *data, int32 pitch, uint8 *str, uint8 *font);
Common::MemoryWriteStreamDynamic *_tempThumbnail;
static const uint8 _languageStrings[8 * 20][43];
static const uint8 _akellaLanguageStrings[20][43];
static const uint8 _mediaHouseLanguageStrings[20][43];
static const uint8 _polishTranslationLanguageStrings[20][43];
uint8 _customStrings[20][43];
const uint8(*_lStrings)[43];
const uint8 _psxPauseStrings[3][7] = { "Paused", "Pause", "Pausa" };
SwordEngine *_vm;
Common::SaveFileManager *_saveFileMan;
ObjectMan *_objMan;
ResMan *_resMan;
OSystem *_system;
Mouse *_mouse;
Sound *_sound;
Screen *_screen;
Logic *_logic;
uint8 *_screenBuf;
Common::KeyState _keyPressed;
Common::CustomEventType _customType;
Common::Point _mouseCoord;
uint16 _mouseState;
int _oldSnrStatus = SNR_BLANK;
bool _newPal = false;
Sprite *_slabs[SAVEBUTTONS - 6];
int32 _scrollIndex[2] = { 0, 0 };
int32 _speedFlag = 0;
int32 _currentButton = 0;
int32 _buttonPressed = 0;
int32 _buttonHold = 0;
int32 _slabSelected = 0;
int32 _firstDescription = 0;
byte _fileDescriptions[MAXSAVEGAMES][40];
int32 _editingDescription = 0;
int32 _gamesSaved = 0;
int32 _textCursor;
int32 _curCharCount;
char _oldString[40];
int32 _scroll = 0;
int32 _scrollCount = 0;
uint8 *_restoreBuf = nullptr;
uint32 _selectedSavegame = 0;
uint8 _numButtons = 0;
uint8 _selectedButton = 0;
bool _panelShown = false;
static const Button panelButtons[8];
// We want the death screen buttons to have
// the same numbers as the panel buttons
static const Button deathButtons[8];
static const Button confirmButtons[2];
static const Button speedButtons[3];
static const Button saveButtons[SAVEBUTTONS];
static const Button restoreButtons[SAVEBUTTONS];
static const Button volumeButtons[25];
};
} // End of namespace Sword1
#endif //BSCONTROL_H

View File

@@ -0,0 +1,6 @@
begin_section("Sword1");
add_person("Fabio Battaglia", "Hkz", "PSX version support");
add_person("Andrea Boscarino", "AndywinXp", "Palette fading, menus, audio");
add_person("Thierry Crozat", "criezy", "Mac version support");
add_person("Robert G&ouml;ffringmann", "lavosspawn", "(retired)");
end_section();

139
engines/sword1/debug.cpp Normal file
View File

@@ -0,0 +1,139 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "common/debug.h"
#include "common/util.h"
#include "sword1/debug.h"
namespace Sword1 {
void Debug::interpretScript(uint32 id, uint32 level, uint32 script, uint32 pc) {
debug(8, "\nInterpreting %d@%d: script %X from %X", id, level, script, pc);
}
void Debug::callMCode(uint32 mcodeNum, uint32 paramCount, int32 a, int32 b, int32 c, int32 d, int32 e, int32 f) {
debug(9, "mcode: %s(%d, %d, %d, %d, %d, %d) [%d]", _mCodeNames[mcodeNum], a, b, c, d, e, f, paramCount);
}
const char Debug::_mCodeNames[100][35] = {
"fnBackground",
"fnForeground",
"fnSort",
"fnNoSprite",
"fnMegaSet",
"fnAnim",
"fnSetFrame",
"fnFullAnim",
"fnFullSetFrame",
"fnFadeDown",
"fnFadeUp",
"fnCheckFade",
"fnSetSpritePalette",
"fnSetWholePalette",
"fnSetFadeTargetPalette",
"fnSetPaletteToFade",
"fnSetPaletteToCut",
"fnPlaySequence",
"fnIdle",
"fnPause",
"fnPauseSeconds",
"fnQuit",
"fnKillId",
"fnSuicide",
"fnNewScript",
"fnSubScript",
"fnRestartScript",
"fnSetBookmark",
"fnGotoBookmark",
"fnSendSync",
"fnWaitSync",
"cfnClickInteract",
"cfnSetScript",
"cfnPresetScript",
"fnInteract",
"fnIssueEvent",
"fnCheckForEvent",
"fnWipeHands",
"fnISpeak",
"fnTheyDo",
"fnTheyDoWeWait",
"fnWeWait",
"fnChangeSpeechText",
"fnTalkError",
"fnStartTalk",
"fnCheckForTextLine",
"fnAddTalkWaitStatusBit",
"fnRemoveTalkWaitStatusBit",
"fnNoHuman",
"fnAddHuman",
"fnBlankMouse",
"fnNormalMouse",
"fnLockMouse",
"fnUnlockMouse",
"fnSetMousePointer",
"fnSetMouseLuggage",
"fnMouseOn",
"fnMouseOff",
"fnChooser",
"fnEndChooser",
"fnStartMenu",
"fnEndMenu",
"cfnReleaseMenu",
"fnAddSubject",
"fnAddObject",
"fnRemoveObject",
"fnEnterSection",
"fnLeaveSection",
"fnChangeFloor",
"fnWalk",
"fnTurn",
"fnStand",
"fnStandAt",
"fnFace",
"fnFaceXy",
"fnIsFacing",
"fnGetTo",
"fnGetToError",
"fnGetPos",
"fnGetGamepadXy",
"fnPlayFx",
"fnStopFx",
"fnPlayMusic",
"fnStopMusic",
"fnInnerSpace",
"fnRandom",
"fnSetScreen",
"fnPreload",
"fnCheckCD",
"fnRestartGame",
"fnQuitGame",
"fnDeathScreen",
"fnSetParallax",
"fnTdebug",
"fnRedFlash",
"fnBlueFlash",
"fnYellow",
"fnGreen",
"fnPurple",
"fnBlack"
};
} // End of namespace Sword1

40
engines/sword1/debug.h Normal file
View File

@@ -0,0 +1,40 @@
/* 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 SWORD1_DEBUG_H
#define SWORD1_DEBUG_H
#include "common/scummsys.h"
namespace Sword1 {
class Debug {
public:
static void interpretScript(uint32 id, uint32 level, uint32 script, uint32 pc);
static void callMCode(uint32 mcodeNum, uint32 paramCount, int32 a, int32 b, int32 c, int32 d, int32 e, int32 f);
private:
static const char _mCodeNames[100][35];
};
} // End of namespace Sword1
#endif // BSDEBUG_H

View File

@@ -0,0 +1,75 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "base/plugins.h"
#include "common/fs.h"
#include "engines/advancedDetector.h"
#include "engines/obsolete.h"
#include "sword1/detection.h"
#include "sword1/obsolete.h" // Obsolete ID table.
static const PlainGameDescriptor swordGames[] = {
{"sword1", "Broken Sword: The Shadow of the Templars"},
{nullptr, nullptr}
};
#include "sword1/detection_tables.h"
static const char *const directoryGlobs[] = {
"smackshi",
"video",
nullptr
};
class SwordMetaEngineDetection : public AdvancedMetaEngineDetection<ADGameDescription> {
public:
SwordMetaEngineDetection() : AdvancedMetaEngineDetection(Sword1::gameDescriptions, swordGames) {
_guiOptions = GUIO2(GUIO_NOMIDI, GUIO_NOASPECT);
_flags = kADFlagMatchFullPaths;
_directoryGlobs = directoryGlobs;
}
PlainGameDescriptor findGame(const char *gameId) const override {
return Engines::findGameID(gameId, _gameIds, obsoleteGameIDsTable);
}
Common::Error identifyGame(DetectedGame &game, const void **descriptor) override {
Engines::upgradeTargetIfNecessary(obsoleteGameIDsTable);
return AdvancedMetaEngineDetection::identifyGame(game, descriptor);
}
const char *getName() const override {
return "sword1";
}
const char *getEngineName() const override {
return "Broken Sword: The Shadow of the Templars";
}
const char *getOriginalCopyright() const override {
return "Broken Sword: The Shadow of the Templars (C) Revolution";
}
};
REGISTER_PLUGIN_STATIC(SWORD1_DETECTION, PLUGIN_TYPE_ENGINE_DETECTION, SwordMetaEngineDetection);

View File

@@ -0,0 +1,37 @@
/* 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.
*
* Additional copyright for this file:
* Copyright (C) 1994-1998 Revolution Software Ltd.
*
* 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 SWORD1_DETECTION_H
#define SWORD1_DETECTION_H
#include "engines/advancedDetector.h"
namespace Sword1 {
#define GAMEOPTION_WINDOWS_AUDIO_MODE GUIO_GAMEOPTIONS1
#define GAMEOPTION_MULTILANGUAGE GUIO_GAMEOPTIONS2
#define GAMEOPTION_MULTILANGUAGE_EXTENDED GUIO_GAMEOPTIONS3
} // End of namespace Sword1
#endif // SWORD1_DETECTION_H

View File

@@ -0,0 +1,720 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace Sword1 {
static const ADGameDescription gameDescriptions[] = {
{
"sword1",
"Demo",
AD_ENTRY4s("clusters/scripts.clu", "9f6de3bea49a1ef4d8b1b020c41c950e", 1070644,
"clusters/swordres.rif", "bc01bc995f23e46bb076f66ba5c516c4", 58388,
"clusters/text.clu", "76f93f5feecc8915435105478f3c6615", 2696228,
"smackshi/intro.smk", "f50d773c362d03a52a6a4d541d09449c", 13298480),
Common::EN_ANY,
Common::kPlatformWindows,
ADGF_DEMO,
GUIO1(GAMEOPTION_WINDOWS_AUDIO_MODE)
},
{
"sword1",
"Demo",
AD_ENTRY4s("clusters/scripts.clu", "72b10193714e8c6e4daca51791c0db0c", 1088232,
"clusters/swordres.rif", "3786c6850e51ecbadb65bbedb5395664", 59788,
"clusters/text.clu", "76f93f5feecc8915435105478f3c6615", 3186195,
"smackshi/intro.smk", "95071cd6c12c10c9a30f45a70384cf05", 13448344),
Common::ES_ESP,
Common::kPlatformWindows,
ADGF_DEMO,
GUIO1(GAMEOPTION_WINDOWS_AUDIO_MODE)
},
{
"sword1",
"Demo",
AD_ENTRY4s("clusters/scripts.clu", "9f6de3bea49a1ef4d8b1b020c41c950e", 1070644,
"clusters/swordres.rif", "babe2ab6c352bdeb0fc256a94c934bb8", 58388,
"clusters/text.clu", "5d5bf40629364115da603da378e9d4c9", 2685487,
"smackshi/intro.smk", "f50d773c362d03a52a6a4d541d09449c", 13298480),
Common::PT_BRA,
Common::kPlatformWindows,
ADGF_DEMO,
GUIO1(GAMEOPTION_WINDOWS_AUDIO_MODE)
},
{
"sword1",
"Demo",
AD_ENTRY4s("clusters/scripts.clm", "6b6d9a32668e6f0285318dbe33f167fe", 1088468,
"clusters/swordres.rif", "6b579d7cd94756f5c1e362a9b61f94a3", 59788,
"speech/speech.clu", "36919b35067bf56b68ad538732a618c2", 45528200,
"smackshi/intro.smk", "d82a7869ace8fcecaa519c04c4bfc483", 13233268),
Common::EN_ANY,
Common::kPlatformMacintosh,
ADGF_DEMO,
GUIO0()
},
{
"sword1",
"Demo",
AD_ENTRY3s("scripts.clu", "72b10193714e8c6e4daca51791c0db0c", 1088372,
"swordres.rif", "5bd8928071b91830be6fbcdb7f59786d", 59788,
"english/speech.inf", "57f6d6949262cd63fc0378dd2375c819", 1662),
Common::EN_ANY,
Common::kPlatformPSX,
ADGF_DEMO,
GUIO0()
},
{
"sword1",
"Demo",
AD_ENTRY3s("scripts.clu", "72b10193714e8c6e4daca51791c0db0c", 1088372,
"swordres.rif", "5bd8928071b91830be6fbcdb7f59786d", 59788,
"italian/speech.inf", "af982fbfd4fdd39ea7108dc8f77cf1b3", 1652),
Common::IT_ITA,
Common::kPlatformPSX,
ADGF_DEMO,
GUIO0()
},
{
"sword1",
"",
AD_ENTRY4s("clusters/scripts.clu", "72b10193714e8c6e4daca51791c0db0c", 1088232,
"clusters/swordres.rif", "08d4942cf7c904182a31a1d5333244f3", 59788,
"clusters/text.clu", "76f93f5feecc8915435105478f3c6615", 3193923,
"smackshi/intro.smk", "6689aa8f84cb0387b292481d2a2428b4", 13076700),
Common::EN_USA,
Common::kPlatformWindows,
ADGF_NO_FLAGS,
GUIO2(GAMEOPTION_WINDOWS_AUDIO_MODE, GAMEOPTION_MULTILANGUAGE)
},
{
"sword1",
"",
AD_ENTRY4s("clusters/scripts.clu", "72b10193714e8c6e4daca51791c0db0c", 1087240,
"clusters/swordres.rif", "d21d6321ee2dbb2d7d7ca2d2a940c34a", 58916,
"clusters/text.clu", "76f93f5feecc8915435105478f3c6615", 2704592,
"smackshi/intro.smk", "d82a7869ace8fcecaa519c04c4bfc483", 13233268),
Common::EN_GRB,
Common::kPlatformWindows,
ADGF_NO_FLAGS,
GUIO2(GAMEOPTION_WINDOWS_AUDIO_MODE, GAMEOPTION_MULTILANGUAGE)
},
{ // 25th Anniversary rerelease / Steam DLC for Director's Cut
"sword1",
"25th Anniversary",
AD_ENTRY4s("scripts.clu", "72b10193714e8c6e4daca51791c0db0c", 1087240,
"swordres.rif", "d21d6321ee2dbb2d7d7ca2d2a940c34a", 58916,
"text.clu", "76f93f5feecc8915435105478f3c6615", 2704592,
"smackshi/intro.smk", "d82a7869ace8fcecaa519c04c4bfc483", 13233268),
Common::EN_GRB,
Common::kPlatformWindows,
ADGF_NO_FLAGS,
GUIO2(GAMEOPTION_WINDOWS_AUDIO_MODE, GAMEOPTION_MULTILANGUAGE)
},
{
"sword1",
"Rerelease",
AD_ENTRY4s("clusters/scripts.clu", "72b10193714e8c6e4daca51791c0db0c", 1088292,
"clusters/swordres.rif", "5463362dc77b6efc36e46ac84998bd2f", 59788,
"clusters/text.clu", "76f93f5feecc8915435105478f3c6615", 3193159,
"smackshi/intro.smk", "d82a7869ace8fcecaa519c04c4bfc483", 13233268),
Common::EN_GRB,
Common::kPlatformWindows,
ADGF_NO_FLAGS,
GUIO2(GAMEOPTION_WINDOWS_AUDIO_MODE, GAMEOPTION_MULTILANGUAGE)
},
{
"sword1",
"GOG.com",
AD_ENTRY4s("clusters/scripts.clu", "72b10193714e8c6e4daca51791c0db0c", 1088292,
"clusters/swordres.rif", "5463362dc77b6efc36e46ac84998bd2f", 59788,
"clusters/text.clu", "76f93f5feecc8915435105478f3c6615", 3193159,
"video/intro.dxa", "e27cd33593c08b66e8d20fbc40938789", 7420364),
Common::EN_ANY,
Common::kPlatformWindows,
ADGF_NO_FLAGS,
GUIO2(GAMEOPTION_WINDOWS_AUDIO_MODE, GAMEOPTION_MULTILANGUAGE)
},
{ // Bundled with ScummVM 0.8
// Reported by YetAnotherGuy via IRC
"sword1",
"SoldOut rerelease",
AD_ENTRY4s("clusters/scripts.clu", "72b10193714e8c6e4daca51791c0db0c", 1088292,
"clusters/swordres.rif", "5463362dc77b6efc36e46ac84998bd2f", 59788,
"clusters/text.clu", "76f93f5feecc8915435105478f3c6615", 3193159,
"video/intro.mp2", "c3e0ab75e8686c746899a9b6cecceac9", 8739102),
Common::EN_ANY,
Common::kPlatformWindows,
ADGF_NO_FLAGS,
GUIO2(GAMEOPTION_WINDOWS_AUDIO_MODE, GAMEOPTION_MULTILANGUAGE)
},
{
"sword1",
"",
AD_ENTRY4s("clusters/scripts.clu", "72b10193714e8c6e4daca51791c0db0c", 1087984,
"clusters/swordres.rif", "c7df52094d590b568a4ed35b70390d9e", 58916,
"clusters/text.clu", "76f93f5feecc8915435105478f3c6615", 2705446,
"smackshi/intro.smk", "d602a28f5f5c583bf9870a23a94a9bc5", 13525168),
Common::FR_FRA,
Common::kPlatformWindows,
ADGF_NO_FLAGS,
GUIO2(GAMEOPTION_WINDOWS_AUDIO_MODE, GAMEOPTION_MULTILANGUAGE)
},
{ // This is apparently one of the frankensten versions floating around
// See reports #14865, #15089 and #15376
"sword1",
"",
AD_ENTRY4s("scripts.clu", "72b10193714e8c6e4daca51791c0db0c", 1087984,
"swordres.rif", "c7df52094d590b568a4ed35b70390d9e", 58916,
"text.clu", "76f93f5feecc8915435105478f3c6615", 2705446,
"smackshi/intro.smk", "d602a28f5f5c583bf9870a23a94a9bc5", 13525168),
Common::FR_FRA,
Common::kPlatformWindows,
ADGF_PIRATED,
GUIO2(GAMEOPTION_WINDOWS_AUDIO_MODE, GAMEOPTION_MULTILANGUAGE)
},
{
"sword1",
"",
AD_ENTRY4s("clusters/scripts.clu", "72b10193714e8c6e4daca51791c0db0c", 1088372,
"clusters/swordres.rif", "665b7ed64c13013ec4a8bcd101a1e862", 59788,
"clusters/text.clu", "76f93f5feecc8915435105478f3c6615", 3188750,
"smackshi/intro.smk", "d602a28f5f5c583bf9870a23a94a9bc5", 13525168),
Common::FR_FRA,
Common::kPlatformWindows,
ADGF_NO_FLAGS,
GUIO2(GAMEOPTION_WINDOWS_AUDIO_MODE, GAMEOPTION_MULTILANGUAGE)
},
{
"sword1",
"Steam",
AD_ENTRY4s("scripts.clu", "72b10193714e8c6e4daca51791c0db0c", 1087984,
"swordres.rif", "c7df52094d590b568a4ed35b70390d9e", 58916,
"text.clu", "76f93f5feecc8915435105478f3c6615", 2705446,
"smackshi/intro.smk", "fe087447e0e525e371cf10cfabf589eb", 14524000),
Common::FR_FRA,
Common::kPlatformWindows,
ADGF_NO_FLAGS,
GUIO2(GAMEOPTION_WINDOWS_AUDIO_MODE, GAMEOPTION_MULTILANGUAGE)
},
{
"sword1",
"",
AD_ENTRY4s("clusters/scripts.clu", "72b10193714e8c6e4daca51791c0db0c", 1087984,
"clusters/swordres.rif", "c7df52094d590b568a4ed35b70390d9e", 58916,
"clusters/text.clu", "76f93f5feecc8915435105478f3c6615", 2705446,
"smackshi/intro.smk", "78e3ba96f33be8c2ef8feb46724cfef5", 11537716),
Common::FR_FRA,
Common::kPlatformWindows,
ADGF_NO_FLAGS,
GUIO2(GAMEOPTION_WINDOWS_AUDIO_MODE, GAMEOPTION_MULTILANGUAGE)
},
{
"sword1",
"",
AD_ENTRY4s("clusters/scripts.clu", "72b10193714e8c6e4daca51791c0db0c", 1087240,
"clusters/swordres.rif", "b0ae5a47aba74dc0acb3442d4c84b225", 58916,
"clusters/text.clu", "76f93f5feecc8915435105478f3c6615", 2705446,
"smackshi/intro.smk", "d1d0e958aeef9b1375b55df8f8831f26", 13281776),
Common::DE_DEU,
Common::kPlatformWindows,
ADGF_NO_FLAGS,
GUIO2(GAMEOPTION_WINDOWS_AUDIO_MODE, GAMEOPTION_MULTILANGUAGE)
},
{
"sword1",
"Steam",
AD_ENTRY4s("scripts.clu", "72b10193714e8c6e4daca51791c0db0c", 1087240,
"swordres.rif", "b0ae5a47aba74dc0acb3442d4c84b225", 58916,
"text.clu", "76f93f5feecc8915435105478f3c6615", 2705446,
"smackshi/intro.smk", "d1d0e958aeef9b1375b55df8f8831f26", 13281776),
Common::DE_DEU,
Common::kPlatformWindows,
ADGF_NO_FLAGS,
GUIO2(GAMEOPTION_WINDOWS_AUDIO_MODE, GAMEOPTION_MULTILANGUAGE)
},
{ // German DVD Trilogy collection
"sword1",
"",
AD_ENTRY4s("clusters/scripts.clu", "72b10193714e8c6e4daca51791c0db0c", 1087240,
"clusters/swordres.rif", "b0ae5a47aba74dc0acb3442d4c84b225", 58916,
"clusters/text.clu", "76f93f5feecc8915435105478f3c6615", 2705446,
"video/intro.dxa", "e27cd33593c08b66e8d20fbc40938789", 7397543),
Common::DE_DEU,
Common::kPlatformWindows,
ADGF_NO_FLAGS,
GUIO2(GAMEOPTION_WINDOWS_AUDIO_MODE, GAMEOPTION_MULTILANGUAGE)
},
{ // GOG.com version + german translation from tickets #14592, #14642, #15763
"sword1",
"GOG.com",
AD_ENTRY4s("clusters/scripts.clu", "72b10193714e8c6e4daca51791c0db0c", 1088292,
"clusters/swordres.rif", "5463362dc77b6efc36e46ac84998bd2f", 59788,
"clusters/text.clu", "76f93f5feecc8915435105478f3c6615", 3193159,
"video/intro.dxa", "e27cd33593c08b66e8d20fbc40938789", 7397543),
Common::DE_DEU,
Common::kPlatformWindows,
ADGF_NO_FLAGS,
GUIO1(GAMEOPTION_WINDOWS_AUDIO_MODE)
},
{ // German version of "Revolution Classic Adventures"
"sword1",
"",
AD_ENTRY4s("clusters/scripts.clu", "72b10193714e8c6e4daca51791c0db0c", 1087240,
"clusters/swordres.rif", "b0ae5a47aba74dc0acb3442d4c84b225", 58916,
"clusters/text.clu", "76f93f5feecc8915435105478f3c6615", 2705446,
"smackshi/intro.smk", "40a2106393c2a749304de0545ddcb7f6", 11542280),
Common::DE_DEU,
Common::kPlatformWindows,
ADGF_NO_FLAGS,
GUIO2(GAMEOPTION_WINDOWS_AUDIO_MODE, GAMEOPTION_MULTILANGUAGE)
},
{
"sword1",
"",
AD_ENTRY4s("clusters/scripts.clu", "72b10193714e8c6e4daca51791c0db0c", 1088232,
"clusters/swordres.rif", "08d4942cf7c904182a31a1d5333244f3", 59788,
"clusters/text.clu", "76f93f5feecc8915435105478f3c6615", 3193923,
"smackshi/intro.smk", "95071cd6c12c10c9a30f45a70384cf05", 13448344),
Common::ES_ESP,
Common::kPlatformWindows,
ADGF_NO_FLAGS,
GUIO2(GAMEOPTION_WINDOWS_AUDIO_MODE, GAMEOPTION_MULTILANGUAGE)
},
{ // Alternate version
"sword1",
"",
AD_ENTRY4s("clusters/scripts.clu", "72b10193714e8c6e4daca51791c0db0c", 1088232,
"clusters/swordres.rif", "08d4942cf7c904182a31a1d5333244f3", 59788,
"clusters/text.clu", "76f93f5feecc8915435105478f3c6615", 3193923,
"smackshi/intro.smk", "a8c6a8770cb4b2669f4263ece8830985", 13293740),
Common::ES_ESP,
Common::kPlatformWindows,
ADGF_NO_FLAGS,
GUIO2(GAMEOPTION_WINDOWS_AUDIO_MODE, GAMEOPTION_MULTILANGUAGE)
},
{
"sword1",
"Steam",
AD_ENTRY4s("scripts.clu", "72b10193714e8c6e4daca51791c0db0c", 1088232,
"swordres.rif", "08d4942cf7c904182a31a1d5333244f3", 59788,
"text.clu", "76f93f5feecc8915435105478f3c6615", 3193923,
"smackshi/intro.smk", "a8c6a8770cb4b2669f4263ece8830985", 13293740),
Common::ES_ESP,
Common::kPlatformWindows,
ADGF_NO_FLAGS,
GUIO2(GAMEOPTION_WINDOWS_AUDIO_MODE, GAMEOPTION_MULTILANGUAGE)
},
{
"sword1",
"",
AD_ENTRY4s("clusters/scripts.clu", "72b10193714e8c6e4daca51791c0db0c", 1088372,
"clusters/swordres.rif", "239bdd76c405bad0f804a8ae5df4adb0", 59788,
"clusters/text.clu", "76f93f5feecc8915435105478f3c6615", 3188725,
"smackshi/intro.smk", "83060041aa155d802e51b7211b62ea2f", 13525252),
Common::IT_ITA,
Common::kPlatformWindows,
ADGF_NO_FLAGS,
GUIO2(GAMEOPTION_WINDOWS_AUDIO_MODE, GAMEOPTION_MULTILANGUAGE)
},
{
"sword1",
"",
AD_ENTRY4s("scripts.clu", "72b10193714e8c6e4daca51791c0db0c", 1088372,
"swordres.rif", "239bdd76c405bad0f804a8ae5df4adb0", 59788,
"text.clu", "76f93f5feecc8915435105478f3c6615", 3188725,
"smackshi/intro.smk", "83060041aa155d802e51b7211b62ea2f", 13525252),
Common::IT_ITA,
Common::kPlatformWindows,
ADGF_NO_FLAGS,
GUIO2(GAMEOPTION_WINDOWS_AUDIO_MODE, GAMEOPTION_MULTILANGUAGE)
},
{
"sword1",
"Steam",
AD_ENTRY4s("scripts.clu", "72b10193714e8c6e4daca51791c0db0c", 1088372,
"swordres.rif", "239bdd76c405bad0f804a8ae5df4adb0", 59788,
"text.clu", "76f93f5feecc8915435105478f3c6615", 3188725,
"smackshi/intro.smk", "939643be076c73068f47ce0fd6c27183", 13305080),
Common::IT_ITA,
Common::kPlatformWindows,
ADGF_NO_FLAGS,
GUIO2(GAMEOPTION_WINDOWS_AUDIO_MODE, GAMEOPTION_MULTILANGUAGE)
},
{
"sword1",
"English speech",
AD_ENTRY4s("clusters/scripts.clu", "72b10193714e8c6e4daca51791c0db0c", 1088468,
"clusters/swordres.rif", "34c111f224e75050a523dc758c71d54e", 60612,
"clusters/text.clu", "76f93f5feecc8915435105478f3c6615", 3164478,
"smackshi/intro.smk", "d82a7869ace8fcecaa519c04c4bfc483", 13233268),
Common::PT_PRT,
Common::kPlatformWindows,
ADGF_NO_FLAGS,
GUIO2(GAMEOPTION_WINDOWS_AUDIO_MODE, GAMEOPTION_MULTILANGUAGE_EXTENDED)
},
{
"sword1",
"TecToy",
AD_ENTRY4s("clusters/scripts.clu", "72b10193714e8c6e4daca51791c0db0c", 1088468,
"clusters/swordres.rif", "34c111f224e75050a523dc758c71d54e", 60612,
"clusters/text.clu", "76f93f5feecc8915435105478f3c6615", 3164478,
"smackshi/intro.smk", "4a7343c3d59526dcab04be7a6af3943a", 13238300),
Common::PT_BRA,
Common::kPlatformWindows,
ADGF_NO_FLAGS,
GUIO2(GAMEOPTION_WINDOWS_AUDIO_MODE, GAMEOPTION_MULTILANGUAGE_EXTENDED)
},
{
"sword1",
"English speech",
AD_ENTRY4s("clusters/scripts.clu", "72b10193714e8c6e4daca51791c0db0c", 1088292,
"clusters/swordres.rif", "ba6f881c3ace6408880c8e07cd8a1dfe", 59788,
"clusters/text.clu", "0c0f9eadf20a497834685ccb3ba53a3f", 397478,
"video/intro.smk", "d07ba8a1be7d8a47de50cc4eac2bc243", 13082688),
Common::HE_ISR,
Common::kPlatformWindows,
ADGF_NO_FLAGS,
GUIO1(GAMEOPTION_WINDOWS_AUDIO_MODE)
},
{
"sword1",
"English speech",
AD_ENTRY4s("clusters/scripts.clu", "72b10193714e8c6e4daca51791c0db0c", 1087240,
"clusters/swordres.rif", "88c0793a4fa908083b00f6677c545f78", 58916,
"clusters/text.clu", "7d9e47533fde5333dc310bfd73eaeb5c", 2666944,
"smackshi/intro.smk", "d82a7869ace8fcecaa519c04c4bfc483", 13233268),
Common::PL_POL,
Common::kPlatformWindows,
ADGF_NO_FLAGS,
GUIO1(GAMEOPTION_WINDOWS_AUDIO_MODE)
},
{
"sword1",
"English speech and DXA cutscenes",
AD_ENTRY4s("clusters/scripts.clu", "72b10193714e8c6e4daca51791c0db0c", 1088292,
"clusters/swordres.rif", "04a41fc5783d18a8958d41aa9a3823af", 59788,
"clusters/text.clu", "b9e7b3e342569be68738e4681f2adeff", 3164267,
"intro.dxa", nullptr, AD_NO_SIZE),
Common::PL_POL,
Common::kPlatformWindows,
ADGF_NO_FLAGS,
GUIO1(GAMEOPTION_WINDOWS_AUDIO_MODE)
},
{
"sword1",
"English speech",
AD_ENTRY4s("clusters/scripts.clu", "72b10193714e8c6e4daca51791c0db0c", 1087240,
"clusters/swordres.rif", "7188a3ec8d486fd9179f06968369c011", 58916,
"clusters/text.clu", "b9e7b3e342569be68738e4681f2adeff", 2675700,
"smackshi/intro.smk", "d82a7869ace8fcecaa519c04c4bfc483", 13233268),
Common::PL_POL,
Common::kPlatformWindows,
ADGF_NO_FLAGS,
GUIO1(GAMEOPTION_WINDOWS_AUDIO_MODE)
},
{
"sword1",
"English speech",
AD_ENTRY4s("clusters/scripts.clu", "72b10193714e8c6e4daca51791c0db0c", 1087240,
"clusters/swordres.rif", "31ea11161d5d2200b6b44a833b7d5aa8", 58916,
"clusters/text.clu", "7d9e47533fde5333dc310bfd73eaeb5c", 2666334,
"smackshi/intro.smk", "d82a7869ace8fcecaa519c04c4bfc483", 13233268),
Common::PL_POL,
Common::kPlatformWindows,
ADGF_NO_FLAGS,
GUIO1(GAMEOPTION_WINDOWS_AUDIO_MODE)
},
{
"sword1",
"English speech",
AD_ENTRY4s("clusters/scripts.clu", "72b10193714e8c6e4daca51791c0db0c", 1088372,
"clusters/swordres.rif", "04a41fc5783d18a8958d41aa9a3823af", 59788,
"clusters/text.clu", "b9e7b3e342569be68738e4681f2adeff", 3164267,
"smackshi/intro.smk", "d82a7869ace8fcecaa519c04c4bfc483", 13233268),
Common::PL_POL,
Common::kPlatformWindows,
ADGF_NO_FLAGS,
GUIO1(GAMEOPTION_WINDOWS_AUDIO_MODE)
},
{
"sword1",
"English speech",
AD_ENTRY4s("clusters/scripts.clu", "72b10193714e8c6e4daca51791c0db0c", 1088292,
"clusters/swordres.rif", "5463362dc77b6efc36e46ac84998bd2f", 59788,
"clusters/text.clu", "cf6a85c2d60386a3c978f0c6fbb377bd", 3193159,
"smackshi/intro.smk", "d82a7869ace8fcecaa519c04c4bfc483", 13233268),
Common::HU_HUN,
Common::kPlatformWindows,
ADGF_NO_FLAGS,
GUIO1(GAMEOPTION_WINDOWS_AUDIO_MODE)
},
{
"sword1",
"English speech",
AD_ENTRY4s("clusters/scripts.clu", "72b10193714e8c6e4daca51791c0db0c", 1088372,
"clusters/swordres.rif", "239bdd76c405bad0f804a8ae5df4adb0", 59788,
"clusters/text.clu", "76f93f5feecc8915435105478f3c6615", 3199652,
"smackshi/intro.smk", "d82a7869ace8fcecaa519c04c4bfc483", 13233268),
Common::CS_CZE,
Common::kPlatformWindows,
ADGF_NO_FLAGS,
GUIO2(GAMEOPTION_WINDOWS_AUDIO_MODE, GAMEOPTION_MULTILANGUAGE_EXTENDED)
},
{
"sword1",
"English speech and DXA cutscenes",
AD_ENTRY4s("clusters/scripts.clu", "72b10193714e8c6e4daca51791c0db0c", 1088372,
"clusters/swordres.rif", "239bdd76c405bad0f804a8ae5df4adb0", 59788,
"clusters/text.clu", "76f93f5feecc8915435105478f3c6615", 3199652,
"intro.dxa", nullptr, AD_NO_SIZE),
Common::CS_CZE,
Common::kPlatformWindows,
ADGF_NO_FLAGS,
GUIO2(GAMEOPTION_WINDOWS_AUDIO_MODE, GAMEOPTION_MULTILANGUAGE_EXTENDED)
},
{
"sword1",
"Akella",
AD_ENTRY4s("clusters/scripts.clu", "72b10193714e8c6e4daca51791c0db0c", 1087240,
"clusters/swordres.rif", "e7021abec62dd774010d1f432ef9f03a", 58916,
"clusters/text.clu", "524706e42583f6c23a5a7ae3e1784068", 2683625,
"smackshi/intro.smk", "ef3ae780668c087fae00ed9c46c2eb35", 13386716),
Common::RU_RUS,
Common::kPlatformWindows,
ADGF_NO_FLAGS,
GUIO1(GAMEOPTION_WINDOWS_AUDIO_MODE)
},
{
"sword1",
"Mediahauz",
AD_ENTRY4s("clusters/scripts.clu", "72b10193714e8c6e4daca51791c0db0c", 1087240,
"clusters/swordres.rif", "7a6e896064c8d2ee266e961549487204", 58916,
"clusters/text.clu", "76f93f5feecc8915435105478f3c6615", 3198686,
"smackshi/intro.smk", "d82a7869ace8fcecaa519c04c4bfc483", 13233268),
Common::RU_RUS,
Common::kPlatformWindows,
ADGF_NO_FLAGS,
GUIO1(GAMEOPTION_WINDOWS_AUDIO_MODE)
},
{ // Alternate version, from a DVD collection containing both BS1 and BS2
"sword1",
"Mediahauz",
AD_ENTRY4s("clusters/scripts.clu", "72b10193714e8c6e4daca51791c0db0c", 1087240,
"clusters/swordres.rif", "7a6e896064c8d2ee266e961549487204", 58916,
"clusters/text.clu", "76f93f5feecc8915435105478f3c6615", 3198686,
"smackshi/intro.smk", "432215e04bb74ad823e033bc774f97d7", 14098520),
Common::RU_RUS,
Common::kPlatformWindows,
ADGF_NO_FLAGS,
GUIO1(GAMEOPTION_WINDOWS_AUDIO_MODE)
},
{
"sword1",
"Novy Disk",
AD_ENTRY4s("clusters/scripts.clu", "72b10193714e8c6e4daca51791c0db0c", 1088292,
"clusters/swordres.rif", "b5d9ddbe26d453415a43596f86452435", 59788,
"clusters/text.clu", "8392ae2af0a8bec1dca511b2fedddc4c", 3178811,
"video/intro.dxa", "e27cd33593c08b66e8d20fbc40938789", 7420364),
Common::RU_RUS,
Common::kPlatformWindows,
ADGF_NO_FLAGS,
GUIO1(GAMEOPTION_WINDOWS_AUDIO_MODE)
},
{ // Korean fan translation
"sword1",
"",
AD_ENTRY6s("clusters/scripts.clu", "72b10193714e8c6e4daca51791c0db0c", 1087240,
"clusters/swordres.rif", "d21d6321ee2dbb2d7d7ca2d2a940c34a", 58916,
"clusters/text.clu", "76f93f5feecc8915435105478f3c6615", 2704592,
"smackshi/intro.smk", "d82a7869ace8fcecaa519c04c4bfc483", 13233268,
"bs1k.fnt", NULL, 1222000,
"korean.clu", NULL, AD_NO_SIZE),
Common::KO_KOR,
Common::kPlatformWindows,
ADGF_NO_FLAGS,
GUIO1(GAMEOPTION_WINDOWS_AUDIO_MODE)
},
{
"sword1",
"",
AD_ENTRY4s("clusters/scripts.clm", "6b6d9a32668e6f0285318dbe33f167fe", 1088468,
"clusters/swordres.rif", "6b579d7cd94756f5c1e362a9b61f94a3", 59788,
"smackshi/credits.smk", "eacbc81d3ef88628d3710abbbcdc9aa0", 17300736,
"smackshi/intro.smk", "6689aa8f84cb0387b292481d2a2428b4", 13076700),
Common::EN_USA,
Common::kPlatformMacintosh,
ADGF_NO_FLAGS,
GUIO1(GAMEOPTION_MULTILANGUAGE)
},
{
"sword1",
"",
AD_ENTRY4s("clusters/scripts.clm", "6b6d9a32668e6f0285318dbe33f167fe", 1088468,
"clusters/swordres.rif", "6b579d7cd94756f5c1e362a9b61f94a3", 59788,
"smackshi/credits.smk", "9a3fe9cb76bc7ca8a9987c173befb90d", 16315740,
"smackshi/intro.smk", "d82a7869ace8fcecaa519c04c4bfc483", 13233268),
Common::EN_GRB,
Common::kPlatformMacintosh,
ADGF_NO_FLAGS,
GUIO1(GAMEOPTION_MULTILANGUAGE)
},
{
"sword1",
"",
AD_ENTRY4s("clusters/scripts.clm", "6b6d9a32668e6f0285318dbe33f167fe", 1088468,
"clusters/swordres.rif", "6b579d7cd94756f5c1e362a9b61f94a3", 59788,
"smackshi/credits.smk", "0e4eb849d60baab975130efd35f15ace", 17528016,
"smackshi/intro.smk", "d602a28f5f5c583bf9870a23a94a9bc5", 13525168),
Common::FR_FRA,
Common::kPlatformMacintosh,
ADGF_NO_FLAGS,
GUIO1(GAMEOPTION_MULTILANGUAGE)
},
{
"sword1",
"",
AD_ENTRY4s("scripts.clu", "72b10193714e8c6e4daca51791c0db0c", 1088468,
"swordres.rif", "a810e6dc5c8e636151a3e1370d41d138", 59788,
"credits.dat", "2ec14f1f262cdd2c87dd95acced9e2f6", 3312,
"speech.inf", "ed14c2a235cf5388ac3b5f29db129837", 21310),
Common::EN_USA,
Common::kPlatformPSX,
ADGF_NO_FLAGS,
GUIO0()
},
{
"sword1",
"",
AD_ENTRY4s("scripts.clu", "72b10193714e8c6e4daca51791c0db0c", 1088468,
"swordres.rif", "a810e6dc5c8e636151a3e1370d41d138", 59788,
"credits.dat", "69349710eef6b653ed2c02643ed6c4a0", 2799,
"speech.inf", "ed14c2a235cf5388ac3b5f29db129837", 21310),
Common::EN_GRB,
Common::kPlatformPSX,
ADGF_NO_FLAGS,
GUIO0()
},
{
"sword1",
"",
AD_ENTRY4s("scripts.clu" , "72b10193714e8c6e4daca51791c0db0c", 1088468,
"swordres.rif", "a810e6dc5c8e636151a3e1370d41d138", 59788,
"credits.dat", "0b119d49f27260e6115504c135b9bb19", 2382,
"speech.inf", "2ccb9be1a3d8d0e33d6efd6a12a24320", 21450),
Common::FR_FRA,
Common::kPlatformPSX,
ADGF_NO_FLAGS,
GUIO0()
},
{
"sword1",
"",
AD_ENTRY4s("scripts.clu", "72b10193714e8c6e4daca51791c0db0c", 1088468,
"swordres.rif", "a810e6dc5c8e636151a3e1370d41d138", 59788,
"credits.dat", "c4f84aaa17f80fb549a5c8a867a9836a", 2382,
"speech.inf", "403fb61f9de6ce6cb374edd9985066ae", 21304),
Common::DE_DEU,
Common::kPlatformPSX,
ADGF_NO_FLAGS,
GUIO0()
},
{
"sword1",
"",
AD_ENTRY4s("scripts.clu", "72b10193714e8c6e4daca51791c0db0c", 1088372,
"swordres.rif", "5bd8928071b91830be6fbcdb7f59786d", 59788,
"credits.dat", "949806fa3eaa4ff3a6c19ee4b5caa9f5", 2823,
"speech.inf", "1165f01823e4d2df72fcc5b592a4960e", 21374),
Common::IT_ITA,
Common::kPlatformPSX,
ADGF_NO_FLAGS,
GUIO0()
},
{
"sword1",
"",
AD_ENTRY4s("scripts.clu", "72b10193714e8c6e4daca51791c0db0c", 1088372,
"swordres.rif", "5bd8928071b91830be6fbcdb7f59786d", 59788,
"credits.dat", "cd97e8f5006d91914904b3bfdb0ff588", 2412,
"speech.inf", "d4558d96ce696a906b086c2b44ffb301", 21342),
Common::ES_ESP,
Common::kPlatformPSX,
ADGF_NO_FLAGS,
GUIO0()
},
AD_TABLE_END_MARKER
};
} // End of namespace Sword1

105
engines/sword1/eventman.cpp Normal file
View File

@@ -0,0 +1,105 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "sword1/eventman.h"
#include "sword1/sworddefs.h"
#include "common/textconsole.h"
#include "common/util.h"
namespace Sword1 {
EventManager::EventManager() {
for (uint8 cnt = 0; cnt < TOTAL_EVENT_SLOTS; cnt++)
_eventPendingList[cnt].delay = _eventPendingList[cnt].eventNumber = 0;
}
void EventManager::serviceGlobalEventList() {
for (uint8 slot = 0; slot < TOTAL_EVENT_SLOTS; slot++)
if (_eventPendingList[slot].delay)
_eventPendingList[slot].delay--;
}
void EventManager::checkForEvent(Object *compact) {
for (uint8 objCnt = 0; objCnt < O_TOTAL_EVENTS; objCnt++) {
if (compact->o_event_list[objCnt].o_event)
for (uint8 globCnt = 0; globCnt < TOTAL_EVENT_SLOTS; globCnt++) {
if (_eventPendingList[globCnt].delay &&
(_eventPendingList[globCnt].eventNumber == compact->o_event_list[objCnt].o_event)) {
compact->o_logic = LOGIC_script; //force into script mode
_eventPendingList[globCnt].delay = 0; //started, so remove from queue
compact->o_tree.o_script_level++;
compact->o_tree.o_script_id[compact->o_tree.o_script_level] =
compact->o_event_list[objCnt].o_event_script;
compact->o_tree.o_script_pc[compact->o_tree.o_script_level] =
compact->o_event_list[objCnt].o_event_script;
break;
}
}
}
}
bool EventManager::eventValid(int32 event) {
for (uint8 slot = 0; slot < TOTAL_EVENT_SLOTS; slot++)
if ((_eventPendingList[slot].eventNumber == event) &&
(_eventPendingList[slot].delay))
return true;
return false;
}
int EventManager::fnCheckForEvent(Object *cpt, int32 id, int32 pause) {
if (pause) {
cpt->o_pause = pause;
cpt->o_logic = LOGIC_pause_for_event;
return SCRIPT_STOP;
}
for (uint8 objCnt = 0; objCnt < O_TOTAL_EVENTS; objCnt++) {
if (cpt->o_event_list[objCnt].o_event)
for (uint8 globCnt = 0; globCnt < TOTAL_EVENT_SLOTS; globCnt++) {
if (_eventPendingList[globCnt].delay &&
(_eventPendingList[globCnt].eventNumber == cpt->o_event_list[objCnt].o_event)) {
cpt->o_logic = LOGIC_script; //force into script mode
_eventPendingList[globCnt].delay = 0; //started, so remove from queue
cpt->o_tree.o_script_level++;
cpt->o_tree.o_script_id[cpt->o_tree.o_script_level] =
cpt->o_event_list[objCnt].o_event_script;
cpt->o_tree.o_script_pc[cpt->o_tree.o_script_level] =
cpt->o_event_list[objCnt].o_event_script;
return SCRIPT_STOP;
}
}
}
return SCRIPT_CONT;
}
void EventManager::fnIssueEvent(Object *compact, int32 id, int32 event, int32 delay) {
uint8 evSlot = 0;
while (_eventPendingList[evSlot].delay)
evSlot++;
if (evSlot >= TOTAL_EVENT_SLOTS)
error("EventManager ran out of event slots");
_eventPendingList[evSlot].delay = delay;
_eventPendingList[evSlot].eventNumber = event;
}
} // End of namespace Sword1

50
engines/sword1/eventman.h Normal file
View File

@@ -0,0 +1,50 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef SWORD1_EVENTMAN_H
#define SWORD1_EVENTMAN_H
#include "sword1/object.h"
namespace Sword1 {
#define TOTAL_EVENT_SLOTS 20
struct GlobalEvent {
int32 eventNumber;
int32 delay;
};
class EventManager {
public:
EventManager();
void serviceGlobalEventList();
void checkForEvent(Object *compact);
int fnCheckForEvent(Object *cpt, int32 id, int32 pause);
void fnIssueEvent(Object *compact, int32 id, int32 event, int32 delay);
bool eventValid(int32 event);
private:
GlobalEvent _eventPendingList[TOTAL_EVENT_SLOTS];
};
} // End of namespace Sword1
#endif // BSEVENTMAN_H

1987
engines/sword1/logic.cpp Normal file

File diff suppressed because it is too large Load Diff

249
engines/sword1/logic.h Normal file
View File

@@ -0,0 +1,249 @@
/* 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 SWORD1_LOGIC_H
#define SWORD1_LOGIC_H
// combination of logic.c and scr_int.c
#include "sword1/sworddefs.h"
#include "sword1/objectman.h"
#include "common/util.h"
#include "common/random.h"
class OSystem;
namespace Audio {
class Mixer;
}
namespace Sword1 {
#define NON_ZERO_SCRIPT_VARS 95
#define NUM_SCRIPT_VARS 1179
class SwordEngine;
class Text;
class Sound;
class EventManager;
class Menu;
class Router;
class Screen;
class Mouse;
class Control;
class Logic;
typedef int (Logic::*BSMcodeTable)(Object *, int32, int32, int32, int32, int32, int32, int32);
class Logic {
friend class Control;
public:
Logic(SwordEngine *vm, ObjectMan *pObjMan, ResMan *resMan, Screen *pScreen, Mouse *pMouse, Sound *pSound, Menu *pMenu, OSystem *system, Audio::Mixer *mixer);
~Logic();
void initialize();
void setControlPanelObject(Control *control);
void newScreen(uint32 screen);
void engine();
void updateScreenParams();
void runMouseScript(Object *cpt, int32 scriptId);
void startPositions(uint32 pos);
bool canShowDebugTextNumber();
void plotRouteGrid(Object *megaObject);
static uint32 _scriptVars[NUM_SCRIPT_VARS];
// public for mouse (menu looking)
int cfnPresetScript(Object *cpt, int32 id, int32 c, int32 d, int32 e, int32 f, int32 z, int32 x);
private:
SwordEngine *_vm;
ObjectMan *_objMan;
OSystem *_system;
Audio::Mixer *_mixer;
ResMan *_resMan;
Screen *_screen;
Sound *_sound;
Mouse *_mouse;
Router *_router;
Text *_textMan;
EventManager *_eventMan;
Menu *_menu;
Control *_control;
uint32 _newScript; // <= ugly, but I can't avoid it.
uint8 _speechClickDelay = 0;
Common::RandomSource _rnd;
bool _psxFudgeRandom = false; // Used within fnIdle() and fnRandom() for the PSX version
int scriptManager(Object *compact, uint32 id);
void processLogic(Object *compact, uint32 id);
int interpretScript(Object *compact, int id, Header *scriptModule, int scriptBase, int scriptNum);
int logicWaitTalk(Object *compact);
int logicStartTalk(Object *compact);
int logicArAnimate(Object *compact, uint32 id);
int speechDriver(Object *compact);
int fullAnimDriver(Object *compact);
int animDriver(Object *compact);
void setupMcodeTable();
const BSMcodeTable *_mcodeTable;
//- mcodeTable:
int fnBackground(Object *cpt, int32 id, int32 c, int32 d, int32 e, int32 f, int32 z, int32 x);
int fnForeground(Object *cpt, int32 id, int32 c, int32 d, int32 e, int32 f, int32 z, int32 x);
int fnSort(Object *cpt, int32 id, int32 c, int32 d, int32 e, int32 f, int32 z, int32 x);
int fnNoSprite(Object *cpt, int32 id, int32 c, int32 d, int32 e, int32 f, int32 z, int32 x);
int fnMegaSet(Object *cpt, int32 id, int32 c, int32 d, int32 e, int32 f, int32 z, int32 x);
int fnAnim(Object *cpt, int32 id, int32 c, int32 d, int32 e, int32 f, int32 z, int32 x);
int fnSetFrame(Object *cpt, int32 id, int32 c, int32 d, int32 e, int32 f, int32 z, int32 x);
int fnFullAnim(Object *cpt, int32 id, int32 c, int32 d, int32 e, int32 f, int32 z, int32 x);
int fnFullSetFrame(Object *cpt, int32 id, int32 c, int32 d, int32 e, int32 f, int32 z, int32 x);
int fnFadeDown(Object *cpt, int32 id, int32 c, int32 d, int32 e, int32 f, int32 z, int32 x);
int fnFadeUp(Object *cpt, int32 id, int32 c, int32 d, int32 e, int32 f, int32 z, int32 x);
int fnCheckFade(Object *cpt, int32 id, int32 c, int32 d, int32 e, int32 f, int32 z, int32 x);
int fnSetSpritePalette(Object *cpt, int32 id, int32 c, int32 d, int32 e, int32 f, int32 z, int32 x);
int fnSetWholePalette(Object *cpt, int32 id, int32 c, int32 d, int32 e, int32 f, int32 z, int32 x);
int fnSetFadeTargetPalette(Object *cpt, int32 id, int32 c, int32 d, int32 e, int32 f, int32 z, int32 x);
int fnSetPaletteToFade(Object *cpt, int32 id, int32 c, int32 d, int32 e, int32 f, int32 z, int32 x);
int fnSetPaletteToCut(Object *cpt, int32 id, int32 c, int32 d, int32 e, int32 f, int32 z, int32 x);
int fnPlaySequence(Object *cpt, int32 id, int32 c, int32 d, int32 e, int32 f, int32 z, int32 x);
int fnIdle(Object *cpt, int32 id, int32 c, int32 d, int32 e, int32 f, int32 z, int32 x);
int fnPause(Object *cpt, int32 id, int32 c, int32 d, int32 e, int32 f, int32 z, int32 x);
int fnPauseSeconds(Object *cpt, int32 id, int32 c, int32 d, int32 e, int32 f, int32 z, int32 x);
int fnQuit(Object *cpt, int32 id, int32 c, int32 d, int32 e, int32 f, int32 z, int32 x);
int fnKillId(Object *cpt, int32 id, int32 c, int32 d, int32 e, int32 f, int32 z, int32 x);
int fnSuicide(Object *cpt, int32 id, int32 c, int32 d, int32 e, int32 f, int32 z, int32 x);
int fnNewScript(Object *cpt, int32 id, int32 c, int32 d, int32 e, int32 f, int32 z, int32 x);
int fnSubScript(Object *cpt, int32 id, int32 c, int32 d, int32 e, int32 f, int32 z, int32 x);
int fnRestartScript(Object *cpt, int32 id, int32 c, int32 d, int32 e, int32 f, int32 z, int32 x);
int fnSetBookmark(Object *cpt, int32 id, int32 c, int32 d, int32 e, int32 f, int32 z, int32 x);
int fnGotoBookmark(Object *cpt, int32 id, int32 c, int32 d, int32 e, int32 f, int32 z, int32 x);
int fnSendSync(Object *cpt, int32 id, int32 c, int32 d, int32 e, int32 f, int32 z, int32 x);
int fnWaitSync(Object *cpt, int32 id, int32 c, int32 d, int32 e, int32 f, int32 z, int32 x);
int cfnClickInteract(Object *cpt, int32 id, int32 c, int32 d, int32 e, int32 f, int32 z, int32 x);
int cfnSetScript(Object *cpt, int32 id, int32 c, int32 d, int32 e, int32 f, int32 z, int32 x);
int fnInteract(Object *cpt, int32 id, int32 c, int32 d, int32 e, int32 f, int32 z, int32 x);
int fnIssueEvent(Object *cpt, int32 id, int32 c, int32 d, int32 e, int32 f, int32 z, int32 x);
int fnCheckForEvent(Object *cpt, int32 id, int32 c, int32 d, int32 e, int32 f, int32 z, int32 x);
int fnWipeHands(Object *cpt, int32 id, int32 c, int32 d, int32 e, int32 f, int32 z, int32 x);
int fnISpeak(Object *cpt, int32 id, int32 c, int32 d, int32 e, int32 f, int32 z, int32 x);
int fnTheyDo(Object *cpt, int32 id, int32 c, int32 d, int32 e, int32 f, int32 z, int32 x);
int fnTheyDoWeWait(Object *cpt, int32 id, int32 c, int32 d, int32 e, int32 f, int32 z, int32 x);
int fnWeWait(Object *cpt, int32 id, int32 c, int32 d, int32 e, int32 f, int32 z, int32 x);
int fnChangeSpeechText(Object *cpt, int32 id, int32 c, int32 d, int32 e, int32 f, int32 z, int32 x);
int fnTalkError(Object *cpt, int32 id, int32 c, int32 d, int32 e, int32 f, int32 z, int32 x);
int fnStartTalk(Object *cpt, int32 id, int32 c, int32 d, int32 e, int32 f, int32 z, int32 x);
int fnCheckForTextLine(Object *cpt, int32 id, int32 c, int32 d, int32 e, int32 f, int32 z, int32 x);
int fnAddTalkWaitStatusBit(Object *cpt, int32 id, int32 c, int32 d, int32 e, int32 f, int32 z, int32 x);
int fnRemoveTalkWaitStatusBit(Object *cpt, int32 id, int32 c, int32 d, int32 e, int32 f, int32 z, int32 x);
int fnNoHuman(Object *cpt, int32 id, int32 c, int32 d, int32 e, int32 f, int32 z, int32 x);
int fnAddHuman(Object *cpt, int32 id, int32 c, int32 d, int32 e, int32 f, int32 z, int32 x);
int fnBlankMouse(Object *cpt, int32 id, int32 c, int32 d, int32 e, int32 f, int32 z, int32 x);
int fnNormalMouse(Object *cpt, int32 id, int32 c, int32 d, int32 e, int32 f, int32 z, int32 x);
int fnLockMouse(Object *cpt, int32 id, int32 c, int32 d, int32 e, int32 f, int32 z, int32 x);
int fnUnlockMouse(Object *cpt, int32 id, int32 c, int32 d, int32 e, int32 f, int32 z, int32 x);
int fnSetMousePointer(Object *cpt, int32 id, int32 c, int32 d, int32 e, int32 f, int32 z, int32 x);
int fnSetMouseLuggage(Object *cpt, int32 id, int32 c, int32 d, int32 e, int32 f, int32 z, int32 x);
int fnMouseOn(Object *cpt, int32 id, int32 c, int32 d, int32 e, int32 f, int32 z, int32 x);
int fnMouseOff(Object *cpt, int32 id, int32 c, int32 d, int32 e, int32 f, int32 z, int32 x);
int fnChooser(Object *cpt, int32 id, int32 c, int32 d, int32 e, int32 f, int32 z, int32 x);
int fnEndChooser(Object *cpt, int32 id, int32 c, int32 d, int32 e, int32 f, int32 z, int32 x);
int fnStartMenu(Object *cpt, int32 id, int32 c, int32 d, int32 e, int32 f, int32 z, int32 x);
int fnEndMenu(Object *cpt, int32 id, int32 c, int32 d, int32 e, int32 f, int32 z, int32 x);
int cfnReleaseMenu(Object *cpt, int32 id, int32 c, int32 d, int32 e, int32 f, int32 z, int32 x);
int fnAddSubject(Object *cpt, int32 id, int32 c, int32 d, int32 e, int32 f, int32 z, int32 x);
int fnAddObject(Object *cpt, int32 id, int32 c, int32 d, int32 e, int32 f, int32 z, int32 x);
int fnRemoveObject(Object *cpt, int32 id, int32 c, int32 d, int32 e, int32 f, int32 z, int32 x);
int fnEnterSection(Object *cpt, int32 id, int32 c, int32 d, int32 e, int32 f, int32 z, int32 x);
int fnLeaveSection(Object *cpt, int32 id, int32 c, int32 d, int32 e, int32 f, int32 z, int32 x);
int fnChangeFloor(Object *cpt, int32 id, int32 c, int32 d, int32 e, int32 f, int32 z, int32 x);
int fnWalk(Object *cpt, int32 id, int32 c, int32 d, int32 e, int32 f, int32 z, int32 x);
int fnTurn(Object *cpt, int32 id, int32 c, int32 d, int32 e, int32 f, int32 z, int32 x);
int fnStand(Object *cpt, int32 id, int32 c, int32 d, int32 e, int32 f, int32 z, int32 x);
int fnStandAt(Object *cpt, int32 id, int32 c, int32 d, int32 e, int32 f, int32 z, int32 x);
int fnFace(Object *cpt, int32 id, int32 c, int32 d, int32 e, int32 f, int32 z, int32 x);
int fnFaceXy(Object *cpt, int32 id, int32 c, int32 d, int32 e, int32 f, int32 z, int32 x);
int fnIsFacing(Object *cpt, int32 id, int32 c, int32 d, int32 e, int32 f, int32 z, int32 x);
int fnGetTo(Object *cpt, int32 id, int32 c, int32 d, int32 e, int32 f, int32 z, int32 x);
int fnGetToError(Object *cpt, int32 id, int32 c, int32 d, int32 e, int32 f, int32 z, int32 x);
int fnGetPos(Object *cpt, int32 id, int32 c, int32 d, int32 e, int32 f, int32 z, int32 x);
int fnGetGamepadXy(Object *cpt, int32 id, int32 c, int32 d, int32 e, int32 f, int32 z, int32 x);
int fnPlayFx(Object *cpt, int32 id, int32 c, int32 d, int32 e, int32 f, int32 z, int32 x);
int fnStopFx(Object *cpt, int32 id, int32 c, int32 d, int32 e, int32 f, int32 z, int32 x);
int fnPlayMusic(Object *cpt, int32 id, int32 c, int32 d, int32 e, int32 f, int32 z, int32 x);
int fnStopMusic(Object *cpt, int32 id, int32 c, int32 d, int32 e, int32 f, int32 z, int32 x);
int fnInnerSpace(Object *cpt, int32 id, int32 c, int32 d, int32 e, int32 f, int32 z, int32 x);
int fnRandom(Object *cpt, int32 id, int32 c, int32 d, int32 e, int32 f, int32 z, int32 x);
int fnSetScreen(Object *cpt, int32 id, int32 c, int32 d, int32 e, int32 f, int32 z, int32 x);
int fnPreload(Object *cpt, int32 id, int32 c, int32 d, int32 e, int32 f, int32 z, int32 x);
int fnCheckCD(Object *cpt, int32 id, int32 c, int32 d, int32 e, int32 f, int32 z, int32 x);
int fnRestartGame(Object *cpt, int32 id, int32 c, int32 d, int32 e, int32 f, int32 z, int32 x);
int fnQuitGame(Object *cpt, int32 id, int32 c, int32 d, int32 e, int32 f, int32 z, int32 x);
int fnDeathScreen(Object *cpt, int32 id, int32 c, int32 d, int32 e, int32 f, int32 z, int32 x);
int fnSetParallax(Object *cpt, int32 id, int32 c, int32 d, int32 e, int32 f, int32 z, int32 x);
int fnTdebug(Object *cpt, int32 id, int32 c, int32 d, int32 e, int32 f, int32 z, int32 x);
int fnRedFlash(Object *cpt, int32 id, int32 c, int32 d, int32 e, int32 f, int32 z, int32 x);
int fnBlueFlash(Object *cpt, int32 id, int32 c, int32 d, int32 e, int32 f, int32 z, int32 x);
int fnYellow(Object *cpt, int32 id, int32 c, int32 d, int32 e, int32 f, int32 z, int32 x);
int fnGreen(Object *cpt, int32 id, int32 c, int32 d, int32 e, int32 f, int32 z, int32 x);
int fnPurple(Object *cpt, int32 id, int32 c, int32 d, int32 e, int32 f, int32 z, int32 x);
int fnBlack(Object *cpt, int32 id, int32 c, int32 d, int32 e, int32 f, int32 z, int32 x);
static const uint32 _scriptVarInit[NON_ZERO_SCRIPT_VARS][2];
static const uint8 *const _startData[];
static const uint8 *const _helperData[];
void startPosCallFn(uint8 fnId, uint32 param1, uint32 param2, uint32 param3);
void runStartScript(const uint8 *data);
};
enum StartPosOpcodes {
opcSeqEnd = 0,
opcCallFn,
opcCallFnLong,
opcSetVar8,
opcSetVar16,
opcSetVar32,
opcGeorge,
opcRunStart,
opcRunHelper,
opcPlaySequence,
opcAddObject,
opcRemoveObject,
opcMegaSet,
opcNoSprite
};
enum HelperScripts {
HELP_IRELAND = 0,
HELP_SYRIA,
HELP_SPAIN,
HELP_NIGHTTRAIN,
HELP_SCOTLAND,
HELP_WHITECOAT,
HELP_SPAIN2
};
} // End of namespace Sword1
#endif //BSLOGIC_H

138
engines/sword1/memman.cpp Normal file
View File

@@ -0,0 +1,138 @@
/* 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 "sword1/memman.h"
#include "common/textconsole.h"
#include "common/util.h"
namespace Sword1 {
MemMan::MemMan() {
_alloced = 0;
_memListFree = _memListFreeEnd = nullptr;
}
MemMan::~MemMan() {
flush();
if (_alloced)
warning("deleting MemMan, still %d bytes alloced", _alloced);
}
void MemMan::alloc(MemHandle *bsMem, uint32 pSize, uint16 pCond) {
_alloced += pSize;
bsMem->data = (void *)malloc(pSize);
if (!bsMem->data)
error("MemMan::alloc(): Can't alloc %d bytes of memory.", pSize);
bsMem->cond = pCond;
bsMem->size = pSize;
if (pCond == MEM_CAN_FREE) {
warning("%d Bytes alloced as FREEABLE.", pSize); // why should one want to alloc mem if it can be freed?
addToFreeList(bsMem);
} else if (bsMem->next || bsMem->prev) // it's in our _freeAble list, remove it from there
removeFromFreeList(bsMem);
checkMemoryUsage();
}
void MemMan::freeNow(MemHandle *bsMem) {
if (bsMem->cond != MEM_FREED) {
_alloced -= bsMem->size;
removeFromFreeList(bsMem);
free(bsMem->data);
bsMem->cond = MEM_FREED;
}
}
void MemMan::setCondition(MemHandle *bsMem, uint16 pCond) {
if ((pCond == MEM_FREED) || (pCond > MEM_DONT_FREE))
error("MemMan::setCondition: program tried to set illegal memory condition");
if (bsMem->cond != pCond) {
bsMem->cond = pCond;
if (pCond == MEM_DONT_FREE)
removeFromFreeList(bsMem);
else if (pCond == MEM_CAN_FREE)
addToFreeList(bsMem);
}
}
void MemMan::flush() {
while (_memListFree) {
if (_memListFreeEnd == nullptr) {
warning("MemMan::flush(): _memListFreeEnd is nullptr");
break;
}
free(_memListFreeEnd->data);
_memListFreeEnd->data = nullptr;
_memListFreeEnd->cond = MEM_FREED;
_alloced -= _memListFreeEnd->size;
removeFromFreeList(_memListFreeEnd);
}
if (_alloced)
warning("MemMan::flush: Something's wrong: still %d bytes alloced", _alloced);
}
void MemMan::checkMemoryUsage() {
while ((_alloced > MAX_ALLOC) && _memListFree) {
if (_memListFreeEnd == nullptr) {
warning("MemMan::checkMemoryUsage(): _memListFreeEnd is nullptr");
break;
}
free(_memListFreeEnd->data);
_memListFreeEnd->data = nullptr;
_memListFreeEnd->cond = MEM_FREED;
_alloced -= _memListFreeEnd->size;
removeFromFreeList(_memListFreeEnd);
}
}
void MemMan::addToFreeList(MemHandle *bsMem) {
if (bsMem->next || bsMem->prev) {
warning("addToFreeList: mem block is already in freeList");
return;
}
bsMem->prev = nullptr;
bsMem->next = _memListFree;
if (bsMem->next)
bsMem->next->prev = bsMem;
_memListFree = bsMem;
if (!_memListFreeEnd)
_memListFreeEnd = _memListFree;
}
void MemMan::removeFromFreeList(MemHandle *bsMem) {
if (_memListFree == bsMem)
_memListFree = bsMem->next;
if (_memListFreeEnd == bsMem)
_memListFreeEnd = bsMem->prev;
if (bsMem->next)
bsMem->next->prev = bsMem->prev;
if (bsMem->prev)
bsMem->prev->next = bsMem->next;
bsMem->next = bsMem->prev = nullptr;
}
void MemMan::initHandle(MemHandle *bsMem) {
memset(bsMem, 0, sizeof(MemHandle));
}
} // End of namespace Sword1

63
engines/sword1/memman.h Normal file
View File

@@ -0,0 +1,63 @@
/* 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 SWORD1_MEMMAN_H
#define SWORD1_MEMMAN_H
#include "common/scummsys.h"
namespace Sword1 {
struct MemHandle {
void *data;
uint32 size;
uint32 refCount;
uint16 cond;
MemHandle *next, *prev;
};
// mem conditions:
#define MEM_FREED 0
#define MEM_CAN_FREE 1
#define MEM_DONT_FREE 2
#define MAX_ALLOC (6*1024*1024) // max amount of mem we want to alloc().
class MemMan {
public:
MemMan();
~MemMan();
void alloc(MemHandle *bsMem, uint32 pSize, uint16 pCond = MEM_DONT_FREE);
void setCondition(MemHandle *bsMem, uint16 pCond);
void freeNow(MemHandle *bsMem);
void initHandle(MemHandle *bsMem);
void flush();
private:
void addToFreeList(MemHandle *bsMem);
void removeFromFreeList(MemHandle *bsMem);
void checkMemoryUsage();
uint32 _alloced; //currently allocated memory
MemHandle *_memListFree;
MemHandle *_memListFreeEnd;
};
} // End of namespace Sword1
#endif //MEMMAN_H

452
engines/sword1/menu.cpp Normal file
View File

@@ -0,0 +1,452 @@
/* 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 "sword1/menu.h"
#include "sword1/resman.h"
#include "common/scummsys.h"
#include "common/util.h"
#include "common/system.h"
#include "sword1/mouse.h"
#include "sword1/screen.h"
#include "sword1/logic.h"
namespace Sword1 {
enum {
MENU_CLOSED,
MENU_CLOSING,
MENU_OPENING,
MENU_OPEN
};
const byte Menu::_fadeEffectTop[64] = {
1, 7, 5, 3, 2, 4, 6, 0,
3, 1, 7, 5, 4, 6, 0, 2,
5, 3, 1, 7, 6, 0, 2, 4,
7, 5, 3, 1, 0, 2, 4, 6,
7, 5, 3, 1, 0, 2, 4, 6,
5, 3, 1, 7, 6, 0, 2, 4,
3, 1, 7, 5, 4, 6, 0, 2,
1, 7, 5, 3, 2, 4, 6, 0
};
const byte Menu::_fadeEffectBottom[64] = {
7, 6, 5, 4, 3, 2, 1, 0,
0, 7, 6, 5, 4, 3, 2, 1,
1, 0, 7, 6, 5, 4, 3, 2,
2, 1, 0, 7, 6, 5, 4, 3,
3, 2, 1, 0, 7, 6, 5, 4,
4, 3, 2, 1, 0, 7, 6, 5,
5, 4, 3, 2, 1, 0, 7, 6,
6, 5, 4, 3, 2, 1, 0, 7
};
MenuIcon::MenuIcon(uint8 menuType, uint8 menuPos, uint32 resId, uint32 frame, Screen *screen) {
_menuType = menuType;
_menuPos = menuPos;
_resId = resId;
_frame = frame;
_screen = screen;
_selected = false;
}
bool MenuIcon::wasClicked(uint16 mouseX, uint16 mouseY) {
if (((_menuType == MENU_TOP) && (mouseY >= 40)) || ((_menuType == MENU_BOT) && (mouseY < 440)))
return false;
if ((mouseX >= _menuPos * 40) && (mouseX < (_menuPos + 1) * 40))
return true;
else
return false;
}
void MenuIcon::setSelect(bool pSel) {
_selected = pSel;
}
void MenuIcon::draw(const byte *fadeMask, int8 fadeStatus) {
uint16 x = _menuPos * 40;
uint16 y = (_menuType == MENU_TOP) ? (0) : (440);
_screen->showFrame(x, y, _resId, _frame + (_selected ? 1 : 0), fadeMask, fadeStatus);
}
Menu::Menu(Screen *pScreen, Mouse *pMouse) {
uint8 cnt;
_screen = pScreen;
_mouse = pMouse;
_subjectBarStatus = MENU_CLOSED;
_objectBarStatus = MENU_CLOSED;
_fadeSubject = 0;
_fadeObject = 0;
for (cnt = 0; cnt < 16; cnt++)
_subjects[cnt] = nullptr;
for (cnt = 0; cnt < TOTAL_pockets; cnt++)
_objects[cnt] = nullptr;
_inMenu = 0;
}
Menu::~Menu() {
int i;
// the menu may be open, so delete the icons
for (i = 0; i < TOTAL_pockets; i++) {
delete _objects[i];
_objects[i] = nullptr;
}
for (i = 0; i < 16; i++) {
delete _subjects[i];
_subjects[i] = nullptr;
}
}
void Menu::refreshMenus() {
if (_objectBarStatus == MENU_OPEN) {
buildMenu();
for (uint8 cnt = 0; cnt < 16; cnt++) {
if (_objects[cnt])
_objects[cnt]->draw();
else
_screen->showFrame(cnt * 40, 0, 0xffffffff, 0);
}
}
if (_subjectBarStatus == MENU_OPEN) {
buildSubjects();
for (uint8 cnt = 0; cnt < 16; cnt++) {
if (_subjects[cnt])
_subjects[cnt]->draw();
else
_screen->showFrame(cnt * 40, 440, 0xffffffff, 0);
}
}
}
uint8 Menu::checkMenuClick(uint8 menuType) {
Common::StackLock lock(_menuMutex);
uint16 mouseEvent = _mouse->testEvent();
if (!mouseEvent)
return 0;
uint16 x, y;
_mouse->giveCoords(&x, &y);
if (_subjectBarStatus == MENU_OPEN) {
// Conversation mode. Icons are highlighted on mouse-down, but
// the actual response is made on mouse-up.
if (menuType == MENU_BOT) {
if (Logic::_scriptVars[OBJECT_HELD] && (mouseEvent & BS1L_BUTTON_UP)) {
for (uint8 cnt = 0; cnt < Logic::_scriptVars[IN_SUBJECT]; cnt++) {
if (_subjectBar[cnt] == Logic::_scriptVars[OBJECT_HELD])
return cnt + 1;
}
} else if (mouseEvent & BS1L_BUTTON_DOWN) {
for (uint8 cnt = 0; cnt < Logic::_scriptVars[IN_SUBJECT]; cnt++) {
if (_subjects[cnt] && _subjects[cnt]->wasClicked(x, y)) {
Logic::_scriptVars[OBJECT_HELD] = _subjectBar[cnt];
refreshMenus();
break;
}
}
}
} else {
if (Logic::_scriptVars[OBJECT_HELD] && (mouseEvent & BS1L_BUTTON_UP)) {
for (uint8 cnt = 0; cnt < _inMenu; cnt++) {
if (_menuList[cnt] == Logic::_scriptVars[OBJECT_HELD])
return cnt + 1;
}
} else if (mouseEvent & BS1L_BUTTON_DOWN) {
for (uint8 cnt = 0; cnt < _inMenu; cnt++) {
if (_objects[cnt] && _objects[cnt]->wasClicked(x, y)) {
Logic::_scriptVars[OBJECT_HELD] = _menuList[cnt];
refreshMenus();
break;
}
}
}
}
} else {
// Normal use, i.e. inventory. Things happen on mouse-down.
if (menuType == MENU_TOP) {
for (uint8 cnt = 0; cnt < _inMenu; cnt++) {
if (_objects[cnt] && _objects[cnt]->wasClicked(x, y)) {
if (mouseEvent & BS1R_BUTTON_DOWN) { // looking at item
Logic::_scriptVars[OBJECT_HELD] = _menuList[cnt];
Logic::_scriptVars[MENU_LOOKING] = 1;
Logic::_scriptVars[DEFAULT_ICON_TEXT] = _objectDefs[_menuList[cnt]].textDesc;
} else if (mouseEvent & BS1L_BUTTON_DOWN) {
if (Logic::_scriptVars[OBJECT_HELD]) {
if (Logic::_scriptVars[OBJECT_HELD] == _menuList[cnt]) {
_mouse->setLuggage(0, 0);
Logic::_scriptVars[OBJECT_HELD] = 0; // reselected => deselect it
} else { // the player is clicking another item on this one.
// run its use-script, if there is one
Logic::_scriptVars[SECOND_ITEM] = _menuList[cnt];
_mouse->setLuggage(0, 0);
}
} else {
Logic::_scriptVars[OBJECT_HELD] = _menuList[cnt];
_mouse->setLuggage(_objectDefs[_menuList[cnt]].luggageIconRes, 0);
}
}
refreshMenus();
break;
}
}
}
}
return 0;
}
void Menu::buildSubjects() {
Common::StackLock lock(_menuMutex);
uint8 cnt;
for (cnt = 0; cnt < 16; cnt++)
if (_subjects[cnt]) {
delete _subjects[cnt];
_subjects[cnt] = nullptr;
}
for (cnt = 0; cnt < Logic::_scriptVars[IN_SUBJECT]; cnt++) {
uint32 res = _subjectList[(_subjectBar[cnt] & 65535) - BASE_SUBJECT].subjectRes;
uint32 frame = _subjectList[(_subjectBar[cnt] & 65535) - BASE_SUBJECT].frameNo;
_subjects[cnt] = new MenuIcon(MENU_BOT, cnt, res, frame, _screen);
if (Logic::_scriptVars[OBJECT_HELD])
_subjects[cnt]->setSelect(_subjectBar[cnt] == Logic::_scriptVars[OBJECT_HELD]);
else
_subjects[cnt]->setSelect(true);
}
}
void Menu::refresh(uint8 menuType) {
Common::StackLock lock(_menuMutex);
uint i;
if (menuType == MENU_TOP) {
if (_objectBarStatus == MENU_OPENING || _objectBarStatus == MENU_CLOSING) {
for (i = 0; i < 16; i++) {
if (_objects[i])
_objects[i]->draw(_fadeEffectTop, _fadeObject);
else
_screen->showFrame(i * 40, 0, 0xffffffff, 0, _fadeEffectTop, _fadeObject);
}
}
if (_objectBarStatus == MENU_OPENING) {
if (_fadeObject < 8)
_fadeObject++;
else
_objectBarStatus = MENU_OPEN;
} else if (_objectBarStatus == MENU_CLOSING) {
if (_fadeObject > 0)
_fadeObject--;
else {
for (i = 0; i < _inMenu; i++) {
delete _objects[i];
_objects[i] = nullptr;
}
_objectBarStatus = MENU_CLOSED;
}
}
} else {
if (_subjectBarStatus == MENU_OPENING || _subjectBarStatus == MENU_CLOSING) {
for (i = 0; i < 16; i++) {
if (_subjects[i])
_subjects[i]->draw(_fadeEffectBottom, _fadeSubject);
else
_screen->showFrame(i * 40, 440, 0xffffffff, 0, _fadeEffectBottom, _fadeSubject);
}
}
if (_subjectBarStatus == MENU_OPENING) {
if (_fadeSubject < 8)
_fadeSubject++;
else
_subjectBarStatus = MENU_OPEN;
} else if (_subjectBarStatus == MENU_CLOSING) {
if (_fadeSubject > 0)
_fadeSubject--;
else {
for (i = 0; i < Logic::_scriptVars[IN_SUBJECT]; i++) {
delete _subjects[i];
_subjects[i] = nullptr;
}
_subjectBarStatus = MENU_CLOSED;
}
}
}
}
void Menu::buildMenu() {
Common::StackLock lock(_menuMutex);
uint32 *pockets = Logic::_scriptVars + POCKET_1;
for (uint8 cnt = 0; cnt < _inMenu; cnt++)
if (_objects[cnt]) {
delete _objects[cnt];
_objects[cnt] = nullptr;
}
_inMenu = 0;
for (uint32 pocketNo = 0; pocketNo < TOTAL_pockets; pocketNo++)
if (pockets[pocketNo]) {
_menuList[_inMenu] = pocketNo + 1;
_inMenu++;
}
for (uint32 menuSlot = 0; menuSlot < _inMenu; menuSlot++) {
_objects[menuSlot] = new MenuIcon(MENU_TOP, menuSlot, _objectDefs[_menuList[menuSlot]].bigIconRes, _objectDefs[_menuList[menuSlot]].bigIconFrame, _screen);
uint32 objHeld = Logic::_scriptVars[OBJECT_HELD];
// check highlighting
if (Logic::_scriptVars[MENU_LOOKING] || _subjectBarStatus == MENU_OPEN) { // either we're in the chooser or we're doing a 'LOOK AT'
if ((!objHeld) || (objHeld == _menuList[menuSlot]))
_objects[menuSlot]->setSelect(true);
} else if (Logic::_scriptVars[SECOND_ITEM]) { // clicked luggage onto 2nd icon - we need to color-highlight the 2 relevant icons & grey out the rest
if ((_menuList[menuSlot] == objHeld) || (_menuList[menuSlot] == Logic::_scriptVars[SECOND_ITEM]))
_objects[menuSlot]->setSelect(true);
} else { // this object is selected - ie. GREYED OUT
if (objHeld != _menuList[menuSlot])
_objects[menuSlot]->setSelect(true);
}
}
}
void Menu::showMenu(uint8 menuType) {
Common::StackLock lock(_menuMutex);
if (menuType == MENU_TOP) {
if (_objectBarStatus == MENU_OPEN) {
for (uint8 cnt = 0; cnt < 16; cnt++) {
if (_objects[cnt])
_objects[cnt]->draw();
else
_screen->showFrame(cnt * 40, 0, 0xffffffff, 0);
}
} else if (_objectBarStatus == MENU_CLOSED) {
_objectBarStatus = MENU_OPENING;
_fadeObject = 0;
} else if (_objectBarStatus == MENU_CLOSING)
_objectBarStatus = MENU_OPENING;
}
}
void Menu::fnStartMenu() {
Logic::_scriptVars[OBJECT_HELD] = 0; // icon no longer selected
Logic::_scriptVars[SECOND_ITEM] = 0; // second icon no longer selected (after using one on another)
Logic::_scriptVars[MENU_LOOKING] = 0; // no longer 'looking at' an icon
buildMenu();
showMenu(MENU_TOP);
}
void Menu::fnEndMenu() {
Common::StackLock lock(_menuMutex);
if (_objectBarStatus != MENU_CLOSED)
_objectBarStatus = MENU_CLOSING;
}
void Menu::fnChooser(Object *compact) {
Logic::_scriptVars[OBJECT_HELD] = 0;
_mouse->setLuggage(0, 0);
buildSubjects();
compact->o_logic = LOGIC_choose;
_mouse->controlPanel(true); // so the mouse cursor will be shown.
Common::StackLock lock(_menuMutex);
_subjectBarStatus = MENU_OPENING;
}
void Menu::fnEndChooser() {
Logic::_scriptVars[OBJECT_HELD] = 0;
_menuMutex.lock();
_subjectBarStatus = MENU_CLOSING;
_objectBarStatus = MENU_CLOSING;
_menuMutex.unlock();
_mouse->controlPanel(false);
_mouse->setLuggage(0, 0);
}
void Menu::checkTopMenu() {
Common::StackLock lock(_menuMutex);
if (_objectBarStatus == MENU_OPEN)
checkMenuClick(MENU_TOP);
}
void Menu::setToTargetState() {
Common::StackLock lock(_menuMutex);
// This is an optimization for all the locks introduced
// with the fade palette changes: we disable the menu
// updates whenever the palette is fading, and we bring
// the menu to its target state.
// Note that we are only doing this for the top menu:
// I haven't seen any instance of a bottom menu (dialog)
// being able to immediately open after a palette fade.
if (_objectBarStatus == MENU_CLOSING) {
_objectBarStatus = MENU_CLOSED;
_fadeObject = 0;
for (int i = 0; i < 16; i++) {
if (_objects[i])
_objects[i]->draw(_fadeEffectTop, _fadeObject);
else
_screen->showFrame(i * 40, 0, 0xffffffff, 0, _fadeEffectTop, _fadeObject);
}
}
if (_objectBarStatus == MENU_OPENING) {
_objectBarStatus = MENU_OPEN;
_fadeObject = 8;
showMenu(MENU_TOP);
}
if (_subjectBarStatus == MENU_CLOSING) {
_subjectBarStatus = MENU_CLOSED;
_fadeSubject = 0;
for (int i = 0; i < 16; i++) {
if (_subjects[i])
_subjects[i]->draw(_fadeEffectBottom, _fadeSubject);
else
_screen->showFrame(i * 40, 440, 0xffffffff, 0, _fadeEffectBottom, _fadeSubject);
}
}
if (_subjectBarStatus == MENU_OPENING) {
_subjectBarStatus = MENU_OPEN;
_fadeSubject = 8;
showMenu(MENU_TOP);
}
}
int Menu::logicChooser(Object *compact) {
Common::StackLock lock(_menuMutex);
uint8 objSelected = 0;
if (_objectBarStatus == MENU_OPEN)
objSelected = checkMenuClick(MENU_TOP);
if (!objSelected)
objSelected = checkMenuClick(MENU_BOT);
if (objSelected) {
compact->o_logic = LOGIC_script;
return 1;
}
return 0;
}
void Menu::fnAddSubject(int32 sub) {
Common::StackLock lock(_menuMutex);
_subjectBar[Logic::_scriptVars[IN_SUBJECT]] = sub;
Logic::_scriptVars[IN_SUBJECT]++;
}
void Menu::cfnReleaseMenu() {
Common::StackLock lock(_menuMutex);
_objectBarStatus = MENU_CLOSING;
}
} // End of namespace Sword1

112
engines/sword1/menu.h Normal file
View File

@@ -0,0 +1,112 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef SWORD1_MENU_H
#define SWORD1_MENU_H
#include "sword1/sworddefs.h"
#include "sword1/object.h"
#include "common/mutex.h"
namespace Sword1 {
class Screen;
class Mouse;
class ResMan;
#define MENU_TOP 0
#define MENU_BOT 1
struct Subject {
uint32 subjectRes;
uint32 frameNo;
};
struct MenuObject {
int32 textDesc;
uint32 bigIconRes;
uint32 bigIconFrame;
uint32 luggageIconRes;
uint32 useScript;
};
class MenuIcon {
public:
MenuIcon(uint8 menuType, uint8 menuPos, uint32 resId, uint32 frame, Screen *screen);
bool wasClicked(uint16 mouseX, uint16 mouseY);
void setSelect(bool pSel);
void draw(const byte *fadeMask = NULL, int8 fadeStatus = 0);
private:
uint8 _menuType, _menuPos;
uint32 _resId, _frame;
bool _selected;
Screen *_screen;
};
class Menu {
public:
Menu(Screen *pScreen, Mouse *pMouse);
~Menu();
void fnChooser(Object *compact);
void fnEndChooser();
void fnAddSubject(int32 sub);
void cfnReleaseMenu();
int logicChooser(Object *compact);
void engine();
void refresh(uint8 menuType);
void fnStartMenu();
void fnEndMenu();
void checkTopMenu();
void setToTargetState();
static const MenuObject _objectDefs[TOTAL_pockets + 1];
private:
void buildSubjects();
void buildMenu();
void showMenu(uint8 menuType);
byte _subjectBarStatus;
byte _objectBarStatus;
int8 _fadeSubject;
int8 _fadeObject;
void refreshMenus();
uint8 checkMenuClick(uint8 menuType);
//- lower menu, speech subjects:
MenuIcon *_subjects[16];
uint32 _subjectBar[16];
//- top menu, items
MenuIcon *_objects[TOTAL_pockets];
uint32 _menuList[TOTAL_pockets];
uint32 _inMenu;
Screen *_screen;
Mouse *_mouse;
static const Subject _subjectList[TOTAL_subjects];
static const byte _fadeEffectTop[64];
static const byte _fadeEffectBottom[64];
Common::Mutex _menuMutex;
};
} // End of namespace Sword1
#endif //BSMENU_H

View File

@@ -0,0 +1,436 @@
/* 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/advancedDetector.h"
#include "sword1/sword1.h"
#include "sword1/control.h"
#include "sword1/logic.h"
#include "common/savefile.h"
#include "common/system.h"
#include "common/translation.h"
#include "backends/keymapper/action.h"
#include "backends/keymapper/keymapper.h"
#include "backends/keymapper/standard-actions.h"
#include "engines/dialogs.h"
#include "graphics/thumbnail.h"
#include "graphics/surface.h"
#include "gui/ThemeEval.h"
#include "gui/widget.h"
#include "gui/widgets/popup.h"
namespace Sword1 {
#define GAMEOPTION_WINDOWS_AUDIO_MODE GUIO_GAMEOPTIONS1
#define GAMEOPTION_MULTILANGUAGE GUIO_GAMEOPTIONS2
#define GAMEOPTION_MULTILANGUAGE_EXTENDED GUIO_GAMEOPTIONS3
class Sword1OptionsWidget : public GUI::OptionsContainerWidget {
public:
explicit Sword1OptionsWidget(GuiObject *boss, const Common::String &name, const Common::String &domain);
// OptionsContainerWidget API
void load() override;
bool save() override;
private:
// OptionsContainerWidget API
void defineLayout(GUI::ThemeEval &layouts, const Common::String &layoutName, const Common::String &overlayedLayout) const override;
Common::StringArray _availableLangCodes = {"en", "de", "fr", "it", "es", "pt", "cs"};
Common::StringArray _availableLangs = {_("English"), _("German"), _("French"), _("Italian"), _("Spanish"), _("Brazilian Portuguese"), _("Czech")};
uint32 _numAvailableLangs = 0;
bool _atLeastOneAdditionalOpt = false;
GUI::PopUpWidget *_langPopUp;
GUI::CheckboxWidget *_windowsAudioMode;
};
Sword1OptionsWidget::Sword1OptionsWidget(GuiObject *boss, const Common::String &name, const Common::String &domain) :
OptionsContainerWidget(boss, name, "Sword1GameOptionsDialog", domain) {
if (Common::checkGameGUIOption(GAMEOPTION_MULTILANGUAGE, ConfMan.get("guioptions", domain))) {
_numAvailableLangs = 5;
} else if (Common::checkGameGUIOption(GAMEOPTION_MULTILANGUAGE_EXTENDED, ConfMan.get("guioptions", domain))) {
_numAvailableLangs = 7;
}
// Language
if (Common::checkGameGUIOption(GAMEOPTION_MULTILANGUAGE, ConfMan.get("guioptions", domain)) ||
Common::checkGameGUIOption(GAMEOPTION_MULTILANGUAGE_EXTENDED, ConfMan.get("guioptions", domain))) {
GUI::StaticTextWidget *textWidget = new GUI::StaticTextWidget(
widgetsBoss(),
_dialogLayout + ".subtitles_lang_desc",
_("Text language:"),
_("Set the language for the subtitles. This will not affect voices.")
);
textWidget->setAlign(Graphics::kTextAlignLeft);
_langPopUp = new GUI::PopUpWidget(
widgetsBoss(),
_dialogLayout + ".subtitles_lang",
_("Set the language for the subtitles. This will not affect voices.")
);
_langPopUp->appendEntry(_("<default>"), (uint32)-1);
for (uint32 i = 0; i < _numAvailableLangs; i++) {
_langPopUp->appendEntry(_availableLangs[i], i);
}
_atLeastOneAdditionalOpt = true;
} else {
_langPopUp = nullptr;
}
// Windows audio mode
if (Common::checkGameGUIOption(GAMEOPTION_WINDOWS_AUDIO_MODE, ConfMan.get("guioptions", domain))) {
_windowsAudioMode = new GUI::CheckboxWidget(
widgetsBoss(),
_dialogLayout + ".windows_audio_mode",
_("Simulate the audio engine from the Windows executable"),
_("Makes the game use softer (logarithmic) audio curves, but removes fade-in and fade-out for "
"sound effects, fade-in for music, and automatic music volume attenuation for when speech is playing"));
_atLeastOneAdditionalOpt = true;
} else {
_windowsAudioMode = nullptr;
}
if (_atLeastOneAdditionalOpt) {
GUI::StaticTextWidget *additionalOptsWidget = new GUI::StaticTextWidget(
widgetsBoss(),
_dialogLayout + ".additional_opts_label",
_("Additional options:"));
additionalOptsWidget->setAlign(Graphics::kTextAlignLeft);
}
}
void Sword1OptionsWidget::load() {
Common::ConfigManager::Domain *gameConfig = ConfMan.getDomain(_domain);
if (!gameConfig)
return;
if (_langPopUp) {
uint32 curLangIndex = (uint32)-1;
Common::String curLang;
gameConfig->tryGetVal("subtitles_language_override", curLang);
if (!curLang.empty()) {
for (uint i = 0; i < _numAvailableLangs; ++i) {
if (_availableLangCodes[i].equalsIgnoreCase(curLang)) {
curLangIndex = i;
break;
}
}
}
_langPopUp->setSelectedTag(curLangIndex);
}
if (_windowsAudioMode) {
Common::String windowsAudioMode;
gameConfig->tryGetVal("windows_audio_mode", windowsAudioMode);
if (!windowsAudioMode.empty()) {
bool val;
if (parseBool(windowsAudioMode, val))
_windowsAudioMode->setState(val);
}
}
}
bool Sword1OptionsWidget::save() {
if (_langPopUp) {
uint langIndex = _langPopUp->getSelectedTag();
if (langIndex < _numAvailableLangs)
ConfMan.set("subtitles_language_override", _availableLangCodes[langIndex], _domain);
else
ConfMan.removeKey("subtitles_language_override", _domain);
}
if (_windowsAudioMode)
ConfMan.setBool("windows_audio_mode", _windowsAudioMode->getState(), _domain);
return true;
}
void Sword1OptionsWidget::defineLayout(GUI::ThemeEval &layouts, const Common::String &layoutName, const Common::String &overlayedLayout) const {
layouts.addDialog(layoutName, overlayedLayout);
layouts.addLayout(GUI::ThemeLayout::kLayoutVertical).addPadding(16, 0, 0, 0); // Layout 1
layouts.addWidget("additional_opts_label", "OptionsLabel");
layouts.addLayout(GUI::ThemeLayout::kLayoutVertical).addPadding(8, 0, 4, 0); // Layout 2
layouts.addWidget("subtitles_lang_desc", "OptionsLabel");
layouts.addWidget("subtitles_lang", "PopUp");
// This third layout is added for further separation from the dropdown list
layouts.addLayout(GUI::ThemeLayout::kLayoutVertical).addPadding(0, 0, 0, 0); // Layout 3
if (_langPopUp) // Don't draw padding if there's no lang selection
layouts.addPadding(0, 0, 8, 0);
layouts.addWidget("windows_audio_mode", "Checkbox");
layouts.closeLayout(); // Close layout 3
layouts.closeLayout(); // Close layout 2
layouts.closeLayout().closeDialog(); // Close layout 1
}
} // End of namespace Sword1
class SwordMetaEngine : public AdvancedMetaEngine<ADGameDescription> {
public:
const char *getName() const override {
return "sword1";
}
bool hasFeature(MetaEngineFeature f) const override;
SaveStateList listSaves(const char *target) const override;
int getMaximumSaveSlot() const override;
bool removeSaveState(const char *target, int slot) const override;
SaveStateDescriptor querySaveMetaInfos(const char *target, int slot) const override;
GUI::OptionsContainerWidget *buildEngineOptionsWidget(GUI::GuiObject *boss, const Common::String &name, const Common::String &target) const override;
Common::Error createInstance(OSystem *syst, Engine **engine, const ADGameDescription *desc) const override;
Common::String getSavegameFile(int saveGameIdx, const char *target) const override {
if (saveGameIdx == kSavegameFilePattern)
return Common::String::format("sword1.###");
else
return Common::String::format("sword1.%03d", saveGameIdx);
}
Common::KeymapArray initKeymaps(const char *target) const override;
};
bool SwordMetaEngine::hasFeature(MetaEngineFeature f) const {
return
(f == kSupportsListSaves) ||
(f == kSupportsLoadingDuringStartup) ||
(f == kSupportsDeleteSave) ||
(f == kSavesSupportMetaInfo) ||
(f == kSavesSupportThumbnail) ||
(f == kSavesSupportCreationDate) ||
(f == kSavesSupportPlayTime);
}
bool Sword1::SwordEngine::hasFeature(EngineFeature f) const {
return
(f == kSupportsReturnToLauncher) ||
(f == kSupportsSavingDuringRuntime) ||
(f == kSupportsLoadingDuringRuntime);
}
GUI::OptionsContainerWidget *SwordMetaEngine::buildEngineOptionsWidget(GUI::GuiObject *boss, const Common::String &name, const Common::String &target) const {
return new Sword1::Sword1OptionsWidget(boss, name, target);
}
Common::Error SwordMetaEngine::createInstance(OSystem *syst, Engine **engine, const ADGameDescription *desc) const {
*engine = new Sword1::SwordEngine(syst, desc);
return Common::kNoError;
}
Common::KeymapArray SwordMetaEngine::initKeymaps(const char *target) const {
using namespace Common;
using namespace Sword1;
Keymap *engineKeyMap = new Keymap(Keymap::kKeymapTypeGame, "sword1-default", _("Default keymappings"));
Keymap *gameKeyMap = new Keymap(Keymap::kKeymapTypeGame, "game-shortcuts", _("Game keymappings"));
Action *act;
act = new Action(kStandardActionLeftClick, _("Left click"));
act->setLeftClickEvent();
act->addDefaultInputMapping("MOUSE_LEFT");
act->addDefaultInputMapping("JOY_A");
engineKeyMap->addAction(act);
act = new Action(kStandardActionRightClick, _("Right click"));
act->setRightClickEvent();
act->addDefaultInputMapping("MOUSE_RIGHT");
act->addDefaultInputMapping("JOY_B");
engineKeyMap->addAction(act);
act = new Action("ESCAPE", _("Exit / Skip"));
act->setCustomEngineActionEvent(kActionEscape);
act->addDefaultInputMapping("ESCAPE");
act->addDefaultInputMapping("JOY_BACK");
gameKeyMap->addAction(act);
act = new Action("PAUSE", _("Pause game"));
act->setCustomEngineActionEvent(kActionPause);
act->addDefaultInputMapping("p");
act->addDefaultInputMapping("JOY_X");
gameKeyMap->addAction(act);
act = new Action("QUIT", _("Quit game"));
act->setCustomEngineActionEvent(kActionQuit);
act->addDefaultInputMapping("C+q");
act->addDefaultInputMapping("JOY_CENTER");
gameKeyMap->addAction(act);
act = new Action("MAINPANEL", _("Main menu"));
act->setCustomEngineActionEvent(kActionMainPanel);
act->addDefaultInputMapping("F5");
act->addDefaultInputMapping("JOY_Y");
gameKeyMap->addAction(act);
KeymapArray keymaps(2);
keymaps[0] = engineKeyMap;
keymaps[1] = gameKeyMap;
return keymaps;
}
SaveStateList SwordMetaEngine::listSaves(const char *target) const {
Common::SaveFileManager *saveFileMan = g_system->getSavefileManager();
SaveStateList saveList;
char saveName[40];
Common::StringArray filenames = saveFileMan->listSavefiles("sword1.###");
int slotNum = 0;
for (const auto &filename : filenames) {
// Obtain the last 3 digits of the filename, since they correspond to the save slot
slotNum = atoi(filename.c_str() + filename.size() - 3);
if (slotNum >= 0 && slotNum <= 999) {
Common::InSaveFile *in = saveFileMan->openForLoading(filename);
if (in) {
in->readUint32LE(); // header
in->read(saveName, 40);
saveList.push_back(SaveStateDescriptor(this, slotNum, saveName));
delete in;
}
}
}
// Sort saves based on slot number.
Common::sort(saveList.begin(), saveList.end(), SaveStateDescriptorSlotComparator());
return saveList;
}
int SwordMetaEngine::getMaximumSaveSlot() const { return 999; }
bool SwordMetaEngine::removeSaveState(const char *target, int slot) const {
return g_system->getSavefileManager()->removeSavefile(Common::String::format("sword1.%03d", slot));
}
SaveStateDescriptor SwordMetaEngine::querySaveMetaInfos(const char *target, int slot) const {
Common::String fileName = Common::String::format("sword1.%03d", slot);
char name[40];
uint32 playTime = 0;
byte versionSave;
Common::InSaveFile *in = g_system->getSavefileManager()->openForLoading(fileName);
if (in) {
in->skip(4); // header
in->read(name, sizeof(name));
in->read(&versionSave, 1); // version
SaveStateDescriptor desc(this, slot, name);
if (versionSave < 2) // These older version of the savegames used a flag to signal presence of thumbnail
in->skip(1);
if (Graphics::checkThumbnailHeader(*in)) {
Graphics::Surface *thumbnail;
if (!Graphics::loadThumbnail(*in, thumbnail)) {
delete in;
return SaveStateDescriptor();
}
desc.setThumbnail(thumbnail);
}
uint32 saveDate = in->readUint32BE();
uint16 saveTime = in->readUint16BE();
if (versionSave > 1) // Previous versions did not have playtime data
playTime = in->readUint32BE();
int day = (saveDate >> 24) & 0xFF;
int month = (saveDate >> 16) & 0xFF;
int year = saveDate & 0xFFFF;
desc.setSaveDate(year, month, day);
int hour = (saveTime >> 8) & 0xFF;
int minutes = saveTime & 0xFF;
desc.setSaveTime(hour, minutes);
if (versionSave > 1) {
desc.setPlayTime(playTime * 1000);
} else { //We have no playtime data
desc.setPlayTime(0);
}
delete in;
return desc;
}
return SaveStateDescriptor();
}
#if PLUGIN_ENABLED_DYNAMIC(SWORD1)
REGISTER_PLUGIN_DYNAMIC(SWORD1, PLUGIN_TYPE_ENGINE, SwordMetaEngine);
#else
REGISTER_PLUGIN_STATIC(SWORD1, PLUGIN_TYPE_ENGINE, SwordMetaEngine);
#endif
namespace Sword1 {
Common::Error SwordEngine::loadGameState(int slot) {
_systemVars.controlPanelMode = CP_NORMAL;
_control->restoreGameFromFile(slot);
reinitialize();
_control->doRestore();
reinitRes();
return Common::kNoError; // TODO: return success/failure
}
bool SwordEngine::canLoadGameStateCurrently(Common::U32String *msg) {
return (mouseIsActive() && !_control->isPanelShown()); // Disable GMM loading when game panel is shown
}
Common::Error SwordEngine::saveGameState(int slot, const Common::String &desc, bool isAutosave) {
_control->setSaveDescription(slot, desc.c_str());
_control->saveGameToFile(slot);
return Common::kNoError; // TODO: return success/failure
}
bool SwordEngine::canSaveGameStateCurrently(Common::U32String *msg) {
return (mouseIsActive() && !_control->isPanelShown() && Logic::_scriptVars[SCREEN] != 91);
}
} // End of namespace Sword1

32
engines/sword1/module.mk Normal file
View File

@@ -0,0 +1,32 @@
MODULE := engines/sword1
MODULE_OBJS := \
animation.o \
console.o \
control.o \
debug.o \
eventman.o \
logic.o \
memman.o \
menu.o \
metaengine.o \
mouse.o \
objectman.o \
resman.o \
router.o \
screen.o \
sound.o \
staticres.o \
sword1.o \
text.o
# This module can be built as a plugin
ifeq ($(ENABLE_SWORD1), DYNAMIC_PLUGIN)
PLUGIN := 1
endif
# Include common rules
include $(srcdir)/rules.mk
# Detection objects
DETECT_OBJS += $(MODULE)/detection.o

377
engines/sword1/mouse.cpp Normal file
View File

@@ -0,0 +1,377 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "common/system.h"
#include "graphics/cursorman.h"
#include "sword1/mouse.h"
#include "sword1/menu.h"
#include "sword1/screen.h"
#include "sword1/logic.h"
#include "sword1/resman.h"
#include "sword1/objectman.h"
#include "sword1/sworddefs.h"
#include "sword1/swordres.h"
#include "sword1/sword1.h"
namespace Sword1 {
Mouse::Mouse(OSystem *system, ResMan *pResMan, ObjectMan *pObjMan) {
_resMan = pResMan;
_objMan = pObjMan;
_system = system;
_currentPtr = nullptr;
}
Mouse::~Mouse() {
setLuggage(0, 0);
setPointer(0, 0);
for (uint8 cnt = 0; cnt < 17; cnt++) // close mouse cursor resources
_resMan->resClose(MSE_POINTER + cnt);
}
void Mouse::initialize() {
_numObjs = 0;
Logic::_scriptVars[MOUSE_STATUS] = 0; // mouse off and unlocked
_getOff = 0;
_inTopMenu = false;
_lastState = 0;
_mouseOverride = false;
_currentPtrId = _currentLuggageId = 0;
for (uint8 cnt = 0; cnt < 17; cnt++) // force res manager to keep mouse
_resMan->resOpen(MSE_POINTER + cnt); // cursors in memory all the time
CursorMan.showMouse(false);
createPointer(0, 0);
}
void Mouse::controlPanel(bool on) { // true on entering cpanel, false when leaving
static uint32 savedPtrId = 0;
if (on) {
savedPtrId = _currentPtrId;
_mouseOverride = true;
setPointer(MSE_POINTER, 0);
} else {
_currentPtrId = savedPtrId;
_mouseOverride = false;
setPointer(_currentPtrId, 0);
}
}
void Mouse::useLogicAndMenu(Logic *pLogic, Menu *pMenu) {
_logic = pLogic;
_menu = pMenu;
}
void Mouse::useScreenMutex(Common::Mutex *mutex) {
_screenAccessMutex = mutex;
}
void Mouse::addToList(int id, Object *compact) {
_objList[_numObjs].id = id;
_objList[_numObjs].compact = compact;
_numObjs++;
}
void Mouse::engine(uint16 x, uint16 y, uint16 eventFlags) {
_state = 0; // all mouse events are flushed after one cycle.
if (_lastState) { // delay all events by one cycle to notice L_button + R_button clicks correctly.
_state = _lastState | eventFlags;
_lastState = 0;
} else if (eventFlags)
_lastState = eventFlags;
// if we received both, mouse down and mouse up event in this cycle, resort them so that
// we'll receive the up event in the next one.
if ((_state & MOUSE_DOWN_MASK) && (_state & MOUSE_UP_MASK)) {
_lastState = _state & MOUSE_UP_MASK;
_state &= MOUSE_DOWN_MASK;
}
_mouse.x = x;
_mouse.y = y;
if (!(Logic::_scriptVars[MOUSE_STATUS] & 1)) { // no human?
_numObjs = 0;
return; // no human, so we don't want the mouse engine
}
if (!Logic::_scriptVars[TOP_MENU_DISABLED]) {
if (y < 40) { // okay, we are in the top menu.
if (!_inTopMenu) { // are we just entering it?
if (!Logic::_scriptVars[OBJECT_HELD])
_menu->fnStartMenu();
setPointer(MSE_POINTER, 0);
}
_menu->checkTopMenu();
_inTopMenu = true;
} else if (_inTopMenu) { // we're not in the menu. did we just leave it?
if (!Logic::_scriptVars[OBJECT_HELD])
_menu->fnEndMenu();
_inTopMenu = false;
}
} else if (_inTopMenu) {
_menu->fnEndMenu();
_inTopMenu = false;
}
Logic::_scriptVars[MOUSE_X] = Logic::_scriptVars[SCROLL_OFFSET_X] + x + 128;
Logic::_scriptVars[MOUSE_Y] = Logic::_scriptVars[SCROLL_OFFSET_Y] + y + 128 - 40;
//-
int32 touchedId = 0;
uint16 clicked = 0;
if ((y > 40 && _inTopMenu) || !_inTopMenu) {
for (uint16 priority = 0; (priority < 10) && (!touchedId); priority++) {
for (uint16 cnt = 0; (cnt < _numObjs) && (!touchedId); cnt++) {
if ((_objList[cnt].compact->o_priority == priority) &&
(Logic::_scriptVars[MOUSE_X] >= (uint32)_objList[cnt].compact->o_mouse_x1) &&
(Logic::_scriptVars[MOUSE_X] <= (uint32)_objList[cnt].compact->o_mouse_x2) &&
(Logic::_scriptVars[MOUSE_Y] >= (uint32)_objList[cnt].compact->o_mouse_y1) &&
(Logic::_scriptVars[MOUSE_Y] <= (uint32)_objList[cnt].compact->o_mouse_y2)) {
touchedId = _objList[cnt].id;
clicked = cnt;
}
}
}
if (touchedId != (int)Logic::_scriptVars[SPECIAL_ITEM]) { //the mouse collision situation has changed in one way or another
Logic::_scriptVars[SPECIAL_ITEM] = touchedId;
if (_getOff) { // there was something else selected before, run its get-off script
_logic->runMouseScript(nullptr, _getOff);
_getOff = 0;
}
if (touchedId) { // there's something new selected, now.
if (_objList[clicked].compact->o_mouse_on) //run its get on
_logic->runMouseScript(_objList[clicked].compact, _objList[clicked].compact->o_mouse_on);
_getOff = _objList[clicked].compact->o_mouse_off; //setup get-off for later
}
}
} else
Logic::_scriptVars[SPECIAL_ITEM] = 0;
if (_state & MOUSE_DOWN_MASK) {
if (_inTopMenu) {
if (Logic::_scriptVars[SECOND_ITEM]) {
if (Logic::_scriptVars[GEORGE_DOING_REST_ANIM] == 1) {
Logic::_scriptVars[GEORGE_DOING_REST_ANIM] = 0;
} else if (Logic::_scriptVars[GEORGE_WALKING]) {
Logic::_scriptVars[GEORGE_WALKING] = 2;
}
_logic->runMouseScript(nullptr, _menu->_objectDefs[Logic::_scriptVars[SECOND_ITEM]].useScript);
}
if (Logic::_scriptVars[MENU_LOOKING]) {
if (Logic::_scriptVars[GEORGE_DOING_REST_ANIM] == 1) {
Logic::_scriptVars[GEORGE_DOING_REST_ANIM] = 0;
} else if (Logic::_scriptVars[GEORGE_WALKING]) {
Logic::_scriptVars[GEORGE_WALKING] = 2;
}
_logic->cfnPresetScript(nullptr, -1, PLAYER, SCR_menu_look, 0, 0, 0, 0);
}
}
Logic::_scriptVars[MOUSE_BUTTON] = _state & MOUSE_DOWN_MASK;
if (Logic::_scriptVars[SPECIAL_ITEM]) {
Object *compact = _objMan->fetchObject(Logic::_scriptVars[SPECIAL_ITEM]);
_logic->runMouseScript(compact, compact->o_mouse_click);
}
}
_numObjs = 0;
}
uint16 Mouse::testEvent() {
return _state;
}
void Mouse::createPointer(uint32 ptrId, uint32 luggageId) {
if (_currentPtr) {
free(_currentPtr);
_currentPtr = nullptr;
}
if (ptrId) {
MousePtr *lugg = nullptr;
MousePtr *ptr = (MousePtr *)_resMan->openFetchRes(ptrId);
uint16 noFrames = _resMan->getLEUint16(ptr->numFrames);
uint16 ptrSizeX = _resMan->getLEUint16(ptr->sizeX);
uint16 ptrSizeY = _resMan->getLEUint16(ptr->sizeY);
uint16 luggSizeX = 0;
uint16 luggSizeY = 0;
uint16 resSizeX;
uint16 resSizeY;
if (SwordEngine::isPsx()) //PSX pointers are half height
ptrSizeY *= 2;
if (luggageId) {
lugg = (MousePtr *)_resMan->openFetchRes(luggageId);
luggSizeX = _resMan->getLEUint16(lugg->sizeX);
luggSizeY = _resMan->getLEUint16(lugg->sizeY);
if (SwordEngine::isPsx())
luggSizeY *= 2;
resSizeX = MAX(ptrSizeX, (uint16)((ptrSizeX / 2) + luggSizeX));
resSizeY = MAX(ptrSizeY, (uint16)((ptrSizeY / 2) + luggSizeY));
} else {
resSizeX = ptrSizeX;
resSizeY = ptrSizeY;
}
_currentPtr = (MousePtr *)malloc(sizeof(MousePtr) + resSizeX * resSizeY * noFrames);
_currentPtr->hotSpotX = _resMan->getLEUint16(ptr->hotSpotX);
_currentPtr->hotSpotY = _resMan->getLEUint16(ptr->hotSpotY);
_currentPtr->numFrames = noFrames;
_currentPtr->sizeX = resSizeX;
_currentPtr->sizeY = resSizeY;
uint8 *ptrData = (uint8 *)_currentPtr + sizeof(MousePtr);
memset(ptrData, 255, resSizeX * resSizeY * noFrames);
if (luggageId) {
uint8 *dstData = ptrData + resSizeX - luggSizeX;
for (uint32 frameCnt = 0; frameCnt < noFrames; frameCnt++) {
uint8 *luggSrc = (uint8 *)lugg + sizeof(MousePtr);
dstData += (resSizeY - luggSizeY) * resSizeX;
for (uint32 cnty = 0; cnty < (uint32)(SwordEngine::isPsx() ? luggSizeY / 2 : luggSizeY); cnty++) {
for (uint32 cntx = 0; cntx < luggSizeX; cntx++)
if (luggSrc[cntx])
dstData[cntx] = luggSrc[cntx];
if (SwordEngine::isPsx()) {
dstData += resSizeX;
for (uint32 cntx = 0; cntx < luggSizeX; cntx++)
if (luggSrc[cntx])
dstData[cntx] = luggSrc[cntx];
}
dstData += resSizeX;
luggSrc += luggSizeX;
}
}
_resMan->resClose(luggageId);
}
uint8 *dstData = ptrData;
uint8 *srcData = (uint8 *)ptr + sizeof(MousePtr);
for (uint32 frameCnt = 0; frameCnt < noFrames; frameCnt++) {
for (uint32 cnty = 0; cnty < (uint32)(SwordEngine::isPsx() ? ptrSizeY / 2 : ptrSizeY); cnty++) {
for (uint32 cntx = 0; cntx < ptrSizeX; cntx++)
if (srcData[cntx])
dstData[cntx] = srcData[cntx];
if (SwordEngine::isPsx()) {
dstData += resSizeX;
for (uint32 cntx = 0; cntx < ptrSizeX; cntx++)
if (srcData[cntx])
dstData[cntx] = srcData[cntx];
}
srcData += ptrSizeX;
dstData += resSizeX;
}
dstData += (resSizeY - ptrSizeY) * resSizeX;
}
_resMan->resClose(ptrId);
}
}
void Mouse::setPointer(uint32 resId, uint32 rate) {
_currentPtrId = resId;
_frame = 0;
_activeFrame = -1;
createPointer(resId, _currentLuggageId);
if ((resId == 0) || (!(Logic::_scriptVars[MOUSE_STATUS] & 1) && (!_mouseOverride))) {
CursorMan.showMouse(false);
} else {
animate();
CursorMan.showMouse(true);
}
}
void Mouse::setLuggage(uint32 resId, uint32 rate) {
_currentLuggageId = resId;
_frame = 0;
_activeFrame = -1;
createPointer(_currentPtrId, resId);
}
void Mouse::animate() {
if ((Logic::_scriptVars[MOUSE_STATUS] == 1) || (_mouseOverride && _currentPtr)) {
_frame = (_frame + 1) % _currentPtr->numFrames;
if (_activeFrame == _frame)
return;
uint8 *ptrData = (uint8 *)_currentPtr + sizeof(MousePtr);
ptrData += _frame * _currentPtr->sizeX * _currentPtr->sizeY;
_screenAccessMutex->lock();
CursorMan.replaceCursor(ptrData, _currentPtr->sizeX, _currentPtr->sizeY, _currentPtr->hotSpotX, _currentPtr->hotSpotY, 255);
_screenAccessMutex->unlock();
_activeFrame = _frame;
}
}
void Mouse::fnNoHuman() {
if (Logic::_scriptVars[MOUSE_STATUS] & 2) // locked, can't do anything
return;
Logic::_scriptVars[MOUSE_STATUS] = 0; // off & unlocked
setLuggage(0, 0);
setPointer(0, 0);
}
void Mouse::fnAddHuman() {
if (Logic::_scriptVars[MOUSE_STATUS] & 2) // locked, can't do anything
return;
Logic::_scriptVars[MOUSE_STATUS] = 1;
Logic::_scriptVars[SPECIAL_ITEM] = 0;
_getOff = SCR_std_off;
setPointer(MSE_POINTER, 0);
}
void Mouse::fnBlankMouse() {
setPointer(0, 0);
}
void Mouse::fnNormalMouse() {
setPointer(MSE_POINTER, 0);
}
void Mouse::fnLockMouse() {
Logic::_scriptVars[MOUSE_STATUS] |= 2;
}
void Mouse::fnUnlockMouse() {
Logic::_scriptVars[MOUSE_STATUS] &= 1;
}
void Mouse::giveCoords(uint16 *x, uint16 *y) {
*x = _mouse.x;
*y = _mouse.y;
}
} // End of namespace Sword1

113
engines/sword1/mouse.h Normal file
View File

@@ -0,0 +1,113 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef SWORD1_MOUSE_H
#define SWORD1_MOUSE_H
#include "common/mutex.h"
#include "common/scummsys.h"
#include "common/rect.h"
#include "sword1/sworddefs.h"
#include "sword1/object.h"
class OSystem;
namespace Sword1 {
#define MAX_MOUSE 30
#define BS1L_BUTTON_DOWN 2
#define BS1L_BUTTON_UP 4
#define BS1R_BUTTON_DOWN 8
#define BS1R_BUTTON_UP 16
#define BS1_WHEEL_UP 32
#define BS1_WHEEL_DOWN 64
#define MOUSE_BOTH_BUTTONS (BS1L_BUTTON_DOWN | BS1R_BUTTON_DOWN)
#define MOUSE_DOWN_MASK (BS1L_BUTTON_DOWN | BS1R_BUTTON_DOWN)
#define MOUSE_UP_MASK (BS1L_BUTTON_UP | BS1R_BUTTON_UP)
struct MouseObj {
int id;
Object *compact;
};
#include "common/pack-start.h" // START STRUCT PACKING
struct MousePtr {
uint16 numFrames;
uint16 sizeX;
uint16 sizeY;
uint16 hotSpotX;
uint16 hotSpotY;
uint8 dummyData[0x30];
} PACKED_STRUCT;
#include "common/pack-end.h" // END STRUCT PACKING
class Logic;
class Menu;
class ResMan;
class ObjectMan;
class Mouse {
public:
Mouse(OSystem *system, ResMan *pResMan, ObjectMan *pObjMan);
~Mouse();
void initialize();
void addToList(int id, Object *compact);
void useLogicAndMenu(Logic *pLogic, Menu *pMenu);
void useScreenMutex(Common::Mutex *mutex);
void setLuggage(uint32 resID, uint32 rate);
void setPointer(uint32 resID, uint32 rate);
void animate();
void engine(uint16 x, uint16 y, uint16 eventFlags);
uint16 testEvent();
void giveCoords(uint16 *x, uint16 *y);
void fnNoHuman();
void fnAddHuman();
void fnBlankMouse();
void fnNormalMouse();
void fnLockMouse();
void fnUnlockMouse();
void controlPanel(bool on);
private:
void createPointer(uint32 ptrId, uint32 luggageId);
OSystem *_system;
Logic *_logic;
Menu *_menu;
MouseObj _objList[MAX_MOUSE];
ResMan *_resMan;
ObjectMan *_objMan;
Common::Point _mouse;
Common::Mutex *_screenAccessMutex;
uint32 _currentPtrId, _currentLuggageId;
MousePtr *_currentPtr;
int _frame, _activeFrame;
uint16 _numObjs;
uint16 _lastState, _state;
uint32 _getOff;
bool _inTopMenu, _mouseOverride;
};
} // End of namespace Sword1
#endif //BSMOUSE_H

124
engines/sword1/object.h Normal file
View File

@@ -0,0 +1,124 @@
/* 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 SWORD1_OBJECT_H
#define SWORD1_OBJECT_H
#include "common/scummsys.h"
namespace Sword1 {
#define O_TOTAL_EVENTS 5
#define O_WALKANIM_SIZE 600 //max number of nodes in router output
#define O_GRID_SIZE 200
#define EXTRA_GRID_SIZE 20
#include "common/pack-start.h" // START STRUCT PACKING
struct OEventSlot { //receiving event list in the compact -
int32 o_event; //array of these with O_TOTAL_EVENTS elements
int32 o_event_script;
} PACKED_STRUCT; // size = 2*int32 = 8 bytes
#define TOTAL_script_levels 5
struct ScriptTree { //this is a logic tree, used by OBJECTs
int32 o_script_level; //logic level
int32 o_script_id[TOTAL_script_levels]; //script id's (are unique to each level)
int32 o_script_pc[TOTAL_script_levels]; //pc of script for each (if script_manager)
} PACKED_STRUCT; // size = 11*int32 = 44 bytes
struct TalkOffset {
int32 x;
int32 y;
} PACKED_STRUCT; // size = 2*int32 = 8 bytes
struct WalkData {
int32 frame;
int32 x;
int32 y;
int32 step;
int32 dir;
} PACKED_STRUCT; // size = 5*int32 = 20 bytes
struct Object {
int32 o_type; // 0 broad description of type - object, floor, etc.
int32 o_status; // 4 bit flags for logic, graphics, mouse, etc.
int32 o_logic; // 8 logic type
int32 o_place; // 12 where is the mega character
int32 o_down_flag; // 16 pass back down with this - with C possibly both are unnecessary?
int32 o_target; // 20 target object for the GTM *these are linked to script
int32 o_screen; // 24 physical screen/section
int32 o_frame; // 28 frame number &
int32 o_resource; // 32 id of spr file it comes from
int32 o_sync; // 36 receive sync here
int32 o_pause; // 40 logic_engine() pauses these cycles
int32 o_xcoord; // 44
int32 o_ycoord; // 48
int32 o_mouse_x1; // 52 top-left of mouse area is (x1,y1)
int32 o_mouse_y1; // 56
int32 o_mouse_x2; // 60 bottom-right of area is (x2,y2) (these coords are inclusive)
int32 o_mouse_y2; // 64
int32 o_priority; // 68
int32 o_mouse_on; // 72
int32 o_mouse_off; // 76
int32 o_mouse_click; // 80
int32 o_interact; // 84
int32 o_get_to_script; // 88
int32 o_scale_a; // 92 used by floors
int32 o_scale_b; // 96
int32 o_anim_x; // 100
int32 o_anim_y; // 104
ScriptTree o_tree; // 108 size = 44 bytes
ScriptTree o_bookmark; // 152 size = 44 bytes
int32 o_dir; // 196
int32 o_speech_pen; // 200
int32 o_speech_width; // 204
int32 o_speech_time; // 208
int32 o_text_id; // 212 working back from o_ins1
int32 o_tag; // 216
int32 o_anim_pc; // 220 position within an animation structure
int32 o_anim_resource; // 224 cdt or anim table
int32 o_walk_pc; // 228
TalkOffset talk_table[6]; // 232 size = 6*8 bytes = 48
OEventSlot o_event_list[O_TOTAL_EVENTS]; // 280 size = 5*8 bytes = 40
int32 o_ins1; // 320
int32 o_ins2; // 324
int32 o_ins3; // 328
int32 o_mega_resource; // 332
int32 o_walk_resource; // 336
WalkData o_route[O_WALKANIM_SIZE]; // 340 size = 600*20 bytes = 12000
// mega size = 12340 bytes (+ 8 byte offset table + 20 byte header = 12368)
} PACKED_STRUCT;
#include "common/pack-end.h" // END STRUCT PACKING
} // End of namespace Sword1
#endif //BSOBJECT_H

View File

@@ -0,0 +1,468 @@
/* 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/textconsole.h"
#include "common/util.h"
#include "sword1/objectman.h"
#include "sword1/sworddefs.h"
#include "sword1/swordres.h"
#include "sword1/sword1.h"
namespace Sword1 {
ObjectMan::ObjectMan(ResMan *pResourceMan) {
_resMan = pResourceMan;
}
void ObjectMan::initialize() {
uint16 cnt;
for (cnt = 0; cnt < TOTAL_SECTIONS; cnt++)
_liveList[cnt] = 0; // we don't need to close the files here. When this routine is
// called, the memory was flushed() anyways, so these resources
// already *are* closed.
_liveList[128] = _liveList[129] = _liveList[130] = _liveList[131] = _liveList[133] =
_liveList[134] = _liveList[145] = _liveList[146] = _liveList[TEXT_sect] = 1;
for (cnt = 0; cnt < TOTAL_SECTIONS; cnt++) {
if (_liveList[cnt])
_cptData[cnt] = (uint8 *)_resMan->cptResOpen(_objectList[cnt]) + sizeof(Header);
else
_cptData[cnt] = nullptr;
}
}
ObjectMan::~ObjectMan() {
for (uint16 cnt = 0; cnt < TOTAL_SECTIONS; cnt++)
if (_liveList[cnt])
_resMan->resClose(_objectList[cnt]);
}
bool ObjectMan::sectionAlive(uint16 section) {
return (_liveList[section] > 0);
}
void ObjectMan::megaEntering(uint16 section) {
_liveList[section]++;
if (_liveList[section] == 1)
_cptData[section] = ((uint8 *)_resMan->cptResOpen(_objectList[section])) + sizeof(Header);
}
void ObjectMan::megaLeaving(uint16 section, int id) {
if (_liveList[section] == 0)
error("mega %d is leaving empty section %d", id, section);
_liveList[section]--;
if ((_liveList[section] == 0) && (id != PLAYER)) {
_resMan->resClose(_objectList[section]);
_cptData[section] = nullptr;
}
/* if the player is leaving the section then we have to close the resources after
mainloop ends, because the screen will still need the resources*/
}
uint8 ObjectMan::fnCheckForTextLine(uint32 textId) {
uint8 retVal = 0;
if (!_textList[textId / ITM_PER_SEC][0])
return 0; // section does not exist
uint8 lang = SwordEngine::_systemVars.language;
uint32 *textData = (uint32 *)((uint8 *)_resMan->openFetchRes(_textList[textId / ITM_PER_SEC][lang]) + sizeof(Header));
if ((textId & ITM_ID) < _resMan->readUint32(textData)) {
textData++;
if (textData[textId & ITM_ID])
retVal = 1;
}
_resMan->resClose(_textList[textId / ITM_PER_SEC][lang]);
return retVal;
}
char *ObjectMan::lockText(uint32 textId) {
uint8 lang = SwordEngine::_systemVars.language;
char *text = lockText(textId, lang);
if (text == 0) {
if (lang != BS1_ENGLISH) {
text = lockText(textId, BS1_ENGLISH);
if (text != 0)
warning("Missing translation for textId %u (\"%s\")", textId, text);
unlockText(textId, BS1_ENGLISH);
}
return _missingSubTitleStr;
}
return text;
}
char *ObjectMan::lockText(uint32 textId, uint8 lang) {
char *addr = (char *)_resMan->openFetchRes(_textList[textId / ITM_PER_SEC][lang]);
if (addr == nullptr)
return nullptr;
addr += sizeof(Header);
if ((textId & ITM_ID) >= _resMan->readUint32(addr)) {
// Workaround for missing sentences in some languages in the demo.
switch(textId) {
case 8455194:
return const_cast<char *>(_translationId8455194[lang]);
case 8455195:
return const_cast<char *>(_translationId8455195[lang]);
case 8455196:
return const_cast<char *>(_translationId8455196[lang]);
case 8455197:
return const_cast<char *>(_translationId8455197[lang]);
case 8455198:
return const_cast<char *>(_translationId8455198[lang]);
case 8455199:
return const_cast<char *>(_translationId8455199[lang]);
case 8455200:
return const_cast<char *>(_translationId8455200[lang]);
case 8455201:
return const_cast<char *>(_translationId8455201[lang]);
case 8455202:
return const_cast<char *>(_translationId8455202[lang]);
case 8455203:
return const_cast<char *>(_translationId8455203[lang]);
case 8455204:
return const_cast<char *>(_translationId8455204[lang]);
case 8455205:
return const_cast<char *>(_translationId8455205[lang]);
case 6488080:
return const_cast<char *>(_translationId6488080[lang]);
case 6488081:
return const_cast<char *>(_translationId6488081[lang]);
case 6488082:
return const_cast<char *>(_translationId6488082[lang]);
case 6488083:
return const_cast<char *>(_translationId6488083[lang]);
default:
break;
}
warning("ObjectMan::lockText(%d): only %d texts in file", textId & ITM_ID, _resMan->readUint32(addr));
return nullptr;
}
uint32 offset = _resMan->readUint32(addr + ((textId & ITM_ID) + 1) * 4);
if (offset == 0) {
switch(textId) {
// Workaround bug for missing sentence in some languages in Syria (see bug #3753).
// We use the hardcoded text in this case.
case 2950145:
return const_cast<char *>(_translationId2950145[lang]);
// Workaround for some strings in spanish demo
case 6488080:
return const_cast<char *>(_translationId6488080[lang]);
case 6488081:
return const_cast<char *>(_translationId6488081[lang]);
case 6488082:
return const_cast<char *>(_translationId6488082[lang]);
case 6488083:
return const_cast<char *>(_translationId6488083[lang]);
}
warning("ObjectMan::lockText(%d): text number has no text lines", textId);
return nullptr;
}
return addr + offset;
}
void ObjectMan::unlockText(uint32 textId) {
unlockText(textId, SwordEngine::_systemVars.language);
}
void ObjectMan::unlockText(uint32 textId, uint8 lang) {
_resMan->resClose(_textList[textId / ITM_PER_SEC][lang]);
}
uint32 ObjectMan::lastTextNumber(int section) {
uint8 *data = (uint8 *)_resMan->openFetchRes(_textList[section][SwordEngine::_systemVars.language]) + sizeof(Header);
uint32 result = _resMan->readUint32(data) - 1;
_resMan->resClose(_textList[section][SwordEngine::_systemVars.language]);
return result;
}
Object *ObjectMan::fetchObject(uint32 id) {
uint8 *addr = _cptData[id / ITM_PER_SEC];
if (!addr)
addr = _cptData[id / ITM_PER_SEC] = ((uint8 *)_resMan->cptResOpen(_objectList[id / ITM_PER_SEC])) + sizeof(Header);
id &= ITM_ID;
// DON'T do endian conversion here. it's already done.
return (Object *)(addr + * (uint32 *)(addr + (id + 1) * 4));
}
uint32 ObjectMan::fetchNoObjects(int section) {
if (_cptData[section] == nullptr)
error("fetchNoObjects: section %d is not open", section);
return *(uint32 *)_cptData[section];
}
void ObjectMan::closeSection(uint32 screen) {
if (_liveList[screen] == 0) // close the section that PLAYER has just left, if it's empty now
_resMan->resClose(_objectList[screen]);
}
void ObjectMan::loadLiveList(uint16 *src) {
for (uint16 cnt = 0; cnt < TOTAL_SECTIONS; cnt++) {
if (_liveList[cnt]) {
_resMan->resClose(_objectList[cnt]);
_cptData[cnt] = nullptr;
}
_liveList[cnt] = src[cnt];
if (_liveList[cnt])
_cptData[cnt] = ((uint8 *)_resMan->cptResOpen(_objectList[cnt])) + sizeof(Header);
}
}
void ObjectMan::mainLoopPatch() {
// This patch is available in every executable after the
// original UK one. Its purpose is to turn off scripts which
// were causing issues by continuing running past their scope.
// The patch, as descripted within the original source
// code, checks if the game is past the Syria section,
// and if so it checks if the Market Stall section is still
// alive, and if so it closes both the section (45) and
// the Mega object for Duane (134), effectively terminating
// their scripts.
if (_liveList[45] > 0) {
_liveList[45] = 0; // Turn off the Syria Market Stall
_resMan->resClose(_objectList[45]);
if (_liveList[134] > 0) {
_liveList[134] = 0; // Turn off Duane
_resMan->resClose(_objectList[134]);
}
}
}
void ObjectMan::saveLiveList(uint16 *dest) {
memcpy(dest, _liveList, TOTAL_SECTIONS * sizeof(uint16));
}
// String displayed when a subtitle sentence is missing in the cluster file.
// It happens with at least one sentence in Syria in some languages (see bug
// #3753).
// Note: an empty string or a null pointer causes a crash.
char ObjectMan::_missingSubTitleStr[] = " ";
// Missing translation for textId 2950145 (see bug #3753).
// Currently text is missing for Portuguese languages. (It's possible that it
// is not needed. The English version of the game does not include Portuguese
// so I cannot check.)
const char *const ObjectMan::_translationId2950145[7] = {
"Oh?", // English (not needed)
"Quoi?", // French
"Oh?", // German
"Eh?", // Italian
"\277Eh?", // Spanish
"Ano?", // Czech
nullptr // Portuguese
};
// The translations for the next texts are missing in the demo but are present
// in the full game. The translations were therefore extracted from the full game.
// Missing translation for textId 8455194 (in the demo).
const char *const ObjectMan::_translationId8455194[7] = {
nullptr, // "Who was the guy you were supposed to meet?", // English (not needed)
"Qui \351tait l'homme que vous deviez rencontrer?", // French
"Wer war der Typ, den Du treffen wolltest?", // German
"Chi dovevi incontrare?", // Italian
"\277Qui\351n era el hombre con el que ten\355as que encontrarte?", // Spanish
nullptr, // Czech
nullptr // Portuguese
};
// Missing translation for textId 8455195 (in the demo).
const char *const ObjectMan::_translationId8455195[7] = {
nullptr, // "His name was Plantard. I didn't know him, but he called me last night.", // English (not needed)
"Son nom \351tait Plantard. Je ne le connaissais pas, mais il m'avait t\351l\351phon\351 la veille.", // French
"Sein Name war Plantard. Ich kannte ihn nicht, aber er hat mich letzte Nacht angerufen.", // German
"Si chiamava Plantard. Mi ha chiamato ieri sera, ma non lo conoscevo.", // Italian
"Su nombre era Plantard. Yo no lo conoc\355a pero \351l me llam\363 ayer por la noche.", // Spanish
nullptr, // Czech
nullptr // Portuguese
};
// Missing translation for textId 8455196 (in the demo).
const char *const ObjectMan::_translationId8455196[7] = {
nullptr, // "He said he had a story which would interest me.", // English (not needed)
"Il a dit qu'il avait une histoire qui devrait m'int\351resser.", // French
"Er sagte, er h\344tte eine Story, die mich interessieren w\374rde.", // German
"Mi disse che aveva una storia che mi poteva interessare.", // Italian
"Dijo que ten\355a una historia que me interesar\355a.", // Spanish
nullptr, // Czech
nullptr // Portuguese
};
// Missing translation for textId 8455197 (in the demo).
const char *const ObjectMan::_translationId8455197[7] = {
nullptr, // "He asked me to meet him at the caf\351.", // English (not needed)
"Il m'a demand\351 de le rencontrer au caf\351.", // French
"Er fragte mich, ob wir uns im Caf\351 treffen k\366nnten.", // German
"Mi chiese di incontrarci al bar.", // Italian
"Me pidi\363 que nos encontr\341ramos en el caf\351.", // Spanish
nullptr, // Czech
nullptr // Portuguese
};
// Missing translation for textId 8455198 (in the demo).
const char *const ObjectMan::_translationId8455198[7] = {
nullptr, // "I guess I'll never know what he wanted to tell me...", // English (not needed)
"Je suppose que je ne saurai jamais ce qu'il voulait me dire...", // French
"Ich werde wohl nie erfahren, was er mir sagen wollte...", // German
"Penso che non sapr\362 mai che cosa voleva dirmi...", // Italian
"Supongo que nunca sabr\351 qu\351 me quer\355a contar...", // Spanish
nullptr, // Czech
nullptr // Portuguese
};
// Missing translation for textId 8455199 (in the demo).
const char *const ObjectMan::_translationId8455199[7] = {
nullptr, // "Not unless you have Rosso's gift for psychic interrogation.", // English (not needed)
"Non, \340 moins d'avoir les dons de Rosso pour les interrogatoires psychiques.", // French
"Es sei denn, Du h\344ttest Rosso's Gabe der parapsychologischen Befragung.", // German
"A meno che tu non riesca a fare interrogatori telepatici come Rosso.", // Italian
"A no ser que tengas el don de Rosso para la interrogaci\363n ps\355quica.", // Spanish
nullptr, // Czech
nullptr // Portuguese
};
// Missing translation for textId 8455200 (in the demo).
const char *const ObjectMan::_translationId8455200[7] = {
nullptr, // "How did Plantard get your name?", // English (not needed)
"Comment Plantard a-t-il obtenu votre nom?", // French
"Woher hat Plantard Deinen Namen?", // German
"Come ha fatto Plantard a sapere il tuo nome?", // Italian
"\277C\363mo consigui\363 Plantard tu nombre?", // Spanish
nullptr, // Czech
nullptr // Portuguese
};
// Missing translation for textId 8455201 (in the demo).
const char *const ObjectMan::_translationId8455201[7] = {
nullptr, // "Through the newspaper - La Libert\351.", // English (not needed)
"Par mon journal... La Libert\351.", // French
"\334ber die Zeitung - La Libert\351.", // German
"Tramite il giornale La Libert\351.", // Italian
"Por el peri\363dico - La Libert\351.", // Spanish
nullptr, // Czech
nullptr // Portuguese
};
// Missing translation for textId 8455202 (in the demo).
const char *const ObjectMan::_translationId8455202[7] = {
nullptr, // "I'd written an article linking two unsolved murders, one in Italy, the other in Japan.", // English (not needed)
"J'ai \351crit un article o\371 je faisais le lien entre deux meurtres inexpliqu\351s, en Italie et au japon.", // French
"Ich habe einen Artikel geschrieben, in dem ich zwei ungel\366ste Morde miteinander in Verbindung bringe, einen in Italien, einen anderen in Japan.", // German
"Ho scritto un articolo che metteva in collegamento due omicidi insoluti in Italia e Giappone.", // Italian
"Yo hab\355a escrito un art\355culo conectando dos asesinatos sin resolver, uno en Italia, el otro en Jap\363n.", // Spanish
nullptr, // Czech
nullptr // Portuguese
};
// Missing translation for textId 8455203 (in the demo).
const char *const ObjectMan::_translationId8455203[7] = {
nullptr, // "The cases were remarkably similar...", // English (not needed)
"Les affaires \351taient quasiment identiques...", // French
"Die F\344lle sind sich bemerkenswert \344hnlich...", // German
"I casi erano sorprendentemente uguali...", // Italian
"Los casos eran incre\355blemente parecidos...", // Spanish
nullptr, // Czech
nullptr // Portuguese
};
// Missing translation for textId 8455204 (in the demo).
const char *const ObjectMan::_translationId8455204[7] = {
nullptr, // "...a wealthy victim, no apparent motive, and a costumed killer.", // English (not needed)
"...une victime riche, pas de motif apparent, et un tueur d\351guis\351.", // French
"...ein wohlhabendes Opfer, kein offensichtliches Motiv, und ein verkleideter Killer.", // German
"...una vittima ricca, nessun motivo apparente e un assassino in costume.", // Italian
"...una v\355ctima rica, sin motivo aparente, y un asesino disfrazado.", // Spanish
nullptr, // Czech
nullptr // Portuguese
};
// Missing translation for textId 8455205 (in the demo).
const char *const ObjectMan::_translationId8455205[7] = {
nullptr, // "Plantard said he could supply me with more information.", // English (not needed)
"Plantard m'a dit qu'il pourrait me fournir des renseignements.", // French
"Plantard sagte, er k\366nne mir weitere Informationen beschaffen.", // German
"Plantard mi disse che mi avrebbe fornito ulteriori informazioni.", // Italian
"Plantard dijo que \351l me pod\355a proporcionar m\341s informaci\363n.", // Spanish
nullptr, // Czech
nullptr // Portuguese
};
// Missing translation for textId 6488080 (in the demo).
const char *const ObjectMan::_translationId6488080[7] = {
nullptr, // "I wasn't going to head off all over Paris until I'd investigated some more.", // English (not needed)
"Je ferais mieux d'enqu\351ter un peu par ici avant d'aller me promener ailleurs.", // French
"Ich durchquere nicht ganz Paris, bevor ich etwas mehr herausgefunden habe.", // German
"Non mi sarei incamminato per tutta Parigi prima di ulteriori indagini.", // Italian
"No iba a empezar a recorrerme todo Par\355s hasta haber investigado algo m\341s.", // Spanish
nullptr, // Czech
nullptr // Portuguese
};
// The next three sentences are specific to the demo and only the english text is present.
// The translations were provided by:
// French: Thierry Crozat
// German: Simon Sawatzki
// Italian: Matteo Angelino
// Spanish: Tomás Maidagan
// Missing translation for textId 6488081 (in the demo).
const char *const ObjectMan::_translationId6488081[7] = {
nullptr, // "I wasn't sure what I was going to do when I caught up with that clown...", // English (not needed)
"Je ne savais pas ce que je ferais quand je rattraperais le clown...", // French
"Ich wu\337te nicht, worauf ich mich einlie\337, als ich dem Clown nachjagte...", // German
"Non sapevo cosa avrei fatto una volta raggiunto quel clown...", // Italian
"No estaba seguro de qu\351 iba a hacer cuando alcanzara a ese payaso...", // Spanish
nullptr, // Czech
nullptr // Portuguese
};
// Missing translation for textId 6488082 (in the demo).
const char *const ObjectMan::_translationId6488082[7] = {
nullptr, // "...but before I knew it, I was drawn into a desperate race between two ruthless enemies.", // English (not needed)
"...mais avant de m'en rendre compte je me retrouvais happ\351 dans une course effr\351n\351e entre deux ennemis impitoyables.", // French
"... doch bevor ich mich versah, war ich inmitten eines Wettlaufs von zwei r\374cksichtslosen Feinden.", // German
"... ma prima che me ne rendessi conto, fui trascinato in una corsa disperata con due spietati nemici.", // Italian
"... pero antes de que me diera tiempo a pensarlo, me encontr\351 metido en una carrera desesperada entre dos enemigos sin piedad.", // Spanish
nullptr, // Czech
nullptr // Portuguese
};
// Missing translation for textId 6488083 (in the demo).
const char *const ObjectMan::_translationId6488083[7] = {
nullptr, // "The goal: the mysterious power of the Broken Sword.", // English (not needed)
"Le but: les pouvoirs myst\351rieux de l'\351p\351e bris\351e.", // French
"Das Ziel: die geheimnisvolle Macht des zerbrochenen Schwertes.", // German
"Obiettivo: il misterioso potere della Spada spezzata.", // Italian
"El objetivo: el misterioso poder de la Espada Rota.", // Spanish
nullptr, // Czech
nullptr // Portuguese
};
} // End of namespace Sword1

View File

@@ -0,0 +1,88 @@
/* 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/>.
*
*/
// this is the object manager. our equivalent to protocol.c and coredata.c
#ifndef SWORD1_OBJECTMAN_H
#define SWORD1_OBJECTMAN_H
#include "sword1/resman.h"
#include "sword1/sworddefs.h"
#include "sword1/object.h"
namespace Sword1 {
class ObjectMan {
public:
ObjectMan(ResMan *pResourceMan);
~ObjectMan();
void initialize();
Object *fetchObject(uint32 id);
uint32 fetchNoObjects(int section);
bool sectionAlive(uint16 section);
void megaEntering(uint16 section);
void megaLeaving(uint16 section, int id);
uint8 fnCheckForTextLine(uint32 textId);
char *lockText(uint32 textId);
void unlockText(uint32 textId);
uint32 lastTextNumber(int section);
void closeSection(uint32 screen);
void saveLiveList(uint16 *dest); // for loading/saving
void loadLiveList(uint16 *src);
void mainLoopPatch();
private:
char *lockText(uint32 textId, uint8 language);
void unlockText(uint32 textId, uint8 language);
ResMan *_resMan;
static const uint32 _objectList[TOTAL_SECTIONS]; //a table of pointers to object files
static const uint32 _textList[TOTAL_SECTIONS][7]; //a table of pointers to text files
uint16 _liveList[TOTAL_SECTIONS]; //which sections are active
uint8 *_cptData[TOTAL_SECTIONS];
static char _missingSubTitleStr[];
static const char *const _translationId2950145[7]; //translation for textId 2950145 (missing from cluster file for some languages)
static const char *const _translationId8455194[7]; //translation for textId 8455194 (missing in the demo)
static const char *const _translationId8455195[7]; //translation for textId 8455195 (missing in the demo)
static const char *const _translationId8455196[7]; //translation for textId 8455196 (missing in the demo)
static const char *const _translationId8455197[7]; //translation for textId 8455197 (missing in the demo)
static const char *const _translationId8455198[7]; //translation for textId 8455198 (missing in the demo)
static const char *const _translationId8455199[7]; //translation for textId 8455199 (missing in the demo)
static const char *const _translationId8455200[7]; //translation for textId 8455200 (missing in the demo)
static const char *const _translationId8455201[7]; //translation for textId 8455201 (missing in the demo)
static const char *const _translationId8455202[7]; //translation for textId 8455202 (missing in the demo)
static const char *const _translationId8455203[7]; //translation for textId 8455203 (missing in the demo)
static const char *const _translationId8455204[7]; //translation for textId 8455204 (missing in the demo)
static const char *const _translationId8455205[7]; //translation for textId 8455205 (missing in the demo)
static const char *const _translationId6488080[7]; //translation for textId 6488081 (missing in the demo)
static const char *const _translationId6488081[7]; //translation for textId 6488081 (missing in the demo)
static const char *const _translationId6488082[7]; //translation for textId 6488082 (missing in the demo)
static const char *const _translationId6488083[7]; //translation for textId 6488083 (missing in the demo)
};
} // End of namespace Sword1
#endif //OBJECTMAN_H

34
engines/sword1/obsolete.h Normal file
View File

@@ -0,0 +1,34 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef SWORD1_OBSOLETE_H
#define SWORD1_OBSOLETE_H
static const Engines::ObsoleteGameID obsoleteGameIDsTable[] = {
{"sword1demo", "sword1", Common::kPlatformWindows},
{"sword1mac", "sword1", Common::kPlatformMacintosh},
{"sword1macdemo", "sword1", Common::kPlatformMacintosh},
{"sword1psx", "sword1", Common::kPlatformPSX},
{"sword1psxdemo", "sword1", Common::kPlatformPSX},
{0, 0, Common::kPlatformUnknown}
};
#endif // SWORD1_OBSOLETE_H

587
engines/sword1/resman.cpp Normal file
View File

@@ -0,0 +1,587 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "common/debug.h"
#include "common/textconsole.h"
#include "sword1/memman.h"
#include "sword1/resman.h"
#include "sword1/sword1.h"
#include "sword1/swordres.h"
#include "gui/message.h"
namespace Sword1 {
void guiFatalError(char *msg) {
// Displays a dialog on-screen before terminating the engine.
// TODO: We really need to setup a special palette for cases when
// the engine is erroring before setting one... otherwise invisible cursor :)
GUI::MessageDialog dialog(msg);
dialog.runModal();
error("%s", msg);
}
#define MAX_PATH_LEN 260
ResMan::ResMan(const char *fileName, bool isMacFile, bool isKorean) {
_openCluStart = _openCluEnd = nullptr;
_openClus = 0;
_isBigEndian = isMacFile;
_isKorTrs = isKorean;
_memMan = new MemMan();
loadCluDescript(fileName);
}
ResMan::~ResMan() {
#if 0
for (uint32 clusCnt = 0; clusCnt < _prj.noClu; clusCnt++) {
Clu *cluster = _prj.clu[clusCnt];
if (cluster) {
for (uint32 grpCnt = 0; grpCnt < cluster->noGrp; grpCnt++) {
Grp *group = cluster->grp[grpCnt];
if (group) {
for (uint32 resCnt = 0; resCnt < group->noRes; resCnt++) {
if (group->resHandle[resCnt].cond == MEM_DONT_FREE) {
warning("ResMan::~ResMan: Resource %02X.%04X.%02X is still open",
clusCnt + 1, grpCnt, resCnt);
}
}
}
}
}
}
debug(0, "ResMan closed\n");
#endif
flush();
freeCluDescript();
delete _memMan;
}
void ResMan::loadCluDescript(const char *fileName) {
// The cluster description file is always little endian (even on the mac version, whose cluster files are big endian)
Common::File file;
file.open(fileName);
if (!file.isOpen()) {
char msg[512];
Common::sprintf_s(msg, "Couldn't open CLU description '%s'\n\nIf you are running from CD, please ensure you have read the ScummVM documentation regarding multi-cd games.", fileName);
guiFatalError(msg);
}
_prj.noClu = file.readUint32LE();
_prj.clu = new Clu[_prj.noClu]();
uint32 *cluIndex = (uint32 *)malloc(_prj.noClu * 4);
file.read(cluIndex, _prj.noClu * 4);
for (uint32 clusCnt = 0; clusCnt < _prj.noClu; clusCnt++)
if (cluIndex[clusCnt]) {
Clu *cluster = _prj.clu + clusCnt;
file.read(cluster->label, MAX_LABEL_SIZE);
cluster->file = nullptr;
cluster->noGrp = file.readUint32LE();
cluster->grp = new Grp[cluster->noGrp];
cluster->nextOpen = nullptr;
memset(cluster->grp, 0, cluster->noGrp * sizeof(Grp));
cluster->refCount = 0;
uint32 *grpIndex = (uint32 *)malloc(cluster->noGrp * 4);
file.read(grpIndex, cluster->noGrp * 4);
for (uint32 grpCnt = 0; grpCnt < cluster->noGrp; grpCnt++)
if (grpIndex[grpCnt]) {
Grp *group = cluster->grp + grpCnt;
group->noRes = file.readUint32LE();
group->resHandle = new MemHandle[group->noRes];
group->offset = new uint32[group->noRes];
group->length = new uint32[group->noRes];
uint32 *resIdIdx = (uint32 *)malloc(group->noRes * 4);
file.read(resIdIdx, group->noRes * 4);
for (uint32 resCnt = 0; resCnt < group->noRes; resCnt++) {
if (resIdIdx[resCnt]) {
group->offset[resCnt] = file.readUint32LE();
group->length[resCnt] = file.readUint32LE();
_memMan->initHandle(group->resHandle + resCnt);
} else {
group->offset[resCnt] = 0xFFFFFFFF;
group->length[resCnt] = 0;
_memMan->initHandle(group->resHandle + resCnt);
}
}
free(resIdIdx);
}
free(grpIndex);
}
free(cluIndex);
if (_prj.clu[3].grp[5].noRes == 29)
for (uint8 cnt = 0; cnt < 29; cnt++)
_srIdList[cnt] = 0x04050000 | cnt;
if (_isKorTrs) {
Common::File cluFile;
cluFile.open("korean.clu");
if (cluFile.isOpen()) {
for (uint32 resCnt = 0, resOffset = 0; resCnt < _prj.clu[2].grp[0].noRes; resCnt++) {
Header header;
cluFile.read(&header, sizeof(Header));
_prj.clu[2].grp[0].offset[resCnt] = resOffset;
_prj.clu[2].grp[0].length[resCnt] = header.comp_length;
resOffset += header.comp_length;
cluFile.seek(header.decomp_length, SEEK_CUR);
}
Common::strcpy_s(_prj.clu[2].label, "korean");
} else {
_isKorTrs = false;
}
}
}
void ResMan::freeCluDescript() {
for (uint32 clusCnt = 0; clusCnt < _prj.noClu; clusCnt++) {
Clu *cluster = _prj.clu + clusCnt;
for (uint32 grpCnt = 0; grpCnt < cluster->noGrp; grpCnt++) {
Grp *group = cluster->grp + grpCnt;
if (group->resHandle != nullptr) {
for (uint32 resCnt = 0; resCnt < group->noRes; resCnt++)
_memMan->freeNow(group->resHandle + resCnt);
delete[] group->resHandle;
delete[] group->offset;
delete[] group->length;
}
}
delete[] cluster->grp;
delete cluster->file;
}
delete[] _prj.clu;
}
void ResMan::flush() {
Common::StackLock lock(_resourceAccessMutex);
for (uint32 clusCnt = 0; clusCnt < _prj.noClu; clusCnt++) {
Clu *cluster = _prj.clu + clusCnt;
for (uint32 grpCnt = 0; grpCnt < cluster->noGrp; grpCnt++) {
Grp *group = cluster->grp + grpCnt;
for (uint32 resCnt = 0; resCnt < group->noRes; resCnt++)
if (group->resHandle[resCnt].cond != MEM_FREED) {
_memMan->setCondition(group->resHandle + resCnt, MEM_CAN_FREE);
group->resHandle[resCnt].refCount = 0;
}
}
if (cluster->file) {
cluster->file->close();
delete cluster->file;
cluster->file = nullptr;
cluster->refCount = 0;
}
}
_openClus = 0;
_openCluStart = _openCluEnd = nullptr;
// the memory manager cached the blocks we asked it to free, so explicitly make it free them
_memMan->flush();
}
void *ResMan::fetchRes(uint32 id) {
MemHandle *memHandle = resHandle(id);
if (!memHandle) {
warning("fetchRes:: resource %d out of bounds", id);
return nullptr;
}
if (!memHandle->data)
error("fetchRes:: resource %d is not open", id);
return memHandle->data;
}
void *ResMan::openFetchRes(uint32 id) {
resOpen(id);
return fetchRes(id);
}
void ResMan::dumpRes(uint32 id) {
char outn[30];
Common::sprintf_s(outn, "DUMP%08X.BIN", id);
Common::DumpFile outf;
if (outf.open(outn)) {
resOpen(id);
MemHandle *memHandle = resHandle(id);
if (memHandle) {
outf.write(memHandle->data, memHandle->size);
outf.close();
}
resClose(id);
}
}
Header *ResMan::lockScript(uint32 scrID) {
if (!_scriptList[scrID / ITM_PER_SEC])
error("Script id %d not found", scrID);
scrID = _scriptList[scrID / ITM_PER_SEC];
#ifdef SCUMM_BIG_ENDIAN
openScriptResourceBigEndian(scrID);
#else
openScriptResourceLittleEndian(scrID);
#endif
MemHandle *handle = resHandle(scrID);
if (!handle)
error("Script resource handle %d not found", scrID);
return (Header *)handle->data;
}
void ResMan::unlockScript(uint32 scrID) {
resClose(_scriptList[scrID / ITM_PER_SEC]);
}
void *ResMan::cptResOpen(uint32 id) {
#ifdef SCUMM_BIG_ENDIAN
openCptResourceBigEndian(id);
#else
openCptResourceLittleEndian(id);
#endif
MemHandle *handle = resHandle(id);
return handle != nullptr ? handle->data : nullptr;
}
void ResMan::resOpen(uint32 id) { // load resource ID into memory
Common::StackLock lock(_resourceAccessMutex);
MemHandle *memHandle = resHandle(id);
if (!memHandle)
return;
if (memHandle->cond == MEM_FREED) { // memory has been freed
if (id == GAME_FONT && _isKorTrs) {
// Load Korean Font
uint32 size = resLength(id);
uint32 korFontSize = 0;
Common::File korFontFile;
korFontFile.open("bs1k.fnt");
if (korFontFile.isOpen()) {
korFontSize = korFontFile.size();
}
_memMan->alloc(memHandle, size + korFontSize);
Common::File *clusFile = resFile(id);
assert(clusFile);
clusFile->seek(resOffset(id));
clusFile->read(memHandle->data, size);
if (clusFile->err() || clusFile->eos()) {
error("Can't read %d bytes from offset %d from cluster file %s\nResource ID: %d (%08X)", size, resOffset(id), _prj.clu[(id >> 24) - 1].label, id, id);
}
if (korFontSize > 0) {
korFontFile.read((uint8 *)memHandle->data + size, korFontSize);
}
} else {
uint32 size = resLength(id);
_memMan->alloc(memHandle, size);
Common::File *clusFile = resFile(id);
assert(clusFile);
clusFile->seek(resOffset(id));
clusFile->read(memHandle->data, size);
if (clusFile->err() || clusFile->eos()) {
error("Can't read %d bytes from offset %d from cluster file %s\nResource ID: %d (%08X)", size, resOffset(id), _prj.clu[(id >> 24) - 1].label, id, id);
}
}
} else
_memMan->setCondition(memHandle, MEM_DONT_FREE);
memHandle->refCount++;
if (memHandle->refCount > 20) {
debug(1, "%d references to id %d. Guess there's something wrong.", memHandle->refCount, id);
}
}
void ResMan::resClose(uint32 id) {
Common::StackLock lock(_resourceAccessMutex);
MemHandle *handle = resHandle(id);
if (!handle)
return;
if (!handle->refCount) {
warning("Resource Manager fail: unlocking object with refCount 0. Id: %d", id);
} else {
handle->refCount--;
if (!handle->refCount)
_memMan->setCondition(handle, MEM_CAN_FREE);
}
}
FrameHeader *ResMan::fetchFrame(void *resourceData, uint32 frameNo) {
uint8 *frameFile = (uint8 *)resourceData;
uint8 *idxData = frameFile + sizeof(Header);
if (_isBigEndian) {
if (frameNo >= READ_BE_UINT32(idxData))
error("fetchFrame:: frame %d doesn't exist in resource.", frameNo);
frameFile += READ_BE_UINT32(idxData + (frameNo + 1) * 4);
} else {
if (frameNo >= READ_LE_UINT32(idxData))
error("fetchFrame:: frame %d doesn't exist in resource.", frameNo);
frameFile += READ_LE_UINT32(idxData + (frameNo + 1) * 4);
}
return (FrameHeader *)frameFile;
}
Common::File *ResMan::resFile(uint32 id) {
Clu *cluster = _prj.clu + ((id >> 24) - 1);
if (cluster->file == nullptr) {
_openClus++;
if (_openCluEnd == nullptr) {
_openCluStart = _openCluEnd = cluster;
} else {
_openCluEnd->nextOpen = cluster;
_openCluEnd = cluster;
}
cluster->file = new Common::File();
char fileName[36];
// Supposes that big endian means mac cluster file and little endian means PC cluster file.
// This works, but we may want to separate the file name from the endianess or try .CLM extension if opening.clu file fail.
if (_isBigEndian)
Common::sprintf_s(fileName, "%s.CLM", _prj.clu[(id >> 24) - 1].label);
else
Common::sprintf_s(fileName, "%s.CLU", _prj.clu[(id >> 24) - 1].label);
cluster->file->open(fileName);
if (!cluster->file->isOpen()) {
char msg[512];
Common::sprintf_s(msg, "Couldn't open game cluster file '%s'\n\nIf you are running from CD, please ensure you have read the ScummVM documentation regarding multi-cd games.", fileName);
guiFatalError(msg);
}
while (_openClus > MAX_OPEN_CLUS) {
assert(_openCluStart);
Clu *closeClu = _openCluStart;
_openCluStart = _openCluStart->nextOpen;
if (closeClu->file)
closeClu->file->close();
delete closeClu->file;
closeClu->file = nullptr;
closeClu->nextOpen = nullptr;
_openClus--;
}
}
return cluster->file;
}
MemHandle *ResMan::resHandle(uint32 id) {
if ((id >> 16) == 0x0405)
id = _srIdList[id & 0xFFFF];
uint8 cluster = (uint8)((id >> 24) - 1);
uint8 group = (uint8)(id >> 16);
// There is a known case of reading beyond array boundaries when trying to use
// portuguese subtitles (cluster file 2, group 6) with a version that does not
// contain subtitles for this languages (i.e. has only 6 languages and not 7).
if (cluster >= _prj.noClu || group >= _prj.clu[cluster].noGrp)
return nullptr;
return &(_prj.clu[cluster].grp[group].resHandle[id & 0xFFFF]);
}
uint32 ResMan::resLength(uint32 id) {
if ((id >> 16) == 0x0405)
id = _srIdList[id & 0xFFFF];
uint8 cluster = (uint8)((id >> 24) - 1);
uint8 group = (uint8)(id >> 16);
if (cluster >= _prj.noClu || group >= _prj.clu[cluster].noGrp)
return 0;
return _prj.clu[cluster].grp[group].length[id & 0xFFFF];
}
uint32 ResMan::resOffset(uint32 id) {
if ((id >> 16) == 0x0405)
id = _srIdList[id & 0xFFFF];
uint8 cluster = (uint8)((id >> 24) - 1);
uint8 group = (uint8)(id >> 16);
if (cluster >= _prj.noClu || group >= _prj.clu[cluster].noGrp)
return 0;
return _prj.clu[cluster].grp[group].offset[id & 0xFFFF];
}
void ResMan::openCptResourceBigEndian(uint32 id) {
bool needByteSwap = false;
if (!_isBigEndian) {
// Cluster files are in little endian fomat.
// If the resource are not in memory anymore, and therefore will be read
// from disk, they will need to be byte swaped.
MemHandle *memHandle = resHandle(id);
if (memHandle)
needByteSwap = (memHandle->cond == MEM_FREED);
}
resOpen(id);
if (needByteSwap) {
MemHandle *handle = resHandle(id);
if (!handle)
return;
uint32 totSize = handle->size;
uint32 *data = (uint32 *)((uint8 *)handle->data + sizeof(Header));
totSize -= sizeof(Header);
if (totSize & 3)
error("Illegal compact size for id %d: %d", id, totSize);
totSize /= 4;
for (uint32 cnt = 0; cnt < totSize; cnt++) {
*data = READ_LE_UINT32(data);
data++;
}
}
}
void ResMan::openCptResourceLittleEndian(uint32 id) {
bool needByteSwap = false;
if (_isBigEndian) {
// Cluster files are in big endian fomat.
// If the resource are not in memory anymore, and therefore will be read
// from disk, they will need to be byte swaped.
MemHandle *memHandle = resHandle(id);
if (memHandle)
needByteSwap = (memHandle->cond == MEM_FREED);
}
resOpen(id);
if (needByteSwap) {
MemHandle *handle = resHandle(id);
if (!handle)
return;
uint32 totSize = handle->size;
uint32 *data = (uint32 *)((uint8 *)handle->data + sizeof(Header));
totSize -= sizeof(Header);
if (totSize & 3)
error("Illegal compact size for id %d: %d", id, totSize);
totSize /= 4;
for (uint32 cnt = 0; cnt < totSize; cnt++) {
*data = READ_BE_UINT32(data);
data++;
}
}
}
void ResMan::openScriptResourceBigEndian(uint32 id) {
bool needByteSwap = false;
if (!_isBigEndian) {
// Cluster files are in little endian fomat.
// If the resource are not in memory anymore, and therefore will be read
// from disk, they will need to be byte swaped.
MemHandle *memHandle = resHandle(id);
if (memHandle)
needByteSwap = (memHandle->cond == MEM_FREED);
}
resOpen(id);
if (needByteSwap) {
MemHandle *handle = resHandle(id);
if (!handle)
return;
// uint32 totSize = handle->size;
Header *head = (Header *)handle->data;
head->comp_length = FROM_LE_32(head->comp_length);
head->decomp_length = FROM_LE_32(head->decomp_length);
head->version = FROM_LE_16(head->version);
uint32 *data = (uint32 *)((uint8 *)handle->data + sizeof(Header));
uint32 size = handle->size - sizeof(Header);
if (size & 3)
error("Odd size during script endian conversion. Resource ID =%d, size = %d", id, size);
size >>= 2;
for (uint32 cnt = 0; cnt < size; cnt++) {
*data = READ_LE_UINT32(data);
data++;
}
}
}
void ResMan::openScriptResourceLittleEndian(uint32 id) {
bool needByteSwap = false;
if (_isBigEndian) {
// Cluster files are in big endian fomat.
// If the resource are not in memory anymore, and therefore will be read
// from disk, they will need to be byte swaped.
MemHandle *memHandle = resHandle(id);
if (memHandle)
needByteSwap = (memHandle->cond == MEM_FREED);
}
resOpen(id);
if (needByteSwap) {
MemHandle *handle = resHandle(id);
if (!handle)
return;
// uint32 totSize = handle->size;
Header *head = (Header *)handle->data;
head->comp_length = FROM_BE_32(head->comp_length);
head->decomp_length = FROM_BE_32(head->decomp_length);
head->version = FROM_BE_16(head->version);
uint32 *data = (uint32 *)((uint8 *)handle->data + sizeof(Header));
uint32 size = handle->size - sizeof(Header);
if (size & 3)
error("Odd size during script endian conversion. Resource ID =%d, size = %d", id, size);
size >>= 2;
for (uint32 cnt = 0; cnt < size; cnt++) {
*data = READ_BE_UINT32(data);
data++;
}
}
}
uint32 ResMan::getDeathFontId() {
// At some point in the releases (as evidenced by the disasms of all
// known executables, and by the source code in our possession), Revolution
// changed the resource offsets for some files. I have tried EVERYTHING to
// try and discern which file we are dealing with and spectacularly failed.
// The only choice which seems to work correctly is to check the file size,
// as the newer GENERAL.CLU file is bigger. Sorry.
if (SwordEngine::isPsx())
return SR_FONT;
Common::File fp;
if (fp.open(SwordEngine::isMac() ? "GENERAL.CLM" : "GENERAL.CLU")) {
fp.seek(0, SEEK_END);
int64 fileSize = fp.pos();
if (SwordEngine::_systemVars.realLanguage == Common::RU_RUS) {
switch (fileSize) {
case 6081261: // Akella
return SR_DEATHFONT;
case 6354790: // Mediahauz
return SR_FONT;
case 6350630: // Novy Disk
return SR_DEATHFONT_ALT;
default:
warning("ResMan::getDeathFontId(): Unrecognized version of russian GENERAL.CLU, size %d", (int)fileSize);
break;
}
return SR_FONT;
} else if (fileSize < 6295679) {
return SR_DEATHFONT;
} else {
return SR_DEATHFONT_ALT;
}
}
return 0;
}
} // End of namespace Sword1

169
engines/sword1/resman.h Normal file
View File

@@ -0,0 +1,169 @@
/* 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 SWORD1_RESMAN_H
#define SWORD1_RESMAN_H
#include "sword1/memman.h"
#include "sword1/swordres.h"
#include "common/file.h"
#include "sword1/sworddefs.h"
#include "common/endian.h"
#include "common/mutex.h"
namespace Sword1 {
#define MAX_LABEL_SIZE (31+1)
#if defined(__PSP__)
#define MAX_OPEN_CLUS 4 // the PSP can't have more than 8 files open simultaneously
// since we also need filehandles for music and sometimes savegames
// set the maximum number of open clusters to 4.
#else
#define MAX_OPEN_CLUS 8 // don't open more than 8 files at once
#endif
struct Grp {
uint32 noRes;
MemHandle *resHandle;
uint32 *offset;
uint32 *length;
};
struct Clu {
uint32 refCount;
Common::File *file;
char label[MAX_LABEL_SIZE];
uint32 noGrp;
Grp *grp;
Clu *nextOpen;
};
struct Prj {
uint32 noClu;
Clu *clu;
};
class ResMan {
public:
ResMan(const char *fileName, bool isMacFile, bool isKorean);
~ResMan();
void flush();
void resClose(uint32 id);
void resOpen(uint32 id);
void *fetchRes(uint32 id);
void dumpRes(uint32 id);
void *openFetchRes(uint32 id);
void *cptResOpen(uint32 id);
Header *lockScript(uint32 scrID);
void unlockScript(uint32 scrID);
FrameHeader *fetchFrame(void *resourceData, uint32 frameNo);
uint16 getUint16(uint16 value) {
return (_isBigEndian) ? FROM_BE_16(value) : FROM_LE_16(value);
}
uint32 getUint32(uint32 value) {
return (_isBigEndian) ? FROM_BE_32(value) : FROM_LE_32(value);
}
uint16 getLEUint16(uint16 value) {
return FROM_LE_16(value);
}
uint32 getLEUint32(uint32 value) {
return FROM_LE_32(value);
}
uint16 readUint16(const void *ptr) {
return (_isBigEndian) ? READ_BE_UINT16(ptr) : READ_LE_UINT16(ptr);
}
uint32 readUint32(const void *ptr) {
return (_isBigEndian) ? READ_BE_UINT32(ptr) : READ_LE_UINT32(ptr);
}
uint32 readLEUint32(const void *ptr) {
return READ_LE_UINT32(ptr);
}
uint16 toUint16(uint16 value) {
return (_isBigEndian) ? TO_BE_16(value) : TO_LE_16(value);
}
uint32 toUint32(uint32 value) {
return (_isBigEndian) ? TO_BE_32(value) : TO_LE_32(value);
}
uint32 getDeathFontId();
private:
uint32 resLength(uint32 id);
MemHandle *resHandle(uint32 id);
uint32 resOffset(uint32 id);
Common::File *resFile(uint32 id);
void openCptResourceBigEndian(uint32 id);
void openScriptResourceBigEndian(uint32 id);
void openCptResourceLittleEndian(uint32 id);
void openScriptResourceLittleEndian(uint32 id);
void loadCluDescript(const char *fileName);
void freeCluDescript();
Prj _prj;
MemMan *_memMan;
static const uint32 _scriptList[TOTAL_SECTIONS]; //a table of resource tags
Clu *_openCluStart, *_openCluEnd;
int _openClus;
bool _isBigEndian;
bool _isKorTrs = false;
Common::Mutex _resourceAccessMutex;
uint32 _srIdList[29] = {
// the file numbers differ for the control panel file IDs, so we need this array
OTHER_SR_FONT, // SR_FONT
0x04050000, // SR_BUTTON
OTHER_SR_REDFONT, // SR_REDFONT
0x04050001, // SR_PALETTE
0x04050002, // SR_PANEL_ENGLISH
0x04050003, // SR_PANEL_FRENCH
0x04050004, // SR_PANEL_GERMAN
0x04050005, // SR_PANEL_ITALIAN
0x04050006, // SR_PANEL_SPANISH
0x04050007, // SR_PANEL_AMERICAN
0x04050008, // SR_TEXT_BUTTON
0x04050009, // SR_SPEED
0x0405000A, // SR_SCROLL1
0x0405000B, // SR_SCROLL2
0x0405000C, // SR_CONFIRM
0x0405000D, // SR_VOLUME
0x0405000E, // SR_VLIGHT
0x0405000F, // SR_VKNOB
0x04050010, // SR_WINDOW
0x04050011, // SR_SLAB1
0x04050012, // SR_SLAB2
0x04050013, // SR_SLAB3
0x04050014, // SR_SLAB4
0x04050015, // SR_BUTUF
0x04050016, // SR_BUTUS
0x04050017, // SR_BUTDS
0x04050018, // SR_BUTDF
0x04050019, // SR_DEATHPANEL
SR_DEATHFONT,
};
};
} // End of namespace Sword1
#endif //RESMAN_H

2135
engines/sword1/router.cpp Normal file

File diff suppressed because it is too large Load Diff

161
engines/sword1/router.h Normal file
View File

@@ -0,0 +1,161 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 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 SWORD1_ROUTER_H
#define SWORD1_ROUTER_H
#include "sword1/object.h"
namespace Sword1 {
#include "common/pack-start.h" // START STRUCT PACKING
struct BarData {
int16 x1;
int16 y1;
int16 x2;
int16 y2;
int16 xmin;
int16 ymin;
int16 xmax;
int16 ymax;
int16 dx; // x2 - x1
int16 dy; // y2 - y1
int32 co; // co = (y1*dx) - (x1*dy) from an equation for a line y*dx = x*dy + co
} PACKED_STRUCT;
struct NodeData {
int16 x;
int16 y;
int16 level;
int16 prev;
int16 dist;
} PACKED_STRUCT;
#include "common/pack-end.h" // END STRUCT PACKING
struct FloorData {
int32 nbars;
BarData *bars;
int32 nnodes;
NodeData *node;
};
struct RouteData {
int32 x;
int32 y;
int32 dirS;
int32 dirD;
};
struct PathData {
int32 x;
int32 y;
int32 dir;
int32 num;
};
#define MAX_FRAMES_PER_CYCLE 16
#define NO_DIRECTIONS 8
#define MAX_FRAMES_PER_CHAR (MAX_FRAMES_PER_CYCLE * NO_DIRECTIONS)
#define ROUTE_END_FLAG 255
#define O_GRID_SIZE 200
#define O_ROUTE_SIZE 50
class ObjectMan;
class ResMan;
class Screen;
extern int whatTarget(int32 startX, int32 startY, int32 destX, int32 destY);
class Router {
public:
Router(ObjectMan *pObjMan, ResMan *pResMan);
int32 routeFinder(int32 id, Object *mega, int32 x, int32 y, int32 dir);
void setPlayerTarget(int32 x, int32 y, int32 dir, int32 stance);
// these should be private but are read by Screen for debugging:
BarData _bars[O_GRID_SIZE];
NodeData _node[O_GRID_SIZE];
int32 _nBars;
int32 _nNodes;
private:
// when the player collides with another mega, we'll receive a ReRouteRequest here.
// that's why we need to remember the player's target coordinates
int32 _playerTargetX, _playerTargetY, _playerTargetDir, _playerTargetStance;
ObjectMan *_objMan;
ResMan *_resMan;
int32 _startX, _startY, _startDir;
int32 _targetX, _targetY, _targetDir;
int32 _scaleA, _scaleB;
int32 megaId;
RouteData _route[O_ROUTE_SIZE];
PathData _smoothPath[O_ROUTE_SIZE];
PathData _modularPath[O_ROUTE_SIZE];
int32 _routeLength;
int32 _framesPerStep, _framesPerChar;
uint8 _nWalkFrames, _nTurnFrames;
int32 _dx[NO_DIRECTIONS + MAX_FRAMES_PER_CHAR];
int32 _dy[NO_DIRECTIONS + MAX_FRAMES_PER_CHAR];
int32 _modX[NO_DIRECTIONS];
int32 _modY[NO_DIRECTIONS];
int32 _diagonalx, _diagonaly;
int32 standFrames;
int32 turnFramesLeft, turnFramesRight;
int32 walkFramesLeft, walkFramesRight; // left/right walking turn
int32 slowInFrames, slowOutFrames;
bool _slidyWalkAnimatorState;
int32 LoadWalkResources(Object *mega, int32 x, int32 y, int32 dir);
int32 getRoute();
int32 checkTarget(int32 x, int32 y);
bool scan(int32 level);
int32 newCheck(int32 status, int32 x1, int32 x2, int32 y1, int32 y2);
bool check(int32 x1, int32 y1, int32 x2, int32 y2);
bool horizCheck(int32 x1, int32 y, int32 x2);
bool vertCheck(int32 x, int32 y1, int32 y2);
bool lineCheck(int32 x1, int32 y1, int32 x2, int32 y2);
void extractRoute();
void slidyPath();
void slidyWalkAnimator(WalkData *walkAnim);
int32 smoothestPath();
void smoothCheck(int32 &steps, int32 best, int32 p, int32 dirS, int32 dirD);
void solidPath();
int32 solidWalkAnimator(WalkData *walkAnim);
};
} // End of namespace Sword1
#endif //BSROUTER_H

1631
engines/sword1/screen.cpp Normal file

File diff suppressed because it is too large Load Diff

198
engines/sword1/screen.h Normal file
View File

@@ -0,0 +1,198 @@
/* 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 SWORD1_SCREEN_H
#define SWORD1_SCREEN_H
#include "sword1/sworddefs.h"
#include "common/mutex.h"
class OSystem;
namespace Sword1 {
#define MAX_FORE 20
#define MAX_BACK 20
#define MAX_SORT 20
struct SortSpr {
int32 id, y;
};
struct RoomDef {
int totalLayers;
int sizeX;
int sizeY;
int gridWidth; //number of 16*16 grid blocks across - including off screen edges.
uint32 layers[4];
uint32 grids[3];
uint32 palettes[2];
uint32 parallax[2];
};
struct PSXDataCache { // Cache for PSX screen, to avoid decompressing background at every screen update
uint8 *decodedBackground;
uint8 *extPlxCache; // If this screen requires an external parallax, save it here
};
#define SCRNGRID_X 16
#define SCRNGRID_Y 8
#define SHRINK_BUFFER_SIZE 50000
#define RLE_BUFFER_SIZE 50000
#define FLASH_RED 0
#define FLASH_BLUE 1
#define BORDER_YELLOW 2
#define BORDER_GREEN 3
#define BORDER_PURPLE 4
#define BORDER_BLACK 5
#define TEXT_WHITE 6
class SwordEngine;
class ResMan;
class ObjectMan;
class Text; // Text objects use sprites that are created internally at run-time
// the buffer belongs to Text, so we need a reference here.
class Screen {
friend class Text;
public:
Screen(OSystem *system, SwordEngine *vm, ResMan *pResMan, ObjectMan *pObjMan);
~Screen();
void clearScreen();
void useTextManager(Text *pTextMan);
void draw();
void initFadePaletteServer();
void quitScreen();
void newScreen(uint32 screen);
void setScrolling(int16 offsetX, int16 offsetY);
void addToGraphicList(uint8 listId, uint32 objId);
void startFadePaletteDown(int speed);
void startFadePaletteUp(int speed);
void fadePalette();
void fnSetPalette(uint8 start, uint16 length, uint32 id);
void fnSetFadeTargetPalette(uint8 start, uint16 length, uint32 id, int singleColor = -1);
int16 stillFading();
void fullRefresh(bool soft = false);
void setNextFadeOutToBlack();
bool showScrollFrame();
void updateScreen();
void showFrame(uint16 x, uint16 y, uint32 resId, uint32 frameNo, const byte *fadeMask = nullptr, int8 fadeStatus = 0);
void fnSetParallax(uint32 screen, uint32 resId);
void fnFlash(uint8 color);
static void decompressHIF(uint8 *src, uint8 *dest);
void printDebugLine(uint8 *ascii, uint8 first, int x, int y);
// Functions used by the router debug visualization routines
void plotLine(int32 x1, int32 y1, int32 x2, int32 y2, uint8 color);
void plotPoint(int32 x, int32 y, uint8 color);
void bresenhamLine(int32 x1, int32 y1, int32 x2, int32 y2, uint8 color);
Common::Mutex _screenAccessMutex; // To coordinate actions between the main thread and the palette fade thread
private:
// The original values are 6-bit RGB numbers, so they have to be shifted,
// except for white, which for some reason has to stay unshifted in order
// to work correctly.
const uint8 _white[3] = { 63, 63, 63 };
const uint8 _red[3] = { 63 << 2, 0 << 2, 0 << 2 };
const uint8 _blue[3] = { 0 << 2, 0 << 2, 63 << 2 };
const uint8 _yellow[3] = { 63 << 2, 63 << 2, 0 << 2 };
const uint8 _green[3] = { 0 << 2, 63 << 2, 0 << 2 };
const uint8 _purple[3] = { 32 << 2, 0 << 2, 32 << 2 };
const uint8 _black[3] = { 0 << 2, 0 << 2, 0 << 2 };
//const uint8 _grey[3] = { 32 << 2, 32 << 2, 32 << 2 };
struct PaletteFadeInfo {
int16 paletteStatus;
int16 paletteIndex;
int16 paletteCount;
int16 fadeCount;
uint8 srcPalette[256 * 3];
uint8 dstPalette[256 * 3];
};
PaletteFadeInfo _paletteFadeInfo;
void verticalMask(uint16 x, uint16 y, uint16 bWidth, uint16 bHeight);
void blitBlockClear(uint16 x, uint16 y, uint8 *data);
void renderParallax(uint8 *data);
void processImage(uint32 id);
void spriteClipAndSet(uint16 *pSprX, uint16 *pSprY, uint16 *sprWidth, uint16 *sprHeight, uint16 *incr);
void drawSprite(uint8 *sprData, uint16 sprX, uint16 sprY, uint16 sprWidth, uint16 sprHeight, uint16 sprPitch);
void drawPsxHalfShrinkedSprite(uint8 *sprData, uint16 sprX, uint16 sprY, uint16 sprWidth, uint16 sprHeight, uint16 sprPitch);
void drawPsxFullShrinkedSprite(uint8 *sprData, uint16 sprX, uint16 sprY, uint16 sprWidth, uint16 sprHeight, uint16 sprPitch);
uint8 *psxBackgroundToIndexed(uint8 *psxBackground, uint32 bakXres, uint32 bakYres);
uint8 *psxShrinkedBackgroundToIndexed(uint8 *psxBackground, uint32 bakXres, uint32 bakYres);
void fetchPsxParallaxSize(uint8 *psxParallax, uint16 *paraSizeX, uint16 *paraSizeY);
void drawPsxParallax(uint8 *psxParallax, uint16 paraScrlX, uint16 scrnScrlX, uint16 scrnWidth);
void decompressRLE7(uint8 *src, uint32 compSize, uint8 *dest);
void decompressRLE0(uint8 *src, uint32 compSize, uint8 *dest);
void decompressTony(uint8 *src, uint32 compSize, uint8 *dest);
void fastShrink(uint8 *src, uint32 width, uint32 height, uint32 scale, uint8 *dest);
void flushPsxCache();
OSystem *_system;
SwordEngine *_vm;
ResMan *_resMan;
ObjectMan *_objMan;
Text *_textMan;
uint16 _currentScreen;
uint8 *_screenBuf;
uint8 *_screenGrid;
uint16 *_layerGrid[4];
uint8 *_layerBlocks[4];
uint8 *_parallax[2];
uint8 _rleBuffer[RLE_BUFFER_SIZE];
uint8 _shrinkBuffer[SHRINK_BUFFER_SIZE];
bool _fullRefresh;
bool _updatePalette;
uint16 _oldScrollX, _oldScrollY; // for drawing additional frames
PSXDataCache _psxCache; // Cache used for PSX backgrounds
uint32 _foreList[MAX_FORE];
uint32 _backList[MAX_BACK];
SortSpr _sortList[MAX_SORT];
uint8 _foreLength, _backLength, _sortLength;
uint16 _scrnSizeX, _scrnSizeY, _gridSizeX, _gridSizeY;
static RoomDef _roomDefTable[TOTAL_ROOMS]; // from ROOMS.C (not const, see fnSetParallax)
uint8 _targetPalette[256 * 3];
uint8 _currentPalette[256 * 3]; // for fading
uint8 _zeroPalette[256 * 3];
bool _forceNextFadeOutToBlack = false;
};
} // End of namespace Sword1
#endif //BSSCREEN_H

1688
engines/sword1/sound.cpp Normal file

File diff suppressed because it is too large Load Diff

269
engines/sword1/sound.h Normal file
View File

@@ -0,0 +1,269 @@
/* 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 SWORD1_SOUND_H
#define SWORD1_SOUND_H
#include "sword1/object.h"
#include "sword1/sworddefs.h"
#include "common/file.h"
#include "common/mutex.h"
#include "common/random.h"
#include "common/timer.h"
#include "common/util.h"
#include "audio/mixer.h"
#include "audio/audiostream.h"
namespace Audio {
class Mixer;
}
namespace Sword1 {
#define TOTAL_FX_PER_ROOM 7 // total loop & random fx per room (see fx_list.c)
#define MAX_ROOMS_PER_FX 7 // max no. of rooms in the fx's room,vol list
#define MAX_FXQ_LENGTH 32 // max length of sound queue - ie. max number of fx that can be stored up/playing together
#define FX_SPOT 1
#define FX_LOOP 2
#define FX_RANDOM 3
struct QueueElement {
uint32 id, delay;
Audio::SoundHandle handle;
void reset() {
id = 0, delay = 0;
handle = Audio::SoundHandle();
}
};
struct RoomVol {
int32 roomNo, leftVol, rightVol;
};
struct SampleId {
byte cluster;
byte idStd;
byte idWinDemo;
};
struct FxDef {
SampleId sampleId;
uint32 type, delay;
RoomVol roomVolList[MAX_ROOMS_PER_FX];
};
class ResMan;
class SwordEngine;
#define MUSIC_UNDERSCORE 50
#define NEG_MOUTH_THRESHOLD -750
#define POS_MOUTH_THRESHOLD 750
#define MAX_FX 4
#define MAX_MUSIC 2
#define S_STATUS_FINISHED 1
#define S_STATUS_RUNNING 0
#define MUSIC_BUFFER_SIZE 0x2000
#define TOTAL_TUNES 270
#define DEFAULT_SAMPLE_RATE 11025
enum CowMode {
CowWave = 0,
CowFLAC,
CowVorbis,
CowMP3,
CowDemo,
CowPSX
};
enum MusCompMode {
MusWav = 0,
MusAif,
MusFLAC,
MusVorbis,
MusMP3,
MusPSX
};
class Sound {
friend class SwordConsole;
friend class Control;
public:
Sound(Audio::Mixer *mixer, SwordEngine *vm, ResMan *pResMan);
~Sound();
void newScreen(uint32 screen);
void closeCowSystem();
void engine();
void checkSpeechFileEndianness();
double endiannessHeuristicValue(int16* data, uint32 dataSize, uint32 &maxSamples);
void installFadeTimer();
void uninstallFadeTimer();
void playSample(int32 fxNo);
void stopSample(int32 fxNo);
void setFXVolume(byte targetVolume, int handleIdx);
void clearAllFx();
int addToQueue(uint32 fxNo);
void removeFromQueue(uint32 fxNo);
void startSpeech(uint16 roomNo, uint16 localNo);
bool amISpeaking();
int32 checkSpeechStatus();
void playSpeech();
void stopSpeech();
void streamMusicFile(int32 tuneId, int32 looped);
void updateMusicStreaming();
void setCrossFadeIncrement();
void fadeMusicDown(int32 rate);
void fadeFxDown(int32 rate);
void fadeFxUp(int32 rate);
void pauseSpeech();
void unpauseSpeech();
void pauseMusic();
void unpauseMusic();
void pauseFx();
void unpauseFx();
void getVolumes();
void setVolumes();
byte clampVolume(int32 volume);
Common::Mutex _soundMutex;
Audio::Mixer *_mixer;
// Handles for external volume changes (control panel)
uint32 _volFX[2] = { 0, 0 };
uint32 _volSpeech[2] = { 0, 0 };
uint32 _volMusic[2] = { 0, 0 };
// Volume fading variables
int32 _fxCount = 0;
int32 _fxFadingFlag = 0;
int32 _fxFadingRate = 0;
int32 _fxFadeVolume[2] = { 0, 0 };
int32 _musicFadeVolume[2] = { 0, 0 };
// Sound FX information
bool _fxSampleBusy[MAX_FX] = { false, false, false, false };
// Speech data
byte *_speechSample = nullptr;
private:
struct WaveHeader {
uint32 riffTag;
uint32 riffSize;
uint32 waveTag;
uint32 fmtTag;
uint32 fmtSize;
// Format subchunk
uint16 wFormatTag;
uint16 wChannels;
uint32 dwSamplesPerSec;
uint32 dwAvgBytesPerSec;
uint16 wBlockAlign;
uint16 wBitsPerSample;
uint32 dwDataTag;
uint32 dwDataSize;
};
void initCowSystem();
uint32 getSampleId(int32 fxNo);
void playFX(int32 fxID, int32 type, uint8 *wavData, uint32 vol[2]);
void stopFX(int32 fxID);
int32 checkSampleStatus(int32 id);
bool expandSpeech(byte *src, byte *dst, uint32 dstSize,
bool *endiannessCheck = nullptr, uint32 *sizeForEndiannessCheck = nullptr);
int32 getSpeechSize(byte *compData, uint32 compSize);
bool prepareMusicStreaming(const Common::Path &filename, int newHandleId, int32 tuneId,
uint32 volume, int8 pan, MusCompMode assignedMode);
void serveSample(Common::File *file, int32 i);
void reduceMusicVolume();
void restoreMusicVolume();
int8 scalePan(int pan); // From 0,127 to -127,127
Common::File _cowFile;
uint32 *_cowHeader;
uint32 _cowHeaderSize;
uint8 _currentCowFile;
CowMode _cowMode;
Common::RandomSource _rnd;
QueueElement _fxQueue[MAX_FXQ_LENGTH];
uint8 _endOfQueue;
SwordEngine *_vm;
ResMan *_resMan;
bool _bigEndianSpeech;
static const char _musicList[270];
static const uint16 _roomsFixedFx[TOTAL_ROOMS][TOTAL_FX_PER_ROOM];
static const FxDef _fxList[312];
static const char _tuneList[TOTAL_TUNES][8]; // In staticres.cpp
// Volume fading variables
bool _crossFadeIncrement = false;
// Speech variables
int32 _speechLipsyncCounter = 0;
int32 _speechSize = 0;
bool _speechSampleBusy = false;
// Sound handles
Audio::SoundHandle _hSampleFX[MAX_FX];
Audio::SoundHandle _hSampleSpeech;
Audio::SoundHandle _hSampleMusic[MAX_MUSIC];
// Music stream information
bool _musicStreamPlaying[MAX_MUSIC] = { false, false };
bool _streamLoopingFlag[MAX_MUSIC] = { false, false };
int32 _musicStreamFading[MAX_MUSIC] = { 0, 0 };
MusCompMode _musicStreamFormat[MAX_MUSIC] = { MusWav, MusWav };
Audio::QueuingAudioStream *_musicOutputStream[MAX_MUSIC] = { nullptr, nullptr };
Audio::RewindableAudioStream *_compressedMusicStream[MAX_MUSIC] = { nullptr, nullptr };
Common::File _musicFile[MAX_MUSIC];
// Sound FX information
int32 _fxSampleId[MAX_FX] = { 0, 0, 0, 0 };
// Pause variables
bool _speechPaused = false;
bool _fxPaused[MAX_FX] = { false, false, false, false };
bool _musicPaused[MAX_MUSIC] = { false, false };
};
} // End of namespace Sword1
#endif // SOUND_H

7150
engines/sword1/staticres.cpp Normal file

File diff suppressed because it is too large Load Diff

1255
engines/sword1/sword1.cpp Normal file

File diff suppressed because it is too large Load Diff

204
engines/sword1/sword1.h Normal file
View File

@@ -0,0 +1,204 @@
/* 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 SWORD1_SWORD1_H
#define SWORD1_SWORD1_H
#include "engines/engine.h"
#include "common/error.h"
#include "common/keyboard.h"
#include "common/rect.h"
#include "common/util.h"
#include "common/events.h"
#include "sword1/sworddefs.h"
#include "sword1/console.h"
struct ADGameDescription;
/**
* This is the namespace of the Sword1 engine.
*
* Status of this engine: ???
*
* Games using this engine:
* - Broken Sword: The Shadow of the Templars
*/
namespace Sword1 {
enum SWORD1Action {
kActionNone,
kActionPause,
kActionQuit,
kActionMainPanel,
kActionEscape
};
enum ControlPanelMode {
CP_NORMAL = 0,
CP_DEATHSCREEN,
CP_THEEND,
CP_NEWGAME
};
class Screen;
class Sound;
class Logic;
class Mouse;
class ResMan;
class ObjectMan;
class Menu;
class Control;
struct SystemVars {
bool runningFromCd;
uint32 currentCD; // starts at zero, then either 1 or 2 depending on section being played
uint32 justRestoredGame; // see main() in sword.c & New_screen() in gtm_core.c
uint8 controlPanelMode; // 1 death screen version of the control panel, 2 = successful end of game, 3 = force restart
uint8 saveGameFlag;
int snrStatus;
bool wantFade; // when true => fade during scene change, else cut.
bool playSpeech;
bool textRunning;
uint32 speechRunning;
bool speechFinished;
bool showText;
int32 textNumber;
uint8 language;
bool isDemo;
bool isSpanishDemo;
Common::Platform platform;
Common::Language realLanguage;
bool isLangRtl;
bool debugMode;
bool slowMode;
bool fastMode;
bool parallaxOn;
bool gamePaused;
bool displayDebugText;
bool displayDebugMouse;
bool displayDebugGrid;
uint32 framesPerSecondCounter;
uint32 gameCycle;
bool useWindowsAudioMode; // DOS and Windows use different implementations of the audio driver, each with their own behavior
};
class SwordEngine : public Engine {
friend class SwordConsole;
friend class Screen;
friend class Control;
public:
SwordEngine(OSystem *syst, const ADGameDescription *gameDesc);
~SwordEngine() override;
static SystemVars _systemVars;
void reinitialize();
uint32 _features;
int _inTimer = -1; // Is the timer running?
int32 _vbl60HzUSecElapsed = 0; // 60 Hz counter for palette fades
int _vblCount = 0; // How many vblCallback calls have been made?
int _rate = DEFAULT_FRAME_TIME / 10;
int _targetFrameTime = DEFAULT_FRAME_TIME;
uint32 _mainLoopFrameCount = 0;
uint32 _ticker = 0; // For the frame time shown within the debug text
bool mouseIsActive();
static bool isMac() { return _systemVars.platform == Common::kPlatformMacintosh; }
static bool isPsx() { return _systemVars.platform == Common::kPlatformPSX; }
static bool isWindows() { return _systemVars.platform == Common::kPlatformWindows ; }
// Used by timer
void updateTopMenu();
void updateBottomMenu();
void fadePaletteStep();
void startFadePaletteDown(int speed);
void startFadePaletteUp(int speed);
void waitForFade();
bool screenIsFading();
bool fadeDirectionIsUp();
void setMenuToTargetState();
void showDebugInfo();
protected:
// Engine APIs
Common::Error init();
Common::Error go();
Common::Error run() override {
Common::Error err;
err = init();
if (err.getCode() != Common::kNoError)
return err;
return go();
}
bool hasFeature(EngineFeature f) const override;
void syncSoundSettings() override;
Common::Error loadGameState(int slot) override;
bool canLoadGameStateCurrently(Common::U32String *msg = nullptr) override;
Common::Error saveGameState(int slot, const Common::String &desc, bool isAutosave = false) override;
bool canSaveGameStateCurrently(Common::U32String *msg = nullptr) override;
Common::String getSaveStateName(int slot) const override {
return Common::String::format("sword1.%03d", slot);
}
private:
void pollInput(uint32 delay);
void checkKeys();
void checkCdFiles();
void checkCd();
void askForCd();
void showFileErrorMsg(uint8 type, bool *fileExists);
void flagsToBool(bool *dest, uint8 flags);
void reinitRes(); //Reinits the resources after a GMM load
void installTimerRoutines();
void uninstallTimerRoutines();
uint8 mainLoop();
Common::Point _mouseCoord;
uint16 _mouseState;
Common::KeyState _keyPressed;
Common::CustomEventType _customType;
ResMan *_resMan;
ObjectMan *_objectMan;
Screen *_screen;
Mouse *_mouse;
Logic *_logic;
Sound *_sound;
Menu *_menu;
Control *_control;
static const uint8 _cdList[TOTAL_SECTIONS];
static const CdFile _pcCdFileList[];
static const CdFile _macCdFileList[];
static const CdFile _psxCdFileList[];
};
} // End of namespace Sword1
#endif // SWORD1_SWORD1_H

1615
engines/sword1/sworddefs.h Normal file

File diff suppressed because it is too large Load Diff

5224
engines/sword1/swordres.h Normal file

File diff suppressed because it is too large Load Diff

360
engines/sword1/text.cpp Normal file
View File

@@ -0,0 +1,360 @@
/* 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/textconsole.h"
#include "common/str.h"
#include "common/unicode-bidi.h"
#include "sword1/logic.h"
#include "sword1/text.h"
#include "sword1/resman.h"
#include "sword1/objectman.h"
#include "sword1/swordres.h"
#include "sword1/sworddefs.h"
#include "sword1/screen.h"
#include "sword1/sword1.h"
namespace Sword1 {
#define OVERLAP 3
#define DEMO_OVERLAP 1
#define DEBUG_OVERLAP 2
#define SPACE ' '
#define MAX_LINES 30
Text::Text(SwordEngine *vm, Logic *pLogic, ObjectMan *pObjMan, ResMan *pResMan, Screen *pScreen, bool czechVersion) {
_vm = vm;
_logic = pLogic;
_objMan = pObjMan;
_resMan = pResMan;
_screen = pScreen;
_textCount = 0;
_fontId = (czechVersion) ? CZECH_GAME_FONT : GAME_FONT;
_font = (uint8 *)_resMan->openFetchRes(_fontId);
_joinWidth = charWidth(SPACE);
if (!SwordEngine::_systemVars.isDemo) {
_joinWidth -= 2 * OVERLAP;
} else {
_joinWidth -= 2 * DEMO_OVERLAP;
}
_charHeight = _resMan->getUint16(_resMan->fetchFrame(_font, 0)->height); // all chars have the same height
if (SwordEngine::isPsx())
_charHeight /= 2;
for (int i = 0; i < MAX_TEXT_OBS; i++)
_textBlocks[i] = NULL;
}
Text::~Text() {
for (int i = 0; i < MAX_TEXT_OBS; i++)
free(_textBlocks[i]);
//_resMan->resClose(_fontId); => wiped automatically by _resMan->flush();
}
uint32 Text::lowTextManager(uint8 *ascii, int32 width, uint8 pen) {
_textCount++;
if (_textCount > MAX_TEXT_OBS)
error("Text::lowTextManager: MAX_TEXT_OBS exceeded");
uint32 textObjId = (TEXT_sect * ITM_PER_SEC) - 1;
do {
textObjId++;
} while (_objMan->fetchObject(textObjId)->o_status);
// okay, found a free text object
_objMan->fetchObject(textObjId)->o_status = STAT_FORE;
makeTextSprite((uint8)textObjId, ascii, (uint16)width, pen);
return textObjId;
}
void Text::makeTextSprite(uint8 slot, const uint8 *text, uint16 maxWidth, uint8 pen) {
LineInfo lines[MAX_LINES];
uint16 numLines = analyzeSentence(text, maxWidth, lines);
uint16 sprWidth = 0;
uint16 lineCnt;
for (lineCnt = 0; lineCnt < numLines; lineCnt++)
if (lines[lineCnt].width > sprWidth)
sprWidth = lines[lineCnt].width;
uint16 sprHeight = _charHeight * numLines;
if (SwordEngine::isPsx()) {
sprHeight = 2 * _charHeight * numLines - 4 * (numLines - 1);
sprWidth = (sprWidth + 1) & 0xFFFE;
}
uint32 sprSize = sprWidth * sprHeight;
assert(!_textBlocks[slot]); // if this triggers, the speechDriver failed to call Text::releaseText.
_textBlocks[slot] = (FrameHeader *)malloc(sprSize + sizeof(FrameHeader));
memcpy(_textBlocks[slot]->runTimeComp, "Nu ", 4);
_textBlocks[slot]->compSize = 0;
_textBlocks[slot]->width = _resMan->toUint16(sprWidth);
_textBlocks[slot]->height = _resMan->toUint16(sprHeight);
_textBlocks[slot]->offsetX = 0;
_textBlocks[slot]->offsetY = 0;
uint8 *linePtr = ((uint8 *)_textBlocks[slot]) + sizeof(FrameHeader);
memset(linePtr, NO_COL, sprSize);
for (lineCnt = 0; lineCnt < numLines; lineCnt++) {
uint8 *sprPtr = linePtr + (sprWidth - lines[lineCnt].width) / 2; // center the text
Common::String textString;
const uint8 *curTextLine = text;
if (SwordEngine::_systemVars.isLangRtl) {
Common::String textLogical = Common::String((const char *)text, (uint32)lines[lineCnt].length);
textString = Common::convertBiDiString(textLogical, Common::kWindows1255);
curTextLine = (const uint8 *)textString.c_str();
}
for (uint16 pos = 0; pos < lines[lineCnt].length; pos++) {
if (isKoreanChar(*curTextLine, *(curTextLine + 1))) {
sprPtr += copyWChar(*curTextLine, *(curTextLine + 1), sprPtr, sprWidth, pen) - OVERLAP;
curTextLine += 2;
pos++;
} else {
sprPtr += copyChar(*curTextLine++, sprPtr, sprWidth, pen);
if (!SwordEngine::_systemVars.isDemo) {
sprPtr -= OVERLAP;
} else {
sprPtr -= DEMO_OVERLAP;
}
}
}
curTextLine++; // skip space at the end of the line
text += lines[lineCnt].length + 1;
if (SwordEngine::isPsx())
linePtr += (_charHeight - 4) * sprWidth;
else
linePtr += _charHeight * sprWidth;
}
}
uint16 Text::charWidth(uint8 ch) {
if (ch < SPACE)
ch = 64;
return _resMan->getUint16(_resMan->fetchFrame(_font, ch - SPACE)->width);
}
uint16 Text::analyzeSentence(const uint8 *text, uint16 maxWidth, LineInfo *line) {
uint16 lineNo = 0;
bool firstWord = true;
if (SwordEngine::isPsx())
maxWidth = 254;
while (*text) {
uint16 wordWidth = 0;
uint16 wordLength = 0;
while ((*text != SPACE) && *text) {
if (isKoreanChar(*text, *(text + 1))) {
wordWidth += wCharWidth(*text, *(text + 1)) - OVERLAP;
wordLength += 2;
text += 2;
} else {
wordWidth += charWidth(*text);
if (!SwordEngine::_systemVars.isDemo) {
wordWidth -= OVERLAP;
} else {
wordWidth -= DEMO_OVERLAP;
}
wordLength++;
text++;
}
}
if (*text == SPACE)
text++;
// no overlap on final letter of word!
if (!SwordEngine::_systemVars.isDemo) {
wordWidth += OVERLAP;
} else {
wordWidth += DEMO_OVERLAP;
}
if (firstWord) { // first word on first line, so no separating SPACE needed
line[0].width = wordWidth;
line[0].length = wordLength;
firstWord = false;
} else {
// see how much extra space this word will need to fit on current line
// (with a separating space character - also overlapped)
uint16 spaceNeeded = _joinWidth + wordWidth;
if (line[lineNo].width + spaceNeeded <= maxWidth) {
line[lineNo].width += spaceNeeded;
line[lineNo].length += 1 + wordLength; // NB. space+word characters
} else { // put word (without separating SPACE) at start of next line
lineNo++;
assert(lineNo < MAX_LINES);
line[lineNo].width = wordWidth;
line[lineNo].length = wordLength;
}
}
}
return lineNo + 1; // return no of lines
}
uint16 Text::copyChar(uint8 ch, uint8 *sprPtr, uint16 sprWidth, uint8 pen) {
if (ch < SPACE)
ch = 64;
FrameHeader *chFrame = _resMan->fetchFrame(_font, ch - SPACE);
uint8 *chData = ((uint8 *)chFrame) + sizeof(FrameHeader);
uint8 *dest = sprPtr;
uint8 *decBuf = NULL;
uint8 *decChr;
uint16 frameHeight = 0;
if (SwordEngine::isPsx()) {
frameHeight = _resMan->getUint16(chFrame->height) / 2;
if (_fontId == CZECH_GAME_FONT) { //Czech game fonts are compressed
decBuf = (uint8 *)malloc((_resMan->getUint16(chFrame->width)) * (_resMan->getUint16(chFrame->height) / 2));
Screen::decompressHIF(chData, decBuf);
decChr = decBuf;
} else //Normal game fonts are not compressed
decChr = chData;
} else {
frameHeight = _resMan->getUint16(chFrame->height);
decChr = chData;
}
for (uint16 cnty = 0; cnty < frameHeight; cnty++) {
for (uint16 cntx = 0; cntx < _resMan->getUint16(chFrame->width); cntx++) {
if (*decChr == LETTER_COL)
dest[cntx] = pen;
else if (((*decChr == BORDER_COL) || (*decChr == BORDER_COL_PSX)) && (!dest[cntx])) // don't do a border if there's already a color underneath (chars can overlap)
dest[cntx] = BORDER_COL;
decChr++;
}
dest += sprWidth;
}
free(decBuf);
return _resMan->getUint16(chFrame->width);
}
FrameHeader *Text::giveSpriteData(uint32 textTarget) {
// textTarget is the resource ID of the Compact linking the textdata.
// that's 0x950000 for slot 0 and 0x950001 for slot 1. easy, huh? :)
textTarget &= ITM_ID;
assert(textTarget < MAX_TEXT_OBS);
return _textBlocks[textTarget];
}
void Text::releaseText(uint32 id, bool updateCount) {
id &= ITM_ID;
assert(id < MAX_TEXT_OBS);
if (_textBlocks[id]) {
free(_textBlocks[id]);
_textBlocks[id] = NULL;
if (updateCount)
_textCount--;
}
}
void Text::printDebugLine(uint8 *ascii, uint8 first, int x, int y) {
FrameHeader *head;
int chr;
do {
chr = (int)*(ascii);
chr -= first;
head = (FrameHeader *)_resMan->fetchFrame(_font, chr);
uint8 *sprData = (uint8 *)head + sizeof(FrameHeader);
// The original drawSprite routine also clipped the sprite, so
// let's do that as well in order to produce an accurate result...
uint16 newCoordsX = x;
uint16 newCoordsY = y;
uint16 newWidth = _resMan->getUint16(head->width);
uint16 newHeight = SwordEngine::isPsx() ?
_resMan->getUint16(head->height) / 2 : _resMan->getUint16(head->height);
uint16 incr;
_screen->spriteClipAndSet(&newCoordsX, &newCoordsY, &newWidth, &newHeight, &incr);
_screen->drawSprite(sprData + incr, newCoordsX, newCoordsY, newWidth, newHeight, newWidth);
x += _resMan->getUint16(head->width);
if (SwordEngine::isPsx()) {
x -= OVERLAP;
} else {
// The very first executable version didn't use any overlap (verified on UK disasm)
if (SwordEngine::_systemVars.realLanguage != Common::EN_ANY)
x -= DEBUG_OVERLAP;
}
ascii++;
} while (*ascii);
}
uint16 Text::wCharWidth(uint8 hi, uint8 lo) {
if (isKoreanChar(hi, lo)) {
return 20; // fixed width : 20
}
return charWidth(hi) + charWidth(lo);
}
uint16 Text::copyWChar(uint8 hi, uint8 lo, uint8 *sprPtr, uint16 sprWidth, uint8 pen) {
if (!isKoreanChar(hi, lo)) {
return copyChar(hi, sprPtr, sprWidth, pen) + copyChar(lo, sprPtr, sprWidth, pen);
}
uint16 frameWidth = 20;
uint16 frameHeight = 26;
FrameHeader *chFrame = _resMan->fetchFrame(_font, 0xFF - SPACE);
uint8 *dest = sprPtr;
uint8 *decChr = ((uint8 *)chFrame) + sizeof(FrameHeader) + chFrame->width * chFrame->height + ((hi - 0xB0) * 94 + (lo - 0xA1)) * frameWidth * frameHeight;
for (uint16 cnty = 0; cnty < frameHeight; cnty++) {
for (uint16 cntx = 0; cntx < frameWidth; cntx++) {
if (*decChr == LETTER_COL)
dest[cntx] = pen;
else if (((*decChr == BORDER_COL) || (*decChr == BORDER_COL_PSX)) && (!dest[cntx])) // don't do a border if there's already a color underneath (chars can overlap)
dest[cntx] = BORDER_COL;
decChr++;
}
dest += sprWidth;
}
return frameWidth;
}
bool Text::isKoreanChar(uint8 hi, uint8 lo) {
if (SwordEngine::_systemVars.realLanguage != Common::KO_KOR)
return false;
if (hi >= 0xB0 && hi <= 0xC8 && lo >= 0xA1 && lo <= 0xFE)
return true;
return false;
}
} // End of namespace Sword1

80
engines/sword1/text.h Normal file
View File

@@ -0,0 +1,80 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef SWORD1_TEXT_H
#define SWORD1_TEXT_H
#include "sword1/object.h"
#include "sword1/sworddefs.h"
namespace Sword1 {
#define MAX_TEXT_OBS 3
#define BORDER_COL 200
#define BORDER_COL_PSX 199
#define LETTER_COL 193
#define NO_COL 0 // sprite background - 0 for transparency
class ObjectMan;
class ResMan;
class Screen;
class Logic;
class SwordEngine;
struct LineInfo {
uint16 width; // width of line in pixels
uint16 length; // length of line in characters
};
class Text {
public:
Text(SwordEngine *vm, Logic *pLogic, ObjectMan *pObjMan, ResMan *pResMan, Screen *pScreen, bool czechVersion);
~Text();
FrameHeader *giveSpriteData(uint32 textTarget);
uint32 lowTextManager(uint8 *text, int32 width, uint8 pen);
void makeTextSprite(uint8 slot, const uint8 *text, uint16 maxWidth, uint8 pen);
void releaseText(uint32 id, bool updateCount = true);
void printDebugLine(uint8 *ascii, uint8 first, int x, int y);
private:
uint16 analyzeSentence(const uint8 *text, uint16 maxWidth, LineInfo *info);
uint16 charWidth(uint8 ch);
uint16 wCharWidth(uint8 hi, uint8 lo);
uint16 copyChar(uint8 ch, uint8 *sprPtr, uint16 sprWidth, uint8 pen);
uint16 copyWChar(uint8 hi, uint8 lo, uint8 *sprPtr, uint16 sprWidth, uint8 pen);
bool isKoreanChar(uint8 hi, uint8 lo);
uint8 *_font;
uint8 _textCount;
uint16 _charHeight, _joinWidth;
SwordEngine *_vm;
Logic *_logic;
ObjectMan *_objMan;
ResMan *_resMan;
Screen *_screen;
FrameHeader *_textBlocks[MAX_TEXT_OBS];
uint32 _fontId;
};
} // End of namespace Sword1
#endif //BSTEXT_H