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