scummvm/engines/pink/archive.cpp
2018-06-28 23:51:32 +02:00

347 lines
11 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 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 "pink/objects/condition.h"
#include "pink/objects/module.h"
#include "pink/objects/side_effect.h"
#include "pink/objects/actions/action_hide.h"
#include "pink/objects/actions/action_play_with_sfx.h"
#include "pink/objects/actions/action_sound.h"
#include "pink/objects/actions/action_talk.h"
#include "pink/objects/actions/action_text.h"
#include "pink/objects/actions/walk_action.h"
#include "pink/objects/actors/audio_info_pda_button.h"
#include "pink/objects/actors/cursor_actor.h"
#include "pink/objects/actors/inventory_actor.h"
#include "pink/objects/actors/lead_actor.h"
#include "pink/objects/actors/pda_button_actor.h"
#include "pink/objects/actors/supporting_actor.h"
#include "pink/objects/handlers/handler.h"
#include "pink/objects/handlers/handler_timer.h"
#include "pink/objects/pages/game_page.h"
#include "pink/objects/sequences/seq_timer.h"
#include "pink/objects/sequences/sequence.h"
#include "pink/objects/sequences/sequence_item.h"
#include "pink/objects/walk/walk_location.h"
namespace Pink {
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},
{"HandlerTimerActions", kHandlerTimerActions},
{"HandlerTimerSequences", kHandlerTimerSequences},
{"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 kActionLoop:
return new ActionLoop;
case kActionPlay:
return new ActionPlay;
case kActionPlayWithSfx:
return new ActionPlayWithSfx;
case kActionSfx:
return new ActionSfx;
case kActionSound:
return new ActionSound;
case kActionStill:
return new ActionStill;
case kActionTalk:
return new ActionTalk;
case kActionText:
return new ActionText;
case kActor:
return new Actor;
case kAudioInfoPDAButton:
return new AudioInfoPDAButton;
case kConditionGameVariable:
return new ConditionGameVariable;
case kConditionInventoryItemOwner:
return new ConditionInventoryItemOwner;
case kConditionModuleVariable:
return new ConditionModuleVariable;
case kConditionNotInventoryItemOwner:
return new ConditionNotInventoryItemOwner;
case kConditionNotModuleVariable:
return new ConditionNotModuleVariable;
case kConditionNotPageVariable:
return new ConditionNotPageVariable;
case kConditionPageVariable:
return new ConditionPageVariable;
case kCursorActor:
return new CursorActor;
case kGamePage:
return new GamePage;
case kHandlerLeftClick:
return new HandlerLeftClick;
case kHandlerStartPage:
return new HandlerStartPage;
case kHandlerTimer:
case kHandlerTimerActions:
return new HandlerTimerActions; // hack for Peril, but behavior is correct
case kHandlerTimerSequences:
return new HandlerTimerSequences;
case kHandlerUseClick:
return new HandlerUseClick;
case kInventoryActor:
return new InventoryActor;
case kInventoryItem:
return new InventoryItem;
case kLeadActor:
return new LeadActor;
case kModuleProxy:
return new ModuleProxy;
case kPDAButtonActor:
return new PDAButtonActor;
case kParlSqPink:
return new ParlSqPink;
case kPubPink:
return new PubPink;
case kSeqTimer:
return new SeqTimer;
case kSequence:
return new Sequence;
case kSequenceAudio:
return new SequenceAudio;
case kSequenceItem:
return new SequenceItem;
case kSequenceItemDefaultAction:
return new SequenceItemDefaultAction;
case kSequenceItemLeader:
return new SequenceItemLeader;
case kSequenceItemLeaderAudio:
return new SequenceItemLeaderAudio;
case kSideEffectExit:
return new SideEffectExit;
case kSideEffectGameVariable:
return new SideEffectGameVariable;
case kSideEffectInventoryItemOwner:
return new SideEffectInventoryItemOwner;
case kSideEffectLocation:
return new SideEffectLocation;
case kSideEffectModuleVariable:
return new SideEffectModuleVariable;
case kSideEffectPageVariable:
return new SideEffectPageVariable;
case kSideEffectRandomPageVariable:
return new SideEffectRandomPageVariable;
case kSupportingActor:
return new SupportingActor;
case kWalkAction:
return new WalkAction;
case kWalkLocation:
return new WalkLocation;
default:
error("Unknown object id");
return nullptr;
}
}
Archive::Archive(Common::SeekableReadStream *stream)
: _readStream(stream), _writeStream(nullptr)
{
_objectMap.push_back(0);
_objectIdMap.push_back(kNullObject);
}
Archive::Archive(Common::WriteStream *stream)
: _writeStream(stream), _readStream(nullptr)
{
_objectMap.push_back(0);
_objectIdMap.push_back(kNullObject);
}
Archive::~Archive()
{}
void Archive::mapObject(Object *obj) {
_objectMap.push_back(obj);
_objectIdMap.push_back(0);
}
int Archive::readCount() {
int count = _readStream->readUint16LE();
if (count == 0xffff)
count = _readStream->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 = nullptr;
uint obTag = _readStream->readUint16LE();
if (obTag == 0x0000) {
return nullptr;
} else if (obTag == 0xffff) {
int schema = _readStream->readUint16LE();
int size = _readStream->readUint16LE();
_readStream->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 in class Map", name);
return found->id;
}
Common::String Archive::readString() {
char buffer[kMaxStringLength];
byte len = _readStream->readByte();
assert(len <= kMaxStringLength);
_readStream->read(buffer, len);
return Common::String(buffer, len);
}
uint32 Archive::readDWORD() {
return _readStream->readUint32LE();
}
uint16 Archive::readWORD() {
return _readStream->readUint16LE();
}
void Archive::writeDWORD(uint32 val) {
_writeStream->writeUint32LE(val);
}
void Archive::writeWORD(uint16 val) {
_writeStream->writeUint16LE(val);
}
void Archive::writeString(const Common::String &string) {
_writeStream->writeByte(string.size());
_writeStream->write(string.c_str(), string.size());
}
} // End of namespace Pink