mirror of
https://github.com/libretro/scummvm.git
synced 2025-02-23 12:44:02 +00:00
378 lines
16 KiB
C++
378 lines
16 KiB
C++
/* ResidualVM - A 3D game interpreter
|
|
*
|
|
* ResidualVM is the legal property of its developers, whose names
|
|
* are too numerous to list here. Please refer to the AUTHORS
|
|
* 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 2
|
|
* 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, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
*
|
|
*/
|
|
|
|
#include "engines/stark/tools/command.h"
|
|
|
|
#include "common/debug.h"
|
|
#include "common/tokenizer.h"
|
|
|
|
namespace Stark {
|
|
namespace Tools {
|
|
|
|
Command::Command(Command *command) {
|
|
_index = command->_index;
|
|
_subType = command->_subType;
|
|
_subTypeDesc = command->_subTypeDesc;
|
|
_arguments = command->_arguments;
|
|
}
|
|
|
|
Command::Command(Resources::Command *resource) {
|
|
_index = resource->getIndex();
|
|
_subType = (Resources::Command::SubType) resource->getSubType();
|
|
_subTypeDesc = searchSubTypeDesc(_subType);
|
|
_arguments = resource->getArguments();
|
|
}
|
|
|
|
const Command::SubTypeDesc *Command::searchSubTypeDesc(Resources::Command::SubType subType) {
|
|
static const SubTypeDesc typeNames[] = {
|
|
{ Resources::Command::kCommandBegin, "begin", kFlowNormal },
|
|
{ Resources::Command::kCommandEnd, "end", kFlowEnd },
|
|
{ Resources::Command::kScriptCall, "scriptCall", kFlowNormal },
|
|
{ Resources::Command::kDialogCall, "dialogCall", kFlowNormal },
|
|
{ Resources::Command::kSetInteractiveMode, "setInteractiveMode", kFlowNormal },
|
|
{ Resources::Command::kLocationGoTo, "locationGoTo", kFlowEnd },
|
|
{ Resources::Command::kWalkTo, "walkTo", kFlowBranch },
|
|
{ Resources::Command::kGameLoop, "gameLoop", kFlowNormal },
|
|
{ Resources::Command::kScriptPause, "scriptPause", kFlowNormal },
|
|
{ Resources::Command::kScriptPauseRandom, "scriptPauseRandom", kFlowNormal },
|
|
{ Resources::Command::kScriptPauseSkippable, "scriptPauseSkippable", kFlowNormal },
|
|
{ Resources::Command::kScriptAbort, "scriptAbort", kFlowNormal },
|
|
{ Resources::Command::kExit2DLocation, "exit2DLocation", kFlowEnd },
|
|
{ Resources::Command::kGoto2DLocation, "goto2DLocation", kFlowEnd },
|
|
{ Resources::Command::kRumbleScene, "rumbleScene", kFlowNormal },
|
|
{ Resources::Command::kFadeScene, "fadeScene", kFlowNormal },
|
|
{ Resources::Command::kSwayScene, "swayScene", kFlowNormal },
|
|
{ Resources::Command::kLocationGoToNewCD, "locationGoToNewCD", kFlowEnd },
|
|
{ Resources::Command::kGameEnd, "gameEnd", kFlowNormal },
|
|
{ Resources::Command::kInventoryOpen, "inventoryOpen", kFlowNormal },
|
|
{ Resources::Command::kFloatScene, "floatScene", kFlowNormal },
|
|
{ Resources::Command::kBookOfSecretsOpen, "bookOfSecretsOpen", kFlowNormal },
|
|
{ Resources::Command::kDoNothing, "doNothing", kFlowNormal },
|
|
{ Resources::Command::kItem3DPlaceOn, "item3DPlaceOn", kFlowNormal },
|
|
{ Resources::Command::kItem3DWalkTo, "item3DWalkTo", kFlowNormal },
|
|
{ Resources::Command::kItem3DFollowPath, "item3DFollowPath", kFlowNormal },
|
|
{ Resources::Command::kItemLookAt, "itemLookAt", kFlowNormal },
|
|
{ Resources::Command::kItem2DFollowPath, "item2DFollowPath", kFlowNormal },
|
|
{ Resources::Command::kItemEnable, "itemEnable", kFlowNormal },
|
|
{ Resources::Command::kItemSetActivity, "itemSetActivity", kFlowNormal },
|
|
{ Resources::Command::kItemSelectInInventory, "itemSelectInInventory", kFlowNormal },
|
|
{ Resources::Command::kUseAnimHierarchy, "useAnimHierarchy", kFlowNormal },
|
|
{ Resources::Command::kPlayAnimation, "playAnimation", kFlowNormal },
|
|
{ Resources::Command::kScriptEnable, "scriptEnable", kFlowNormal },
|
|
{ Resources::Command::kShowPlay, "showPlay", kFlowNormal },
|
|
{ Resources::Command::kKnowledgeSetBoolean, "knowledgeSetBoolean", kFlowNormal },
|
|
{ Resources::Command::kKnowledgeSetInteger, "knowledgeSetInteger", kFlowNormal },
|
|
{ Resources::Command::kKnowledgeAddInteger, "knowledgeAddInteger", kFlowNormal },
|
|
{ Resources::Command::kEnableFloorField, "enableFloorField", kFlowNormal },
|
|
{ Resources::Command::kPlayAnimScriptItem, "playAnimScriptItem", kFlowNormal },
|
|
{ Resources::Command::kItemAnimFollowPath, "itemAnimFollowPath", kFlowNormal },
|
|
{ Resources::Command::kKnowledgeAssignBool, "knowledgeAssignBool", kFlowNormal },
|
|
{ Resources::Command::kKnowledgeAssignInteger, "knowledgeAssignInteger", kFlowNormal },
|
|
{ Resources::Command::kLocationScrollTo, "locationScrollTo", kFlowNormal },
|
|
{ Resources::Command::kSoundPlay, "soundPlay", kFlowNormal },
|
|
{ Resources::Command::kKnowledgeSetIntRandom, "knowledgeSetIntRandom", kFlowNormal },
|
|
{ Resources::Command::kKnowledgeSubValue, "knowledgeSubValue", kFlowNormal },
|
|
{ Resources::Command::kItemLookDirection, "itemLookDirection", kFlowNormal },
|
|
{ Resources::Command::kStopPlayingSound, "stopPlayingSound", kFlowNormal },
|
|
{ Resources::Command::kLayerGoTo, "layerGoTo", kFlowNormal },
|
|
{ Resources::Command::kLayerEnable, "layerEnable", kFlowNormal },
|
|
{ Resources::Command::kLocationScrollSet, "locationScrollSet", kFlowNormal },
|
|
{ Resources::Command::kFullMotionVideoPlay, "fullMotionVideoPlay", kFlowNormal },
|
|
{ Resources::Command::kAnimSetFrame, "animSetFrame", kFlowNormal },
|
|
{ Resources::Command::kKnowledgeAssignNegatedBool, "knowledgeAssignNegatedBool", kFlowNormal },
|
|
{ Resources::Command::kDiaryEnableEntry, "diaryEnableEntry", kFlowNormal },
|
|
{ Resources::Command::kPATChangeTooltip, "pATChangeTooltip", kFlowNormal },
|
|
{ Resources::Command::kSoundChange, "soundChange", kFlowNormal },
|
|
{ Resources::Command::kLightSetColor, "lightSetColor", kFlowNormal },
|
|
{ Resources::Command::kLightFollowPath, "lightFollowPath", kFlowNormal },
|
|
{ Resources::Command::kItem3DRunTo, "item3DRunTo", kFlowNormal },
|
|
{ Resources::Command::kItemPlaceDirection, "itemPlaceDirection", kFlowNormal },
|
|
{ Resources::Command::kItemRotateDirection, "itemRotateDirection", kFlowNormal },
|
|
{ Resources::Command::kActivateTexture, "activateTexture", kFlowNormal },
|
|
{ Resources::Command::kActivateMesh, "activateMesh", kFlowNormal },
|
|
{ Resources::Command::kItem3DSetWalkTarget, "item3DSetWalkTarget", kFlowNormal },
|
|
{ Resources::Command::kSpeakWithoutTalking, "speakWithoutTalking", kFlowNormal },
|
|
{ Resources::Command::kIsOnFloorField, "isOnFloorField", kFlowBranch },
|
|
{ Resources::Command::kIsItemEnabled, "isItemEnabled", kFlowBranch },
|
|
{ Resources::Command::kIsScriptEnabled, "isScriptEnabled", kFlowBranch },
|
|
{ Resources::Command::kIsKnowledgeBooleanSet, "isKnowledgeBooleanSet", kFlowBranch },
|
|
{ Resources::Command::kIsKnowledgeIntegerInRange, "isKnowledgeIntegerInRange", kFlowBranch },
|
|
{ Resources::Command::kIsKnowledgeIntegerAbove, "isKnowledgeIntegerAbove", kFlowBranch },
|
|
{ Resources::Command::kIsKnowledgeIntegerEqual, "isKnowledgeIntegerEqual", kFlowBranch },
|
|
{ Resources::Command::kIsKnowledgeIntegerLower, "isKnowledgeIntegerLower", kFlowBranch },
|
|
{ Resources::Command::kIsScriptActive, "isScriptActive", kFlowBranch },
|
|
{ Resources::Command::kIsRandom, "isRandom", kFlowBranch },
|
|
{ Resources::Command::kIsAnimScriptItemReached, "isAnimScriptItemReached", kFlowBranch },
|
|
{ Resources::Command::kIsItemOnPlace, "isItemOnPlace", kFlowBranch },
|
|
{ Resources::Command::kIsAnimPlaying, "isAnimPlaying", kFlowBranch },
|
|
{ Resources::Command::kIsItemActivity, "isItemActivity", kFlowBranch },
|
|
{ Resources::Command::kIsItemNearPlace, "isItemNearPlace", kFlowBranch },
|
|
{ Resources::Command::kIsAnimAtTime, "isAnimAtTime", kFlowBranch },
|
|
{ Resources::Command::kIsLocation2D, "isLocation2D", kFlowBranch },
|
|
{ Resources::Command::kIsInventoryOpen, "isInventoryOpen", kFlowBranch }
|
|
};
|
|
|
|
for (uint i = 0; i < ARRAYSIZE(typeNames); i++) {
|
|
if (typeNames[i].subType == subType) {
|
|
return &typeNames[i];
|
|
}
|
|
}
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
Command::ArgumentArray Command::getEffectiveArguments() const {
|
|
uint effectiveArgumentsStart;
|
|
switch (_subTypeDesc->controlFlowType) {
|
|
case kFlowEnd:
|
|
effectiveArgumentsStart = 0;
|
|
break;
|
|
case kFlowNormal:
|
|
effectiveArgumentsStart = 1;
|
|
break;
|
|
case kFlowBranch:
|
|
effectiveArgumentsStart = 2;
|
|
break;
|
|
default:
|
|
error("Unhandled control flow type '%d'", _subTypeDesc->controlFlowType);
|
|
}
|
|
|
|
ArgumentArray effectiveArguments;
|
|
for (uint i = effectiveArgumentsStart; i < _arguments.size(); i++) {
|
|
effectiveArguments.push_back(_arguments[i]);
|
|
}
|
|
|
|
return effectiveArguments;
|
|
}
|
|
|
|
Common::String Command::describeArguments(DefinitionRegistry *definitions) const {
|
|
Common::String desc;
|
|
|
|
for (uint i = 0; i < _arguments.size(); i++) {
|
|
switch (_arguments[i].type) {
|
|
case Resources::Command::Argument::kTypeInteger1:
|
|
case Resources::Command::Argument::kTypeInteger2:
|
|
desc += Common::String::format("%d", _arguments[i].intValue);
|
|
break;
|
|
|
|
case Resources::Command::Argument::kTypeResourceReference: {
|
|
if (definitions) {
|
|
desc += definitions->getFromReference(_arguments[i].referenceValue);
|
|
} else {
|
|
desc += _arguments[i].referenceValue.describe();
|
|
}
|
|
}
|
|
break;
|
|
case Resources::Command::Argument::kTypeString:
|
|
desc += _arguments[i].stringValue;
|
|
break;
|
|
default:
|
|
error("Unknown argument type %d", _arguments[i].type);
|
|
}
|
|
|
|
if (i != _arguments.size() - 1) {
|
|
desc += ", ";
|
|
}
|
|
}
|
|
|
|
return desc;
|
|
}
|
|
|
|
void Command::printCall() const {
|
|
debug("%d: %s(%s)", _index, _subTypeDesc->name, describeArguments(nullptr).c_str());
|
|
}
|
|
|
|
uint16 Command::getIndex() const {
|
|
return _index;
|
|
}
|
|
|
|
bool Command::hasSubtypeDescription() const {
|
|
return _subTypeDesc != nullptr;
|
|
}
|
|
|
|
Resources::Command::SubType Command::getSubType() const {
|
|
return _subType;
|
|
}
|
|
|
|
CFGCommand::CFGCommand(Resources::Command *resource) :
|
|
Command(resource),
|
|
_followerIndex(-1),
|
|
_trueBranchIndex(-1),
|
|
_falseBranchIndex(-1),
|
|
_follower(nullptr),
|
|
_trueBranch(nullptr),
|
|
_falseBranch(nullptr),
|
|
_block(nullptr) {
|
|
if (_subTypeDesc) {
|
|
initBranches();
|
|
}
|
|
}
|
|
|
|
void CFGCommand::initBranches() {
|
|
switch (_subTypeDesc->controlFlowType) {
|
|
case kFlowNormal:
|
|
_followerIndex = _arguments[0].intValue;
|
|
break;
|
|
case kFlowBranch:
|
|
if (_arguments[0].intValue == _arguments[1].intValue) {
|
|
// Degenerate conditions are handled here so that blocks are not split after them
|
|
_followerIndex = _arguments[0].intValue;
|
|
} else {
|
|
_falseBranchIndex = _arguments[0].intValue;
|
|
_trueBranchIndex = _arguments[1].intValue;
|
|
}
|
|
break;
|
|
case kFlowEnd:
|
|
// No followers
|
|
break;
|
|
}
|
|
}
|
|
|
|
bool CFGCommand::isEntryPoint() const {
|
|
return _subType == Resources::Command::kCommandBegin;
|
|
}
|
|
|
|
bool CFGCommand::isBranch() const {
|
|
return _trueBranchIndex >= 0 && _falseBranchIndex >= 0;
|
|
}
|
|
|
|
bool CFGCommand::isBranchTarget() const {
|
|
return _predecessors.size() > 1;
|
|
}
|
|
|
|
Block *CFGCommand::getBlock() const {
|
|
return _block;
|
|
}
|
|
|
|
void CFGCommand::setBlock(Block *block) {
|
|
_block = block;
|
|
}
|
|
|
|
CFGCommand *CFGCommand::getFollower() const {
|
|
return _follower;
|
|
}
|
|
|
|
CFGCommand *CFGCommand::getTrueBranch() const {
|
|
return _trueBranch;
|
|
}
|
|
|
|
CFGCommand *CFGCommand::getFalseBranch() const {
|
|
return _falseBranch;
|
|
}
|
|
|
|
void CFGCommand::linkBranches(const Common::Array<CFGCommand *> &commands) {
|
|
if (_followerIndex >= 0) {
|
|
_follower = findCommandWithIndex(commands, _followerIndex);
|
|
_follower->_predecessors.push_back(this);
|
|
}
|
|
|
|
if (_falseBranchIndex >= 0) {
|
|
_falseBranch = findCommandWithIndex(commands, _falseBranchIndex);
|
|
_falseBranch->_predecessors.push_back(this);
|
|
}
|
|
|
|
if (_trueBranchIndex >= 0) {
|
|
_trueBranch = findCommandWithIndex(commands, _trueBranchIndex);
|
|
_trueBranch->_predecessors.push_back(this);
|
|
}
|
|
}
|
|
|
|
CFGCommand *CFGCommand::findCommandWithIndex(const Common::Array<CFGCommand *> &commands, int32 index) {
|
|
for (uint i = 0; i < commands.size(); i++) {
|
|
CFGCommand *command = commands[i];
|
|
|
|
if (command->_index == index) {
|
|
return command;
|
|
}
|
|
}
|
|
|
|
error("Unable to find command with index %d", index);
|
|
}
|
|
|
|
void DefinitionRegistry::registerReference(const ResourceReference &reference) {
|
|
if (!reference.canResolve()) {
|
|
// The reference uses archives that are not currently loaded
|
|
return;
|
|
}
|
|
|
|
Resources::Object *object = reference.resolve<Resources::Object>();
|
|
if (!_definitions.contains(object)) {
|
|
// TODO: There is no guarantee the definition is unique
|
|
_definitions[object] = object->getType().getName() + stringToCamelCase(object->getName());
|
|
}
|
|
}
|
|
|
|
Common::String DefinitionRegistry::getFromReference(const ResourceReference &reference) const {
|
|
if (!reference.canResolve()) {
|
|
// The reference uses archives that are not currently loaded
|
|
return reference.describe();
|
|
}
|
|
|
|
Resources::Object *object = reference.resolve<Resources::Object>();
|
|
|
|
if (_definitions.contains(object)) {
|
|
return _definitions.getVal(object);
|
|
} else {
|
|
return reference.describe();
|
|
}
|
|
}
|
|
|
|
Common::String DefinitionRegistry::stringToCamelCase(const Common::String &input) {
|
|
Common::String clean = input;
|
|
|
|
// First replace all non alphanumerical characters with spaces
|
|
for (uint i = 0; i < clean.size(); i++) {
|
|
if (!Common::isAlnum(clean[i])) {
|
|
clean.setChar(' ', i);
|
|
}
|
|
}
|
|
|
|
// Then turn the string into camel case
|
|
Common::String output;
|
|
Common::StringTokenizer tokens = Common::StringTokenizer(clean);
|
|
while (!tokens.empty()) {
|
|
Common::String token = tokens.nextToken();
|
|
|
|
char upperFirstLetter = toupper(token[0]);
|
|
token.setChar(upperFirstLetter, 0);
|
|
|
|
output += token;
|
|
}
|
|
|
|
return output;
|
|
}
|
|
|
|
void DefinitionRegistry::printAll() const {
|
|
DefinitionMap::const_iterator it = _definitions.begin();
|
|
while (it != _definitions.end()) {
|
|
ResourceReference reference;
|
|
reference.buildFromResource(it->_key);
|
|
|
|
debug("let %s = %s", it->_value.c_str(), reference.describe().c_str());
|
|
|
|
it++;
|
|
}
|
|
}
|
|
|
|
} // End of namespace Tools
|
|
} // End of namespace Stark
|