Initial commit
This commit is contained in:
397
engines/mediastation/mediastation.cpp
Normal file
397
engines/mediastation/mediastation.cpp
Normal file
@@ -0,0 +1,397 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "common/config-manager.h"
|
||||
|
||||
#include "mediastation/mediastation.h"
|
||||
#include "mediastation/debugchannels.h"
|
||||
#include "mediastation/detection.h"
|
||||
#include "mediastation/boot.h"
|
||||
#include "mediastation/context.h"
|
||||
#include "mediastation/actor.h"
|
||||
#include "mediastation/actors/document.h"
|
||||
#include "mediastation/actors/movie.h"
|
||||
#include "mediastation/actors/screen.h"
|
||||
#include "mediastation/actors/palette.h"
|
||||
#include "mediastation/actors/hotspot.h"
|
||||
#include "mediastation/actors/stage.h"
|
||||
#include "mediastation/mediascript/scriptconstants.h"
|
||||
|
||||
namespace MediaStation {
|
||||
|
||||
MediaStationEngine *g_engine;
|
||||
|
||||
MediaStationEngine::MediaStationEngine(OSystem *syst, const ADGameDescription *gameDesc) : Engine(syst),
|
||||
_gameDescription(gameDesc),
|
||||
_randomSource("MediaStation") {
|
||||
g_engine = this;
|
||||
|
||||
_gameDataDir = Common::FSNode(ConfMan.getPath("path"));
|
||||
SearchMan.addDirectory(_gameDataDir, 0, 3);
|
||||
for (uint i = 0; MediaStation::directoryGlobs[i]; i++) {
|
||||
Common::String directoryGlob = directoryGlobs[i];
|
||||
SearchMan.addSubDirectoryMatching(_gameDataDir, directoryGlob, 0, 5);
|
||||
}
|
||||
|
||||
_channelIdent = MKTAG('i', 'g', 'o', 'd'); // ImtGod
|
||||
}
|
||||
|
||||
MediaStationEngine::~MediaStationEngine() {
|
||||
for (auto it = _loadedContexts.begin(); it != _loadedContexts.end(); ++it) {
|
||||
destroyContext(it->_value->_id, false);
|
||||
}
|
||||
_loadedContexts.clear();
|
||||
|
||||
// Only delete the document actor.
|
||||
// The root stage is deleted from stage director, and
|
||||
// the other actors are deleted from their contexts.
|
||||
destroyActor(DocumentActor::DOCUMENT_ACTOR_ID);
|
||||
|
||||
delete _displayManager;
|
||||
_displayManager = nullptr;
|
||||
|
||||
delete _cursorManager;
|
||||
_cursorManager = nullptr;
|
||||
|
||||
delete _functionManager;
|
||||
_functionManager = nullptr;
|
||||
|
||||
delete _document;
|
||||
_document = nullptr;
|
||||
|
||||
delete _deviceOwner;
|
||||
_deviceOwner = nullptr;
|
||||
|
||||
delete _stageDirector;
|
||||
_stageDirector = nullptr;
|
||||
|
||||
unregisterWithStreamManager();
|
||||
delete _streamFeedManager;
|
||||
_streamFeedManager = nullptr;
|
||||
|
||||
_contextReferences.clear();
|
||||
_streamMap.clear();
|
||||
_engineResourceDeclarations.clear();
|
||||
_screenReferences.clear();
|
||||
_fileMap.clear();
|
||||
_actors.clear();
|
||||
}
|
||||
|
||||
Actor *MediaStationEngine::getActorById(uint actorId) {
|
||||
return _actors.getValOrDefault(actorId);
|
||||
}
|
||||
|
||||
SpatialEntity *MediaStationEngine::getSpatialEntityById(uint spatialEntityId) {
|
||||
Actor *actor = getActorById(spatialEntityId);
|
||||
if (actor != nullptr) {
|
||||
if (!actor->isSpatialActor()) {
|
||||
error("%s: Actor %d is not a spatial actor", __func__, spatialEntityId);
|
||||
}
|
||||
return static_cast<SpatialEntity *>(actor);
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
ChannelClient *MediaStationEngine::getChannelClientByChannelIdent(uint channelIdent) {
|
||||
return _streamFeedManager->channelClientForChannel(channelIdent);
|
||||
}
|
||||
|
||||
ScriptValue *MediaStationEngine::getVariable(uint variableId) {
|
||||
for (auto it = _loadedContexts.begin(); it != _loadedContexts.end(); ++it) {
|
||||
ScriptValue *variable = it->_value->_variables.getValOrDefault(variableId);
|
||||
if (variable != nullptr) {
|
||||
return variable;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
uint32 MediaStationEngine::getFeatures() const {
|
||||
return _gameDescription->flags;
|
||||
}
|
||||
|
||||
Common::String MediaStationEngine::getGameId() const {
|
||||
return _gameDescription->gameId;
|
||||
}
|
||||
|
||||
Common::Platform MediaStationEngine::getPlatform() const {
|
||||
return _gameDescription->platform;
|
||||
}
|
||||
|
||||
const char *MediaStationEngine::getAppName() const {
|
||||
return _gameDescription->filesDescriptions[0].fileName;
|
||||
}
|
||||
|
||||
bool MediaStationEngine::isFirstGenerationEngine() {
|
||||
return _versionInfo.major == 0;
|
||||
}
|
||||
|
||||
Common::Error MediaStationEngine::run() {
|
||||
initDisplayManager();
|
||||
initCursorManager();
|
||||
initFunctionManager();
|
||||
initDocument();
|
||||
initDeviceOwner();
|
||||
initStageDirector();
|
||||
initStreamFeedManager();
|
||||
setupInitialStreamMap();
|
||||
|
||||
if (ConfMan.hasKey("entry_context")) {
|
||||
// For development purposes, we can choose to start at an arbitrary context
|
||||
// in this title. This might not work in all cases.
|
||||
uint entryContextId = ConfMan.get("entry_context").asUint64();
|
||||
warning("%s: Starting at user-requested context %d", __func__, entryContextId);
|
||||
_document->beginTitle(entryContextId);
|
||||
} else {
|
||||
_document->beginTitle();
|
||||
}
|
||||
|
||||
runEventLoop();
|
||||
return Common::kNoError;
|
||||
}
|
||||
|
||||
void MediaStationEngine::runEventLoop() {
|
||||
while (true) {
|
||||
dispatchSystemEvents();
|
||||
if (shouldQuit()) {
|
||||
break;
|
||||
}
|
||||
_document->process();
|
||||
|
||||
debugC(5, kDebugGraphics, "***** START SCREEN UPDATE ***");
|
||||
for (auto it = _actors.begin(); it != _actors.end(); ++it) {
|
||||
it->_value->process();
|
||||
}
|
||||
draw();
|
||||
debugC(5, kDebugGraphics, "***** END SCREEN UPDATE ***");
|
||||
|
||||
g_system->delayMillis(10);
|
||||
}
|
||||
}
|
||||
|
||||
void MediaStationEngine::initDisplayManager() {
|
||||
_displayManager = new VideoDisplayManager(this);
|
||||
_parameterClients.push_back(_displayManager);
|
||||
}
|
||||
|
||||
void MediaStationEngine::initCursorManager() {
|
||||
if (getPlatform() == Common::kPlatformWindows) {
|
||||
_cursorManager = new WindowsCursorManager(getAppName());
|
||||
} else if (getPlatform() == Common::kPlatformMacintosh) {
|
||||
_cursorManager = new MacCursorManager(getAppName());
|
||||
} else {
|
||||
error("%s: Attempted to use unsupported platform %s", __func__, Common::getPlatformDescription(getPlatform()));
|
||||
}
|
||||
_parameterClients.push_back(_cursorManager);
|
||||
_cursorManager->showCursor();
|
||||
}
|
||||
|
||||
void MediaStationEngine::initFunctionManager() {
|
||||
_functionManager = new FunctionManager();
|
||||
_parameterClients.push_back(_functionManager);
|
||||
}
|
||||
|
||||
void MediaStationEngine::initDocument() {
|
||||
_document = new Document();
|
||||
_parameterClients.push_back(_document);
|
||||
|
||||
DocumentActor *documentActor = new DocumentActor;
|
||||
registerActor(documentActor);
|
||||
}
|
||||
|
||||
void MediaStationEngine::initDeviceOwner() {
|
||||
_deviceOwner = new DeviceOwner();
|
||||
_parameterClients.push_back(_deviceOwner);
|
||||
}
|
||||
|
||||
void MediaStationEngine::initStageDirector() {
|
||||
_stageDirector = new StageDirector;
|
||||
}
|
||||
|
||||
void MediaStationEngine::initStreamFeedManager() {
|
||||
_streamFeedManager = new StreamFeedManager;
|
||||
registerWithStreamManager();
|
||||
}
|
||||
|
||||
void MediaStationEngine::setupInitialStreamMap() {
|
||||
StreamInfo streamInfo;
|
||||
streamInfo._actorId = 0;
|
||||
streamInfo._fileId = MediaStationEngine::BOOT_STREAM_ID;
|
||||
streamInfo._startOffsetInFile = 0;
|
||||
_streamMap.setVal(streamInfo._fileId, streamInfo);
|
||||
|
||||
const Common::String BOOT_STM_FILENAME("BOOT.STM");
|
||||
FileInfo fileInfo;
|
||||
fileInfo._id = MediaStationEngine::BOOT_STREAM_ID;
|
||||
fileInfo._name = BOOT_STM_FILENAME;
|
||||
_fileMap.setVal(fileInfo._id, fileInfo);
|
||||
}
|
||||
|
||||
void MediaStationEngine::dispatchSystemEvents() {
|
||||
while (g_system->getEventManager()->pollEvent(_event)) {
|
||||
debugC(9, kDebugEvents, "\n@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@");
|
||||
debugC(9, kDebugEvents, "@@@@ Dispatching system events");
|
||||
debugC(9, kDebugEvents, "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@\n");
|
||||
|
||||
switch (_event.type) {
|
||||
case Common::EVENT_MOUSEMOVE:
|
||||
_stageDirector->handleMouseMovedEvent(_event);
|
||||
break;
|
||||
|
||||
case Common::EVENT_KEYDOWN:
|
||||
_stageDirector->handleKeyboardEvent(_event);
|
||||
break;
|
||||
|
||||
case Common::EVENT_LBUTTONDOWN:
|
||||
_stageDirector->handleMouseDownEvent(_event);
|
||||
break;
|
||||
|
||||
case Common::EVENT_LBUTTONUP:
|
||||
_stageDirector->handleMouseUpEvent(_event);
|
||||
break;
|
||||
|
||||
case Common::EVENT_FOCUS_LOST:
|
||||
_stageDirector->handleMouseOutOfFocusEvent(_event);
|
||||
break;
|
||||
|
||||
case Common::EVENT_RBUTTONDOWN:
|
||||
// We are using the right button as a quick exit since the Media
|
||||
// Station engine doesn't seem to use the right button itself.
|
||||
warning("%s: EVENT_RBUTTONDOWN: Quitting for development purposes", __func__);
|
||||
quitGame();
|
||||
break;
|
||||
|
||||
default:
|
||||
// Avoid warnings about unimplemented cases by having an explicit
|
||||
// default case.
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void MediaStationEngine::draw(bool dirtyOnly) {
|
||||
if (dirtyOnly) {
|
||||
_stageDirector->drawDirtyRegion();
|
||||
} else {
|
||||
_stageDirector->drawAll();
|
||||
}
|
||||
_displayManager->updateScreen();
|
||||
_displayManager->doTransitionOnSync();
|
||||
}
|
||||
|
||||
void MediaStationEngine::registerActor(Actor *actorToAdd) {
|
||||
if (getActorById(actorToAdd->id())) {
|
||||
error("%s: Actor with ID 0x%d was already defined in this title", __func__, actorToAdd->id());
|
||||
}
|
||||
_actors.setVal(actorToAdd->id(), actorToAdd);
|
||||
}
|
||||
|
||||
void MediaStationEngine::destroyActor(uint actorId) {
|
||||
Actor *actorToDestroy = getActorById(actorId);
|
||||
if (actorToDestroy) {
|
||||
delete _actors[actorId];
|
||||
_actors.erase(actorId);
|
||||
} else {
|
||||
warning("%s: Actor %d is not currently loaded", __func__, actorId);
|
||||
}
|
||||
}
|
||||
|
||||
void MediaStationEngine::destroyContext(uint contextId, bool eraseFromLoadedContexts) {
|
||||
debugC(5, kDebugScript, "%s: Destroying context %d", __func__, contextId);
|
||||
Context *context = _loadedContexts.getValOrDefault(contextId);
|
||||
if (context == nullptr) {
|
||||
error("%s: Attempted to unload context %d that is not currently loaded", __func__, contextId);
|
||||
}
|
||||
|
||||
getRootStage()->deleteChildrenFromContextId(contextId);
|
||||
destroyActorsInContext(contextId);
|
||||
_functionManager->deleteFunctionsForContext(contextId);
|
||||
|
||||
delete context;
|
||||
if (eraseFromLoadedContexts) {
|
||||
// If we are deleting all contexts at once, we don't want to actually do this,
|
||||
// as it will mess up our iterators - the whole structure should be cleared after this.
|
||||
_loadedContexts.erase(contextId);
|
||||
}
|
||||
}
|
||||
|
||||
bool MediaStationEngine::contextIsLocked(uint contextId) {
|
||||
for (auto it = _loadedContexts.begin(); it != _loadedContexts.end(); ++it) {
|
||||
uint id = it->_key;
|
||||
ContextReference contextReference = _contextReferences.getVal(id);
|
||||
for (uint childContextId : contextReference._parentContextIds) {
|
||||
if (childContextId == contextId) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void MediaStationEngine::destroyActorsInContext(uint contextId) {
|
||||
// Collect actors to remove first, then delete them.
|
||||
// This is necessary because calling erase on a hashmap invalidates
|
||||
// the iterators, so collecting them all first makes more sense.
|
||||
Common::Array<uint> actorsToRemove;
|
||||
for (auto it = _actors.begin(); it != _actors.end(); ++it) {
|
||||
uint actorContextId = it->_value->contextId();
|
||||
if (actorContextId == contextId) {
|
||||
actorsToRemove.push_back(it->_key);
|
||||
}
|
||||
}
|
||||
|
||||
// Now remove the collected actors.
|
||||
for (uint actorId : actorsToRemove) {
|
||||
destroyActor(actorId);
|
||||
}
|
||||
}
|
||||
|
||||
void MediaStationEngine::readUnrecognizedFromStream(Chunk &chunk, uint sectionType) {
|
||||
bool paramHandled = false;
|
||||
for (ParameterClient *client : g_engine->_parameterClients) {
|
||||
if (client->attemptToReadFromStream(chunk, sectionType)) {
|
||||
paramHandled = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!paramHandled) {
|
||||
warning("%s: Parameter %d not handled", __func__, sectionType);
|
||||
}
|
||||
}
|
||||
|
||||
void MediaStationEngine::readChunk(Chunk &chunk) {
|
||||
StreamType streamType = static_cast<StreamType>(chunk.readTypedUint16());
|
||||
switch (streamType) {
|
||||
case kDocumentDefStream:
|
||||
readDocumentDef(chunk);
|
||||
break;
|
||||
|
||||
case kControlCommandsStream:
|
||||
readControlCommands(chunk);
|
||||
break;
|
||||
|
||||
default:
|
||||
error("%s: Unhandled section type 0x%x", __func__, static_cast<uint>(streamType));
|
||||
}
|
||||
}
|
||||
|
||||
} // End of namespace MediaStation
|
||||
Reference in New Issue
Block a user