/* 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 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 <common/debug.h> #include <common/file.h> #include <engines/pink/actors/actor.h> #include <engines/pink/walk/walk_location.h> #include <engines/pink/actions/action_hide.h> #include <engines/pink/actions/action_play.h> #include <engines/pink/actions/action_sound.h> #include <engines/pink/sequences/sequence.h> #include <engines/pink/items/sequence_item_default_action.h> #include <engines/pink/items/sequence_item_leader.h> #include <engines/pink/handlers/handler_start_page.h> #include <engines/pink/side_effects/side_effect_exit.h> #include <engines/pink/side_effects/side_effect_module_variable.h> #include "module.h" #include "page.h" #include "actors/lead_actor.h" namespace Pink { enum { kMaxClassLength = 32, kMaxStringLength = 64, // adjust kNullObject = 0 }; enum { kActionHide, kActionLoop, kActionPlay, kActionPlayWithSfx, kActionSfx, kActionSound, kActionStill, kActionTalk, kActionText, kActor, kAudioInfoPDAButton, kConditionGameVariable, kConditionInventoryItemOwner, kConditionModuleVariable, kConditionNotInventoryItemOwner, kConditionNotModuleVariable, kConditionNotPageVariable, kConditionPageVariable, kCursorActor, kGamePage, kHandlerLeftClick, kHandlerStartPage, kHandlerTimer, kHandlerUseClick, kInventoryActor, kInventoryItem, kLeadActor, kModuleProxy, kPDAButtonActor, kParlSqPink, kPubPink, kSeqTimer, kSequence, kSequenceAudio, kSequenceItem, kSequenceItemDefaultAction, kSequenceItemLeader, kSequenceItemLeaderAudio, kSideEffectExit, kSideEffectGameVariable, kSideEffectInventoryItemOwner, kSideEffectLocation, kSideEffectModuleVariable, kSideEffectPageVariable, kSideEffectRandomPageVariable, kSupportingActor, kWalkAction, kWalkLocation }; static const struct RuntimeClass { const char *name; int id; } classMap[] = { {"ActionHide", kActionHide}, {"ActionLoop", kActionLoop}, {"ActionPlay", kActionPlay}, {"ActionPlayWithSfx", kActionPlayWithSfx}, {"ActionSfx", kActionSfx}, {"ActionSound", kActionSound}, {"ActionStill", kActionStill}, {"ActionTalk", kActionTalk}, {"ActionText", kActionText}, {"Actor", kActor}, {"AudioInfoPDAButton", kAudioInfoPDAButton}, {"ConditionGameVariable", kConditionGameVariable}, {"ConditionInventoryItemOwner", kConditionInventoryItemOwner}, {"ConditionModuleVariable", kConditionModuleVariable}, {"ConditionNotInventoryItemOwner", kConditionNotInventoryItemOwner}, {"ConditionNotModuleVariable", kConditionNotModuleVariable}, {"ConditionNotPageVariable", kConditionNotPageVariable}, {"ConditionPageVariable", kConditionPageVariable}, {"CursorActor", kCursorActor}, {"GamePage", kGamePage}, {"HandlerLeftClick", kHandlerLeftClick}, {"HandlerStartPage", kHandlerStartPage}, {"HandlerTimer", kHandlerTimer}, {"HandlerUseClick", kHandlerUseClick}, {"InventoryActor", kInventoryActor}, {"InventoryItem", kInventoryItem}, {"LeadActor", kLeadActor}, {"ModuleProxy", kModuleProxy}, {"PDAButtonActor", kPDAButtonActor}, {"ParlSqPink", kParlSqPink}, {"PubPink", kPubPink}, {"SeqTimer", kSeqTimer}, {"Sequence", kSequence}, {"SequenceAudio", kSequenceAudio}, {"SequenceItem", kSequenceItem}, {"SequenceItemDefaultAction", kSequenceItemDefaultAction}, {"SequenceItemLeader", kSequenceItemLeader}, {"SequenceItemLeaderAudio", kSequenceItemLeaderAudio}, {"SideEffectExit", kSideEffectExit}, {"SideEffectGameVariable", kSideEffectGameVariable}, {"SideEffectInventoryItemOwner", kSideEffectInventoryItemOwner}, {"SideEffectLocation", kSideEffectLocation}, {"SideEffectModuleVariable", kSideEffectModuleVariable}, {"SideEffectPageVariable", kSideEffectPageVariable}, {"SideEffectRandomPageVariable", kSideEffectRandomPageVariable}, {"SupportingActor", kSupportingActor}, {"WalkAction", kWalkAction}, {"WalkLocation", kWalkLocation} }; static Object* createObject(int objectId){ switch (objectId){ case kActionHide: return new ActionHide; case kActionPlay: return new ActionPlay; case kActionSound: return new ActionSound; case kActionStill: return new ActionStill; case kActor: return new Actor; case kGamePage: return new GamePage; case kHandlerStartPage: return new HandlerStartPage; case kInventoryItem: return new InventoryItem; case kLeadActor: return new LeadActor; case kModuleProxy: return new ModuleProxy; case kSequence: return new Sequence; case kSequenceItem: return new SequenceItem; case kSequenceItemDefaultAction: return new SequenceItemDefaultAction; case kSequenceItemLeader: return new SequenceItemLeader; case kSideEffectExit: return new SideEffectExit; case kSideEffectModuleVariable: return new SideEffectModuleVariable; case kWalkLocation: return new WalkLocation; default: return nullptr; } } Archive::Archive(Common::File &file) : _file(file) { debug("Archive created"); _objectMap.push_back(0); _objectIdMap.push_back(kNullObject); } Archive::~Archive() { debug("Archive destroyed"); } void Archive::mapObject(Object *obj) { _objectMap.push_back(obj); _objectIdMap.push_back(0); } int Archive::readCount() { int count = _file.readUint16LE(); if (count == 0xffff) count = _file.readUint32LE(); return count; } Object *Archive::readObject() { bool isCopyReturned; Object *res = parseObject(isCopyReturned); if (res && !isCopyReturned) res->deserialize(*this); return res; } Object *Archive::parseObject(bool &isCopyReturned) { char className[kMaxClassLength]; int objectId = 0; Object *res = 0; uint obTag = _file.readUint16LE(); if (obTag == 0x0000) { return nullptr; } else if (obTag == 0xffff) { int schema = _file.readUint16LE(); int size = _file.readUint16LE(); _file.read(className, size); className[size] = '\0'; objectId = findObjectId(className + 1); res = createObject(objectId); if (!res) error("Class %s is not implemented", className); _objectMap.push_back(res); _objectIdMap.push_back(objectId); _objectMap.push_back(res); // Basically a hack, but behavior is all correct _objectIdMap.push_back(objectId); isCopyReturned = false; } else if ((obTag & 0x8000) == 0) { res = _objectMap[obTag]; isCopyReturned = true; } else { obTag &= ~0x8000; objectId = _objectIdMap[obTag]; res = createObject(objectId); _objectMap.push_back(res); _objectIdMap.push_back(objectId); isCopyReturned = false; } return res; } uint Archive::findObjectId(const char *name) { RuntimeClass * found = static_cast<RuntimeClass*> (bsearch(name, classMap, sizeof(classMap) / sizeof(RuntimeClass) , sizeof(RuntimeClass), [] (const void *a, const void *b) { return strcmp((const char *) a, *(const char **) b); })); if (!found) error("Class %s is not implemented", name); return found->id; } Common::String Archive::readString() { char buffer[kMaxStringLength]; // test and lower then byte len = _file.readByte(); _file.read(buffer, len); return Common::String(buffer, len); } uint32 Archive::readDWORD() { return _file.readUint32LE(); } } // End of namespace Pink