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

1459
engines/agds/agds.cpp Normal file

File diff suppressed because it is too large Load Diff

365
engines/agds/agds.h Normal file
View File

@@ -0,0 +1,365 @@
/* 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 AGDS_H
#define AGDS_H
#include "agds/database.h"
#include "agds/dialog.h"
#include "agds/inventory.h"
#include "agds/mouseMap.h"
#include "agds/processExitCode.h"
#include "agds/resourceManager.h"
#include "agds/screen.h"
#include "agds/soundManager.h"
#include "agds/textLayout.h"
#include "common/array.h"
#include "common/hashmap.h"
#include "common/ptr.h"
#include "common/random.h"
#include "common/rect.h"
#include "common/scummsys.h"
#include "engines/advancedDetector.h"
#include "graphics/pixelformat.h"
#include "video/flic_decoder.h"
/**
* This is the namespace of the AGDS engine.
*
* Status of this engine: In Progress
*
* Games using this engine:
* - Black Mirror (Windows)
*/
namespace Graphics {
class Font;
class ManagedSurface;
} // namespace Graphics
namespace AGDS {
class Animation;
using AnimationPtr = Common::SharedPtr<Animation>;
class Character;
class Font;
class Object;
using ObjectPtr = Common::SharedPtr<Object>;
struct ObjectPatch;
using ObjectPatchPtr = Common::SharedPtr<ObjectPatch>;
struct Patch;
using PatchPtr = Common::SharedPtr<Patch>;
class Process;
using ProcessPtr = Common::SharedPtr<Process>;
struct Region;
using RegionPtr = Common::SharedPtr<Region>;
struct MouseRegion;
class MJPGPlayer;
class Screen;
class SystemVariable;
class Console;
class AGDSEngine : public Engine {
friend class Process;
using ProcessListType = Common::Array<ProcessPtr>;
static constexpr uint MaxProcesses = 100;
public:
struct Color {
uint8 r = 0;
uint8 g = 0;
uint8 b = 0;
void FromString(const Common::String &rgb);
Common::String ToString() const;
uint32 map(const Graphics::PixelFormat &format) const;
};
AGDSEngine(OSystem *syst, const ADGameDescription *gameDesc);
AGDSEngine(const AGDSEngine &) = delete;
~AGDSEngine();
Common::Error run() override;
void setGlobal(const Common::String &name, int value);
int getGlobal(const Common::String &name) const;
bool hasGlobal(const Common::String &name) const {
return _globals.contains(name);
}
private:
void addSystemVar(const Common::String &name, SystemVariable *var);
bool initGraphics(int w, int h);
bool load();
void runProcesses();
void tick();
void fadeAndReactivate();
public:
bool hasFeature(EngineFeature f) const override;
Common::Error loadGameState(int slot) override;
Common::Error saveGameState(int slot, const Common::String &desc, bool isAutosave = false) override;
bool canLoadGameStateCurrently(Common::U32String *msg) override { return true; }
bool canSaveGameStateCurrently(Common::U32String *msg) override { return false; }
ObjectPtr loadObject(const Common::String &name, const Common::String &prototype = Common::String(), bool allowInitialise = true);
ObjectPtr runObject(const Common::String &name, const Common::String &prototype = Common::String(), bool allowInitialise = true);
void runObject(const ObjectPtr &object);
void runProcess(const ObjectPtr &object, uint ip = 0);
ProcessPtr findProcess(const Common::String &name) const;
void reactivate(const Common::String &name, const Common::String &where, bool runNow = false);
bool hasActiveProcesses(const Common::String &name) const;
void runPendingReactivatedProcesses();
void resetCurrentScreen();
void loadScreen(const Common::String &name, ScreenLoadingType type, bool savePatch = true);
void loadNextScreen();
void saveScreenPatch();
RegionPtr loadRegion(const Common::String &name);
Common::String loadText(const Common::String &name);
int appendToSharedStorage(const Common::String &value);
const Common::String &getSharedStorage(int id) const;
bool active() const { return !_mjpgPlayer; }
void playFilm(Process &process, const Common::String &video, const Common::String &audio, const Common::String &subtitles);
void skipFilm();
ResourceManager &resourceManager() {
return _resourceManager;
}
SoundManager &soundManager() {
return _soundManager;
}
Inventory &inventory() {
return _inventory;
}
Dialog &dialog() {
return _dialog;
}
const ProcessListType &processes() const {
return _processes;
}
TextLayout &textLayout() {
return _textLayout;
}
Screen *getCurrentScreen() {
return _currentScreen.get();
}
Console *getConsole();
Common::String &getCurrentScreenName() {
return _currentScreenName;
}
ObjectPtr getCurrentScreenObject(const Common::String &name);
const Graphics::PixelFormat &pixelFormat() const {
return _pixelFormat;
}
Graphics::ManagedSurface *loadPicture(const Common::String &name);
Graphics::Surface *createSurface(int w, int h);
Graphics::ManagedSurface *convertToTransparent(Graphics::Surface *surface); // destroys surface!
int loadFromCache(const Common::String &name) const;
Graphics::ManagedSurface *loadFromCache(int id) const;
int saveToCache(const Common::String &name, Graphics::ManagedSurface *surface);
void loadFont(int id, const Common::String &name, int gw, int gh);
const Graphics::Font *getFont(int id) const;
AnimationPtr loadAnimation(const Common::String &name);
AnimationPtr loadMouseCursor(const Common::String &name);
AnimationPtr findAnimationByPhaseVar(const Common::String &phaseVar);
void loadCharacter(const Common::String &id, const Common::String &name, const Common::String &object);
Character *getCharacter(const Common::String &name) {
return _currentCharacterName == name ? _currentCharacter.get() : nullptr;
}
Character *currentCharacter() const {
return _currentCharacter.get();
}
Character *jokes() const {
return _jokes.get();
}
void loadDefaultMouseCursor(const Common::String &name) {
_defaultMouseCursorName = name;
_defaultMouseCursor = loadMouseCursor(name);
}
void changeMouseArea(int id, int enabled);
void enableUser(bool enabled) {
_userEnabled = enabled;
}
void enableSystemUser(bool enabled) {
_systemUserEnabled = enabled;
}
bool userEnabled() const {
return _userEnabled && _systemUserEnabled && !_mjpgPlayer;
}
void newGame();
void initSystemVariables();
SystemVariable *getSystemVariable(const Common::String &name);
void setNextScreenName(const Common::String &nextScreenName, ScreenLoadingType type);
void returnToPreviousScreen();
void tickInventory();
void playSoundSync(int syncSoundId) {
_syncSoundId = syncSoundId;
}
void setAmbientSoundId(int id) {
stopAmbientSound();
_ambientSoundId = id;
}
void tell(Process &process, const Common::String &region, Common::String text, Common::String sound, bool npc);
bool fastMode() const {
return _fastMode;
}
Common::Point mousePosition() const {
return _mouse;
}
void currentInventoryObject(const ObjectPtr &object);
void resetCurrentInventoryObject();
ObjectPtr popCurrentInventoryObject();
const ObjectPtr &currentInventoryObject() const {
return _currentInventoryObject;
}
bool showHints() const {
return _hintMode;
}
PatchPtr getPatch(const Common::String &screenName) const;
PatchPtr createPatch(const Common::String &screenName);
ObjectPatchPtr getObjectPatch(const Common::String &screenName) const;
ObjectPatchPtr createObjectPatch(const Common::String &screenName);
void shadowIntensity(int intensity) {
_shadowIntensity = intensity;
}
int getRandomNumber(int max);
void curtain(const Common::String &process, int screen, int sound, int music, bool updateGlobals);
bool activeCurtain() const {
return _curtainTimer >= 0;
}
bool v2() const;
int version() const;
private:
void stopAmbientSound();
void loadPatches(Common::SeekableReadStream &file, Database &db);
using PictureCacheType = Common::HashMap<int, Common::ScopedPtr<Graphics::ManagedSurface>>;
using PictureCacheLookup = Common::HashMap<Common::String, int, Common::IgnoreCase_Hash, Common::IgnoreCase_EqualTo>;
using SystemVariablesListType = Common::Array<Common::String>;
using SystemVariablesType = Common::HashMap<Common::String, SystemVariable *, Common::IgnoreCase_Hash, Common::IgnoreCase_EqualTo>;
using GlobalsType = Common::HashMap<Common::String, int, Common::IgnoreCase_Hash, Common::IgnoreCase_EqualTo>;
using FontsType = Common::HashMap<int, Common::ScopedPtr<Graphics::Font>>;
using PatchesType = Common::HashMap<Common::String, PatchPtr, Common::IgnoreCase_Hash, Common::IgnoreCase_EqualTo>;
using ObjectPatchesType = Common::HashMap<Common::String, ObjectPatchPtr, Common::IgnoreCase_Hash, Common::IgnoreCase_EqualTo>;
using PatchDatabase = Common::HashMap<Common::String, Common::Array<uint8>>;
const ADGameDescription *_gameDescription;
ResourceManager _resourceManager;
SoundManager _soundManager;
Database _data;
PictureCacheType _pictureCache;
PictureCacheLookup _pictureCacheLookup;
int _pictureCacheId;
FontsType _fonts;
ProcessListType _processes;
ProcessListType _pendingReactivatedProcesses;
PatchesType _patches;
ObjectPatchesType _objectPatches;
int _sharedStorageIndex;
Common::String _sharedStorage[10];
GlobalsType _globals;
SystemVariablesListType _systemVarList;
SystemVariablesType _systemVars;
Graphics::PixelFormat _pixelFormat;
Color _colorKey;
Color _minShadowColor;
Color _maxShadowColor;
int _shadowIntensity;
Common::ScopedPtr<MJPGPlayer> _mjpgPlayer;
uint32 _filmStarted;
Common::String _filmProcess;
Common::ScopedPtr<Screen> _currentScreen;
Common::String _currentScreenName;
Common::ScopedPtr<Character> _currentCharacter;
Common::ScopedPtr<Character> _jokes;
Common::String _currentCharacterName, _currentCharacterFilename, _currentCharacterObject;
Common::String _nextScreenName;
ScreenLoadingType _nextScreenType;
Common::String _defaultMouseCursorName;
AnimationPtr _defaultMouseCursor;
Common::Point _mouse;
bool _userEnabled;
bool _systemUserEnabled;
MouseMap _mouseMap;
Common::RandomSource _random;
Inventory _inventory;
Common::String _inventoryRegionName;
RegionPtr _inventoryRegion;
ObjectPtr _currentInventoryObject;
Dialog _dialog;
// Original engine use weird names for the vars, I keep them.
int _tellTextTimer;
TextLayout _textLayout;
int _syncSoundId;
int _ambientSoundId;
Common::String _curtainProcess;
int _curtainTimer;
int _curtainScreen;
bool _fastMode;
bool _hintMode;
};
} // End of namespace AGDS
#endif /* AGDS_AGDS_H */

290
engines/agds/animation.cpp Normal file
View File

@@ -0,0 +1,290 @@
/* 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 "agds/animation.h"
#include "agds/agds.h"
#include "agds/object.h"
#include "agds/resourceManager.h"
#include "common/debug.h"
#include "common/textconsole.h"
#include "graphics/managed_surface.h"
#include "video/flic_decoder.h"
namespace AGDS {
Animation::Animation(AGDSEngine *engine, const Common::String &name) : _engine(engine), _name(name), _flic(), _frame(), _scaledFrame(),
_frames(0), _loop(false), _cycles(1), _phaseVarControlled(false),
_phase(0), _paused(false), _speed(100), _z(0), _rotation(0),
_delay(0), _random(0), _scale(1), _onScreen(true),
_visibleHeight(0), _visibleCenter(0) {
}
Animation::~Animation() {
freeFrame();
}
void Animation::freeScaledFrame() {
if (_scaledFrame) {
_scaledFrame->free();
_scaledFrame.reset();
}
}
void Animation::freeFrame() {
freeScaledFrame();
if (_frame) {
_frame->free();
_frame.reset();
}
}
bool Animation::load(Common::SeekableReadStream *stream, const Common::String &fname) {
if (_phaseVarControlled) {
if (_phaseVar.empty()) {
warning("phase var controlled animation with no phase var");
_phaseVarControlled = false;
return false;
}
if (_loop) {
warning("phase var controller animation with loop, resetting");
_loop = false;
}
if (_cycles > 1) {
warning("phase var controller animation with cycles, resetting");
_cycles = 1;
}
if (_random) {
warning("phase var controller animation with random, resetting");
_random = false;
}
if (_delay > 0) {
warning("phase var controller animation with delay, resetting");
_delay = 0;
}
}
_flic.reset();
auto is_bmp = ResourceManager::IsBMP(*stream);
if (fname.hasSuffixIgnoreCase(".bmp") || is_bmp) {
_frame.reset(_engine->loadPicture(fname));
rescaleCurrentFrame();
_frames = 1;
return true;
}
Video::FlicDecoder *flic = new Video::FlicDecoder;
if (flic->loadStream(stream)) {
_frames = flic->getFrameCount();
_flic.reset(flic);
decodeNextFrame();
return true;
} else {
_frames = 0;
return false;
}
}
void Animation::scale(float scale) {
if (scale != _scale) {
debug("changing scale to %g", scale);
_scale = scale;
rescaleCurrentFrame();
}
}
void Animation::rescaleCurrentFrame() {
if (!_frame)
return;
freeScaledFrame();
if (_rotation != 0) {
Graphics::TransformStruct transform(_scale * 100, _scale * 100, 90 * _rotation, _frame->w / 2, _frame->h / 2, Graphics::TSpriteBlendMode::BLEND_NORMAL, Graphics::kDefaultRgbaMod);
_scaledFrame.reset(_frame->rotoscale(transform));
} else if (_scale != 1) {
_scaledFrame.reset(_frame->scale(_frame->w * _scale, _frame->h * _scale, true));
}
auto *frame = _scaledFrame ? _scaledFrame.get() : _frame.get();
if (frame) {
uint h = frame->h, w = frame->w;
_visibleHeight = 0;
uint minX = w, maxX = 0;
for (uint i = 0; i != h; ++i) {
uint y = h - 1 - i;
auto *ptr = static_cast<uint32 *>(frame->getBasePtr(0, y));
for (uint x = 0; x != w; ++x) {
uint8 a, r, g, b;
frame->format.colorToARGB(*ptr++, a, r, g, b);
if (a != 0) {
if (h - i > _visibleHeight)
_visibleHeight = h - i;
if (x > maxX)
maxX = x;
else if (x < minX)
minX = x;
}
}
}
_visibleCenter = minX < maxX ? (maxX + minX) / 2 : 0;
}
}
void Animation::decodeNextFrame() {
if (ended()) {
_phase = 0;
_flic->rewind();
}
auto frame = _flic->decodeNextFrame();
if (!frame) {
debug("frame of %s couldn't be decoded, process: %s, phase var: %s, at end: %d", _name.c_str(), _process.c_str(), _phaseVar.c_str(), _flic->endOfVideo());
warning("frame of %s couldn't be decoded, process: %s, phase var: %s, at end: %d", _name.c_str(), _process.c_str(), _phaseVar.c_str(), _flic->endOfVideo());
return;
}
freeFrame();
_delay = _flic->getCurFrameDelay() * _speed / 4000; // 40 == 1000 / 25, 25 fps
_frame.reset(_engine->convertToTransparent(frame->convertTo(_engine->pixelFormat(), _flic->getPalette())));
rescaleCurrentFrame();
++_phase;
}
void Animation::rewind() {
_phase = 0;
_flic->rewind();
}
void Animation::onScreen(bool onScreen) {
_onScreen = onScreen;
}
bool Animation::tick() {
if (!_flic && _frame) { // static frame
return true;
}
if (_paused || !_frame || (_phaseVarControlled && !_onScreen)) {
return true;
}
if (_phaseVarControlled && _engine->getGlobal(_phaseVar) == -2) {
debug("phase var %s signalled deleting of animation", _phaseVar.c_str());
freeFrame();
return false;
}
if (_delay > 0) {
--_delay;
return true;
}
bool eov = _phase >= _frames;
if (!_phaseVar.empty() && eov) {
_engine->setGlobal(_phaseVar, -1);
onScreen(false);
return true;
}
if (!eov) {
decodeNextFrame();
onScreen(true);
}
if (!_process.empty()) {
if (!_phaseVar.empty())
_engine->setGlobal(_phaseVar, _phase - 1);
if (/*_phase || */ _phaseVarControlled) {
if (eov && _random) {
rewind();
_delay = _engine->getRandomNumber(_random);
return true;
}
return true;
}
}
if (eov && !_loop && --_cycles <= 0) {
if (!_phaseVar.empty()) {
_engine->setGlobal(_phaseVar, _phase - 1);
} else {
_engine->reactivate(_process, "animation end", true);
_engine->runPendingReactivatedProcesses();
}
return false;
}
if (eov && frames() > 1)
rewind();
return true;
}
void Animation::paint(Graphics::Surface &backbuffer, Common::Point dst, Graphics::ManagedSurface *mask, int maskAlpha) const {
dst += _position;
auto *frame = _scaledFrame ? _scaledFrame.get() : _frame.get();
if (!frame || !_onScreen)
return;
Common::Rect srcRect = frame->getBounds();
if (!Common::Rect::getBlitRect(dst, srcRect, backbuffer.getRect()))
return;
if (mask) {
int invMaskAlpha = 255 - maskAlpha;
auto subFrame = frame->getSubArea(srcRect);
auto subMask = mask->getSubArea(srcRect);
byte *dstPixels = static_cast<byte *>(backbuffer.getBasePtr(dst.x, dst.y));
const byte *srcPixels = static_cast<byte *>(subFrame.getPixels());
const byte *maskPixels = static_cast<byte *>(subMask.getPixels());
for (int y = 0; y != srcRect.height(); ++y) {
byte *dstRow = dstPixels;
const byte *srcRow = srcPixels;
const byte *maskRow = maskPixels;
for (int x = 0; x != srcRect.width(); ++x) {
uint8 srcA = *srcRow++;
++dstRow;
++maskRow;
uint8 invSrcA = 255 - srcA;
if (srcA != 0) {
for (int n = 3; n--; ++dstRow) {
*dstRow = ((*dstRow * invSrcA + *srcRow++ * srcA) * invMaskAlpha + *maskRow++ * maskAlpha * srcA) / 65025;
}
} else {
srcRow += 3;
dstRow += 3;
maskRow += 3;
}
}
dstPixels += backbuffer.pitch;
srcPixels += subFrame.pitch;
maskPixels += subMask.pitch;
}
} else
frame->blendBlitTo(backbuffer, dst.x, dst.y, Graphics::FLIP_NONE, &srcRect);
}
uint Animation::width() const {
return _flic ? _flic->getWidth() * _scale : 0;
}
uint Animation::height() const {
return _flic ? _flic->getHeight() * _scale : 0;
}
} // namespace AGDS

201
engines/agds/animation.h Normal file
View File

@@ -0,0 +1,201 @@
/* 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 ANIMATION_H
#define ANIMATION_H
#include "common/ptr.h"
#include "common/rect.h"
#include "common/scummsys.h"
#include "common/str.h"
namespace Common {
class SeekableReadStream;
}
namespace Graphics {
struct Surface;
class ManagedSurface;
} // namespace Graphics
namespace Video {
class FlicDecoder;
}
namespace AGDS {
class AGDSEngine;
class Object;
class Animation {
using FlicPtr = Common::ScopedPtr<Video::FlicDecoder>;
using ManagedSurfacePtr = Common::ScopedPtr<Graphics::ManagedSurface>;
AGDSEngine *_engine;
Common::String _name;
FlicPtr _flic;
ManagedSurfacePtr _frame;
ManagedSurfacePtr _scaledFrame;
int _frames;
Common::Point _position;
Common::String _process;
Common::String _phaseVar;
bool _loop;
int _cycles;
bool _phaseVarControlled;
int _phase;
bool _paused;
int _speed;
int _z;
int _rotation;
int _delay;
int _random;
float _scale;
bool _onScreen;
uint _visibleHeight;
uint _visibleCenter;
public:
Animation(AGDSEngine *engine, const Common::String &name);
~Animation();
bool hasFrame() const {
return _frame != nullptr;
}
int frames() const {
return _frames;
}
bool ended() const {
return _phase >= _frames;
}
const Common::Point &position() const {
return _position;
}
void position(Common::Point position) {
_position = position;
}
const Common::String &phaseVar() const {
return _phaseVar;
}
void phaseVar(const Common::String &phaseVar) {
_phaseVar = phaseVar;
}
const Common::String &process() const {
return _process;
}
void process(const Common::String &process) {
_process = process;
}
void loop(bool loop) {
_loop = loop;
}
void cycles(int cycles) {
_cycles = cycles;
}
void delay(int delay) {
_delay = delay;
}
void setRandom(int value) { // can't declare random() because of stupid macro
_random = value;
}
void phaseVarControlled(bool controlled) {
_phaseVarControlled = controlled;
_onScreen = !controlled;
}
bool paused() const {
return _paused;
}
void pause() {
_paused = true;
}
void resume() {
_paused = false;
}
void rewind();
void speed(int speed) {
_speed = speed;
}
void z(int z) {
_z = z;
}
int z() const {
return _z;
}
void rotate(int rotation) {
if (_rotation == rotation)
return;
_rotation = rotation;
rescaleCurrentFrame();
}
void scale(float scale);
float scale() const {
return _scale;
}
int phase() const {
return _phase;
}
void onScreen(bool onScreen);
bool load(Common::SeekableReadStream *stream, const Common::String &fname);
void paint(Graphics::Surface &backbuffer, Common::Point dst, Graphics::ManagedSurface *mask = nullptr, int maskAlpha = 0) const;
uint width() const;
uint height() const;
uint visibleHeight() const {
return _visibleHeight;
}
uint visibleCenter() const {
return _visibleCenter;
}
bool tick();
void decodeNextFrame();
private:
void rescaleCurrentFrame();
void freeFrame();
void freeScaledFrame();
};
} // End of namespace AGDS
#endif /* AGDS_ANIMATION_H */

439
engines/agds/character.cpp Normal file
View File

@@ -0,0 +1,439 @@
/* 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 "agds/character.h"
#include "agds/agds.h"
#include "agds/animation.h"
#include "agds/object.h"
#include "agds/region.h"
#include "agds/resourceManager.h"
#include "common/array.h"
#include "common/debug.h"
#include "common/stream.h"
#include "common/system.h"
#include "common/textconsole.h"
#include "common/util.h"
#include "graphics/managed_surface.h"
namespace AGDS {
Character::Character(AGDSEngine *engine, const Common::String &name) : _engine(engine), _object(), _animation(nullptr), _jokes(false),
_name(name),
_enabled(true), _visible(false), _stopped(false), _shown(false),
_phase(-1), _frames(0), _direction(-1), _movementDirections(0) {
}
Character::~Character() {
}
void Character::load(Common::SeekableReadStream &stream) {
debug("loading character...");
stream.readUint32LE(); // unk
uint16 magic = stream.readUint16LE();
switch (magic) {
case 0xdead:
_movementDirections = 16;
break;
case 0x8888:
_movementDirections = 8;
break;
default:
error("invalid magic %04x", magic);
}
_animations.clear();
while (stream.pos() < stream.size()) {
uint size = stream.readUint32LE();
uint index = stream.readUint16LE();
debug("header size %u, index: %u", size, index);
uint16 frames = stream.readUint16LE();
uint16 format = stream.readUint16LE();
Common::String filename = readString(stream);
AnimationDescription animation;
animation.filename = filename;
debug("%s:%u: animation %s, frames: %d, format: %d", _name.c_str(), _animations.size(), animation.filename.c_str(), frames, format);
while (frames--) {
int x = stream.readSint16LE();
int y = stream.readSint16LE();
uint w = stream.readUint32LE();
uint h = stream.readUint32LE();
AnimationDescription::Frame frame = {x, y, w, h};
animation.frames.push_back(frame);
debug("frame %d, %d, %dx%d", x, y, w, h);
uint unk1 = stream.readUint32LE();
uint unk2 = stream.readUint32LE();
uint unk3 = stream.readUint32LE();
uint unk4 = stream.readUint32LE(); // GRP file offset?
uint unk5 = stream.readUint32LE();
uint unk6 = stream.readByte();
uint unk7 = stream.readUint32LE();
uint unk8 = stream.readUint32LE();
stream.readUint32LE(); // CDCDCDCD
uint unk9 = stream.readUint32LE();
uint unk10 = stream.readUint32LE();
stream.readUint32LE(); // CDCDCDCD
uint unk11 = stream.readByte();
stream.readUint32LE(); // CDCDCDCD
debug("unknown: %u %u %u 0x%08x - %u %u %u %u - %u %u %u",
unk1, unk2, unk3, unk4,
unk5, unk6, unk7, unk8,
unk9, unk10, unk11);
}
_animations[index] = animation;
}
}
void Character::associate(const Common::String &name) {
_object = _engine->loadObject(name);
_engine->runObject(_object);
}
void Character::visible(bool visible) {
if (visible) {
_shown = true;
}
_visible = visible;
}
void Character::loadState(Common::ReadStream &stream) {
int x = stream.readUint16LE();
int y = stream.readUint16LE();
int dir = stream.readSint16LE();
debug("character at %d, %d, dir: %d", x, y, dir);
position(Common::Point(x, y));
direction(dir);
_visible = stream.readUint16LE();
_enabled = stream.readUint16LE();
}
void Character::saveState(Common::WriteStream &stream) const {
stream.writeUint16LE(_pos.x);
stream.writeUint16LE(_pos.y);
stream.writeUint16LE(_direction);
stream.writeUint16LE(_visible);
stream.writeUint16LE(_enabled);
}
bool Character::direction(int dir) {
debug("setDirection %d", dir);
_direction = dir;
if (dir < 0)
return false;
_animationPos = Common::Point();
return animate(dir, 100, false);
}
void Character::notifyProcess(const Common::String &name) {
debug("%s:notifyProcess %s", _name.c_str(), name.c_str());
if (!_processName.empty())
_engine->reactivate(name, "Character::notifyProcess", false);
_processName = name;
}
bool Character::moveTo(const Common::String &processName, Common::Point dst, int dir) {
if (!_visible)
return false;
debug("character move %d,%d %d", dst.x, dst.y, dir);
notifyProcess(processName);
_pos = dst;
_shown = true;
bool r = direction(dir);
auto *screen = _engine->getCurrentScreen();
if (screen) {
auto objects = screen->find(dst);
for (auto &object : objects) {
auto region = object->getTrapRegion();
if (region && region->pointIn(dst)) {
debug("starting trap process");
_engine->runProcess(object, object->getTrapHandler());
}
}
}
return r;
}
void Character::pointTo(const Common::String &processName, Common::Point dst) {
debug("character point to stub %d,%d, process: %s", dst.x, dst.y, processName.c_str());
notifyProcess(processName);
if (!_processName.empty() && !_engine->activeCurtain()) {
_engine->reactivate(_processName, "Character::pointTo");
_processName.clear();
}
_shown = true;
}
bool Character::animate(int direction, int speed, bool jokes) {
if (direction == -1 || !_enabled)
return false;
if (_stopped) {
debug("character stopped, skipping");
_stopped = false;
return false;
}
auto character = jokes ? _engine->jokes() : this;
auto description = character->animationDescription(direction);
if (!description) {
warning("no %s animation %d", jokes ? "jokes" : "character", direction);
return false;
}
auto animation = _engine->loadAnimation(description->filename);
if (!animation) {
warning("no %s animation file %s", jokes ? "jokes" : "character", description->filename.c_str());
return false;
}
_description = description;
_shown = true;
_animation = animation;
_animation->speed(speed);
_animation->rewind();
_phase = 0;
_frames = _animation->frames();
_jokes = jokes;
if (jokes)
_jokesDirection = direction;
else
_direction = direction;
debug("character animation frames: %d, enabled: %d, visible: %d", _frames, _enabled, _visible);
return true;
}
bool Character::animate(Common::Point pos, int direction, int speed) {
debug("animate character: %d,%d %d %d", pos.x, pos.y, direction, speed);
auto ok = animate(direction, speed, true);
if (!ok)
return false;
_animationPos = pos;
return true;
}
void Character::stop() {
_stopped = true;
}
void Character::leave(const Common::String &processName) {
debug("character %s: leave, process: %s", _object->getName().c_str(), processName.c_str());
notifyProcess(processName);
}
void Character::tick(bool reactivate) {
if (!active())
return;
if (_animation) {
auto screen = _engine->getCurrentScreen();
auto scale = screen ? screen->getZScale(_pos.y) : 1;
_animation->scale(scale);
if (!_stopped && _phase >= 0 && _phase < _frames) {
_animation->tick();
_phase = _animation->phase();
if (_phase >= _frames) {
bool wasJokes = _jokes;
_jokes = false;
_phase = -1;
_frames = 0;
if (wasJokes)
direction(_direction);
}
return;
}
}
if (reactivate && !_processName.empty() && !_engine->activeCurtain()) {
_engine->reactivate(_processName, "Character::tick");
_processName.clear();
}
}
bool Character::pointIn(Common::Point pos) const {
if (!_animation)
return false;
Common::Rect rect(_animation->width(), _animation->height());
rect.moveTo(animationPosition());
return rect.contains(pos);
}
Common::Point Character::animationPosition() const {
Common::Point pos = _pos + _animationPos;
if (_animation) {
pos.y -= _animation->visibleHeight();
pos.x -= _animation->visibleCenter();
if (_description) {
auto &frames = _description->frames;
if (_phase >= 0 && _phase < static_cast<int>(frames.size())) {
auto &frame = frames[_phase];
pos.x += frame.x * _animation->scale();
pos.y += frame.y * _animation->scale();
}
}
}
return pos;
}
void Character::paint(Graphics::Surface &backbuffer, Common::Point pos) const {
if (!_enabled || !visible() || !_animation)
return;
pos += animationPosition();
int fogAlpha = 0;
if (_fog) {
auto z = this->z();
if (z >= _fogMinZ && z < _fogMaxZ) {
fogAlpha = 255 * (z - _fogMinZ) / (_fogMaxZ - _fogMinZ);
} else if (z >= _fogMaxZ) {
fogAlpha = 255;
}
}
_animation->paint(backbuffer, pos, _fog.get(), fogAlpha);
}
int Character::z() const {
int y = _pos.y + _animationPos.y;
// fixme: add temp var : _movePos?
// debug("char z = %d", y);
return g_system->getHeight() - y;
}
void Character::reset() {
_fog.reset();
_shown = false;
_animation.reset();
_phase = -1;
_frames = 0;
}
void Character::setFog(Graphics::ManagedSurface *surface, int minZ, int maxZ) {
_fog.reset(surface);
_fogMinZ = minZ;
_fogMaxZ = maxZ;
}
int Character::getDirectionForMovement(Common::Point delta) {
auto angle = atan2(delta.y, delta.x);
if (angle < 0)
angle += M_PI * 2;
if (_movementDirections == 16) {
if (angle < 6.1850053125 && angle > 0.0981746875) {
if (angle > 0.5235983333333333) {
if (angle > 0.9490219791666666) {
if (angle > 1.374445625) {
if (angle > 1.767144375) {
if (angle > 2.192568020833333) {
if (angle > 2.617991666666666) {
if (angle > 3.0434153125) {
if (angle > 3.2397646875) {
if (angle > 3.665188333333333) {
if (angle > 4.090611979166667) {
if (angle > 4.516035625) {
if (angle > 4.908734375) {
if (angle > 5.334158020833333) {
if (angle > 5.759581666666667)
return 3;
else
return 2;
} else {
return 1;
}
} else {
return 0;
}
} else {
return 15;
}
} else {
return 14;
}
} else {
return 13;
}
} else {
return 12;
}
} else {
return 11;
}
} else {
return 10;
}
} else {
return 9;
}
} else {
return 8;
}
} else {
return 7;
}
} else {
return 6;
}
} else {
return 5;
}
} else {
return 4;
}
} else if (angle < 5.89048125 && angle > 0.39269875) {
if (angle > 1.17809625) {
if (angle > 1.96349375) {
if (angle > 2.74889125) {
if (angle > 3.53428875) {
if (angle > 4.31968625) {
if (angle > 5.105083749999999)
return 2;
else
return 0;
} else {
return 14;
}
} else {
return 12;
}
} else {
return 10;
}
} else {
return 8;
}
} else {
return 6;
}
} else {
return 4;
}
}
} // namespace AGDS

165
engines/agds/character.h Normal file
View File

@@ -0,0 +1,165 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef CHARACTER_H
#define CHARACTER_H
#include "common/hashmap.h"
#include "common/ptr.h"
#include "common/rect.h"
#include "common/scummsys.h"
namespace Common {
class SeekableReadStream;
class ReadStream;
class WriteStream;
} // namespace Common
namespace Graphics {
struct Surface;
class ManagedSurface;
} // namespace Graphics
namespace AGDS {
class AGDSEngine;
class Object;
using ObjectPtr = Common::SharedPtr<Object>;
class Animation;
using AnimationPtr = Common::SharedPtr<Animation>;
class Character {
using FogPtr = Common::ScopedPtr<Graphics::ManagedSurface>;
AGDSEngine *_engine;
ObjectPtr _object;
AnimationPtr _animation;
FogPtr _fog;
bool _jokes;
Common::String _name;
Common::String _processName;
Common::Point _pos;
Common::Point _animationPos;
bool _enabled;
bool _visible;
bool _stopped;
bool _shown;
int _phase;
int _frames;
int _direction;
int _jokesDirection;
int _movementDirections;
int _fogMinZ, _fogMaxZ;
struct AnimationDescription {
struct Frame {
int x, y;
uint w, h;
};
Common::String filename;
Common::Array<Frame> frames;
};
Common::HashMap<uint, AnimationDescription> _animations;
const AnimationDescription *_description;
bool animate(int direction, int speed, bool jokes);
Common::Point animationPosition() const;
public:
Character(AGDSEngine *engine, const Common::String &name);
~Character();
void associate(const Common::String &name);
const AnimationDescription *animationDescription(uint index) const {
auto it = _animations.find(index);
return it != _animations.end() ? &it->_value : nullptr;
}
const Common::String &name() const {
return _name;
}
const ObjectPtr &object() const {
return _object;
}
void load(Common::SeekableReadStream &stream);
void loadState(Common::ReadStream &stream);
void saveState(Common::WriteStream &stream) const;
void enable(bool enabled = true) {
_enabled = enabled;
}
void visible(bool visible);
bool visible() const {
return _visible && _shown;
}
bool active() const {
return _enabled && _visible;
}
bool animate(Common::Point pos, int direction, int speed);
void stop();
void leave(const Common::String &processName);
int phase() const {
return _jokes ? _phase : -1;
}
void phase(int phase) {
_phase = phase;
}
void position(Common::Point pos) {
_pos = pos;
}
Common::Point position() const {
return _pos;
}
bool pointIn(Common::Point pos) const;
void notifyProcess(const Common::String &processName);
bool moveTo(const Common::String &processName, Common::Point dst, int direction);
void pointTo(const Common::String &processName, Common::Point dst);
bool direction(int dir);
int direction() const {
return _jokes ? _jokesDirection : _direction;
}
void tick(bool reactivate);
void paint(Graphics::Surface &backbuffer, Common::Point pos) const;
int getDirectionForMovement(Common::Point delta);
int z() const;
void reset();
void setFog(Graphics::ManagedSurface *surface, int minZ, int maxZ);
};
} // End of namespace AGDS
#endif /* AGDS_CHARACTER_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 agds "AGDS (Black Mirror and NiBiRu)" no "" "" "highres 16bit jpeg" ""

188
engines/agds/console.cpp Normal file
View File

@@ -0,0 +1,188 @@
/* 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 "agds/console.h"
#include "agds/agds.h"
#include "agds/animation.h"
#include "agds/object.h"
#include "agds/patch.h"
#include "agds/process.h"
#include "agds/screen.h"
namespace AGDS {
Console::Console(AGDSEngine *engine) : _engine(engine) {
registerCmd("activate", WRAP_METHOD(Console, activate));
registerCmd("info", WRAP_METHOD(Console, info));
registerCmd("load", WRAP_METHOD(Console, load));
registerCmd("run", WRAP_METHOD(Console, run));
registerCmd("stop", WRAP_METHOD(Console, stop));
registerCmd("set", WRAP_METHOD(Console, setGlobal));
registerCmd("invadd", WRAP_METHOD(Console, inventoryAdd));
registerCmd("patch", WRAP_METHOD(Console, patch));
}
bool Console::load(int argc, const char **argv) {
if (argc < 2) {
debugPrintf("usage: %s object_id\n", argv[0]);
return true;
}
ObjectPtr object = _engine->loadObject(argv[1]);
if (!object) {
debugPrintf("no object %s\n", argv[1]);
return true;
}
_engine->runObject(object);
detach();
return false;
}
bool Console::run(int argc, const char **argv) {
if (argc < 2) {
debugPrintf("usage: %s object_id\n", argv[0]);
return true;
}
ObjectPtr object = _engine->getCurrentScreenObject(argv[1]);
if (!object) {
debugPrintf("no object %s\n", argv[1]);
return true;
}
_engine->getCurrentScreen()->remove(object);
_engine->runObject(object);
detach();
return false;
}
bool Console::stop(int argc, const char **argv) {
if (argc < 2) {
debugPrintf("usage: %s object_id\n", argv[0]);
return true;
}
ObjectPtr object = _engine->getCurrentScreenObject(argv[1]);
if (!object) {
debugPrintf("no object %s\n", argv[1]);
return true;
}
_engine->getCurrentScreen()->remove(object);
auto process = _engine->findProcess(argv[1]);
if (!process) {
debugPrintf("no process %s\n", argv[1]);
return true;
}
process->done();
detach();
return false;
}
bool Console::activate(int argc, const char **argv) {
if (argc < 2) {
debugPrintf("usage: %s object_id\n", argv[0]);
return true;
}
_engine->reactivate(argv[1], "console");
detach();
return false;
}
bool Console::info(int argc, const char **argv) {
auto screen = _engine->getCurrentScreen();
if (screen) {
debugPrintf("screen %s:\n", screen->getName().c_str());
for (auto &object : screen->children()) {
auto pos = object->getPosition();
debugPrintf("object %s [alive: %d] at %d,%d\n", object->getName().c_str(), object->alive(), pos.x, pos.y);
}
for (auto &desc : screen->animations()) {
auto &animation = desc.animation;
auto pos = animation->position();
debugPrintf("animation %s (process: %s, %s) at %d,%d,%d, frame: %d\n",
animation->phaseVar().c_str(), animation->process().c_str(), animation->paused() ? "paused" : "running",
pos.x, pos.y, animation->z(), animation->phase());
}
}
debugPrintf("processes:\n");
auto &processes = _engine->processes();
for (auto &process : processes) {
if (process)
debugPrintf("%s\n", process->getName().c_str());
}
return true;
}
bool Console::setGlobal(int argc, const char **argv) {
if (argc < 3) {
debugPrintf("usage: %s var value\n", argv[0]);
return true;
}
int value;
if (sscanf(argv[2], "%d", &value) != 1) {
debugPrintf("invalid value\n");
return true;
}
_engine->setGlobal(argv[1], value);
return true;
}
bool Console::inventoryAdd(int argc, const char **argv) {
if (argc < 2) {
debugPrintf("usage: %s inv.object\n", argv[0]);
return true;
}
int idx = _engine->inventory().add(argv[1]);
debugPrintf("add = %d\n", idx);
detach();
return true;
}
bool Console::patch(int argc, const char **argv) {
if (argc != 2 && argc != 4) {
debugPrintf("usage: %s screen [object flag]\n", argv[0]);
return true;
}
if (argc == 2) {
auto patch = _engine->getPatch(argv[1]);
if (!patch) {
debugPrintf("no patch for %s found.", argv[1]);
return true;
}
debugPrintf("screen saved: %d\n", patch->screenSaved);
debugPrintf("screen region: %s\n", patch->screenRegionName.c_str());
debugPrintf("previous screen: %s\n", patch->prevScreenName.c_str());
debugPrintf("screen loading type: %d\n", static_cast<int>(patch->loadingType));
debugPrintf("character pos: %d,%d, direction: %d, present: %d\n", patch->characterPosition.x, patch->characterPosition.y, patch->characterDirection, patch->characterPresent);
debugPrintf("mouse cursor: %s\n", patch->defaultMouseCursor.c_str());
for (auto &object : patch->objects) {
debugPrintf(" - object %s: present: %d\n", object.name.c_str(), object.flag);
}
} else if (argc == 4) {
auto patch = _engine->getPatch(argv[1]);
if (!patch)
debugPrintf("no patch found, creating...\n");
patch = _engine->createPatch(argv[1]);
patch->setFlag(argv[2], atoi(argv[3]));
}
detach();
return true;
}
} // namespace AGDS

52
engines/agds/console.h Normal file
View File

@@ -0,0 +1,52 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef CONSOLE_H
#define CONSOLE_H
#include "gui/debugger.h"
namespace AGDS {
class AGDSEngine;
class Console : public GUI::Debugger {
public:
Console(AGDSEngine *engine);
using GUI::Debugger::clearVars;
using GUI::Debugger::registerVar;
private:
bool load(int argc, const char **argv);
bool run(int argc, const char **argv);
bool stop(int argc, const char **argv);
bool activate(int argc, const char **argv);
bool info(int argc, const char **argv);
bool setGlobal(int argc, const char **argv);
bool inventoryAdd(int argc, const char **argv);
bool patch(int argc, const char **argv);
AGDSEngine *_engine;
};
} // namespace AGDS
#endif

3
engines/agds/credits.pl Normal file
View File

@@ -0,0 +1,3 @@
begin_section("AGDS");
add_person("Vladimir Menshakov", "whoozle", "");
end_section();

137
engines/agds/database.cpp Normal file
View File

@@ -0,0 +1,137 @@
/* 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 "agds/database.h"
#include "common/algorithm.h"
#include "common/debug.h"
#include "common/file.h"
namespace AGDS {
bool Database::open(const Common::String &filename) {
Common::File file;
if (!file.open(Common::Path{filename}))
return false;
return open(filename, file);
}
namespace {
static const uint32 kMagic = 666;
static const uint32 kHeaderFieldSize = 0x09;
static const uint32 kHeaderSize = 0x14;
static const uint32 kDefaultNameSize = 0x1f;
} // namespace
uint32 Database::getDataOffset(uint32 maxNameSize, uint32 totalEntries) {
return kHeaderSize + (maxNameSize + kHeaderFieldSize) * totalEntries;
}
bool Database::open(const Common::String &filename, Common::SeekableReadStream &stream) {
_filename = filename;
uint32 magic = stream.readUint32LE();
if (magic != kMagic) {
debug("invalid magic for database %s", _filename.c_str());
return false;
}
_writeable = stream.readUint32LE();
_totalEntries = stream.readUint32LE();
_usedEntries = stream.readUint32LE();
_maxNameSize = stream.readUint32LE();
if (_maxNameSize == 0) {
debug("invalid max name record size");
return false;
}
uint32 dataOffset = getDataOffset(_maxNameSize, _totalEntries);
Common::Array<char> nameBuffer(_maxNameSize + 1);
for (uint32 i = 0; i < _usedEntries; ++i) {
uint32 offset = stream.readUint32LE();
stream.read(nameBuffer.data(), nameBuffer.size());
char *z = Common::find(nameBuffer.begin(), nameBuffer.end(), 0);
Common::String name(nameBuffer.data(), z - nameBuffer.begin());
uint32 size = stream.readUint32LE();
// debug("adb entry: %s, offset %08x, size: %u", name.c_str(), offset, size);
_entries.setVal(name, Entry(dataOffset + offset, size));
}
return true;
}
void Database::write(Common::WriteStream &stream, const Common::HashMap<Common::String, Common::Array<uint8>> &entries) {
auto n = entries.size();
stream.writeUint32LE(kMagic);
stream.writeUint32LE(1);
stream.writeUint32LE(n);
stream.writeUint32LE(n);
stream.writeUint32LE(kDefaultNameSize);
auto dataOffset = getDataOffset(kDefaultNameSize, n);
debug("database data offset: 0x%06x", dataOffset);
uint offset = 0;
for (auto &entry : entries) {
auto &key = entry._key;
auto &value = entry._value;
stream.writeUint32LE(offset);
Common::Array<char> text(kDefaultNameSize + 1);
strncpy(text.data(), key.c_str(), kDefaultNameSize);
stream.write(text.data(), text.size());
debug("database entry %s: 0x%06x", key.c_str(), offset);
offset += value.size();
stream.writeUint32LE(value.size());
}
for (auto &entry : entries) {
auto &value = entry._value;
stream.write(value.data(), value.size());
}
}
Common::Array<Common::String> Database::getEntries() const {
Common::Array<Common::String> names;
for (EntriesType::const_iterator i = _entries.begin(); i != _entries.end(); ++i) {
names.push_back(i->_key);
}
return names;
}
Common::SeekableReadStream *Database::getEntry(const Common::String &name) const {
Common::File file;
if (!file.open(Common::Path{_filename})) {
error("could not open database file %s", _filename.c_str()); // previously available, but now disappeared or no fd, error
return NULL;
}
return getEntry(file, name);
}
Common::SeekableReadStream *Database::getEntry(Common::SeekableReadStream &parent, const Common::String &name) const {
EntriesType::const_iterator i = _entries.find(name);
if (i == _entries.end())
return NULL;
const Entry &entry = i->_value;
parent.seek(entry.offset);
return parent.readStream(entry.size);
}
} // namespace AGDS

69
engines/agds/database.h Normal file
View File

@@ -0,0 +1,69 @@
/* 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 DATABASE_H
#define DATABASE_H
#include "common/hash-str.h"
#include "common/hashmap.h"
#include "common/scummsys.h"
#include "common/str.h"
#include "common/stream.h"
namespace AGDS {
class Database {
private:
struct Entry {
uint32 offset;
uint32 size;
Entry() : offset(), size() {}
Entry(uint32 o, uint32 s) : offset(o), size(s) {}
};
using EntriesType = Common::HashMap<Common::String, Entry>;
Common::String _filename;
bool _writeable;
uint32 _totalEntries;
uint32 _usedEntries;
uint32 _maxNameSize;
EntriesType _entries;
private:
static uint32 getDataOffset(uint32 maxNameSize, uint32 totalEntries);
public:
bool open(const Common::String &filename);
bool open(const Common::String &filename, Common::SeekableReadStream &stream);
Common::Array<Common::String> getEntries() const;
Common::SeekableReadStream *getEntry(const Common::String &name) const;
Common::SeekableReadStream *getEntry(Common::SeekableReadStream &parent, const Common::String &name) const;
static void write(Common::WriteStream &stream, const Common::HashMap<Common::String, Common::Array<uint8>> &entries);
};
} // End of namespace AGDS
#endif /* AGDS_DATABASE_H */

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/>.
*
*/
#include "engines/advancedDetector.h"
static const PlainGameDescriptor agdsGames[] = {
{"nibiru", "NiBiRu: Age of Secrets"},
{"black-mirror", "Black Mirror"},
{0, 0}};
#include "agds/detection_tables.h"
class AGDSMetaEngineDetection : public AdvancedMetaEngineDetection<ADGameDescription> {
public:
AGDSMetaEngineDetection() : AdvancedMetaEngineDetection(AGDS::gameDescriptions, agdsGames) {
_maxScanDepth = 1;
}
const char *getName() const override {
return "agds";
}
const char *getEngineName() const override {
return "AGDS Engine";
}
const char *getOriginalCopyright() const override {
return "AGDS (C) Future Games";
}
};
REGISTER_PLUGIN_STATIC(AGDS_DETECTION, PLUGIN_TYPE_ENGINE_DETECTION, AGDSMetaEngineDetection);

33
engines/agds/detection.h Normal file
View File

@@ -0,0 +1,33 @@
/* 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 DETECTION_H
#define DETECTION_H
namespace AGDS {
enum AGDSGameFlag {
AGDS_V2 = (1 << 0),
};
} // End of namespace AGDS
#endif // DETECTION_H

View File

@@ -0,0 +1,100 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "engines/advancedDetector.h"
#include "engines/agds/detection.h"
namespace AGDS {
static const ADGameDescription gameDescriptions[] = {
// Black Mirror
{
"black-mirror",
0,
AD_ENTRY2s(
"gfx1.grp", "6665ce103cf12a362fd55f863d1ec9e6", 907820240,
"data.adb", "3b2d85f16e24ac81c4ede7a1da55f786", 2169482),
Common::EN_USA,
Common::kPlatformWindows,
ADGF_DROPPLATFORM,
GUIO1(GUIO_NONE)},
{"black-mirror",
"CD version protected with StarForce",
AD_ENTRY2s(
"gfx1.grp", "6665ce103cf12a362fd55f863d1ec9e6", 907820240,
"data.adb", "98d6a1e673e1d3f21be8c89fff25bc4a", 2169482),
Common::EN_USA,
Common::kPlatformWindows,
ADGF_DROPPLATFORM | ADGF_CD | ADGF_UNSUPPORTED,
GUIO1(GUIO_NONE)},
{"black-mirror",
0,
AD_ENTRY2s(
"gfx1.grp", "a5f84365d1e15a8403237fa3ad339f86", 850840170,
"data.adb", "5f98eabecf94569be7046d3813edc49a", 2169111),
Common::EN_GRB,
Common::kPlatformWindows,
ADGF_DROPPLATFORM | ADGF_CD,
GUIO1(GUIO_NONE)},
{"black-mirror",
"Demo",
AD_ENTRY2s(
"gfx_en.grp", "7539f35f9242461114c8746b62f884a6", 67516239,
"data.adb", "56680d118afddf9477c57e508d118dc8", 114764),
Common::EN_ANY,
Common::kPlatformWindows,
ADGF_DROPPLATFORM | ADGF_DEMO | ADGF_UNSTABLE,
GUIO1(GUIO_NONE)},
{"black-mirror",
0,
AD_ENTRY2s(
"gfx1.grp", "652f931f02c5a79fb9bcbe32abafbdf7", 907732355,
"data.adb", "d8706b17fb89d58d4dba094a73e5490a", 2152794),
Common::RU_RUS,
Common::kPlatformWindows,
ADGF_DROPPLATFORM,
GUIO1(GUIO_NONE)},
// NiBiRu
{
"nibiru",
0,
AD_ENTRY2s(
"gfx1.grp", "40a7a88f77c35305b6aba0329ed8a9ac", 381768750,
"data.adb", "40a7a88f77c35305b6aba0329ed8a9ac", 1391440),
Common::EN_USA,
Common::kPlatformWindows,
ADGF_DROPPLATFORM | AGDS_V2 | ADGF_UNSTABLE,
GUIO1(GUIO_NONE)},
{"nibiru",
0,
AD_ENTRY2s(
"gfx1.grp", "c8e711bc01b16cd82849cbd996d02642", 381768360,
"data.adb", "40a7a88f77c35305b6aba0329ed8a9ac", 1391440),
Common::RU_RUS,
Common::kPlatformWindows,
ADGF_DROPPLATFORM | AGDS_V2 | ADGF_UNSTABLE,
GUIO1(GUIO_NONE)},
AD_TABLE_END_MARKER};
} // End of namespace AGDS

251
engines/agds/dialog.cpp Normal file
View File

@@ -0,0 +1,251 @@
/* 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 "agds/dialog.h"
#include "agds/agds.h"
#include "agds/object.h"
#include "agds/systemVariable.h"
#include "common/debug.h"
namespace AGDS {
void Dialog::parseDialogDefs(const Common::String &defs) {
_dialogDefs.clear();
Common::String name, value;
bool readName = true;
for (uint32 p = 0, size = defs.size(); p < size; ++p) {
char ch = defs[p];
if (ch == ' ') {
continue;
} else if (ch == '\n' || ch == '\r' || p + 1 == size) {
if (p + 1 == size)
value += ch;
// debug("dialog definition: '%s' = '%s'", name.c_str(), value.c_str());
if (!name.empty() && !value.empty()) {
_dialogDefs[name] = atoi(value.c_str());
}
readName = true;
name.clear();
value.clear();
continue;
} else if (ch == '=') {
if (readName) {
readName = false;
} else {
warning("equal sign in value, skipping");
}
} else {
if (readName)
name += ch;
else
value += ch;
}
}
}
void Dialog::load(const Common::String &processName, const Common::String &dialogScript, const Common::String &defs) {
_currentDef.clear();
_sounds.clear();
parseDialogDefs(defs);
_dialogProcessName = processName;
_dialogScript = dialogScript;
_dialogScriptPos = 0;
_engine->getSystemVariable("dialog_var")->setInteger(-1);
}
int Dialog::textDelay(const Common::String &str) {
int speed = _engine->getSystemVariable("text_speed")->getInteger();
if (speed < 0)
speed = 0;
else if (speed > 100)
speed = 100;
speed /= 10;
int delay = str.size();
if (delay < 20)
delay = 20;
// this looks like an error in original code
// original first value in this table is 0x0FFFFFEB4 (-332)
// 0x0FFFFFEB4 * 20 and then 16-to-32 conversion gives 0xffffe610
// 0xe610 / 20 = 2944
static const int delays[] = {
2944, 631, 398, 251, 158,
100, 63, 40, 25, 16, 10};
return delays[speed] * delay / 41 + 1;
}
bool Dialog::tick() {
if (_dialogProcessName.empty())
return false;
auto dialog_var = _engine->getSystemVariable("dialog_var");
int dialog_var_value = dialog_var->getInteger();
if (dialog_var_value != 0) {
return false;
}
uint n = _dialogScript.size();
if (_dialogScriptPos >= n) {
if (!_dialogProcessName.empty()) {
debug("end of dialog, running %s", _dialogProcessName.c_str());
dialog_var->setInteger(-2);
_engine->reactivate(_dialogProcessName, "end of dialog");
}
return false;
}
Common::String &line = _dialogLine;
line.clear();
bool command = _dialogScript[_dialogScriptPos] == '@';
while (_dialogScriptPos < n) {
if (!command && _dialogScript[_dialogScriptPos] == '@')
break;
while (_dialogScriptPos < n && _dialogScript[_dialogScriptPos] != '\n' && _dialogScript[_dialogScriptPos] != '\r') {
line += _dialogScript[_dialogScriptPos++];
}
if (!command)
line += '\n';
while (_dialogScriptPos < n && (_dialogScript[_dialogScriptPos] == '\n' || _dialogScript[_dialogScriptPos] == '\r'))
++_dialogScriptPos;
if (command)
break;
}
if (line.empty())
return true;
debug("dialog line: %s", line.c_str());
if (line[0] == '@') {
processDirective(line);
} else {
debug("text: %s", line.c_str() + 1);
dialog_var->setInteger(-3);
}
return true;
}
void Dialog::processSoundDirective(const Common::String &line) {
debug("sound: %s", line.c_str());
auto arg1 = line.find('(');
if (arg1 == line.npos) {
warning("invalid sound directive");
return;
}
++arg1;
auto comma1 = line.find(',', arg1);
if (comma1 == line.npos) {
warning("invalid sound directive, missing arg2");
return;
}
++comma1;
auto comma2 = line.find(',', comma1);
if (comma2 == line.npos) {
warning("invalid sound directive, missing arg3");
return;
}
auto end = line.find(')', comma2 + 1);
if (end == line.npos) {
warning("invalid sound directive");
return;
}
--end;
Common::String name = line.substr(arg1, comma1 - arg1 - 1);
while (line[comma1] == ' ')
++comma1;
Common::String sample = line.substr(comma1, comma2 - comma1);
while (line[comma2] == ' ')
++comma2;
Common::String step = line.substr(comma2 + 1, end - comma2);
debug("sound args = %s,%s,%s", name.c_str(), sample.c_str(), step.c_str());
_sounds.push_back(Sound(name, sample, atoi(step.c_str())));
}
void Dialog::processDirective(Common::String line) {
if (line.size() < 2 || line[1] == '@') {
debug("comment, bailing out");
return;
}
line.erase(0, 1);
if (line.hasPrefix("sound")) {
processSoundDirective(line);
} else {
DialogDefsType::const_iterator it = _dialogDefs.find(line);
if (it != _dialogDefs.end()) {
int value = it->_value;
_currentDef = line;
if (!_currentDef.hasPrefix("vybervarianty") && !_currentDef.hasPrefix("varianta")) {
_currentSoundIndex = -1;
for (uint s = 0; s < _sounds.size(); ++s) {
auto &sound = _sounds[s];
if (_currentDef.hasPrefixIgnoreCase(sound.Name)) {
_currentSoundIndex = s;
break;
}
}
}
debug("dialog value %s = %d (0x%04x), sample index: %d", line.c_str(), value, value, _currentSoundIndex);
_engine->getSystemVariable("dialog_var")->setInteger(value);
} else
warning("invalid dialog directive: %s", line.c_str());
}
}
Common::String Dialog::getNextDialogSound() {
if (_currentDef.empty())
return Common::String();
debug("getNextDialogSound %s %d", _currentDef.c_str(), _currentSoundIndex);
if (_currentSoundIndex < 0)
return Common::String();
auto &sound = _sounds[_currentSoundIndex];
auto &sample = sound.Sample;
auto currentSample = sample;
int carry = sound.Step;
for (auto pos = sample.size() - 1; pos > 0 && sample[pos] >= '0' && sample[pos] <= '9'; --pos) {
int d = sample[pos] - '0';
d += carry;
carry = d / 10;
sample.setChar('0' + (d % 10), pos);
if (carry == 0)
break;
}
if (carry != 0)
warning("sample index overflow, %s", sample.c_str());
debug("returning sample name %s", currentSample.c_str());
return currentSample + ".ogg";
}
} // namespace AGDS

81
engines/agds/dialog.h Normal file
View File

@@ -0,0 +1,81 @@
/* 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 DIALOG_H
#define DIALOG_H
#include "common/hash-str.h"
#include "common/hashmap.h"
#include "common/scummsys.h"
#include "common/str.h"
namespace AGDS {
class AGDSEngine;
class Dialog {
using DialogDefsType = Common::HashMap<Common::String, int, Common::IgnoreCase_Hash, Common::IgnoreCase_EqualTo>;
private:
struct Sound {
Common::String Name;
Common::String Sample;
int Step;
Sound() : Step(1) {
}
Sound(const Common::String &name, const Common::String &sample, int step) : Name(name), Sample(sample), Step(step) {
}
};
using SoundsType = Common::Array<Sound>;
AGDSEngine *_engine;
DialogDefsType _dialogDefs;
Common::String _dialogScript;
uint32 _dialogScriptPos;
Common::String _dialogProcessName;
Common::String _dialogLine;
SoundsType _sounds;
Common::String _currentDef;
int _currentSoundIndex;
void parseDialogDefs(const Common::String &defs);
public:
Dialog(AGDSEngine *engine) : _engine(engine), _dialogScriptPos(0), _currentSoundIndex(-1) {}
int textDelay(const Common::String &str);
const Common::String &getNextDialogLine() const {
return _dialogLine;
}
Common::String getNextDialogSound();
void load(const Common::String &processName, const Common::String &dialogScript, const Common::String &defs);
bool tick();
private:
void processSoundDirective(const Common::String &line);
void processDirective(Common::String line);
};
} // End of namespace AGDS
#endif /* AGDS_DIALOG_H */

60
engines/agds/font.cpp Normal file
View File

@@ -0,0 +1,60 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "agds/font.h"
#include "graphics/managed_surface.h"
namespace AGDS {
Font::Font(Graphics::ManagedSurface *surface, int gw, int gh) : _surface(surface),
_glyphW(gw), _glyphH(gh),
_cellW(surface->w / 16), _cellH(surface->h / 16) {
// debug("surface cell %dx%d", _cellW, _cellH);
for (int y = 0; y < 16; ++y) {
for (int x = 0; x < 16; ++x) {
const uint32 *pixels = static_cast<uint32 *>(_surface->getBasePtr(x * _cellW, y * _cellH));
int w;
int ch = (y << 4) | x;
for (w = 0; w <= _glyphW; ++w, ++pixels) {
uint8 r, g, b, a;
// debug("%d color #%08x", ch, *pixels);
surface->format.colorToARGB(*pixels, r, g, b, a);
// debug("%d %d %d %d", r, g, b, a);
if (r == 0) // fixme: mapped incorrectly
break;
}
_width[ch] = w;
}
}
}
void Font::drawChar(Graphics::Surface *dst, uint32 chr, int x, int y, uint32 color) const {
if (chr >= 0x100)
return;
Common::Rect srcRect(getCharWidth(chr), _glyphH);
srcRect.moveTo(_cellW * (chr & 0x0f), _cellH * (chr >> 4));
if (!srcRect.isEmpty())
_surface->blendBlitTo(*dst, x, y, Graphics::FLIP_NONE, &srcRect);
}
} // namespace AGDS

60
engines/agds/font.h Normal file
View File

@@ -0,0 +1,60 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef FONT_H
#define FONT_H
#include "common/ptr.h"
#include "graphics/font.h"
namespace Graphics {
class ManagedSurface;
}
namespace AGDS {
class Font : public Graphics::Font {
Common::ScopedPtr<Graphics::ManagedSurface> _surface;
int _glyphW, _glyphH;
int _cellW, _cellH;
uint8 _width[0x100];
public:
Font(Graphics::ManagedSurface *surface, int gw, int gh);
virtual int getFontHeight() const {
return _glyphH;
}
virtual int getMaxCharWidth() const {
return _glyphW;
}
virtual int getCharWidth(uint32 chr) const {
return chr < 0x100 ? _width[chr] : 0;
}
virtual void drawChar(Graphics::Surface *dst, uint32 chr, int x, int y, uint32 color) const;
};
} // End of namespace AGDS
#endif /* AGDS_FONT_H */

210
engines/agds/inventory.cpp Normal file
View File

@@ -0,0 +1,210 @@
/* 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 "agds/inventory.h"
#include "agds/agds.h"
#include "agds/object.h"
#include "agds/resourceManager.h"
#include "agds/systemVariable.h"
#include "common/debug.h"
#include "common/textconsole.h"
#include "graphics/managed_surface.h"
namespace AGDS {
Inventory::Inventory(AGDSEngine *engine) : _engine(engine), _entries(kMaxSize), _enabled(false), _visible(false) {}
Inventory::~Inventory() {}
void Inventory::visible(bool visible) {
if (_visible == visible)
return;
_visible = visible;
if (!visible) {
debug("closing inventory...");
Common::String inv_close = _engine->getSystemVariable("inv_close")->getString();
if (!inv_close.empty())
_engine->runObject(inv_close);
} else {
debug("opening inventory...");
if (!_engine->currentInventoryObject())
removeGaps();
Common::String inv_open = _engine->getSystemVariable("inv_open")->getString();
if (!inv_open.empty())
_engine->runObject(inv_open);
}
}
int Inventory::free() const {
int free = 0;
for (uint i = 0; i < _entries.size(); ++i)
if (!_entries[i].hasObject)
++free;
return free;
}
bool Inventory::has(int index) const {
if (index >= 0 && index < kMaxSize) {
auto &entry = _entries[index];
return entry.hasObject;
}
return {};
}
ObjectPtr Inventory::get(int index) {
if (index >= 0 && index < kMaxSize) {
auto &entry = _entries[index];
if (entry.hasObject && !entry.object) {
entry.object = _engine->runObject(entry.name);
entry.object->persistent(false);
}
return entry.object;
}
return {};
}
int Inventory::add(const Common::String &name) {
int idx = find(name);
if (idx >= 0) {
warning("Double adding object %s, skipping...", name.c_str());
return idx;
}
for (uint i = 0; i < _entries.size(); ++i) {
auto &entry = _entries[i];
if (!entry.hasObject) {
entry.name = name;
entry.object.reset();
entry.hasObject = true;
return i;
}
}
return idx;
}
int Inventory::add(const ObjectPtr &object) {
for (uint i = 0; i < _entries.size(); ++i) {
auto &entry = _entries[i];
if (entry.hasObject && entry.name == object->getName()) {
warning("Double adding object [pointer] %s, skipping...", object->getName().c_str());
return i;
}
}
object->persistent(false);
for (uint i = 0; i < _entries.size(); ++i) {
auto &entry = _entries[i];
if (!entry.hasObject) {
entry.name = object->getName();
entry.object = object;
entry.hasObject = true;
return i;
}
}
error("inventory overflow");
}
bool Inventory::remove(const Common::String &name) {
bool removed = false;
for (uint i = 0; i < _entries.size(); ++i) {
auto &entry = _entries[i];
if (entry.hasObject && entry.name == name) {
entry.reset();
removed = true;
}
}
return removed;
}
void Inventory::removeGaps() {
auto n = _entries.size();
for (uint src = 0, dst = 0; src < n; ++src) {
auto &entry = _entries[src];
if (entry.hasObject) {
if (dst != src) {
debug("moving inventory object %u -> %u", src, dst);
_entries[dst++] = _entries[src];
_entries[src].reset();
} else
++dst;
}
}
}
int Inventory::find(const Common::String &name) const {
for (uint i = 0; i < _entries.size(); ++i) {
auto &entry = _entries[i];
if (entry.hasObject && entry.name == name)
return i;
}
return -1;
}
ObjectPtr Inventory::find(const Common::Point pos) const {
if (!_enabled)
return {};
for (uint i = 0; i < _entries.size(); ++i) {
auto &entry = _entries[i];
if (!entry.object)
continue;
auto &object = entry.object;
auto picture = object->getPicture();
if (picture) {
auto rect = picture->getBounds();
rect.moveTo(object->getPosition());
if (rect.contains(pos))
return object;
}
}
return {};
}
void Inventory::clear() {
for (uint i = 0; i < _entries.size(); ++i) {
_entries[i].reset();
}
}
void Inventory::load(Common::ReadStream &stream) {
clear();
for (int i = 0; i < kMaxSize; ++i) {
Common::String name = readString(stream);
int refcount = stream.readUint32LE();
int objectPtr = stream.readUint32LE();
if (!name.empty() && refcount) {
debug("load inventory object: %s %d %d", name.c_str(), refcount, objectPtr);
auto &entry = _entries[i];
entry.name = name;
entry.hasObject = true;
}
}
}
void Inventory::save(Common::WriteStream &stream) const {
for (const auto &entry : _entries) {
writeString(stream, entry.name);
stream.writeUint32LE(entry.hasObject ? 1 : 0);
stream.writeSint32LE(entry.hasObject ? -1 : 0);
}
}
} // namespace AGDS

105
engines/agds/inventory.h 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/>.
*
*/
#ifndef INVENTORY_H
#define INVENTORY_H
#include "common/array.h"
#include "common/ptr.h"
#include "common/rect.h"
#include "common/scummsys.h"
#include "common/str.h"
namespace Common {
class ReadStream;
class WriteStream;
} // namespace Common
namespace AGDS {
class Object;
using ObjectPtr = Common::SharedPtr<Object>;
class AGDSEngine;
class Inventory {
class AGDSEngine *_engine;
struct Entry {
Common::String name;
bool hasObject;
ObjectPtr object;
void reset() {
name.clear();
hasObject = false;
object.reset();
}
};
using EntriesType = Common::Array<Entry>;
EntriesType _entries;
bool _enabled;
bool _visible;
public:
static const int kMaxSize = 35;
Inventory(AGDSEngine *engine);
~Inventory();
void load(Common::ReadStream &stream);
void save(Common::WriteStream &stream) const;
bool enabled() const {
return _enabled;
}
void enable(bool enabled) {
_enabled = enabled;
}
bool visible() const {
return _visible;
}
void visible(bool visible);
int add(const Common::String &name);
int add(const ObjectPtr &object);
bool remove(const Common::String &name);
void removeGaps();
bool has(int index) const;
ObjectPtr get(int index);
int find(const Common::String &name) const;
ObjectPtr find(const Common::Point pos) const;
int free() const;
void clear();
const EntriesType &entries() const {
return _entries;
}
uint maxSize() const {
return _entries.size();
}
};
} // End of namespace AGDS
#endif /* AGDS_INVENTORY_H */

View File

@@ -0,0 +1,89 @@
/* 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 "agds/agds.h"
#include "agds/object.h"
#include "base/plugins.h"
#include "common/savefile.h"
#include "common/system.h"
#include "engines/advancedDetector.h"
class AGDSMetaEngine : public AdvancedMetaEngine<ADGameDescription> {
public:
Common::Error createInstance(OSystem *syst, Engine **engine, const ADGameDescription *desc) const override;
bool hasFeature(MetaEngineFeature f) const override {
switch (f) {
case kSimpleSavesNames:
return true;
default:
return false;
}
}
const char *getName() const override {
return "agds";
}
int getMaximumSaveSlot() const override {
return 24;
}
SaveStateList listSaves(const char *target) const override {
Common::SaveFileManager *saveFileMan = g_system->getSavefileManager();
Common::StringArray filenames;
Common::String pattern(getSavegameFilePattern(target));
filenames = saveFileMan->listSavefiles(pattern);
SaveStateList saveList;
for (Common::StringArray::const_iterator file = filenames.begin(); file != filenames.end(); ++file) {
// Obtain the last 2 digits of the filename, since they correspond to the save slot
int slotNum = atoi(file->c_str() + file->size() - 2);
if (slotNum >= 0 && slotNum <= getMaximumSaveSlot()) {
SaveStateDescriptor desc;
desc.setSaveSlot(slotNum);
desc.setDescription(*file);
if (slotNum == getAutosaveSlot())
desc.setWriteProtectedFlag(true);
saveList.push_back(desc);
}
}
// Sort saves based on slot number.
Common::sort(saveList.begin(), saveList.end(), SaveStateDescriptorSlotComparator());
return saveList;
}
};
Common::Error AGDSMetaEngine::createInstance(OSystem *syst, Engine **engine, const ADGameDescription *desc) const {
*engine = new AGDS::AGDSEngine(syst, desc);
return Common::Error(Common::kNoError);
}
#if PLUGIN_ENABLED_DYNAMIC(AGDS)
REGISTER_PLUGIN_DYNAMIC(AGDS, PLUGIN_TYPE_ENGINE, AGDSMetaEngine);
#else
REGISTER_PLUGIN_STATIC(AGDS, PLUGIN_TYPE_ENGINE, AGDSMetaEngine);
#endif

139
engines/agds/mjpgPlayer.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 "agds/mjpgPlayer.h"
#include "agds/agds.h"
#include "agds/font.h"
#include "agds/object.h"
#include "agds/systemVariable.h"
#include "common/debug.h"
namespace AGDS {
MJPGPlayer::MJPGPlayer(Common::SeekableReadStream *stream, const Common::String &subtitles) : _stream(stream), _firstFramePos(_stream->pos()), _framesPlayed(0), _nextSubtitleIndex(0) {
uint pos = 0;
while (pos < subtitles.size()) {
auto next = subtitles.find('\n', pos);
if (next == subtitles.npos)
next = subtitles.size();
Common::String line = subtitles.substr(pos, next - pos);
uint begin, end;
int offset;
if (sscanf(line.c_str(), "{%u}{%u}%n", &begin, &end, &offset) != 2) {
warning("malformed subtitle line: %s", line.c_str());
pos = next + 1;
continue;
}
while (offset < static_cast<int>(line.size()) && line[offset] == ' ')
++offset;
line = line.substr(offset);
debug("subtitle line: %d-%d: %s", begin, end, line.c_str());
Text::Lines lines;
{
uint lineBegin = 0;
while (lineBegin < line.size()) {
uint lineEnd = line.find('|', lineBegin);
if (lineEnd == line.npos)
lineEnd = line.size();
lines.push_back(line.substr(lineBegin, lineEnd - lineBegin));
lineBegin = lineEnd + 1;
}
}
_subtitles.push_back({begin, end, lines});
pos = next + 1;
}
}
MJPGPlayer::~MJPGPlayer() {}
void MJPGPlayer::rewind() {
_framesPlayed = 0;
_nextSubtitleIndex = 0;
_stream->seek(_firstFramePos);
}
const Graphics::Surface *MJPGPlayer::decodeFrame() {
if (_stream->eos())
return NULL;
uint32 size = _stream->readSint32LE();
if (size == 0)
return NULL;
if (_stream->eos())
return NULL;
Common::ScopedPtr<Common::SeekableReadStream> stream(_stream->readStream(size));
const Graphics::Surface *surface = _decoder.decodeFrame(*stream);
++_framesPlayed;
return surface;
}
void MJPGPlayer::paint(AGDSEngine &engine, Graphics::Surface &backbuffer) {
auto *surface = decodeFrame();
if (surface) {
Common::ScopedPtr<Graphics::Surface> scaled;
{
Common::ScopedPtr<Graphics::Surface> converted(surface->convertTo(engine.pixelFormat()));
auto scaleW = 1.0f * backbuffer.w / converted->w;
auto scaleH = 1.0f * backbuffer.h / converted->h;
auto scale = MIN(scaleW, scaleH);
scaled.reset(converted->scale(converted->w * scale, converted->h * scale));
converted->free();
}
Common::Point dst((backbuffer.w - scaled->w) / 2, (backbuffer.h - scaled->h) / 2);
Common::Rect srcRect(scaled->getRect());
if (Common::Rect::getBlitRect(dst, srcRect, backbuffer.getRect()))
backbuffer.copyRectToSurface(*scaled, dst.x, dst.y, srcRect);
scaled->free();
}
if (_subtitles.empty())
return;
while (_nextSubtitleIndex < _subtitles.size() && _framesPlayed > _subtitles[_nextSubtitleIndex].end)
++_nextSubtitleIndex;
if (_nextSubtitleIndex >= _subtitles.size() || _framesPlayed < _subtitles[_nextSubtitleIndex].begin)
return;
auto font = engine.getFont(engine.getSystemVariable("tell_font")->getInteger());
int baseX = engine.getSystemVariable("subtitle_x")->getInteger();
int baseY = engine.getSystemVariable("subtitle_y")->getInteger();
int maxWidth = engine.getSystemVariable("subtitle_width")->getInteger();
auto &lines = _subtitles[_nextSubtitleIndex].lines;
for (auto &line : lines) {
Text::Lines sublines;
font->wordWrapText(line, maxWidth, sublines);
for (auto &subline : sublines) {
int w = font->getStringWidth(subline);
int x = baseX - w / 2;
font->drawString(&backbuffer, subline, x, baseY, backbuffer.w - x, 0);
baseY += font->getFontHeight();
}
}
}
} // namespace AGDS

72
engines/agds/mjpgPlayer.h Normal file
View File

@@ -0,0 +1,72 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef MJPG_PLAYER_H
#define MJPG_PLAYER_H
#include "common/array.h"
#include "common/scummsys.h"
#include "common/str.h"
#include "common/stream.h"
#include "graphics/surface.h"
#include "image/jpeg.h"
namespace AGDS {
class AGDSEngine;
class MJPGPlayer {
using StreamPtr = Common::ScopedPtr<Common::SeekableReadStream>;
StreamPtr _stream;
int32 _firstFramePos;
Image::JPEGDecoder _decoder;
uint _framesPlayed;
struct Text {
using Lines = Common::Array<Common::String>;
uint begin;
uint end;
Lines lines;
};
Common::Array<Text> _subtitles;
uint _nextSubtitleIndex;
public:
MJPGPlayer(Common::SeekableReadStream *stream, const Common::String &subtitles);
~MJPGPlayer();
bool eos() {
return _stream->eos();
}
void rewind();
const Graphics::Surface *decodeFrame();
void paint(AGDSEngine &engine, Graphics::Surface &backbuffer);
uint32 getNextFrameTimestamp() const {
return _framesPlayed * 1000 / 24;
}
};
} // End of namespace AGDS
#endif /* AGDS_MJPG_PLAYER_H */

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

@@ -0,0 +1,35 @@
MODULE := engines/agds
MODULE_OBJS := \
agds.o \
animation.o \
character.o \
console.o \
database.o \
dialog.o \
font.o \
inventory.o \
metaengine.o \
mjpgPlayer.o \
mouseMap.o \
object.o \
patch.o \
process.o \
process_opcodes.o \
region.o \
resourceManager.o \
screen.o \
soundManager.o \
systemVariable.o \
textLayout.o
# This module can be built as a plugin
ifeq ($(ENABLE_AGDS), DYNAMIC_PLUGIN)
PLUGIN := 1
endif
# Include common rules
include $(srcdir)/rules.mk
# Detection objects
DETECT_OBJS += $(MODULE)/detection.o

106
engines/agds/mouseMap.cpp Normal file
View File

@@ -0,0 +1,106 @@
/* 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 "agds/mouseMap.h"
#include "agds/agds.h"
#include "agds/object.h"
#include "agds/region.h"
namespace AGDS {
int MouseMap::findFree() const {
for (int i = 0, n = _mouseRegions.size(); i != n; ++i) {
auto &region = _mouseRegions[i];
if (!region)
return i;
}
error("no mouse region available");
}
int MouseMap::add(MouseRegion area) {
auto id = findFree();
auto &region = _mouseRegions[id];
region.reset(new MouseRegion(Common::move(area)));
region->id = id;
return id;
}
void MouseMap::hideInactive(AGDSEngine *engine, Common::Point pos) {
for (auto &region : _mouseRegions) {
if (!region || !region->enabled)
continue;
if (_disabled) {
region->hide(engine);
} else if (engine->userEnabled()) {
if (!region->region->pointIn(pos)) {
region->hide(engine);
} else if (!region->visible) {
region->show(engine);
return;
}
}
}
}
MouseRegion *MouseMap::find(int id) {
for (auto &region : _mouseRegions) {
if (region && region->id == id)
return region.get();
}
return nullptr;
}
void MouseMap::remove(int id) {
auto &region = _mouseRegions[id];
if (!region) {
warning("removing non-existent mouse region %d", id);
return;
}
region->disable();
region.reset();
}
void MouseRegion::show(AGDSEngine *engine) {
if (visible)
return;
visible = true;
debug("calling mouseArea[%d].onEnter: %s", id, onEnter.c_str());
engine->runObject(onEnter);
}
void MouseRegion::hide(AGDSEngine *engine) {
if (!visible)
return;
visible = false;
debug("calling mouseArea[%d].onLeave: %s...", id, onLeave.c_str());
engine->runObject(onLeave);
}
void MouseMap::hideAll(AGDSEngine *engine) {
for (auto &region : _mouseRegions)
if (region)
region->hide(engine);
}
} // End of namespace AGDS

90
engines/agds/mouseMap.h Normal file
View File

@@ -0,0 +1,90 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef MOUSE_MAP_H
#define MOUSE_MAP_H
#include "common/array.h"
#include "common/ptr.h"
#include "common/rect.h"
#include "common/str.h"
namespace AGDS {
class AGDSEngine;
struct Region;
using RegionPtr = Common::SharedPtr<Region>;
struct MouseRegion {
int id = -1;
RegionPtr region = nullptr;
bool enabled = true;
bool visible = false;
Common::String onEnter;
Common::String onLeave;
void enable() {
enabled = true;
}
void disable() {
enabled = false;
}
MouseRegion() {}
MouseRegion(RegionPtr reg, const Common::String &enter, const Common::String &leave) : region(reg), onEnter(enter), onLeave(leave) {
}
void hide(AGDSEngine *engine);
void show(AGDSEngine *engine);
};
using MouseRegionPtr = Common::ScopedPtr<MouseRegion>;
class MouseMap {
Common::Array<MouseRegionPtr> _mouseRegions;
bool _disabled;
public:
MouseMap() : _mouseRegions(100), _disabled(false) {}
void disable(bool disabled) {
_disabled = disabled;
}
bool disabled() const {
return _disabled;
}
int findFree() const;
int add(MouseRegion area);
void remove(int id);
void hideAll(AGDSEngine *engine);
void hideInactive(AGDSEngine *engine, Common::Point pos);
MouseRegion *find(int id);
};
} // End of namespace AGDS
#endif /* AGDS_SCREEN_H */

298
engines/agds/object.cpp Normal file
View File

@@ -0,0 +1,298 @@
/* 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 "agds/object.h"
#include "agds/agds.h"
#include "agds/animation.h"
#include "agds/font.h"
#include "agds/region.h"
#include "agds/systemVariable.h"
#include "common/debug.h"
#include "common/memstream.h"
#include "common/rect.h"
#include "graphics/managed_surface.h"
#include "graphics/surface.h"
namespace AGDS {
Object::Object(const Common::String &name, Common::SeekableReadStream &stream, int version) : _name(name), _stringTableLoaded(false),
_picture(), _rotatedPicture(), _region(),
_animation(), _mouseCursor(),
_pos(), _z(10), _rotation(0),
_clickHandler(0), _examineHandler(0), _userUseHandler(0),
_throwHandler(0), _useOnHandler(0),
_handlerBD(0), _handlerC1(0),
_alpha(255), _scale(100), _locked(0), _alive(true),
_persistent(true), _allowInitialise(true),
_ignoreRegion(false), _version(version) {
uint16 id = stream.readUint16LE();
debug("id: 0x%02x %u", id, id);
uint16 dataSize = stream.readUint16LE();
if (dataSize != 0)
error("implement me: object with data (%u/0x%04x)", dataSize, dataSize);
uint16 codeSize = stream.readUint16LE();
uint8 flags = stream.readByte();
if (flags != 1)
error("implement me: no flags handling yet");
debug("object code size %u", codeSize);
_code.resize(codeSize);
stream.read(_code.data(), _code.size());
}
Object::~Object() {
freeRotated();
freePicture();
}
void Object::lock() {
++_locked;
}
void Object::unlock() {
if (_locked == 0)
error("%s: object lock counter underrun", _name.c_str());
--_locked;
}
void Object::freeRotated() {
if (_rotatedPicture) {
_rotatedPicture->free();
_rotatedPicture.reset();
}
}
void Object::freePicture() {
if (_picture) {
_picture->free();
_picture.reset();
}
}
void Object::readStringTable(unsigned resOffset, uint16 resCount) {
if (_stringTableLoaded)
return;
resOffset += 5 /*instruction*/ + (_version >= 2 ? 0x13 : 0x11) /*another header*/;
if (resOffset >= _code.size())
error("invalid resource table offset %u/%u", resOffset, _code.size());
// debug("resource table at %04x", resOffset);
Common::MemoryReadStream stream(_code.data() + resOffset, _code.size() - resOffset);
_stringTable.resize(resCount);
for (uint16 i = 0; i < resCount; ++i) {
uint16 offset = stream.readUint16LE();
uint16 flags = stream.readUint16LE();
unsigned nameOffset = resOffset + offset;
if (nameOffset > _code.size())
error("invalid resource name offset %u/%u", nameOffset, _code.size());
const char *nameBegin = reinterpret_cast<const char *>(_code.data() + nameOffset);
const char *codeEnd = reinterpret_cast<const char *>(_code.data() + _code.size());
const char *nameEnd = Common::find(nameBegin, codeEnd, 0);
Common::String name(nameBegin, nameEnd - nameBegin);
// debug("resource table 1[%04u]: 0x%04x %s", i, flags, name.c_str());
_stringTable[i] = StringEntry(name, flags);
}
debug("loaded %u strings", resCount);
_stringTableLoaded = true;
}
const Object::StringEntry &Object::getString(uint16 index) const {
if (!_stringTableLoaded)
error("no string table loaded");
if (index >= _stringTable.size()) {
static StringEntry empty;
warning("no resource name with id %u", index);
return empty;
}
return _stringTable[index];
}
void Object::setPicture(Graphics::ManagedSurface *picture) {
_pos = Common::Point();
freePicture();
_picture.reset(picture);
freeRotated();
_rotation = 0;
if (!picture) {
_offset = Common::Point();
return;
}
assert(picture->format.bytesPerPixel == 4);
const byte *pixels = static_cast<const byte *>(picture->getPixels());
uint pitch = picture->pitch;
uint16 w = picture->w, h = picture->h;
_offset.x = w;
_offset.y = h;
for (uint16 y = 0; y < h; ++y) {
for (uint16 x = 0; x < w; ++x) {
uint32 color = *reinterpret_cast<const uint32 *>(pixels + (y * pitch + x * picture->format.bytesPerPixel));
uint8 r, g, b, a;
picture->format.colorToARGB(color, a, r, g, b);
if (a != 0) {
if (y < _offset.y)
_offset.y = y;
if (x < _offset.x)
_offset.x = x;
break;
}
}
}
createRotated();
}
void Object::rotate(int rot) {
rot %= 4;
if (rot == _rotation)
return;
debug("%s: setting rotation to %d", _name.c_str(), rot);
_rotation = rot;
createRotated();
}
void Object::createRotated() {
freeRotated();
if (_rotation == 0 || !_picture)
return;
Graphics::TransformStruct transform(100, 100, 90 * _rotation, _picture->w / 2, _picture->h / 2);
_rotatedPicture.reset(getPicture()->rotoscale(transform));
}
void Object::alive(bool value) {
_alive = value;
if (!_alive)
_region.reset();
}
void Object::region(RegionPtr region) {
_region = region;
}
void Object::moveTo(Common::Point pos) {
_pos = pos;
}
void Object::generateRegion(Common::Rect rect) {
_region = RegionPtr(new Region(rect));
debug("%s: generated region: %s", _name.c_str(), _region->toString().c_str());
}
void Object::generateRegion() {
if (!getPicture()) {
warning("generateRegion called on null picture");
return;
}
generateRegion(getRect());
}
Common::Rect Object::getRect() const {
auto picture = getPicture();
if (!picture) {
warning("getRect called on null picture");
return Common::Rect();
}
Common::Rect rect = picture->getBounds();
rect.moveTo(_pos.x, _pos.y);
return rect;
}
bool Object::pointIn(Common::Point pos) {
if (!_alive)
return false;
auto picture = getPicture();
if (picture) {
auto rect = getRect();
if (rect.contains(pos)) {
return true;
}
}
if (_animation) {
Common::Rect rect(0, 0, _animation->width(), _animation->height());
rect.moveTo(_pos + _animationPos);
if (rect.contains(pos)) {
return true;
}
}
// pos -= _pos;
pos -= _regionOffset;
if (_trapRegion && _trapRegion->pointIn(pos))
return true;
if (!_ignoreRegion && _region && _region->pointIn(pos))
return true;
return false;
}
void Object::paint(AGDSEngine &engine, Graphics::Surface &backbuffer, Common::Point pos) const {
auto picture = getPicture();
if (picture) {
Common::Point dst = pos + getPosition();
Common::Rect srcRect = picture->getBounds();
if (!_srcRect.isEmpty()) {
srcRect = _srcRect;
}
uint32 color = _picture->format.ARGBToColor(_alpha, 255, 255, 255);
if (Common::Rect::getBlitRect(dst, srcRect, backbuffer.getRect())) {
picture->blendBlitTo(backbuffer, dst.x, dst.y, Graphics::FLIP_NONE, &srcRect, color);
}
}
if (_animation) {
pos += _pos + _animationPos;
_animation->tick();
_animation->paint(backbuffer, pos);
}
if (!_text.empty()) {
pos += _region ? _region->center + _regionOffset : _pos;
int w = backbuffer.w - pos.x;
engine.getFont(engine.getSystemVariable("objtext_font")->getInteger())->drawString(&backbuffer, _text, pos.x, pos.y, w, 0);
}
if (engine.showHints() && !_title.empty()) {
pos += _region ? _region->center : _pos;
int w = backbuffer.w - pos.x;
auto font = engine.getFont(engine.getSystemVariable("tell_font")->getInteger());
pos.x -= font->getStringWidth(_title) / 2;
font->drawString(&backbuffer, _title, pos.x, pos.y, w, 0);
}
}
} // namespace AGDS

343
engines/agds/object.h Normal file
View File

@@ -0,0 +1,343 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef OBJECT_H
#define OBJECT_H
#include "common/array.h"
#include "common/hash-str.h"
#include "common/hashmap.h"
#include "common/ptr.h"
#include "common/rect.h"
#include "common/scummsys.h"
#include "common/stream.h"
namespace Graphics {
struct Surface;
class ManagedSurface;
} // namespace Graphics
namespace AGDS {
class AGDSEngine;
struct Region;
using RegionPtr = Common::SharedPtr<Region>;
class Animation;
using AnimationPtr = Common::SharedPtr<Animation>;
class Object {
public:
using CodeType = Common::Array<uint8>;
struct StringEntry {
Common::String string;
uint16 flags;
StringEntry() : string(), flags() {}
StringEntry(const Common::String &s, uint16 f) : string(s), flags(f) {}
};
private:
using StringTableType = Common::Array<StringEntry>;
using KeyHandlersType = Common::HashMap<Common::String, uint, Common::IgnoreCase_Hash, Common::IgnoreCase_EqualTo>;
using UseHandlersType = Common::HashMap<Common::String, uint>;
using ManagedSurfacePtr = Common::ScopedPtr<Graphics::ManagedSurface>;
Common::String _name;
CodeType _code;
StringTableType _stringTable;
bool _stringTableLoaded;
KeyHandlersType _keyHandlers;
UseHandlersType _useHandlers;
ManagedSurfacePtr _picture;
ManagedSurfacePtr _rotatedPicture;
RegionPtr _region;
RegionPtr _trapRegion;
AnimationPtr _animation;
AnimationPtr _mouseCursor;
Common::Point _pos, _animationPos, _offset;
Common::Point _regionOffset;
Common::Rect _srcRect;
int _z;
int _rotation;
Common::String _text;
Common::String _title;
uint _clickHandler;
uint _examineHandler;
uint _userUseHandler;
uint _throwHandler;
uint _useOnHandler;
uint _trapHandler;
uint _handlerBD;
uint _handlerC1;
int _alpha;
int _scale;
uint _locked;
bool _alive;
bool _persistent;
bool _allowInitialise;
bool _ignoreRegion;
int _version;
private:
void freeRotated();
void freePicture();
void createRotated();
public:
Object(const Common::String &name, Common::SeekableReadStream &stream, int version);
~Object();
bool allowInitialise() const {
return _allowInitialise;
}
void allowInitialise(bool allow) {
_allowInitialise = allow;
}
void persistent(bool persistent) {
_persistent = persistent;
}
bool persistent() const {
return _persistent;
}
void ignoreRegion(bool ignoreRegion) {
_ignoreRegion = ignoreRegion;
}
bool ignoreRegion() const {
return _ignoreRegion;
}
void readStringTable(unsigned resOffset, uint16 resCount);
const StringEntry &getString(uint16 index) const;
uint getStringTableSize() const { return _stringTable.size(); }
const Common::String &getName() const { return _name; }
const CodeType &getCode() const {
return _code;
}
void setAnimation(const AnimationPtr &animation) {
_animation = animation;
}
const AnimationPtr &getAnimation() const {
return _animation;
}
void setAnimationPosition(Common::Point animationPos) {
_animationPos = animationPos;
}
void setMouseCursor(const AnimationPtr &mouseCursor) {
_mouseCursor = mouseCursor;
freeRotated();
}
const AnimationPtr &getMouseCursor() const {
return _mouseCursor;
}
void setPicture(Graphics::ManagedSurface *);
Graphics::ManagedSurface *getPicture() const {
return _rotatedPicture ? _rotatedPicture.get() : _picture.get();
}
void rotate(int rot);
int rotation() const {
return _rotation;
}
void generateRegion();
void generateRegion(Common::Rect rect);
void regionOffset(Common::Point offset) {
_regionOffset = offset;
}
void setAlpha(int alpha) {
if (alpha < 0)
alpha = 0;
if (alpha > 100)
alpha = 100;
_alpha = (100 - alpha) * 255 / 100;
}
int alpha() const {
return _alpha;
}
void scale(int scale) {
_scale = scale;
}
int scale() const {
return _scale;
}
void region(RegionPtr region);
const RegionPtr &region() const {
return _region;
}
void setClickHandler(uint ip) {
_clickHandler = ip;
}
uint getClickHandler() const {
return _clickHandler;
}
void setExamineHandler(uint ip) {
_examineHandler = ip;
}
uint getExamineHandler() const {
return _examineHandler;
}
void setUseHandler(const Common::String &name, uint ip) {
_useHandlers[name] = ip;
}
uint getUseHandler(const Common::String &name) const {
return _useHandlers.getValOrDefault(name, 0); // use handler can never be 0
}
void setUserUseHandler(uint ip) {
_userUseHandler = ip;
}
void setThrowHandler(uint ip) {
_throwHandler = ip;
}
void setUseOnHandler(uint ip) {
_useOnHandler = ip;
}
uint getUserUseHandler() const {
return _userUseHandler;
}
void setHandlerBD(uint ip) {
_handlerBD = ip;
}
uint getHandlerBD() const {
return _handlerBD;
}
void setHandlerC1(uint ip) {
_handlerC1 = ip;
}
uint getHandlerC1() const {
return _handlerC1;
}
void setTrapHandler(uint ip, RegionPtr region) {
_trapHandler = ip;
_trapRegion = region;
}
RegionPtr getTrapRegion() const { return _trapRegion; }
uint getTrapHandler() const { return _trapHandler; }
void paint(AGDSEngine &engine, Graphics::Surface &backbuffer, Common::Point pos) const;
void moveTo(Common::Point pos);
void z(int z) {
_z = z;
}
int z() const {
return _z;
}
const Common::String &getText() const {
return _text;
}
void setText(const Common::String &text) {
_text = text;
}
void title(const Common::String &title) {
_title = title;
}
const Common::String &title() const {
return _title;
}
Common::Point getPosition() const {
return _pos - _offset;
}
Common::Point getOffset() const {
return _offset;
}
Common::Rect getRect() const;
void setKeyHandler(const Common::String &name, uint ip) {
_keyHandlers[name] = ip;
}
uint getKeyHandler(const Common::String &name) const {
KeyHandlersType::const_iterator i = _keyHandlers.find(name);
return i != _keyHandlers.end() ? i->_value : 0;
}
uint throwHandler() const {
return _throwHandler;
}
uint useOnHandler() const {
return _useOnHandler;
}
bool locked() const {
return _locked != 0;
}
void lock();
void unlock();
bool alive() const { return _alive; }
void alive(bool value);
void srcRect(Common::Rect srcRect) { _srcRect = srcRect; }
bool pointIn(Common::Point pos);
};
using ObjectPtr = Common::SharedPtr<Object>;
} // End of namespace AGDS
#endif /* AGDS_OBJECT_H */

524
engines/agds/opcode.h Normal file
View File

@@ -0,0 +1,524 @@
/* 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 OPCODE_H
#define OPCODE_H
#include "common/scummsys.h"
namespace AGDS {
enum Opcode : uint16 {
kEnter = 5,
kJumpZImm16 = 8,
kJumpImm16 = 9,
kPop = 10,
kDup = 11,
kExitProcess = 12,
kSuspendProcess = 13,
kPushImm32 = 14,
kPushImm16 = 15,
kPushImm8 = 16,
kPushImm16_2 = 17,
kPushImm8_2 = 18,
kStub19 = 19,
kStub20 = 20,
kGetGlobalImm8 = 21,
kEquals = 22,
kNotEquals = 23,
kGreater = 24,
kLess = 25,
kGreaterOrEquals = 26,
kLessOrEquals = 27,
kAdd = 28,
kSub = 29,
kMul = 30,
kDiv = 31,
kMod = 32,
kAnd = 33,
kOr = 34,
kXor = 35,
kNot = 36,
kShl = 37,
kShr = 38,
kBoolAnd = 39,
kBoolOr = 40,
kBoolNot = 41,
kNegate = 42,
kPreIncrementGlobal = 43,
kPreDecrementGlobal = 44,
kPostIncrementGlobal = 45,
kPostDecrementGlobal = 46,
kSetGlobalWithTop = 47,
kSetGlobal = 48,
kIncrementGlobalByTop = 49,
kDecrementGlobalByTop = 50,
kMultiplyGlobalByTop = 51,
kDivideGlobalByTop = 52,
kModGlobalByTop = 53,
kShlGlobalByTop = 54,
kShrGlobalByTop = 55,
kAndGlobalByTop = 56,
kOrGlobalByTop = 57,
kXorGlobalByTop = 58,
kObjectInitialise = 59,
kObjectRegisterLookHandler = 60,
kObjectRegisterUseHandler = 61,
kObjectRegisterHandlerC1 = 62,
kObjectRegisterUseObjectHandler = 63,
kObjectRegisterHandlerBD = 64,
kObjectRegisterCharacterTrap = 65,
kLoadMouseCursorFromObject = 66,
kLoadRegionFromObject = 68,
kLoadPictureFromObject = 69,
kLoadAnimationFromObject = 70,
kSetObjectZ = 71,
kSetScreenBackground = 72,
kLoadTextFromObject = 73,
kScreenSetZNearFar = 74,
kScreenLoadRegion = 75,
kScreenLoadObject = 76,
kScreenCloneObject = 77,
kScreenRemoveObject = 78,
kSetNextScreen = 79,
kSetNextScreenSaveOrLoad = 80,
kObjectPatchSetText = 82,
kObjectPatchSetRegionName = 83,
kScreenPatchSetRegionName = 84,
kLoadCharacter = 85,
kUnloadCharacter = 86,
kAssociateCharacter = 87,
kAnimateCharacter = 88,
kHideCharacter = 89,
kShowCharacter = 90,
kDisableCharacter = 91,
kEnableCharacter = 92,
kMoveCharacterUserMove = 93,
kLeaveCharacter = 94,
kSetCharacter = 95,
kSetCharacterDirection = 96,
kPointCharacter = 97,
kDisableUser = 98,
kEnableUser = 99,
kRestartSample = 100,
kStopSample = 101,
kStub102 = 102,
kStub103 = 103,
kStub104 = 104,
kClearScreen = 105,
kInventoryClear = 106,
kStub107 = 107,
kLoadMouse = 108,
kAttachInventoryObjectToMouse0 = 109,
kResetMousePointer = 110,
kInventoryAddObject = 111,
kInventoryRemoveObject = 112,
kStub113 = 113,
kStub114 = 114,
kStub115 = 115,
kStub116 = 116,
kLoadAnimation = 117,
kLoadSample = 118,
kSetPhaseVarControlledFlag = 119,
kPlayerSay120 = 120,
kStub121 = 121,
kPlayerSay122 = 122,
kNPCSayNoSound = 123,
kNPCSay = 124,
kPlayerSay125 = 125,
kStub126 = 126,
kSetTimer = 127,
kProcessResetState = 128,
kSetAnimationZ = 129,
kSetCycles = 130,
kSetRandom = 131,
kSetPeriodic = 132,
kSetPanAndVolume = 133,
kSetAnimationPosition = 134,
kSetPhaseVar = 135,
kSetAnimationLoop = 136,
kSetAnimationSpeed = 137,
kScreenObjectPatchIncRef = 138,
kScreenObjectPatchDecRef = 139,
kScreenCheckScreenPatch = 140,
kGetFreeInventorySpace = 141,
kSetStringSystemVariable = 142,
kSetSystemIntegerVariable = 143,
kGetSavedMouseX = 144,
kGetSavedMouseY = 145,
kGetRegionCenterX = 146,
kGetRegionCenterY = 147,
kGetCharacterAnimationPhase = 148,
kGetCharacterX = 149,
kGetCharacterY = 150,
kCompareScreenName = 151,
kGetPictureBaseX = 152,
kGetPictureBaseY = 153,
kGetObjectSurfaceX = 154,
kGetObjectSurfaceY = 155,
kLoadGame = 156,
kSaveGame = 157,
kQuit = 158,
kStartNewGame = 159,
kLoadSaveSlotNamePicture = 160,
kGetSaveGameName = 161,
kDisableInventory = 162,
kEnableInventory = 163,
kLoadPreviousScreen = 164,
kMoveScreenObject = 165,
kSetObjectRegionOffset = 166,
kReturnCurrentInventoryObject = 167,
kStub168 = 168,
kGetIntegerSystemVariable = 169,
kSetDelay = 170,
kGetRandomNumber = 171,
kSetSampleVolume = 172,
kStub173 = 173,
kStub174 = 174,
kAppendToSharedStorage = 175,
kCloneName = 176,
kAppendNameToSharedStorage = 177,
kGetCloneVar = 178,
kSetCloneVar = 179,
kGetObjectId = 180,
kStub181 = 181,
kSetTileSize = 182,
kGenerateRegion = 183,
kSetObjectTile = 184,
kGetMaxInventorySize = 185,
kInventoryHasObject = 186,
kAppendInventoryObjectNameToSharedSpace = 187,
kSetObjectText = 188,
kInventoryFindObjectByName = 189,
kSetObjectScale = 190,
kDisableMouseAreas = 191,
kObjectIgnoreRegion = 192,
kRemoveGapsFromInventory = 193,
kSampleAmbient = 194,
kGetObjectPictureWidth = 195,
kGetObjectPictureHeight = 196,
kSetRotation = 197,
kLoadPicture = 198,
kStub199 = 199,
kSetTileIndex = 200,
kSetThrowHandler = 201,
kSetUseOnHandler = 202,
kPlayFilm = 203,
kStub204 = 204,
kAddMouseArea = 205,
kModifyMouseArea = 206,
kSetRain = 207,
kFogOnCharacter = 208,
kObjectRegisterUserUseHandler = 209,
kStub210 = 210,
kStub211 = 211,
kSetSampleVolumeAndPan = 212,
kStub213 = 213,
kAddSampleToSoundGroup = 214,
kClearSoundGroup = 215,
kStub216 = 216,
kStub217 = 217,
kSetRainDensity = 218,
kLeaveCharacterEx = 219,
kStopCharacter = 220,
kRestartAnimation = 221,
kSignalAnimationEnd = 222,
kSetShadowIntensity = 223,
kSetNPCTellNotifyVar = 224,
kPauseAnimation = 225,
kFadeObject = 226,
kLoadFont = 227,
kMoveCharacterNoUserMove = 228,
kOnKey = 229,
kStub230 = 230,
kStub231 = 231,
kStub232 = 232,
kObjectFreePictureAndAnimation = 233,
kGetSampleVolume = 234,
kFadeScreen = 235,
kUserEnabled = 236,
kAnimationNextFrame = 237,
kInventoryHasObjectByName = 238,
kStub239 = 239,
kLoadDialog = 240,
kStub241 = 241,
kHasGlobal = 242,
kStub243 = 243,
kSetCharacterNotifyVars = 244,
kAttachInventoryObjectToMouse1 = 245,
kStub246 = 246,
kSetDialogForNextFilm = 247,
kSetCamera = 252,
kStub251 = 251,
kCharacterStub253 = 253,
kStub257 = 257,
kStub258 = 258,
kSetFog = 263,
kStub264 = 264,
kStub265 = 265,
kStub266 = 266,
kStub267 = 267,
kStub275 = 275,
kStub276 = 276,
kStub278 = 278,
kStub279 = 279,
kStub280 = 280,
kStub284 = 284,
kStub291 = 291,
kStub292 = 292,
};
#define AGDS_OPCODE_LIST(OP, OP_C, OP_B, OP_W, OP_U, OP_UD, OP_UU) \
OP_UU(kEnter, enter) \
OP_W(kJumpZImm16, jumpz) \
OP_W(kJumpImm16, jump) \
OP(kPop, popNoResult) \
OP(kDup, dup) \
OP(kExitProcess, exitProcess) \
OP(kSuspendProcess, suspend) \
OP_UD(kPushImm32, push) \
OP_C(kPushImm8, push) \
OP_W(kPushImm16, push) \
OP_C(kPushImm8_2, push2) \
OP_W(kPushImm16_2, push2) \
OP_B(kGetGlobalImm8, getGlobal) \
OP(kPostIncrementGlobal, postIncrementGlobal) \
OP(kPostDecrementGlobal, postDecrementGlobal) \
OP(kIncrementGlobalByTop, incrementGlobalByTop) \
OP(kDecrementGlobalByTop, decrementGlobalByTop) \
OP(kMultiplyGlobalByTop, multiplyGlobalByTop) \
OP(kDivideGlobalByTop, divideGlobalByTop) \
OP(kModGlobalByTop, modGlobalByTop) \
OP(kShlGlobalByTop, shlGlobalByTop) \
OP(kShrGlobalByTop, shrGlobalByTop) \
OP(kAndGlobalByTop, andGlobalByTop) \
OP(kOrGlobalByTop, orGlobalByTop) \
OP(kXorGlobalByTop, xorGlobalByTop) \
OP(kEquals, equals) \
OP(kNotEquals, notEquals) \
OP(kGreater, greater) \
OP(kLess, less) \
OP(kGreaterOrEquals, greaterOrEquals) \
OP(kLessOrEquals, lessOrEquals) \
OP(kAdd, add) \
OP(kSub, sub) \
OP(kMul, mul) \
OP(kDiv, div) \
OP(kMod, mod) \
OP(kSetGlobalWithTop, setGlobalWithTop) \
OP(kSetGlobal, setGlobal) \
OP(kBoolOr, boolOr) \
OP(kBoolAnd, boolAnd) \
OP(kAnd, bitAnd) \
OP(kOr, bitOr) \
OP(kXor, bitXor) \
OP(kNot, bitNot) \
OP(kShl, shl) \
OP(kShr, shr) \
OP(kBoolNot, boolNot) \
OP(kNegate, negate) \
OP_U(kObjectInitialise, initialise) \
OP_U(kObjectRegisterLookHandler, onLook) \
OP_U(kObjectRegisterUseHandler, onUse) \
OP_U(kObjectRegisterHandlerC1, onObjectC1) \
OP_U(kObjectRegisterCharacterTrap, onCharacterTrap) \
OP_U(kObjectRegisterHandlerBD, onObjectBD) \
OP(kLoadMouseCursorFromObject, loadMouseCursorFromObject) \
OP(kLoadRegionFromObject, loadRegionFromObject) \
OP(kLoadPictureFromObject, loadPictureFromObject) \
OP(kLoadAnimationFromObject, loadAnimationFromObject) \
OP(kHideCharacter, hideCharacter) \
OP(kShowCharacter, showCharacter) \
OP(kEnableCharacter, enableCharacter) \
OP(kMoveCharacterUserMove, moveCharacterUserMove) \
OP(kLeaveCharacter, leaveCharacter) \
OP(kSetCharacter, setCharacter) \
OP(kSetCharacterDirection, setCharacterDirection) \
OP(kPointCharacter, pointCharacter) \
OP(kDisableUser, disableUser) \
OP(kEnableUser, enableUser) \
OP(kRestartSample, restartSample) \
OP(kStopSample, stopSample) \
OP(kStub102, stub102) \
OP(kClearScreen, clearScreen) \
OP(kInventoryClear, inventoryClear) \
OP(kLoadMouse, loadMouse) \
OP(kResetMousePointer, resetMousePointer) \
OP(kInventoryAddObject, inventoryAddObject) \
OP(kInventoryRemoveObject, inventoryRemoveObject) \
OP_U(kObjectRegisterUseObjectHandler, onObjectUse) \
OP(kObjectPatchSetText, objectPatchSetText) \
OP(kObjectPatchSetRegionName, objectPatchSetRegionName) \
OP(kScreenPatchSetRegionName, screenPatchSetRegionName) \
OP(kAnimateCharacter, animateCharacter) \
OP(kLoadCharacter, loadCharacter) \
OP(kAssociateCharacter, associateCharacter) \
OP(kSetObjectZ, setObjectZ) \
OP(kSetScreenBackground, setScreenBackground) \
OP(kLoadTextFromObject, loadTextFromObject) \
OP(kCompareScreenName, compareScreenName) \
OP(kScreenSetZNearFar, screenSetZNearFar) \
OP(kScreenLoadObject, loadScreenObject) \
OP(kScreenLoadRegion, loadScreenRegion) \
OP(kScreenCloneObject, cloneObject) \
OP(kSetNextScreen, setNextScreen) \
OP(kSetNextScreenSaveOrLoad, setNextScreenSaveOrLoad) \
OP(kScreenRemoveObject, removeScreenObject) \
OP(kLoadAnimation, loadAnimation) \
OP(kLoadSample, loadSample) \
OP(kSetPhaseVarControlledFlag, setPhaseVarControlledFlag) \
OP(kPlayerSay120, playerSay120) \
OP(kPlayerSay122, playerSay122) \
OP(kPlayerSay125, playerSay125) \
OP(kNPCSay, npcSay) \
OP(kNPCSayNoSound, npcSayNoSound) \
OP(kSetTimer, setTimer) \
OP(kProcessResetState, resetState) \
OP(kSetAnimationZ, setAnimationZ) \
OP(kSetCycles, setCycles) \
OP(kSetRandom, setRandom) \
OP(kSetPeriodic, setPeriodic) \
OP(kSetPanAndVolume, setPanAndVolume) \
OP(kSetAnimationPosition, setAnimationPosition) \
OP(kSetPhaseVar, setPhaseVar) \
OP(kSetAnimationLoop, setAnimationLoop) \
OP(kSetAnimationSpeed, setAnimationSpeed) \
OP(kScreenObjectPatchIncRef, screenObjectPatchIncRef) \
OP(kScreenObjectPatchDecRef, screenObjectPatchDecRef) \
OP(kGetSavedMouseX, getSavedMouseX) \
OP(kGetSavedMouseY, getSavedMouseY) \
OP(kScreenCheckScreenPatch, checkScreenPatch) \
OP(kGetFreeInventorySpace, getInventoryFreeSpace) \
OP(kSetStringSystemVariable, setStringSystemVariable) \
OP(kSetSystemIntegerVariable, setIntegerSystemVariable) \
OP(kGetRegionCenterX, getRegionCenterX) \
OP(kGetRegionCenterY, getRegionCenterY) \
OP(kGetCharacterAnimationPhase, getCharacterAnimationPhase) \
OP(kGetCharacterX, getCharacterX) \
OP(kGetCharacterY, getCharacterY) \
OP(kGetIntegerSystemVariable, getIntegerSystemVariable) \
OP(kGetRandomNumber, getRandomNumber) \
OP(kAppendToSharedStorage, appendToSharedStorage) \
OP(kAppendNameToSharedStorage, appendNameToSharedStorage) \
OP(kCloneName, cloneName) \
OP(kGetCloneVar, getCloneVar) \
OP(kSetCloneVar, setCloneVar) \
OP(kGetPictureBaseX, getPictureBaseX) \
OP(kGetPictureBaseY, getPictureBaseY) \
OP(kGetObjectSurfaceX, getObjectSurfaceX) \
OP(kGetObjectSurfaceY, getObjectSurfaceY) \
OP(kLoadGame, loadGame) \
OP(kSaveGame, saveGame) \
OP(kLoadSaveSlotNamePicture, loadSaveSlotNamePicture) \
OP(kGetSaveGameName, getSaveGameName) \
OP(kSetObjectRegionOffset, setObjectRegionOffset) \
OP(kSetDelay, setDelay) \
OP(kSetSampleVolume, setSampleVolume) \
OP(kStub173, stub173) \
OP(kStub174, stub174) \
OP(kObjectIgnoreRegion, objectIgnoreRegion) \
OP(kQuit, quit) \
OP(kStartNewGame, startNewGame) \
OP(kDisableInventory, disableInventory) \
OP(kEnableInventory, enableInventory) \
OP(kLoadPreviousScreen, loadPreviousScreen) \
OP(kMoveScreenObject, moveScreenObject) \
OP(kGetObjectId, getObjectId) \
OP(kSetTileSize, setTileSize) \
OP(kGenerateRegion, generateRegion) \
OP(kGetMaxInventorySize, getMaxInventorySize) \
OP(kAppendInventoryObjectNameToSharedSpace, appendInventoryObjectNameToSharedSpace) \
OP(kSetObjectTile, setObjectTile) \
OP(kInventoryHasObject, inventoryHasObject) \
OP(kReturnCurrentInventoryObject, returnCurrentInventoryObject) \
OP(kSetObjectText, setObjectText) \
OP(kSetObjectScale, setObjectScale) \
OP(kDisableMouseAreas, disableMouseAreas) \
OP(kRemoveGapsFromInventory, removeGapsFromInventory) \
OP(kSampleAmbient, sampleAmbient) \
OP(kGetObjectPictureWidth, getObjectPictureWidth) \
OP(kGetObjectPictureHeight, getObjectPictureHeight) \
OP(kLoadPicture, loadPicture) \
OP(kSetRotation, setRotation) \
OP(kStub199, stub199) \
OP(kSetSampleVolumeAndPan, setSampleVolumeAndPan) \
OP(kAddSampleToSoundGroup, addSampleToSoundGroup) \
OP(kClearSoundGroup, clearSoundGroup) \
OP(kStub216, stub216) \
OP(kStub217, stub217) \
OP(kStopCharacter, stopCharacter) \
OP(kLeaveCharacterEx, leaveCharacterEx) \
OP(kRestartAnimation, restartAnimation) \
OP(kAnimationNextFrame, animationNextFrame) \
OP(kSignalAnimationEnd, signalAnimationEnd) \
OP(kSetShadowIntensity, setShadowIntensity) \
OP(kSetNPCTellNotifyVar, setNPCTellNotifyVar) \
OP(kPauseAnimation, pauseAnimation) \
OP(kFadeObject, fadeObject) \
OP(kLoadFont, loadFont) \
OP_U(kSetThrowHandler, setThrowHandler) \
OP_U(kSetUseOnHandler, setUseOnHandler) \
OP(kPlayFilm, playFilm) \
OP(kAddMouseArea, addMouseArea) \
OP(kSetRain, setRain) \
OP(kSetRainDensity, setRainDensity) \
OP(kFogOnCharacter, fogOnCharacter) \
OP(kSetTileIndex, setTileIndex) \
OP(kModifyMouseArea, modifyMouseArea) \
OP_U(kObjectRegisterUserUseHandler, onObjectUserUse) \
OP(kMoveCharacterNoUserMove, moveCharacterNoUserMove) \
OP_U(kOnKey, onKey) \
OP(kGetSampleVolume, getSampleVolume) \
OP(kStub231, stub231) \
OP(kObjectFreePictureAndAnimation, objectFreePictureAndAnimation) \
OP(kFadeScreen, fadeScreen) \
OP(kUserEnabled, userEnabled) \
OP(kSetCharacterNotifyVars, setCharacterNotifyVars) \
OP(kInventoryFindObjectByName, inventoryFindObjectByName) \
OP(kInventoryHasObjectByName, inventoryHasObjectByName) \
OP(kLoadDialog, loadDialog) \
OP(kStub241, stub241) \
OP(kHasGlobal, hasGlobal) \
OP(kAttachInventoryObjectToMouse0, attachInventoryObjectToMouse0) \
OP(kAttachInventoryObjectToMouse1, attachInventoryObjectToMouse1) \
OP(kSetDialogForNextFilm, setDialogForNextFilm) \
OP(kCharacterStub253, characterStub253) \
OP(kStub251, stub251) \
OP(kStub257, stub257) \
OP(kStub258, stub258) \
OP(kStub264, stub264) \
OP(kStub265, stub265) \
OP(kStub266, stub266) \
OP(kStub267, stub267) \
OP(kStub275, stub275) \
OP(kStub276, stub276) \
OP(kSetCamera, setCamera) \
OP(kSetFog, setFog) \
OP(kStub278, stub278) \
OP(kStub279, stub279) \
OP(kStub280, stub280) \
OP(kStub284, stub284) \
OP(kStub291, stub291) \
OP(kStub292, stub292)
} // namespace AGDS
#endif

134
engines/agds/patch.cpp Normal file
View File

@@ -0,0 +1,134 @@
/* 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 "agds/patch.h"
#include "agds/resourceManager.h"
#include "common/debug.h"
#include "common/error.h"
#include "common/stream.h"
namespace AGDS {
void ObjectPatch::load(Common::ReadStream &stream) {
text = readString(stream);
region = readString(stream);
z = stream.readUint16LE();
}
void ObjectPatch::save(Common::WriteStream &stream) const {
writeString(stream, text);
writeString(stream, region);
stream.writeUint16LE(z);
}
void Patch::load(Common::ReadStream &stream) {
screenSaved = stream.readByte();
screenRegionName = readString(stream);
prevScreenName = readString(stream);
debug("patch screen, valid: %d region: %s, prev: %s", screenSaved, screenRegionName.c_str(), prevScreenName.c_str());
loadingType = static_cast<ScreenLoadingType>(stream.readUint32LE());
characterPosition.x = stream.readSint32LE();
characterPosition.y = stream.readSint32LE();
characterDirection = stream.readSint32LE();
characterPresent = stream.readUint32LE();
debug("character %s at %u,%u with dir: %d", characterPresent ? "[present]" : "[absent]", characterPosition.x, characterPosition.y, characterDirection);
uint object_count = stream.readUint32LE();
debug("objects in this patch: %u", object_count);
if (stream.read(palette, sizeof(palette)) != sizeof(palette)) {
error("short read, can't read palette");
}
defaultMouseCursor = readString(stream);
debug("default pointer name: %s", defaultMouseCursor.c_str());
objects.clear();
for (uint i = 0; i < object_count; ++i) {
int flag = stream.readSint16LE();
Common::String name = readString(stream);
debug("patch object %s %d", name.c_str(), flag);
objects.push_back(Object(name, flag));
}
}
void Patch::save(Common::WriteStream &stream) {
stream.writeByte(screenSaved);
writeString(stream, screenRegionName);
writeString(stream, prevScreenName);
stream.writeUint32LE(static_cast<uint>(loadingType));
stream.writeUint32LE(characterPosition.x);
stream.writeUint32LE(characterPosition.y);
stream.writeSint32LE(characterDirection);
stream.writeUint32LE(characterPresent);
stream.writeUint32LE(objects.size());
if (stream.write(palette, sizeof(palette)) != sizeof(palette)) {
error("short write, can't write palette");
}
writeString(stream, defaultMouseCursor);
for (auto &object : objects) {
stream.writeSint16LE(object.flag);
writeString(stream, object.name);
}
}
void Patch::setFlag(const Common::String &name, int flag) {
for (auto &object : objects) {
if (object.name == name) {
object.flag = flag;
return;
}
}
objects.push_back({name, flag});
}
int Patch::getFlag(const Common::String &name) const {
for (auto &object : objects) {
if (object.name == name)
return object.flag;
}
return 0;
}
int Patch::incRef(const Common::String &name) {
for (auto &object : objects) {
if (object.name == name) {
return ++object.flag;
}
}
objects.push_back({name, 1});
return 1;
}
int Patch::decRef(const Common::String &name) {
for (auto &object : objects) {
if (object.name == name) {
// this is original code lol
object.flag = 0;
return 0;
}
}
objects.push_back({name, 0});
return 0;
}
} // namespace AGDS

82
engines/agds/patch.h Normal file
View File

@@ -0,0 +1,82 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef PATCH_H
#define PATCH_H
#include "agds/screenLoadingType.h"
#include "common/array.h"
#include "common/rect.h"
#include "common/scummsys.h"
#include "common/str.h"
namespace Common {
class ReadStream;
class WriteStream;
} // namespace Common
namespace AGDS {
class AGDSEngine;
class Object;
struct ObjectPatch {
static constexpr unsigned Size = 66;
Common::String text;
Common::String region;
int z;
void load(Common::ReadStream &stream);
void save(Common::WriteStream &stream) const;
};
struct Patch {
struct Object {
Common::String name;
int16 flag;
Object(const Common::String &n, int f) : name(n), flag(f) {}
};
bool screenSaved = false;
Common::String screenRegionName;
Common::String prevScreenName;
ScreenLoadingType loadingType = ScreenLoadingType::Normal;
Common::Point characterPosition;
int characterDirection = 0;
bool characterPresent = false;
byte palette[0x300] = {};
Common::String defaultMouseCursor;
Common::Array<Object> objects;
void load(Common::ReadStream &stream);
void save(Common::WriteStream &stream);
void setFlag(const Common::String &name, int flag);
int getFlag(const Common::String &name) const;
int incRef(const Common::String &name);
int decRef(const Common::String &name);
};
} // End of namespace AGDS
#endif /* AGDS_PATCH_H */

534
engines/agds/process.cpp Normal file
View File

@@ -0,0 +1,534 @@
/* 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 "agds/process.h"
#include "agds/agds.h"
#include "agds/animation.h"
#include "common/debug.h"
#include "common/system.h"
namespace AGDS {
Process::Process(AGDSEngine *engine, const ObjectPtr &object, unsigned ip, int version) : _engine(engine), _parentScreen(engine->getCurrentScreenName()), _object(object),
_entryPoint(ip), _ip(ip), _lastIp(ip),
_status(kStatusActive), _exited(false), _exitCode(kExitCodeDestroy),
_tileWidth(16), _tileHeight(16), _tileResource(-1), _tileIndex(0),
_timer(0),
_animationCycles(1), _animationLoop(false), _animationZ(0), _animationDelay(-1), _animationRandom(0),
_phaseVarControlled(false), _animationSpeed(100),
_samplePeriodic(false), _sampleAmbient(false), _sampleVolume(100),
_filmSubtitlesResource(-1), _version(version) {
updateWithCurrentMousePosition();
}
void Process::debug(const char *str, ...) {
va_list va;
va_start(va, str);
Common::String format = Common::String::format("%s %04x[%u]: %s", _object->getName().c_str(), _lastIp, _stack.size(), str);
Common::String buf = Common::String::vformat(format.c_str(), va);
buf += '\n';
if (g_system)
g_system->logMessage(LogMessageType::kDebug, buf.c_str());
va_end(va);
}
// fixme: remove copy-paste
void Process::error(const char *str, ...) {
va_list va;
va_start(va, str);
Common::String format = Common::String::format("WARNING: %s %04x[%u]: %s", _object->getName().c_str(), _lastIp + 7, _stack.size(), str);
Common::String buf = Common::String::vformat(format.c_str(), va);
buf += '\n';
if (g_system)
g_system->logMessage(LogMessageType::kWarning, buf.c_str());
va_end(va);
_status = kStatusError;
}
void Process::suspend(ProcessExitCode exitCode, const Common::String &arg1, const Common::String &arg2) {
debug("suspend %d", exitCode);
_exited = true;
_exitCode = exitCode;
_exitIntArg1 = 0;
_exitIntArg2 = 0;
_exitArg1 = arg1;
_exitArg2 = arg2;
}
void Process::suspend(ProcessExitCode exitCode, int arg1, int arg2) {
debug("suspend %d", exitCode);
_exited = true;
_exitCode = exitCode;
_exitIntArg1 = arg1;
_exitIntArg2 = arg2;
_exitArg1.clear();
_exitArg2.clear();
}
uint8 Process::next() {
const Object::CodeType &code = _object->getCode();
if (_ip < code.size()) {
return code[_ip++];
} else {
_status = kStatusError;
return 0;
}
}
void Process::jump(int16 delta) {
_ip += delta;
}
void Process::jumpz(int16 delta) {
int value = pop();
if (value == 0) {
debug("jumpz %d %+d", value, delta);
_ip += delta;
} else
debug("jumpz ignored %d %+d", value, delta);
}
void Process::suspend() {
suspend(kExitCodeSuspend);
}
void Process::suspendIfPassive() {
if (passive())
suspend();
}
int32 Process::pop() {
if (_stack.empty()) {
error("stack underflow at %s:%04x", _object->getName().c_str(), _lastIp + 7);
return 0;
}
return _stack.pop();
}
int32 Process::top() {
if (_stack.empty()) {
error("stack underflow, %s:%04x", _object->getName().c_str(), _lastIp + 7);
return 0;
}
return _stack.top();
}
Common::String Process::getString(int id) {
if (id == -1)
return Common::String();
else if (id <= -2 && id > -12)
return _engine->getSharedStorage(id);
else
return _object->getString(id).string;
}
Common::String Process::popText() {
return _engine->loadText(popString());
}
uint32 Process::popColor() {
int b = pop();
int g = pop();
int r = pop();
return _engine->pixelFormat().RGBToColor(r, g, b);
}
void Process::updateWithCurrentMousePosition() {
setMousePosition(_engine->mousePosition());
}
void Process::setupAnimation(const AnimationPtr &animation) {
animation->position(_animationPosition);
animation->z(_animationZ);
animation->process(getName());
animation->phaseVar(_phaseVar);
animation->loop(_animationLoop);
animation->cycles(_animationCycles);
animation->delay(_animationDelay);
animation->setRandom(_animationRandom);
animation->phaseVarControlled(_phaseVarControlled);
if (_phaseVar.empty())
suspend();
else if (_phaseVarControlled)
_engine->setGlobal(_phaseVar, 0);
}
void Process::removeProcessAnimation() {
if (_processAnimation) {
auto *screen = _engine->getCurrentScreen();
if (screen)
screen->remove(_processAnimation);
_processAnimation = nullptr;
}
}
void Process::activate() {
switch (status()) {
case kStatusPassive:
_status = Process::kStatusActive;
break;
case kStatusDone:
case kStatusError:
debug("finished");
break;
default:
break;
}
}
void Process::deactivate() {
switch (status()) {
case kStatusActive:
debug("deactivated");
_status = Process::kStatusPassive;
break;
case kStatusDone:
case kStatusError:
break;
default:
break;
}
}
void Process::done() {
if (_status != kStatusDone) {
debug("done");
_status = kStatusDone;
if (!_stack.empty())
warning("process %s finished with non-empty stack", getName().c_str());
}
}
void Process::fail() {
_status = kStatusError;
}
void Process::run() {
bool restart = true;
while (_status != kStatusDone && _status != kStatusError && restart) {
restart = false;
ProcessExitCode code = resume();
switch (code) {
case kExitCodeDestroy:
debug("process %s returned destroy exit code", getName().c_str());
done();
if (!getObject()->alive() && !_engine->hasActiveProcesses(getName())) {
removeScreenObject(getName());
}
break;
case kExitCodeLoadScreenObjectAs:
case kExitCodeLoadScreenObject:
deactivate();
_object->lock();
_engine->runObject(getExitArg1(), getExitArg2());
_object->unlock();
activate();
restart = true;
break;
case kExitCodeRunDialog:
deactivate();
_object->lock();
_engine->runObject(getExitArg1());
_object->unlock();
break;
case kExitCodeSetNextScreen:
case kExitCodeSetNextScreenSaveOrLoad:
done();
debug("process %s launches screen: %s, saveorload: ", getName().c_str(), getExitArg1().c_str(), code == kExitCodeSetNextScreenSaveOrLoad);
_engine->setNextScreenName(getExitArg1(), code == kExitCodeSetNextScreenSaveOrLoad ? ScreenLoadingType::SaveOrLoad : ScreenLoadingType::Normal);
break;
case kExitCodeMouseAreaChange:
deactivate();
_object->lock();
_engine->changeMouseArea(getExitIntArg1(), getExitIntArg2());
_object->unlock();
activate();
restart = true;
break;
case kExitCodeLoadInventoryObject:
deactivate();
_object->lock();
_engine->inventory().add(getExitArg1());
_object->unlock();
activate();
restart = true;
break;
case kExitCodeCloseInventory:
_object->lock();
_engine->inventory().enable(false);
updateWithCurrentMousePosition();
_object->unlock();
break;
case kExitCodeSuspend:
updateWithCurrentMousePosition();
break;
case kExitCodeNewGame:
deactivate();
_object->lock();
debug("exitProcessNewGame");
_engine->newGame();
_object->unlock();
activate();
restart = true;
break;
case kExitCodeLoadGame:
deactivate();
_object->lock();
if (_engine->loadGameState(getExitIntArg1()).getCode() == Common::kNoError) {
done();
} else {
debug("save loading failed, resuming execution...");
}
_object->unlock();
activate();
restart = true;
break;
case kExitCodeSaveGame:
break;
default:
error("unknown process exit code %d", code);
}
}
}
#define UNARY_OP(NAME, OP) \
void Process::NAME() { \
int32 arg = pop(); \
debug(#NAME " %d", arg); \
push(OP arg); \
}
#define BINARY_OP(NAME, OP) \
void Process::NAME() { \
int32 arg2 = pop(); \
int32 arg1 = pop(); \
debug(" %d " #NAME " %d", arg1, arg2); \
push(arg1 OP arg2); \
}
UNARY_OP(boolNot, !)
UNARY_OP(bitNot, ~)
UNARY_OP(negate, -)
BINARY_OP(shl, <<)
BINARY_OP(shr, >>)
BINARY_OP(boolOr, ||)
BINARY_OP(boolAnd, &&)
BINARY_OP(equals, ==)
BINARY_OP(notEquals, !=)
BINARY_OP(greater, >)
BINARY_OP(greaterOrEquals, >=)
BINARY_OP(less, <)
BINARY_OP(lessOrEquals, <=)
BINARY_OP(add, +)
BINARY_OP(sub, -)
BINARY_OP(mul, *)
BINARY_OP(div, /)
BINARY_OP(mod, %)
BINARY_OP(bitAnd, &)
BINARY_OP(bitOr, |)
BINARY_OP(bitXor, ^)
#undef UNARY_OP
#undef BINARY_OP
// fixme: add trace here
#define AGDS_OP(NAME, METHOD) \
case NAME: \
METHOD(); \
break;
#define AGDS_OP_C(NAME, METHOD) \
case NAME: { \
int8 arg = next(); \
METHOD(arg); \
} break;
#define AGDS_OP_B(NAME, METHOD) \
case NAME: { \
uint8 arg = next(); \
METHOD(arg); \
} break;
#define AGDS_OP_W(NAME, METHOD) \
case NAME: { \
int16 arg = next16(); \
METHOD(arg); \
} break;
#define AGDS_OP_U(NAME, METHOD) \
case NAME: { \
uint16 arg = next16(); \
METHOD(arg); \
} break;
#define AGDS_OP_UU(NAME, METHOD) \
case NAME: { \
uint16 arg1 = next16(); \
uint16 arg2 = next16(); \
METHOD(arg1, arg2); \
} break;
#define AGDS_OP_UD(NAME, METHOD) \
case NAME: { \
uint16 arg1 = next16(); \
uint32 arg2 = next16(); \
METHOD(static_cast<int32>(arg1 | (arg2 << 16))); \
} break;
uint16 Process::nextOpcode() {
uint16 op = next();
switch (_version) {
case 0:
return op + 5;
case 2:
if (op & 1) {
op |= next() << 8;
op >>= 1;
}
return op - 7995;
default:
return op;
}
}
ProcessExitCode Process::resume() {
_exitCode = kExitCodeDestroy;
if (_timer) {
--_timer;
return kExitCodeSuspend;
}
const Object::CodeType &code = _object->getCode();
while (!_exited && _ip < code.size()) {
if (_timer) {
return kExitCodeSuspend;
}
_lastIp = _ip;
auto op = nextOpcode();
// debug("CODE %04x: %u", _lastIp, (uint)op);
switch (op) {
AGDS_OPCODE_LIST(
AGDS_OP,
AGDS_OP_C, AGDS_OP_B,
AGDS_OP_W, AGDS_OP_U,
AGDS_OP_UD, AGDS_OP_UU)
default:
error("%s: %08x: unknown opcode 0x%02x (%u)", _object->getName().c_str(), _lastIp, (unsigned)op, (unsigned)op);
fail();
break;
}
}
_exited = false;
return _exitCode;
}
#define AGDS_DIS(NAME, METHOD) \
case NAME: \
source += Common::String::format("%s\n", #NAME); \
break;
#define AGDS_DIS_C(NAME, METHOD) \
case NAME: { \
int8 arg = code[ip++]; \
source += Common::String::format("%s %d\n", #NAME, (int)arg); \
} break;
#define AGDS_DIS_B(NAME, METHOD) \
case NAME: { \
uint8 arg = code[ip++]; \
source += Common::String::format("%s %u\n", #NAME, (uint)arg); \
} break;
#define AGDS_DIS_W(NAME, METHOD) \
case NAME: { \
int16 arg = code[ip++]; \
arg |= ((int16)code[ip++]) << 8; \
source += Common::String::format("%s %d\n", #NAME, (int)arg); \
} break;
#define AGDS_DIS_U(NAME, METHOD) \
case NAME: { \
uint16 arg = code[ip++]; \
arg |= ((uint16)code[ip++]) << 8; \
source += Common::String::format("%s %u\n", #NAME, (uint)arg); \
} break;
#define AGDS_DIS_UU(NAME, METHOD) \
case NAME: { \
uint16 arg1 = code[ip++]; \
arg1 |= ((uint16)code[ip++]) << 8; \
uint16 arg2 = code[ip++]; \
arg2 |= ((uint16)code[ip++]) << 8; \
source += Common::String::format("%s %u %u\n", #NAME, (uint)arg1, (uint)arg2); \
} break;
#define AGDS_DIS_UD(NAME, METHOD) \
case NAME: { \
uint16 arg1 = code[ip++]; \
arg1 |= ((uint16)code[ip++]) << 8; \
uint16 arg2 = code[ip++]; \
arg2 |= ((uint16)code[ip++]) << 8; \
source += Common::String::format("%s %u\n", #NAME, (uint)(arg1 | (arg2 << 16))); \
} break;
Common::String Process::disassemble(const ObjectPtr &object, int version) {
Common::String source = Common::String::format("Object %s disassembly:\n", object->getName().c_str());
const auto &code = object->getCode();
uint ip = 0;
while (ip < code.size()) {
uint16 op = code[ip++];
if (version == 2) {
if (op & 1) {
op |= code[ip++] << 8;
op >>= 1;
}
} else if (version == 0) {
op += 5;
}
source += Common::String::format("%04x: %02x: ", ip - 1, op);
switch (op) {
AGDS_OPCODE_LIST(
AGDS_DIS,
AGDS_DIS_C, AGDS_DIS_B,
AGDS_DIS_W, AGDS_DIS_U,
AGDS_DIS_UD, AGDS_DIS_UU)
default:
source += Common::String::format("unknown opcode 0x%02x (%u)\n", (unsigned)op, (unsigned)op);
break;
}
}
source += Common::String::format("Object %s disassembly end\n", object->getName().c_str());
return source;
}
} // namespace AGDS

220
engines/agds/process.h Normal file
View File

@@ -0,0 +1,220 @@
/* 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 PROCESS_H
#define PROCESS_H
#include "agds/object.h"
#include "agds/opcode.h"
#include "agds/processExitCode.h"
#include "common/debug.h"
#include "common/scummsys.h"
#include "common/stack.h"
namespace AGDS {
class AGDSEngine;
class Screen;
class Animation;
using AnimationPtr = Common::SharedPtr<Animation>;
class Process {
public:
enum Status { kStatusActive,
kStatusPassive,
kStatusDone,
kStatusError };
private:
using StackType = Common::Stack<int32>;
AGDSEngine *_engine;
Common::String _parentScreen;
ObjectPtr _object;
StackType _stack;
unsigned _entryPoint;
unsigned _ip, _lastIp;
Status _status;
bool _exited;
ProcessExitCode _exitCode;
Common::String _exitArg1, _exitArg2;
int _exitIntArg1, _exitIntArg2;
int _tileWidth, _tileHeight;
int _tileResource;
int _tileIndex;
Common::String _phaseVar;
int _timer;
int _animationCycles;
bool _animationLoop;
Common::Point _animationPosition;
int _animationZ;
int _animationDelay;
int _animationRandom;
bool _phaseVarControlled;
int _animationSpeed;
bool _samplePeriodic;
bool _sampleAmbient;
int32 _sampleVolume;
Common::Point _mousePosition;
int _filmSubtitlesResource;
AnimationPtr _processAnimation;
int _version;
private:
void debug(const char *str, ...);
void error(const char *str, ...);
void push(bool);
uint8 next();
uint16 next16() {
uint8 l = next();
uint16 h = next();
return (h << 8) | l;
}
uint16 nextOpcode();
int32 pop();
int32 top();
Common::String getString(int id);
Common::String popString() {
return getString(pop());
}
Common::String popText();
uint32 popColor();
#define AGDS_PROCESS_METHOD(opcode, method) \
void method();
#define AGDS_PROCESS_METHOD_C(opcode, method) \
void method(int8);
#define AGDS_PROCESS_METHOD_B(opcode, method) \
void method(uint8);
#define AGDS_PROCESS_METHOD_W(opcode, method) \
void method(int16);
#define AGDS_PROCESS_METHOD_U(opcode, method) \
void method(uint16);
#define AGDS_PROCESS_METHOD_UD(opcode, method) \
void method(int32);
#define AGDS_PROCESS_METHOD_UU(opcode, method) \
void method(uint16, uint16);
AGDS_OPCODE_LIST(AGDS_PROCESS_METHOD,
AGDS_PROCESS_METHOD_C, AGDS_PROCESS_METHOD_B, AGDS_PROCESS_METHOD_W,
AGDS_PROCESS_METHOD_U, AGDS_PROCESS_METHOD_UD, AGDS_PROCESS_METHOD_UU)
void moveCharacter(bool usermove);
void tell(bool npc, const Common::String &sound);
void tell(bool npc) { tell(npc, Common::String()); }
Common::String getCloneVarName(const Common::String &arg1, const Common::String &arg2);
void suspend(ProcessExitCode exitCode, const Common::String &arg1, const Common::String &arg2 = Common::String());
void suspend(ProcessExitCode exitCode, int arg1 = 0, int arg2 = 0);
ProcessExitCode resume();
void suspendIfPassive();
void setupAnimation(const AnimationPtr &animation);
void attachInventoryObjectToMouse(bool flag);
void leaveCharacter(const Common::String &name, const Common::String &regionName, int dir);
void removeScreenObject(const Common::String &name);
public:
Process(AGDSEngine *engine, const ObjectPtr &object, unsigned ip, int version);
unsigned entryPoint() const {
return _entryPoint;
}
static Common::String disassemble(const ObjectPtr &object, int version);
ObjectPtr getObject() const {
return _object;
}
const Common::String &getName() const {
return _object->getName();
}
const Common::String &parentScreenName() const {
return _parentScreen;
}
Status status() const {
return _status;
}
bool active() const {
return _status == kStatusActive;
}
bool passive() const {
return _status == kStatusPassive;
}
void activate();
void deactivate();
void done();
void fail();
bool finished() const {
return _status == kStatusDone || _status == kStatusError;
}
void run();
ProcessExitCode getExitCode() const {
return _exitCode;
}
const Common::String &getExitArg1() const {
return _exitArg1;
}
int getExitIntArg1() const {
return _exitIntArg1;
}
const Common::String &getExitArg2() const {
return _exitArg2;
}
int getExitIntArg2() const {
return _exitIntArg2;
}
void setMousePosition(Common::Point mousePosition) {
_mousePosition = mousePosition;
}
void updateWithCurrentMousePosition();
const Common::String &phaseVar() const {
return _phaseVar;
}
const AnimationPtr &processAnimation() const {
return _processAnimation;
}
void removeProcessAnimation();
};
} // End of namespace AGDS
#endif /* AGDS_PROCESS_H */

View File

@@ -0,0 +1,46 @@
/* 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 PROCESS_EXIT_CODE_H
#define PROCESS_EXIT_CODE_H
namespace AGDS {
enum ProcessExitCode {
kExitCodeDestroy = 2,
kExitCodeSuspend = 5,
kExitCodeSetNextScreen = 6,
kExitCodeSetNextScreenSaveOrLoad = 7,
kExitCodeLoadScreenObject = 8,
kExitCodeLoadScreenObjectAs = 9,
kExitCodeLoadInventoryObject = 10,
kExitCodeMouseAreaChange = 11,
kExitCodeRunDialog = 12,
kExitCodeNewGame = 13,
kExitCodeLoadGame = 14,
kExitCodeExitScreen = 15,
kExitCodeCloseInventory = 16,
kExitCodeSaveGame = 17,
};
}
#endif

File diff suppressed because it is too large Load Diff

171
engines/agds/region.cpp Normal file
View File

@@ -0,0 +1,171 @@
/* 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 "agds/region.h"
#include "agds/resourceManager.h"
#include "common/algorithm.h"
#include "common/debug.h"
#include "common/endian.h"
#include "common/textconsole.h"
namespace AGDS {
Region::Region(const Common::String &resourceName, Common::SeekableReadStream &stream) {
auto size = stream.size();
name = readString(stream);
center.x = stream.readSint16LE();
center.y = stream.readSint16LE();
flags = stream.readUint16LE();
debug("region %s at (%d,%d) %04x", name.c_str(), center.x, center.y, flags);
while (stream.pos() + 2 <= size) {
uint16 ext = stream.readUint16LE();
if (ext)
debug("extended entries %u", ext);
PointsType points;
while (ext--) {
int16 a = stream.readSint16LE();
int16 b = stream.readSint16LE();
int16 c = stream.readUint16LE();
if (c != -12851) // 0xcdcd
debug("extended entry: %d %d %d", a, b, c);
else
debug("extended entry: %d %d", a, b);
points.push_back(Common::Point(a, b));
}
regions.push_back(points);
}
}
Region::Region(const Common::Rect rect) : flags(0) {
PointsType points;
points.push_back(Common::Point(rect.left, rect.top));
points.push_back(Common::Point(rect.right, rect.top));
points.push_back(Common::Point(rect.right, rect.bottom));
points.push_back(Common::Point(rect.left, rect.bottom));
regions.push_back(points);
center.x = (rect.left + rect.right) / 2;
center.y = (rect.top + rect.bottom) / 2;
}
Common::String Region::toString() const {
Common::String str = Common::String::format("region(%d, %d, [", center.x, center.y);
for (size_t i = 0; i < regions.size(); ++i) {
if (i != 0)
str += ", ";
str += "Region {";
const PointsType &points = regions[i];
for (size_t j = 0; j < points.size(); ++j) {
if (j != 0)
str += ", ";
str += Common::String::format("(%d, %d)", points[j].x, points[j].y);
}
str += "}";
}
str += "]";
return str;
}
void Region::move(Common::Point rel) {
if (rel.x == 0 && rel.y == 0)
return;
center += rel;
for (uint i = 0; i < regions.size(); ++i) {
PointsType &points = regions[i];
for (uint j = 0; j < points.size(); ++j)
points[j] += rel;
}
}
Common::Point Region::topLeft() const {
if (regions.empty())
return Common::Point();
Common::Point p = regions[0][0];
for (uint i = 0; i < regions.size(); ++i) {
const PointsType &points = regions[i];
for (uint j = 0; j < points.size(); ++j) {
Common::Point point = points[j];
if (point.x < p.x)
p.x = point.x;
if (point.y < p.y)
p.y = point.y;
}
}
return p;
}
// FIXME: copied from wintermute/base_region.cpp
struct dPoint {
double x, y;
};
bool Region::pointIn(Common::Point point) const {
for (uint r = 0; r < regions.size(); ++r) {
const PointsType &points = regions[r];
uint32 size = points.size();
if (size < 3) {
continue;
}
int counter = 0;
double xinters;
dPoint p, p1, p2;
p.x = (double)point.x;
p.y = (double)point.y;
p1.x = (double)points[0].x;
p1.y = (double)points[0].y;
for (uint32 i = 1; i <= size; i++) {
p2.x = (double)points[i % size].x;
p2.y = (double)points[i % size].y;
if (p.y > MIN(p1.y, p2.y)) {
if (p.y <= MAX(p1.y, p2.y)) {
if (p.x <= MAX(p1.x, p2.x)) {
if (p1.y != p2.y) {
xinters = (p.y - p1.y) * (p2.x - p1.x) / (p2.y - p1.y) + p1.x;
if (p1.x == p2.x || p.x <= xinters) {
counter++;
}
}
}
}
}
p1 = p2;
}
if (counter % 2 == 0) {
continue;
} else {
return true;
}
}
return false;
}
} // namespace AGDS

55
engines/agds/region.h Normal file
View File

@@ -0,0 +1,55 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef REGION_H
#define REGION_H
#include "common/array.h"
#include "common/rect.h"
#include "common/scummsys.h"
#include "common/stream.h"
namespace AGDS {
struct Region {
using PointsType = Common::Array<Common::Point>;
Common::String name;
Common::Point center;
uint16 flags;
Common::Array<PointsType> regions;
Region(const Common::String &resourceName, Common::SeekableReadStream &stream);
Region(const Common::Rect rect);
bool pointIn(Common::Point point) const;
Common::String toString() const;
void move(Common::Point rel);
Common::Point topLeft() const;
bool empty() const {
return regions.empty();
}
};
} // End of namespace AGDS
#endif /* AGDS_REGION_H */

View File

@@ -0,0 +1,262 @@
/* 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 "agds/resourceManager.h"
#include "common/algorithm.h"
#include "common/debug.h"
#include "common/file.h"
#include "common/memstream.h"
#include "common/path.h"
#include "common/ptr.h"
#include "graphics/surface.h"
#include "image/bmp.h"
#include "image/pcx.h"
#include "video/flic_decoder.h"
namespace AGDS {
ResourceManager::ResourceManager(int version) : _version(version) {}
ResourceManager::~ResourceManager() {}
void ResourceManager::decrypt(uint8 *data, unsigned size) {
static const char *kKey = "Vyvojovy tym AGDS varuje: Hackerovani skodi obchodu!";
const char *ptr = kKey;
while (size--) {
*data++ ^= 0xff ^ *ptr++;
if (*ptr == 0)
ptr = kKey;
}
}
bool ResourceManager::GrpFile::load(const Common::Path &grpPath) {
static const char *kSignature = "AGDS group file\x1a";
static const uint32 kMagic = 0x1a03c9e6;
static const uint32 kVersion1 = 44;
static const uint32 kVersion2 = 2;
debug("adding path %s", grpPath.toString().c_str());
if (!_file.open(grpPath)) {
warning("failing opening grp file %s", grpPath.toString().c_str());
return false;
}
uint8 header[0x2c];
if (_file.read(header, sizeof(header)) != sizeof(header)) {
warning("short read from header");
return false;
}
if (strncmp(reinterpret_cast<const char *>(header), kSignature, 0x10) != 0) {
decrypt(header, 0x10);
if (strncmp(reinterpret_cast<const char *>(header), kSignature, 0x10) != 0) {
warning("invalid signature");
return false;
}
debug("load grp file %s, encrypted", grpPath.toString().c_str());
_encrypted = true;
} else {
debug("load grp file %s, unencrypted", grpPath.toString().c_str());
_encrypted = false;
}
Common::MemoryReadStreamEndian reader(header + 0x10, sizeof(header) - 0x10, false);
uint32 version1 = reader.readUint32();
if (version1 != kVersion1) {
warning("invalid version 1 (%d)", version1);
return false;
}
uint32 magic = reader.readUint32();
if (magic != kMagic) {
warning("invalid magic (0x%08x)", magic);
return false;
}
uint32 version2 = reader.readUint32();
if (version2 != kVersion2) {
warning("invalid version 2 (%d)", version2);
return false;
}
unsigned dirCount = reader.readUint32();
if (!reader.skip(3 * 4))
return false;
// debug("+%u files in index", dirCount);
while (dirCount--) {
uint8 dirData[0x31];
uint8 *dirDataEnd = dirData + sizeof(dirData);
if (_file.read(dirData, sizeof(dirData)) != sizeof(dirData)) {
warning("short read, corrupted file");
return false;
}
uint8 *nameEnd = Common::find(dirData, dirDataEnd, 0);
if (nameEnd == dirDataEnd) {
warning("corrupted entry at %d", (int)_file.pos() - 0x31);
continue;
}
unsigned nameLength = nameEnd - dirData;
if (_encrypted)
decrypt(dirData, nameLength);
Common::String name(reinterpret_cast<char *>(dirData), nameLength);
Common::MemoryReadStreamEndian dirReader(dirData + 0x21, 8, false);
uint32 offset = dirReader.readSint32();
uint32 size = dirReader.readSint32();
// debug("\t\tfile %s %u %u", name.c_str(), offset, size);
ArchiveMemberPtr resource(new ArchiveMember(this, name, offset, size));
_members.setVal(name, resource);
}
debug("%s: %u files in index", grpPath.toString().c_str(), _members.size());
return true;
}
int ResourceManager::GrpFile::listMembers(Common::ArchiveMemberList &list) const {
int size = 0;
for (MembersType::const_iterator i = _members.begin(); i != _members.end(); ++i, ++size)
list.push_back(i->_value);
return size;
}
bool ResourceManager::GrpFile::hasFile(const Common::Path &name) const {
return _members.find(name.toString()) != _members.end();
}
const Common::ArchiveMemberPtr ResourceManager::GrpFile::getMember(const Common::Path &name) const {
Common::ArchiveMemberPtr member;
MembersType::const_iterator i = _members.find(name.toString());
if (i != _members.end())
member = i->_value;
return member;
}
Common::SeekableReadStream *ResourceManager::GrpFile::createReadStreamForMember(const Common::Path &name) const {
Common::ArchiveMemberPtr member = getMember(name);
return member ? member->createReadStream() : NULL;
}
bool ResourceManager::addPath(const Common::Path &grpFilename) {
Common::ScopedPtr<GrpFile> grpFile(new GrpFile());
if (!grpFile->load(grpFilename)) {
return false;
}
SearchMan.add(grpFilename.toString(), grpFile.release(), 0, true);
return true;
}
Common::SeekableReadStream *ResourceManager::ArchiveMember::createReadStream() const {
Common::SeekableReadStream &file = _parent->getArchiveStream();
file.seek(_offset);
return file.readStream(_size);
}
Common::SeekableReadStream *ResourceManager::getResource(const Common::String &name) const {
Common::File file;
return (file.open(Common::Path{name})) ? file.readStream(file.size()) : NULL;
}
bool ResourceManager::IsBMP(Common::SeekableReadStream &stream) {
auto b0 = stream.readByte();
auto b1 = stream.readByte();
stream.seek(-2, SEEK_CUR);
return b0 == 'B' && b1 == 'M';
}
Graphics::Surface *ResourceManager::loadPicture(const Common::String &name, const Graphics::PixelFormat &format) {
Common::SeekableReadStream *stream = getResource(name);
if (!stream)
return NULL;
auto is_bmp = IsBMP(*stream);
Common::String lname = name;
lname.toLowercase();
if (lname.hasSuffix(".bmp") || is_bmp) {
Image::BitmapDecoder bmp;
return bmp.loadStream(*stream) ? bmp.getSurface()->convertTo(format) : NULL;
} else if (lname.hasSuffix(".pcx")) {
Image::PCXDecoder pcx;
return pcx.loadStream(*stream) ? pcx.getSurface()->convertTo(format) : NULL;
} else if (lname.hasSuffix(".flc")) {
Video::FlicDecoder flic;
if (!flic.loadStream(stream)) {
warning("flic decoder failed to load %s", name.c_str());
return NULL;
}
const Graphics::Surface *surface = flic.decodeNextFrame();
return surface ? surface->convertTo(format, flic.getPalette()) : NULL;
} else
warning("unknown extensions for resource %s", name.c_str());
return NULL;
}
Common::String ResourceManager::loadText(Common::SeekableReadStream &stream) const {
Common::Array<char> text(stream.size());
if (stream.read(text.data(), text.size()) != text.size())
error("short read from text resource");
if (text.empty())
return Common::String();
char *begin = text.data();
char *end = begin + text.size();
while (begin != end && end[-1] == 0)
--end;
if (_version != 0 || (text[0] < ' ' || text[0] >= 0x7f))
decrypt(reinterpret_cast<uint8 *>(text.data()), end - begin);
while (begin != end && end[-1] == 0)
--end;
return Common::String(begin, end);
}
Common::String ResourceManager::loadText(const Common::String &name) const {
Common::ScopedPtr<Common::SeekableReadStream> stream(getResource(name));
if (!stream)
error("no text resource %s", name.c_str());
return loadText(*stream);
}
Common::String readString(Common::ReadStream &stream, uint size) {
Common::Array<char> text(size);
if (stream.read(text.data(), text.size()) != text.size())
error("readString: short read");
return Common::String(text.data(), strlen(text.data()));
}
void writeString(Common::WriteStream &stream, const Common::String &string, uint size) {
Common::Array<char> text(size);
memcpy(text.data(), string.c_str(), MIN(string.size(), size));
if (stream.write(text.data(), text.size()) != text.size())
error("writeString: short write");
}
} // End of namespace AGDS

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 RESOURCE_MANAGER_H
#define RESOURCE_MANAGER_H
#include "common/archive.h"
#include "common/file.h"
#include "common/hash-str.h"
#include "common/hashmap.h"
#include "common/ptr.h"
#include "common/scummsys.h"
#include "common/str.h"
namespace Graphics {
struct Surface;
struct PixelFormat;
} // namespace Graphics
namespace AGDS {
class ResourceManager {
private:
class GrpFile;
class ArchiveMember : public Common::ArchiveMember {
GrpFile *_parent;
Common::String _name;
uint32 _offset;
uint32 _size;
public:
ArchiveMember(GrpFile *parent, const Common::String &name, uint32 offset, uint32 size) : _parent(parent), _name(name), _offset(offset), _size(size) {
}
Common::SeekableReadStream *createReadStream() const override;
Common::String getName() const override {
return getFileName();
}
Common::Path getPathInArchive() const override {
return Common::Path{_name};
}
Common::String getFileName() const override {
return _name;
}
Common::SeekableReadStream *createReadStreamForAltStream(Common::AltStreamType altStreamType) const override {
return nullptr;
}
};
using ArchiveMemberPtr = Common::SharedPtr<ArchiveMember>;
class GrpFile : public Common::Archive {
Common::File _file;
bool _encrypted;
using MembersType = Common::HashMap<Common::String, ArchiveMemberPtr, Common::IgnoreCase_Hash, Common::IgnoreCase_EqualTo>;
MembersType _members;
public:
bool load(const Common::Path &path);
Common::SeekableReadStream &getArchiveStream() {
return _file;
}
bool hasFile(const Common::Path &name) const override;
int listMembers(Common::ArchiveMemberList &list) const override;
const Common::ArchiveMemberPtr getMember(const Common::Path &name) const override;
Common::SeekableReadStream *createReadStreamForMember(const Common::Path &name) const override;
};
static void decrypt(uint8 *data, unsigned size);
int _version;
public:
ResourceManager(int version);
~ResourceManager();
static bool IsBMP(Common::SeekableReadStream &stream);
bool addPath(const Common::Path &grpFilename);
Common::SeekableReadStream *getResource(const Common::String &name) const;
Graphics::Surface *loadPicture(const Common::String &name, const Graphics::PixelFormat &format);
Common::String loadText(Common::SeekableReadStream &stream) const;
Common::String loadText(const Common::String &name) const;
};
Common::String readString(Common::ReadStream &stream, uint size = 32);
void writeString(Common::WriteStream &stream, const Common::String &string, uint size = 32);
} // End of namespace AGDS
#endif /* AGDS_RESOURCE_MANAGER_H */

348
engines/agds/screen.cpp Normal file
View File

@@ -0,0 +1,348 @@
/* 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 "agds/screen.h"
#include "agds/agds.h"
#include "agds/animation.h"
#include "agds/character.h"
#include "agds/object.h"
#include "agds/patch.h"
#include "agds/region.h"
#include "common/system.h"
#include "graphics/managed_surface.h"
namespace AGDS {
int Screen::ObjectZCompare(const ObjectPtr &a, const ObjectPtr &b) {
return b->z() - a->z();
}
int Screen::AnimationZCompare(const ScreenAnimationDesc &a, const ScreenAnimationDesc &b) {
return b.animation->z() - a.animation->z();
}
Screen::Screen(AGDSEngine *engine, ObjectPtr object, ScreenLoadingType loadingType, const Common::String &prevScreen) : _engine(engine), _object(object), _background(nullptr),
_name(object->getName()), _loadingType(loadingType), _previousScreen(prevScreen),
_children(&ObjectZCompare), _animations(&AnimationZCompare), _applyingPatch(false),
_characterNear(g_system->getHeight()), _characterFar(g_system->getHeight()), _fade(0) {
add(object);
}
Screen::~Screen() {
_children.clear();
}
void Screen::scrollTo(Common::Point scroll) {
int windowW = g_system->getWidth();
int windowH = g_system->getHeight();
if (!_background) {
_scroll.x = _scroll.y = 0;
return;
}
auto rect = _background->getRect();
int w = rect.width(), h = rect.height();
debug("picture size %dx%d", w, h);
if (scroll.x + windowW > w)
scroll.x = w - windowW;
if (scroll.y + windowH > h)
scroll.y = h - windowH;
if (scroll.x < 0)
scroll.x = 0;
if (scroll.y < 0)
scroll.y = 0;
debug("setting scroll to %d,%d", scroll.x, scroll.y);
_scroll = scroll;
}
float Screen::getZScale(int y) const {
int h = g_system->getHeight();
int dy = h - y;
if (dy > _characterNear) {
if (dy < _characterFar)
return 1.0f * (_characterFar - dy) / (_characterFar - _characterNear);
else
return 0.0f;
} else
return 1.0f;
}
bool Screen::add(ObjectPtr object) {
if (object == NULL) {
warning("refusing to add null to scene");
return false;
}
for (auto i = _children.begin(); i != _children.end();) {
if (*i == object) {
if (object->alive()) {
debug("double adding object %s", object->getName().c_str());
return false;
} else {
debug("recovering object %s", object->getName().c_str());
object->alive(true);
object->allowInitialise(false);
return true;
}
} else
++i;
}
_children.insert(object);
return true;
}
void Screen::add(AnimationPtr animation) {
if (animation)
_animations.insert({animation});
else
warning("Screen: skipping null animation");
}
bool Screen::remove(const AnimationPtr &animation) {
if (!animation)
return false;
bool removed = false;
for (auto i = _animations.begin(); i != _animations.end(); ++i) {
if (i->animation == animation) {
debug("removing animation %s:%s", animation->process().c_str(), animation->phaseVar().c_str());
i->removed = true;
removed = true;
}
}
return removed;
}
ObjectPtr Screen::find(const Common::String &name) {
for (auto i = _children.begin(); i != _children.end(); ++i) {
ObjectPtr &object = *i;
if (object->getName() == name)
return *i;
}
return ObjectPtr();
}
bool Screen::remove(const ObjectPtr &object) {
for (auto i = _children.begin(); i != _children.end(); ++i) {
if (*i == object) {
if (object->locked())
object->alive(false);
else
_children.erase(i);
return true;
}
}
return false;
}
bool Screen::remove(const Common::String &name) {
for (auto i = _children.begin(); i != _children.end(); ++i) {
const ObjectPtr &object = *i;
if (object->getName() == name) {
if (object->locked())
object->alive(false);
else
_children.erase(i);
return true;
}
}
return false;
}
AnimationPtr Screen::findAnimationByPhaseVar(const Common::String &phaseVar) {
for (auto i = _animations.begin(); i != _animations.end(); ++i) {
const auto &desc = *i;
const auto &animation = desc.animation;
if (!desc.removed && animation->phaseVar() == phaseVar)
return animation;
}
return NULL;
}
void Screen::tick() {
for (uint i = 0; i < _animations.size();) {
const auto &desc = _animations.data()[i];
const auto &animation = desc.animation;
if (!desc.removed && animation->tick())
++i;
else {
debug("removing animation %s:%s", animation->process().c_str(), animation->phaseVar().c_str());
_animations.erase(_animations.begin() + i);
}
}
}
void Screen::paint(Graphics::Surface &backbuffer) const {
auto &currentInventoryObject = _engine->currentInventoryObject();
Character *character = _engine->currentCharacter();
auto child = _children.begin();
auto desc = _animations.begin();
while (child != _children.end() || desc != _animations.end() || character) {
bool child_valid = child != _children.end();
bool animation_valid = desc != _animations.end();
if (animation_valid && desc->removed) {
++desc;
continue;
}
bool z_valid = false;
int z = 0;
int render_type = -1;
if (animation_valid) {
const auto &animation = desc->animation;
if (!z_valid || animation->z() > z) {
z = animation->z();
z_valid = true;
render_type = 1;
}
}
if (child_valid) {
if (!z_valid || (*child)->z() > z) {
z = (*child)->z();
z_valid = true;
render_type = 0;
}
}
if (character) {
if (!z_valid || character->z() > z) {
z = character->z();
z_valid = true;
render_type = 2;
}
}
auto basePos = Common::Point() - _scroll;
switch (render_type) {
case 0:
// debug("object z: %d", (*child)->z());
if ((*child) != currentInventoryObject && (*child)->alive()) {
if ((*child)->scale() < 0)
basePos = Common::Point();
(*child)->paint(*_engine, backbuffer, basePos);
}
++child;
break;
case 1:
// debug("animation z: %d", (*animation)->z());
if ((*desc).animation->scale() < 0)
basePos = Common::Point();
(*desc).animation->paint(backbuffer, basePos);
++desc;
break;
case 2:
// debug("character z: %d", character->z());
character->paint(backbuffer, basePos);
character = nullptr;
break;
default:
error("invalid logic in z-sort");
}
}
if (_fade != 0) {
auto alpha = (100 - _fade) * 256 / 100;
auto &format = backbuffer.format;
for (int y = 0; y < backbuffer.h; ++y) {
uint32 *line = (uint32 *)backbuffer.getBasePtr(0, y);
int w = backbuffer.w;
while (w--) {
uint8 r, g, b;
format.colorToRGB(*line, r, g, b);
r = (r * alpha) >> 8;
g = (g * alpha) >> 8;
b = (b * alpha) >> 8;
*line++ = format.RGBToColor(r, g, b);
}
}
}
}
Common::Array<ObjectPtr> Screen::find(Common::Point pos) const {
Common::Array<ObjectPtr> objects;
objects.reserve(_children.size());
for (auto i = _children.begin(); i != _children.end(); ++i) {
const auto &object = *i;
auto visiblePos = (object->scale() >= 0) ? pos + _scroll : pos;
if (object->pointIn(visiblePos) && object->alive()) {
objects.insert_at(0, object);
}
}
Character *character = _engine->currentCharacter();
if (character) {
if (character->pointIn(pos - _scroll)) {
objects.insert_at(0, character->object());
}
}
return objects;
}
Screen::KeyHandler Screen::findKeyHandler(const Common::String &keyName) {
KeyHandler keyHandler;
for (auto i = _children.begin(); i != _children.end(); ++i) {
const auto &object = *i;
if (!object->alive())
continue;
uint ip = object->getKeyHandler(keyName);
if (ip) {
keyHandler.ip = ip;
keyHandler.object = object;
break;
}
}
return keyHandler;
}
void Screen::load(const PatchPtr &patch) {
debug("applying patch with %u objects", patch->objects.size());
_applyingPatch = true;
for (uint i = 0; i < patch->objects.size(); ++i) {
const Patch::Object &object = patch->objects[i];
debug("patch object %s %d", object.name.c_str(), object.flag);
if (object.name == _name)
continue;
else if (object.flag <= 0)
remove(object.name);
else
_engine->runObject(object.name, Common::String(), false);
}
_loadingType = patch->loadingType;
if (!patch->prevScreenName.empty())
_previousScreen = patch->prevScreenName;
_applyingPatch = false;
}
void Screen::save(const PatchPtr &patch) {
patch->screenSaved = 1;
patch->prevScreenName = _previousScreen;
patch->loadingType = _loadingType;
patch->objects.clear();
for (auto i = _children.begin(); i != _children.end(); ++i) {
const auto &object = *i;
if (!object->persistent() || !object->alive() || object->getName() == _name)
continue;
debug("saving patch object %s %d", object->getName().c_str(), object->alive());
patch->objects.push_back(Patch::Object(object->getName(), 1));
}
}
} // namespace AGDS

173
engines/agds/screen.h Normal file
View File

@@ -0,0 +1,173 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef SCREEN_H
#define SCREEN_H
#include "agds/screenLoadingType.h"
#include "common/array.h"
#include "common/ptr.h"
#include "common/rect.h"
#include "common/scummsys.h"
#include "common/str.h"
namespace Graphics {
struct Surface;
}
namespace AGDS {
class AGDSEngine;
class Animation;
using AnimationPtr = Common::SharedPtr<Animation>;
class Object;
using ObjectPtr = Common::SharedPtr<Object>;
struct Region;
using RegionPtr = Common::SharedPtr<Region>;
struct Patch;
using PatchPtr = Common::SharedPtr<Patch>;
struct ScreenAnimationDesc {
AnimationPtr animation;
bool removed = false;
ScreenAnimationDesc(const AnimationPtr &a) : animation(a) {
}
};
class Screen {
static int ObjectZCompare(const ObjectPtr &a, const ObjectPtr &b);
static int AnimationZCompare(const ScreenAnimationDesc &a, const ScreenAnimationDesc &b);
using Animations = Common::SortedArray<ScreenAnimationDesc, const ScreenAnimationDesc &>;
using Children = Common::SortedArray<ObjectPtr, const ObjectPtr &>;
AGDSEngine *_engine;
ObjectPtr _object;
Object *_background;
Common::Point _scroll;
Common::String _name;
ScreenLoadingType _loadingType;
Common::String _previousScreen;
Children _children;
Animations _animations;
RegionPtr _region;
bool _applyingPatch;
int _characterNear, _characterFar;
int _fade;
public:
struct KeyHandler {
ObjectPtr object;
uint ip;
KeyHandler() : object(), ip() {}
KeyHandler(Object *o, uint i) : object(o), ip(i) {}
};
Screen(AGDSEngine *engine, ObjectPtr object, ScreenLoadingType loadingType, const Common::String &prevScreen);
~Screen();
int fade() const {
return _fade;
}
void fade(int fade) {
_fade = fade;
}
void setCharacterNearFar(int near, int far) {
_characterNear = near;
_characterFar = far;
}
float getZScale(int y) const;
bool applyingPatch() const {
return _applyingPatch;
}
ObjectPtr getObject() {
return _object;
}
const Common::String &getName() const {
return _name;
}
const Common::String &getPreviousScreenName() const {
return _previousScreen;
}
ScreenLoadingType loadingType() const {
return _loadingType;
}
const RegionPtr &region() const {
return _region;
}
void region(RegionPtr region) {
_region = region;
}
const Children &children() const {
return _children;
}
const Animations &animations() const {
return _animations;
}
void scrollTo(Common::Point scroll);
Common::Point scrollPosition() const {
return _scroll;
}
bool add(ObjectPtr object);
void add(AnimationPtr animation);
bool remove(const AnimationPtr &animation);
void update(const ObjectPtr &object) {
bool found = remove(object);
if (found)
add(object);
}
void setBackground(Object *object) {
_background = object;
}
bool remove(const Common::String &name);
bool remove(const ObjectPtr &object);
void tick();
void paint(Graphics::Surface &backbuffer) const;
Common::Array<ObjectPtr> find(Common::Point pos) const;
ObjectPtr find(const Common::String &name);
KeyHandler findKeyHandler(const Common::String &keyName);
AnimationPtr findAnimationByPhaseVar(const Common::String &phaseVar);
void load(const PatchPtr &patch);
void save(const PatchPtr &patch);
};
} // End of namespace AGDS
#endif /* AGDS_SCREEN_H */

View File

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

View File

@@ -0,0 +1,175 @@
/* 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 "agds/soundManager.h"
#include "agds/agds.h"
#include "agds/object.h"
#include "audio/audiostream.h"
#include "audio/decoders/vorbis.h"
#include "audio/decoders/wave.h"
#include "common/debug.h"
#include "common/file.h"
#include "common/textconsole.h"
namespace AGDS {
void SoundManager::tick() {
for (auto it = _sounds.begin(); it != _sounds.end();) {
Sound &sound = *it;
auto &phaseVar = sound.phaseVar;
bool active = playing(sound.id);
if (!sound.phaseVar.empty()) {
int value = _engine->getGlobal(sound.phaseVar);
if (value <= 1) {
if (value == 1 && !active) {
debug("sample %s:%s resets phase var to 0", sound.resource.c_str(), sound.filename.c_str());
_engine->setGlobal(phaseVar, 0);
}
} else if (value & 2) {
debug("sample %s:%s restarts (via phase var)", sound.resource.c_str(), sound.filename.c_str());
_engine->setGlobal(phaseVar, 1);
_mixer->stopID(sound.id);
play(sound.process, sound.resource, sound.filename, sound.phaseVar, true, sound.volume, sound.pan, sound.id);
} else if (value & 4) {
debug("sample %s:%s stops (via phase var)", sound.resource.c_str(), sound.filename.c_str());
_mixer->stopID(sound.id);
_engine->setGlobal(phaseVar, 0);
}
++it;
} else if (!active) {
_engine->reactivate(sound.process, "sound " + sound.resource + " inactive", true);
it = _sounds.erase(it);
} else
++it;
}
_engine->runPendingReactivatedProcesses();
}
const Sound *SoundManager::find(int id) const {
for (auto i = _sounds.begin(); i != _sounds.end(); ++i) {
auto &sound = *i;
if (sound.id == id)
return &sound;
}
return nullptr;
}
Sound *SoundManager::findSampleByPhaseVar(const Common::String &phaseVar) {
if (phaseVar.empty())
return nullptr;
for (auto i = _sounds.begin(); i != _sounds.end(); ++i) {
auto &sound = *i;
if (sound.phaseVar == phaseVar) {
return &sound;
}
}
return nullptr;
}
void SoundManager::stopAll() {
_mixer->stopAll();
for (auto i = _sounds.begin(); i != _sounds.end(); ++i) {
auto &sound = *i;
if (!sound.phaseVar.empty())
_engine->setGlobal(sound.phaseVar, 0);
}
_sounds.clear();
}
void SoundManager::stopAllFrom(const Common::String &process) {
for (auto i = _sounds.begin(); i != _sounds.end();) {
auto &sound = *i;
if (sound.process == process) {
_mixer->stopID(sound.id);
if (!sound.phaseVar.empty())
_engine->setGlobal(sound.phaseVar, 0);
i = _sounds.erase(i);
} else {
++i;
}
}
}
int SoundManager::play(Common::String process, const Common::String &resource, const Common::String &filename, const Common::String &phaseVar, bool startPlaying, int volume, int pan, int id, bool ambient) {
debug("SoundMan::play(process: '%s', resource: '%s', filename: '%s', phaseVar: '%s', start: %d, volume: %d, pan: %d, id: %d, ambient: %d", process.c_str(), resource.c_str(), filename.c_str(), phaseVar.c_str(), startPlaying, volume, pan, id, ambient);
if (filename.empty())
return -1;
{
auto sample = findSampleByPhaseVar(phaseVar);
if (sample && playing(sample->id)) {
debug("already playing");
return sample->id;
}
}
Common::ScopedPtr<Common::File> file(new Common::File());
if (!file->open(Common::Path{filename})) {
if (!phaseVar.empty())
_engine->setGlobal(phaseVar, 1);
warning("no sound %s", filename.c_str());
return -1;
}
if (ambient)
process.clear();
Common::String lname(filename);
lname.toLowercase();
Audio::SeekableAudioStream *stream = NULL;
if (lname.hasSuffix(".ogg")) {
#ifdef USE_VORBIS
stream = Audio::makeVorbisStream(file.release(), DisposeAfterUse::YES);
#endif
} else if (lname.hasSuffix(".wav")) {
stream = Audio::makeWAVStream(file.release(), DisposeAfterUse::YES);
}
if (!stream) {
warning("could not play sound %s", filename.c_str());
if (!phaseVar.empty())
_engine->setGlobal(phaseVar, _engine->getGlobal(phaseVar) ? 1 : 0);
else
_engine->reactivate(process, "no sound");
return -1;
}
if (id == -1)
id = _nextId++;
_sounds.push_back(Sound(id, process, resource, filename, phaseVar, volume, pan));
auto handle = &_sounds.back().handle;
if (startPlaying)
_mixer->playStream(
ambient ? Audio::Mixer::kMusicSoundType : Audio::Mixer::kPlainSoundType,
&_sounds.back().handle, stream, id,
volume * Audio::Mixer::kMaxChannelVolume / 100, pan * 127 / 100);
if (ambient)
_mixer->loopChannel(*handle);
// if (sound_off)
// setPhaseVar(_sounds.back(), 1);
return id;
}
bool SoundManager::playing(int id) const {
return _mixer->isSoundIDActive(id);
}
} // namespace AGDS

View File

@@ -0,0 +1,84 @@
/* 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 SOUND_MANAGER_H
#define SOUND_MANAGER_H
#include "audio/mixer.h"
#include "common/hashmap.h"
#include "common/list.h"
#include "common/scummsys.h"
#include "common/str.h"
namespace Common {
class SeekableReadStream;
}
namespace Audio {
class Mixer;
}
namespace AGDS {
class AGDSEngine;
struct Sound {
int id;
Common::String process;
Common::String resource;
Common::String filename;
Common::String phaseVar;
Audio::SoundHandle handle;
int volume;
int pan;
int group;
bool paused;
Sound(int id_, const Common::String &process_, const Common::String &res, const Common::String &filename_, const Common::String &var, int volume_, int pan_, int group_ = 0) : id(id_), process(process_), resource(res), filename(filename_), phaseVar(var), handle(), volume(volume_), pan(pan_), group(group_), paused(false) {
}
int leftVolume() const {
return pan < 0 ? volume : volume * (100 - pan) / 100;
}
int rightVolume() const {
return pan < 0 ? volume * (100 + pan) / 100 : volume;
}
};
class SoundManager {
using SoundList = Common::List<Sound>;
int _nextId;
AGDSEngine *_engine;
Audio::Mixer *_mixer;
SoundList _sounds;
public:
SoundManager(AGDSEngine *engine, Audio::Mixer *mixer) : _nextId(1), _engine(engine), _mixer(mixer) {}
void tick();
int play(Common::String process, const Common::String &resource, const Common::String &filename, const Common::String &phaseVar, bool startPlaying, int volume, int pan, int id = -1, bool ambient = false);
bool playing(int id) const;
void stopAllFrom(const Common::String &process);
void stopAll();
const Sound *find(int id) const;
Sound *findSampleByPhaseVar(const Common::String &phaseVar);
};
} // End of namespace AGDS
#endif /* AGDS_SOUND_MANAGER_H */

View File

@@ -0,0 +1,86 @@
/* 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 "agds/systemVariable.h"
#include "common/array.h"
#include "common/stream.h"
#include "common/textconsole.h"
namespace AGDS {
const Common::String &IntegerSystemVariable::getString() const {
error("invalid type");
}
int IntegerSystemVariable::getInteger() const {
return _value;
}
void IntegerSystemVariable::setString(const Common::String &value) {
error("invalid type");
}
void IntegerSystemVariable::setInteger(int value) {
_value = value;
}
void IntegerSystemVariable::read(Common::ReadStream &stream) {
_value = stream.readSint32LE();
}
void IntegerSystemVariable::write(Common::WriteStream &stream) const {
stream.writeSint32LE(_value);
}
const Common::String &StringSystemVariable::getString() const {
return _value;
}
int StringSystemVariable::getInteger() const {
error("invalid type");
}
void StringSystemVariable::setString(const Common::String &value) {
_value = value;
}
void StringSystemVariable::setInteger(int value) {
error("invalid type");
}
void StringSystemVariable::read(Common::ReadStream &stream) {
byte len = stream.readByte();
if (len == 0)
error("invalid string var length");
Common::Array<char> str(len);
stream.read(str.data(), str.size());
_value = Common::String(str.data(), len - 1);
}
void StringSystemVariable::write(Common::WriteStream &stream) const {
uint len = _value.size() + 1;
if (len > 255)
error("variable too long, %u", len);
stream.writeByte(len);
stream.write(_value.c_str(), len);
}
} // namespace AGDS

View File

@@ -0,0 +1,87 @@
/* 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 SYSTEM_VARIABLE_H
#define SYSTEM_VARIABLE_H
#include "common/scummsys.h"
#include "common/str.h"
namespace Common {
class ReadStream;
class WriteStream;
} // namespace Common
namespace AGDS {
class SystemVariable {
public:
virtual ~SystemVariable() {}
virtual const Common::String &getString() const = 0;
virtual int getInteger() const = 0;
virtual void setString(const Common::String &value) = 0;
virtual void setInteger(int value) = 0;
virtual void reset() = 0;
virtual void read(Common::ReadStream &stream) = 0;
virtual void write(Common::WriteStream &stream) const = 0;
};
class IntegerSystemVariable : public SystemVariable {
int _value;
int _defaultValue;
public:
IntegerSystemVariable(int defaultValue = 0) : _value(defaultValue), _defaultValue(defaultValue) {
}
virtual const Common::String &getString() const;
virtual int getInteger() const;
virtual void setString(const Common::String &value);
virtual void setInteger(int value);
virtual void reset() {
_value = _defaultValue;
}
virtual void read(Common::ReadStream &stream);
virtual void write(Common::WriteStream &stream) const;
};
class StringSystemVariable : public SystemVariable {
Common::String _value;
Common::String _defaultValue;
public:
StringSystemVariable(const Common::String &defaultValue = Common::String()) : _value(defaultValue), _defaultValue(defaultValue) {
}
virtual const Common::String &getString() const;
virtual int getInteger() const;
virtual void setString(const Common::String &value);
virtual void setInteger(int value);
virtual void reset() {
_value = _defaultValue;
}
virtual void read(Common::ReadStream &stream);
virtual void write(Common::WriteStream &stream) const;
};
} // End of namespace AGDS
#endif /* AGDS_SYSTEM_VARIABLE_H */

141
engines/agds/textLayout.cpp Normal file
View File

@@ -0,0 +1,141 @@
/* 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 "agds/textLayout.h"
#include "agds/agds.h"
#include "agds/character.h"
#include "agds/font.h"
#include "agds/object.h"
#include "agds/process.h"
#include "agds/systemVariable.h"
#include "common/debug.h"
namespace AGDS {
void TextLayout::paint(AGDSEngine &engine, Graphics::Surface &backbuffer) {
if (!_valid)
return;
auto *font = engine.getFont(_fontId);
for (uint i = 0; i < _lines.size(); ++i) {
Line &line = _lines[i];
font->drawString(&backbuffer, line.text, line.pos.x, line.pos.y, line.size.x, 0);
}
}
void TextLayout::reset(AGDSEngine &engine) {
bool valid = _valid;
_valid = false;
_lines.clear();
if (valid) {
Common::String &var = _npc ? _npcNotifyVar : _charNotifyVar;
if (!var.empty()) {
engine.setGlobal(var, 0);
}
engine.reactivate(_process, "text layout reset");
if (engine.getSystemVariable("tell_close_inv")->getInteger()) {
engine.inventory().enable(true);
}
}
}
void TextLayout::layout(AGDSEngine &engine, Process &process, const Common::String &text, Common::Point pos, int fontId, bool npc) {
if (text.empty()) {
_valid = false;
return;
}
_process = process.getName();
process.deactivate();
_fontId = fontId;
_npc = npc;
auto *font = engine.getFont(fontId);
_lines.clear();
int w = 0;
Common::Point basePos;
size_t begin = 0;
while (begin < text.size()) {
while (begin < text.size() && (text[begin] == '\r' || text[begin] == ' '))
++begin;
size_t end = text.find('\n', begin);
if (end == text.npos)
end = text.size();
Common::String line = text.substr(begin, end - begin);
debug("parsed line: %s", line.c_str());
begin = end + 1;
Common::Point size;
size.x = font->getStringWidth(line);
size.y = font->getFontHeight();
_lines.push_back(Line());
Line &l = _lines.back();
l.pos = basePos;
l.text = line;
l.size = size;
basePos.y += size.y;
if (size.x > w)
w = size.x;
}
int dy = -basePos.y / 2;
for (uint i = 0; i < _lines.size(); ++i) {
Line &line = _lines[i];
line.pos.x += pos.x - line.size.x / 2;
line.pos.y += pos.y + dy;
}
_valid = true;
Common::String &var = _npc ? _npcNotifyVar : _charNotifyVar;
if (!var.empty()) {
if (!engine.getGlobal(var))
engine.setGlobal(var, 1);
}
if (!_npc) {
auto character = engine.currentCharacter();
if (character) {
if (!_charDirectionNotifyVar.empty()) {
if (!engine.getGlobal(_charDirectionNotifyVar))
engine.setGlobal(_charDirectionNotifyVar, character->direction());
} else {
switch (character->direction()) {
case 0:
case 1:
case 2:
case 3:
case 13:
case 14:
case 15:
break;
default:
character->animate(Common::Point(), character->direction(), 100);
}
}
} else
warning("no current character, skipping direction notification");
}
}
} // namespace AGDS

85
engines/agds/textLayout.h Normal file
View File

@@ -0,0 +1,85 @@
/* 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 TEXT_LAYOUT_H
#define TEXT_LAYOUT_H
#include "common/array.h"
#include "common/rect.h"
#include "common/scummsys.h"
#include "common/str.h"
namespace Graphics {
struct Surface;
}
namespace AGDS {
class AGDSEngine;
class Process;
class TextLayout {
int _fontId;
Common::Point _pos;
bool _npc;
bool _valid;
struct Line {
Common::Point pos;
Common::String text;
Common::Point size;
};
Common::Array<Line> _lines;
Common::String _process;
Common::String _charNotifyVar;
Common::String _charDirectionNotifyVar;
Common::String _npcNotifyVar;
public:
TextLayout() : _fontId(-1), _npc(true), _valid(false) {}
bool valid() const {
return _valid;
}
void reset(AGDSEngine &engine);
void setCharNotifyVar(const Common::String &name) {
_charNotifyVar = name;
}
void setCharDirectionNotifyVar(const Common::String &name) {
_charDirectionNotifyVar = name;
}
void setNPCNotifyVar(const Common::String &name) {
_npcNotifyVar = name;
}
void paint(AGDSEngine &engine, Graphics::Surface &backbuffer);
void layout(AGDSEngine &engine, Process &process, const Common::String &text, Common::Point pos, int fontId, bool npc);
};
} // End of namespace AGDS
#endif /* AGDS_TEXT_LAYOUT_H */