Files
2026-02-02 04:50:13 +01:00

307 lines
9.3 KiB
C++

/* 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 "mediastation/actors/screen.h"
#include "mediastation/debugchannels.h"
#include "mediastation/clients.h"
#include "mediastation/context.h"
#include "mediastation/mediastation.h"
namespace MediaStation {
bool DeviceOwner::attemptToReadFromStream(Chunk &chunk, uint sectionType) {
bool handledParam = true;
switch (sectionType) {
case kDeviceOwnerAllowMultipleSounds:
_allowMultipleSounds = chunk.readTypedByte();
break;
case kDeviceOwnerAllowMultipleStreams:
_allowMultipleStreams = chunk.readTypedByte();
break;
default:
handledParam = false;
}
return handledParam;
}
bool Document::attemptToReadFromStream(Chunk &chunk, uint sectionType) {
bool handledParam = true;
switch (sectionType) {
case kDocumentContextLoadComplete:
readContextLoadComplete(chunk);
break;
case kDocumentStartupInformation:
readStartupInformation(chunk);
break;
default:
handledParam = false;
}
return handledParam;
}
void Document::readStartupInformation(Chunk &chunk) {
DocumentSectionType sectionType = static_cast<DocumentSectionType>(chunk.readTypedUint16());
debugC(5, kDebugLoading, "%s: sectionType = 0x%x", __func__, static_cast<uint>(sectionType));
switch (sectionType) {
case kDocumentEntryScreen: {
uint entryPointScreenId = chunk.readTypedUint16();
if (_entryPointScreenId == 0) {
// We don't want to reset the overridden screen entry point.
_entryPointScreenId = entryPointScreenId;
}
break;
}
default:
error("%s: Unhandled section type 0x%x", __func__, static_cast<uint>(sectionType));
}
}
void Document::readContextLoadComplete(Chunk &chunk) {
uint contextId = chunk.readTypedUint16();
debugC(5, kDebugLoading, "%s: Context %d", __func__, contextId);
if (contextId == _loadingContextId) {
contextLoadDidComplete();
}
if (_loadingScreenActorId != 0) {
uint loadingScreenActorContextId = contextIdForScreenActorId(_loadingScreenActorId);
if (contextId == loadingScreenActorContextId) {
screenLoadDidComplete();
}
}
}
void Document::beginTitle(uint overriddenEntryPointScreenId) {
_entryPointStreamId = MediaStationEngine::BOOT_STREAM_ID;
if (overriddenEntryPointScreenId != 0) {
// This lets us override the default entry screen for development purposes.
_entryPointScreenId = overriddenEntryPointScreenId;
_entryPointScreenIdWasOverridden = true;
}
startFeed(_entryPointStreamId);
}
void Document::startContextLoad(uint contextId) {
debugC(5, kDebugLoading, "%s: Loading context %d", __func__, contextId);
Context *existingContext = g_engine->_loadedContexts.getValOrDefault(contextId);
if (existingContext == nullptr) {
if (_loadingContextId == 0) {
const ContextReference &contextRef = g_engine->contextRefWithId(contextId);
if (contextRef._contextId != 0) {
_loadingContextId = contextId;
startFeed(contextRef._streamId);
}
} else {
addToContextLoadQueue(contextId);
}
} else {
if (_currentScreenActorId != 0 && contextId != _loadingContextId) {
Actor *currentScreen = g_engine->getActorById(_currentScreenActorId);
ScriptValue arg;
arg.setToActorId(contextId);
currentScreen->runEventHandlerIfExists(kContextLoadCompleteEvent2, arg);
}
if (_loadingContextId == 0) {
checkQueuedContextLoads();
}
}
}
bool Document::isContextLoadInProgress(uint contextId) {
if (contextId == 0) {
// If we don't have a valid context ID, just check if we are loading any context.
return _loadingContextId != 0;
} else {
// If the context ID is valid, check if we are loading specifically that context.
return contextId == _loadingContextId;
}
}
void Document::branchToScreen() {
if (_loadingScreenActorId == 0) {
_loadingScreenActorId = _requestedScreenBranchId;
_requestedScreenBranchId = 0;
uint contextId = contextIdForScreenActorId(_loadingScreenActorId);
blowAwayCurrentScreen();
preloadParentContexts(contextId);
addToContextLoadQueue(contextId);
if (_loadingContextId == 0) {
checkQueuedContextLoads();
}
}
}
void Document::scheduleScreenBranch(uint screenActorId) {
// This is to support not immediately branching to the wrong screen
// when we are starting at a user-defined context. This is because the click
// handler usually points to the main menu screen rather than the screen
// we're starting at. It would be way too complicated to find the right variable
// and change it at runtime, so we will just branch to the screen we are already on.
// (We have to branch to something because by this point we have already faded out the screen and such,
// so we need to run another screen entry event to bring things back in again.)
if (_entryPointScreenIdWasOverridden) {
_entryPointScreenIdWasOverridden = false;
screenActorId = _currentScreenActorId;
}
_requestedScreenBranchId = screenActorId;
}
void Document::scheduleContextRelease(uint contextId) {
if (!g_engine->contextIsLocked(contextId)) {
_requestedContextReleaseId.push_back(contextId);
}
}
void Document::streamDidClose(uint streamId) {
bool currentStreamIsTargetStream = _currentStreamFeed != nullptr && streamId == _currentStreamFeed->_id;
if (!currentStreamIsTargetStream) {
return;
}
_currentStreamFeed = nullptr;
}
void Document::streamDidFinish(uint streamId) {
bool currentStreamIsTargetStream = _currentStreamFeed != nullptr && streamId == _currentStreamFeed->_id;
if (currentStreamIsTargetStream) {
stopFeed();
if (streamId == _entryPointStreamId) {
_requestedScreenBranchId = _entryPointScreenId;
branchToScreen();
} else {
checkQueuedContextLoads();
}
}
}
void Document::contextLoadDidComplete() {
if (_currentScreenActorId != 0) {
ScriptValue arg;
arg.setToActorId(_loadingContextId);
Actor *currentScreen = g_engine->getActorById(_currentScreenActorId);
if (currentScreen != nullptr) {
currentScreen->runEventHandlerIfExists(kContextLoadCompleteEvent, arg);
}
}
_loadingContextId = 0;
}
void Document::screenLoadDidComplete() {
_currentScreenActorId = _loadingScreenActorId;
Actor *currentScreen = g_engine->getActorById(_loadingScreenActorId);
currentScreen->runEventHandlerIfExists(kScreenEntryEvent);
_loadingScreenActorId = 0;
}
void Document::process() {
if (!_requestedContextReleaseId.empty()) {
for (uint contextId : _requestedContextReleaseId) {
g_engine->destroyContext(contextId);
}
_requestedContextReleaseId.clear();
}
if (_requestedScreenBranchId != 0) {
branchToScreen();
}
}
void Document::blowAwayCurrentScreen() {
if (_currentScreenActorId != 0) {
uint contextId = contextIdForScreenActorId(_currentScreenActorId);
if (contextId != 0) {
Actor *currentScreen = g_engine->getActorById(_currentScreenActorId);
currentScreen->runEventHandlerIfExists(kScreenExitEvent);
g_engine->destroyContext(contextId);
}
}
}
uint Document::contextIdForScreenActorId(uint screenActorId) {
ScreenReference screenRef = g_engine->screenRefWithId(screenActorId);
return screenRef._contextId;
}
void Document::startFeed(uint streamId) {
// The original had some more stuff here, including cache management and device ownership,
// but since we don't need these things right now, this function is rather empty.
_currentStreamFeed = g_engine->getStreamFeedManager()->openStreamFeed(streamId);
_currentStreamFeed->readData();
}
void Document::stopFeed() {
_currentStreamFeed->stopFeed();
g_engine->getStreamFeedManager()->closeStreamFeed(_currentStreamFeed);
_currentStreamFeed = nullptr;
}
void Document::preloadParentContexts(uint contextId) {
ContextReference contextReference = g_engine->contextRefWithId(contextId);
for (uint parentContextId : contextReference._parentContextIds) {
if (parentContextId != 0) {
Context *existingContext = g_engine->_loadedContexts.getValOrDefault(parentContextId);
if (existingContext == nullptr && parentContextId != contextId) {
debugC(5, kDebugLoading, "%s: Loading parent context %d", __func__, parentContextId);
addToContextLoadQueue(parentContextId);
}
}
}
}
void Document::addToContextLoadQueue(uint contextId) {
if (!isContextLoadQueued(contextId)) {
_contextLoadQueue.push_back(contextId);
} else {
warning("%s: Context %d already queued for load", __func__, contextId);
}
}
bool Document::isContextLoadQueued(uint contextId) {
for (uint queuedContextId : _contextLoadQueue) {
if (queuedContextId == contextId) {
return true;
}
}
return false;
}
void Document::checkQueuedContextLoads() {
while (!_contextLoadQueue.empty()) {
uint contextId = _contextLoadQueue.front();
_contextLoadQueue.erase(_contextLoadQueue.begin());
startContextLoad(contextId);
}
}
} // End of namespace MediaStation