1 /* ScummVM - Graphic Adventure Engine
2  *
3  * ScummVM is the legal property of its developers, whose names
4  * are too numerous to list here. Please refer to the COPYRIGHT
5  * file distributed with this source distribution.
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License
9  * as published by the Free Software Foundation; either version 2
10  * of the License, or (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20  *
21  */
22 
23 #include "common/debug.h"
24 #include "common/file.h"
25 
26 #include "pink/objects/condition.h"
27 #include "pink/objects/module.h"
28 #include "pink/objects/side_effect.h"
29 #include "pink/objects/actions/action_hide.h"
30 #include "pink/objects/actions/action_play_with_sfx.h"
31 #include "pink/objects/actions/action_sound.h"
32 #include "pink/objects/actions/action_talk.h"
33 #include "pink/objects/actions/action_text.h"
34 #include "pink/objects/actions/walk_action.h"
35 #include "pink/objects/actors/audio_info_pda_button.h"
36 #include "pink/objects/actors/cursor_actor.h"
37 #include "pink/objects/actors/inventory_actor.h"
38 #include "pink/objects/actors/lead_actor.h"
39 #include "pink/objects/actors/pda_button_actor.h"
40 #include "pink/objects/actors/supporting_actor.h"
41 #include "pink/objects/handlers/handler.h"
42 #include "pink/objects/pages/game_page.h"
43 #include "pink/objects/sequences/seq_timer.h"
44 #include "pink/objects/sequences/sequence.h"
45 #include "pink/objects/walk/walk_location.h"
46 
47 namespace Pink {
48 
49 static const struct RuntimeClass {
50 	const char *name;
51 	int id;
52 } classMap[] = {
53 	{"ActionHide", kActionHide},
54 	{"ActionLoop", kActionLoop},
55 	{"ActionPlay", kActionPlay},
56 	{"ActionPlayWithSfx", kActionPlayWithSfx},
57 	{"ActionSfx", kActionSfx},
58 	{"ActionSound", kActionSound},
59 	{"ActionStill", kActionStill},
60 	{"ActionTalk", kActionTalk},
61 	{"ActionText", kActionText},
62 	{"Actor", kActor},
63 	{"AudioInfoPDAButton", kAudioInfoPDAButton},
64 	{"ConditionGameVariable", kConditionGameVariable},
65 	{"ConditionInventoryItemOwner", kConditionInventoryItemOwner},
66 	{"ConditionModuleVariable", kConditionModuleVariable},
67 	{"ConditionNotInventoryItemOwner", kConditionNotInventoryItemOwner},
68 	{"ConditionNotModuleVariable", kConditionNotModuleVariable},
69 	{"ConditionNotPageVariable", kConditionNotPageVariable},
70 	{"ConditionPageVariable", kConditionPageVariable},
71 	{"CursorActor", kCursorActor},
72 	{"GamePage", kGamePage},
73 	{"HandlerLeftClick", kHandlerLeftClick},
74 	{"HandlerStartPage", kHandlerStartPage},
75 	{"HandlerTimer", kHandlerTimer},
76 	{"HandlerTimerActions", kHandlerTimerActions},
77 	{"HandlerTimerSequences", kHandlerTimerSequences},
78 	{"HandlerUseClick", kHandlerUseClick},
79 	{"InventoryActor", kInventoryActor},
80 	{"InventoryItem", kInventoryItem},
81 	{"LeadActor", kLeadActor},
82 	{"ModuleProxy", kModuleProxy},
83 	{"PDAButtonActor", kPDAButtonActor},
84 	{"ParlSqPink", kParlSqPink},
85 	{"PubPink", kPubPink},
86 	{"SeqTimer", kSeqTimer},
87 	{"Sequence", kSequence},
88 	{"SequenceAudio", kSequenceAudio},
89 	{"SequenceItem", kSequenceItem},
90 	{"SequenceItemDefaultAction", kSequenceItemDefaultAction},
91 	{"SequenceItemLeader", kSequenceItemLeader},
92 	{"SequenceItemLeaderAudio", kSequenceItemLeaderAudio},
93 	{"SideEffectExit", kSideEffectExit},
94 	{"SideEffectGameVariable", kSideEffectGameVariable},
95 	{"SideEffectInventoryItemOwner", kSideEffectInventoryItemOwner},
96 	{"SideEffectLocation", kSideEffectLocation},
97 	{"SideEffectModuleVariable", kSideEffectModuleVariable},
98 	{"SideEffectPageVariable", kSideEffectPageVariable},
99 	{"SideEffectRandomPageVariable", kSideEffectRandomPageVariable},
100 	{"SupportingActor", kSupportingActor},
101 	{"WalkAction", kWalkAction},
102 	{"WalkLocation", kWalkLocation}
103 };
104 
createObject(int objectId)105 static Object *createObject(int objectId) {
106 	switch (objectId) {
107 	case kActionHide:
108 		return new ActionHide;
109 	case kActionLoop:
110 		return new ActionLoop;
111 	case kActionPlay:
112 		return new ActionPlay;
113 	case kActionPlayWithSfx:
114 		return new ActionPlayWithSfx;
115 	case kActionSfx:
116 		return new ActionSfx;
117 	case kActionSound:
118 		return new ActionSound;
119 	case kActionStill:
120 		return new ActionStill;
121 	case kActionTalk:
122 		return new ActionTalk;
123 	case kActionText:
124 		return new ActionText;
125 	case kActor:
126 		return new Actor;
127 	case kAudioInfoPDAButton:
128 		return new AudioInfoPDAButton;
129 	case kConditionGameVariable:
130 		return new ConditionGameVariable;
131 	case kConditionInventoryItemOwner:
132 		return new ConditionInventoryItemOwner;
133 	case kConditionModuleVariable:
134 		return new ConditionModuleVariable;
135 	case kConditionNotInventoryItemOwner:
136 		return new ConditionNotInventoryItemOwner;
137 	case kConditionNotModuleVariable:
138 		return new ConditionNotModuleVariable;
139 	case kConditionNotPageVariable:
140 		return new ConditionNotPageVariable;
141 	case kConditionPageVariable:
142 		return new ConditionPageVariable;
143 	case kCursorActor:
144 		return new CursorActor;
145 	case kGamePage:
146 		return new GamePage;
147 	case kHandlerLeftClick:
148 		return new HandlerLeftClick;
149 	case kHandlerStartPage:
150 		return new HandlerStartPage;
151 	case kHandlerTimer:
152 	case kHandlerTimerActions:
153 		return new HandlerTimerActions; // hack for Peril, but behavior is correct
154 	case kHandlerTimerSequences:
155 		return new HandlerTimerSequences;
156 	case kHandlerUseClick:
157 		return new HandlerUseClick;
158 	case kInventoryActor:
159 		return new InventoryActor;
160 	case kInventoryItem:
161 		return new InventoryItem;
162 	case kLeadActor:
163 		return new LeadActor;
164 	case kModuleProxy:
165 		return new ModuleProxy;
166 	case kPDAButtonActor:
167 		return new PDAButtonActor;
168 	case kParlSqPink:
169 		return new ParlSqPink;
170 	case kPubPink:
171 		return new PubPink;
172 	case kSeqTimer:
173 		return new SeqTimer;
174 	case kSequence:
175 		return new Sequence;
176 	case kSequenceAudio:
177 		return new SequenceAudio;
178 	case kSequenceItem:
179 		return new SequenceItem;
180 	case kSequenceItemDefaultAction:
181 		return new SequenceItemDefaultAction;
182 	case kSequenceItemLeader:
183 		return new SequenceItemLeader;
184 	case kSequenceItemLeaderAudio:
185 		return new SequenceItemLeaderAudio;
186 	case kSideEffectExit:
187 		return new SideEffectExit;
188 	case kSideEffectGameVariable:
189 		return new SideEffectGameVariable;
190 	case kSideEffectInventoryItemOwner:
191 		return new SideEffectInventoryItemOwner;
192 	case kSideEffectLocation:
193 		return new SideEffectLocation;
194 	case kSideEffectModuleVariable:
195 		return new SideEffectModuleVariable;
196 	case kSideEffectPageVariable:
197 		return new SideEffectPageVariable;
198 	case kSideEffectRandomPageVariable:
199 		return new SideEffectRandomPageVariable;
200 	case kSupportingActor:
201 		return new SupportingActor;
202 	case kWalkAction:
203 		return new WalkAction;
204 	case kWalkLocation:
205 		return new WalkLocation;
206 	default:
207 		error("Unknown object id");
208 		return nullptr;
209 	}
210 }
211 
Archive(Common::SeekableReadStream * stream)212 Archive::Archive(Common::SeekableReadStream  *stream)
213 	: _readStream(stream), _writeStream(nullptr) {
214 	_objectMap.push_back(0);
215 	_objectIdMap.push_back(kNullObject);
216 }
217 
Archive(Common::WriteStream * stream)218 Archive::Archive(Common::WriteStream *stream)
219 	: _writeStream(stream), _readStream(nullptr) {
220 	_objectMap.push_back(0);
221 	_objectIdMap.push_back(kNullObject);
222 }
223 
mapObject(Object * obj)224 void Archive::mapObject(Object *obj) {
225 	_objectMap.push_back(obj);
226 	_objectIdMap.push_back(0);
227 }
228 
readObject()229 Object *Archive::readObject() {
230 	bool isCopyReturned;
231 	Object *res = parseObject(isCopyReturned);
232 
233 	if (res && !isCopyReturned)
234 		res->deserialize(*this);
235 
236 	return res;
237 }
238 
parseObject(bool & isCopyReturned)239 Object *Archive::parseObject(bool &isCopyReturned) {
240 	char className[kMaxClassLength];
241 	int objectId = 0;
242 	Object *res = nullptr;
243 
244 	uint obTag = _readStream->readUint16LE();
245 
246 	if (obTag == 0x0000) {
247 		return nullptr;
248 	} else if (obTag == 0xffff) {
249 		/* int schema = */_readStream->readUint16LE();
250 
251 		int size = _readStream->readUint16LE();
252 		_readStream->read(className, size);
253 		className[size] = '\0';
254 
255 		objectId = findObjectId(className + 1);
256 
257 		res = createObject(objectId);
258 		if (!res) error("Class %s is not implemented", className);
259 
260 		_objectMap.push_back(res);
261 		_objectIdMap.push_back(objectId);
262 
263 		_objectMap.push_back(res); // Basically a hack, but behavior is all correct. MFC uses one array for pointers and ids
264 		_objectIdMap.push_back(objectId);
265 
266 		isCopyReturned = false;
267 	} else if ((obTag & 0x8000) == 0) {
268 		res = _objectMap[obTag];
269 
270 		isCopyReturned = true;
271 	} else {
272 		obTag &= ~0x8000;
273 
274 		objectId = _objectIdMap[obTag];
275 
276 		res = createObject(objectId);
277 		_objectMap.push_back(res);
278 		_objectIdMap.push_back(objectId);
279 
280 		isCopyReturned = false;
281 	}
282 
283 	return res;
284 }
285 
runtimeClassCmp(const void * key,const void * elem)286 static int runtimeClassCmp(const void *key, const void *elem) {
287 	return strcmp((const char *)key, *(const char * const *)elem);
288 }
289 
findObjectId(const char * name)290 uint Archive::findObjectId(const char *name) {
291 	RuntimeClass *found = (RuntimeClass *)bsearch(name, classMap, sizeof(classMap) / sizeof(RuntimeClass), sizeof(RuntimeClass), runtimeClassCmp);
292 
293 	if (!found)
294 		error("Class %s is not in class Map", name);
295 
296 	return found->id;
297 }
298 
readString()299 Common::String Archive::readString() {
300 	char buffer[kMaxStringLength];
301 	byte len = _readStream->readByte();
302 	assert(len <= kMaxStringLength);
303 	_readStream->read(buffer, len);
304 	return Common::String(buffer, len);
305 }
306 
skipString()307 void Archive::skipString() {
308 	byte len = _readStream->readByte();
309 	_readStream->skip(len);
310 }
311 
writeString(const Common::String & string)312 void Archive::writeString(const Common::String &string) {
313 	_writeStream->writeByte(string.size());
314 	_writeStream->write(string.c_str(), string.size());
315 }
316 
317 } // End of namespace Pink
318