1 //----------------------------------------------------------------------------
2 //  EDGE New SaveGame Handling (Things)
3 //----------------------------------------------------------------------------
4 //
5 //  Copyright (c) 1999-2009  The EDGE Team.
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 //----------------------------------------------------------------------------
18 //
19 // See the file "docs/save_sys.txt" for a complete description of the
20 // new savegame system.
21 //
22 // This file handles:
23 //    mobj_t        [MOBJ]
24 //    spawnspot_t   [SPWN]
25 //    iteminque_t   [ITMQ]
26 //
27 
28 #include "i_defs.h"
29 
30 #include "p_setup.h"
31 #include "sv_chunk.h"
32 #include "sv_main.h"
33 #include "z_zone.h"
34 
35 
36 #undef SF
37 #define SF  SVFIELD
38 
39 
40 // forward decls.
41 int SV_MobjCountElems(void);
42 int SV_MobjFindElem(mobj_t *elem);
43 void * SV_MobjGetElem(int index);
44 void SV_MobjCreateElems(int num_elems);
45 void SV_MobjFinaliseElems(void);
46 
47 int SV_ItemqCountElems(void);
48 int SV_ItemqFindElem(iteminque_t *elem);
49 void * SV_ItemqGetElem(int index);
50 void SV_ItemqCreateElems(int num_elems);
51 void SV_ItemqFinaliseElems(void);
52 
53 bool SR_MobjGetPlayer(void *storage, int index, void *extra);
54 bool SR_MobjGetMobj(void *storage, int index, void *extra);
55 bool SR_MobjGetType(void *storage, int index, void *extra);
56 bool SR_MobjGetState(void *storage, int index, void *extra);
57 bool SR_MobjGetSpawnPoint(void *storage, int index, void *extra);
58 bool SR_MobjGetAttack(void *storage, int index, void *extra);
59 
60 void SR_MobjPutPlayer(void *storage, int index, void *extra);
61 void SR_MobjPutMobj(void *storage, int index, void *extra);
62 void SR_MobjPutType(void *storage, int index, void *extra);
63 void SR_MobjPutState(void *storage, int index, void *extra);
64 void SR_MobjPutSpawnPoint(void *storage, int index, void *extra);
65 void SR_MobjPutAttack(void *storage, int index, void *extra);
66 
67 //----------------------------------------------------------------------------
68 //
69 //  MOBJ STRUCTURE AND ARRAY
70 //
71 static mobj_t sv_dummy_mobj;
72 
73 #define SV_F_BASE  sv_dummy_mobj
74 
75 static savefield_t sv_fields_mobj[] =
76 {
77 	SF(x, "x", 1, SVT_FLOAT, SR_GetFloat, SR_PutFloat),
78 	SF(y, "y", 1, SVT_FLOAT, SR_GetFloat, SR_PutFloat),
79 	SF(z, "z", 1, SVT_FLOAT, SR_GetFloat, SR_PutFloat),
80 	SF(angle, "angle", 1, SVT_ANGLE, SR_GetAngle, SR_PutAngle),
81 	SF(floorz, "floorz", 1, SVT_FLOAT, SR_GetFloat, SR_PutFloat),
82 	SF(ceilingz, "ceilingz", 1, SVT_FLOAT, SR_GetFloat, SR_PutFloat),
83 	SF(dropoffz, "dropoffz", 1, SVT_FLOAT, SR_GetFloat, SR_PutFloat),
84 	SF(radius, "radius", 1, SVT_FLOAT, SR_GetFloat, SR_PutFloat),
85 	SF(height, "height", 1, SVT_FLOAT, SR_GetFloat, SR_PutFloat),
86 	SF(mom, "mom", 1, SVT_VEC3, SR_GetVec3, SR_PutVec3),
87 	SF(health, "health", 1, SVT_FLOAT, SR_GetFloat, SR_PutFloat),
88 	SF(speed, "speed", 1, SVT_FLOAT, SR_GetFloat, SR_PutFloat),
89 	SF(fuse, "fuse", 1, SVT_INT, SR_GetInt, SR_PutInt),
90 	SF(info, "info", 1, SVT_STRING, SR_MobjGetType, SR_MobjPutType),
91 	SF(state, "state", 1, SVT_STRING, SR_MobjGetState, SR_MobjPutState),
92 	SF(next_state, "next_state", 1, SVT_STRING, SR_MobjGetState, SR_MobjPutState),
93 	SF(tics, "tics", 1, SVT_INT, SR_GetInt, SR_PutInt),
94 	SF(flags, "flags", 1, SVT_INT, SR_GetInt, SR_PutInt),
95 	SF(extendedflags, "extendedflags", 1, SVT_INT, SR_GetInt, SR_PutInt),
96 	SF(hyperflags, "hyperflags", 1, SVT_INT, SR_GetInt, SR_PutInt),
97 	SF(movedir, "movedir", 1, SVT_INT, SR_GetInt, SR_PutInt),
98 	SF(movecount, "movecount", 1, SVT_INT, SR_GetInt, SR_PutInt),
99 	SF(reactiontime, "reactiontime", 1, SVT_INT, SR_GetInt, SR_PutInt),
100 	SF(threshold, "threshold", 1, SVT_INT, SR_GetInt, SR_PutInt),
101 	SF(model_skin, "model_skin", 1, SVT_INT, SR_GetInt, SR_PutInt),
102 	SF(tag, "tag", 1, SVT_INT, SR_GetInt, SR_PutInt),
103 	SF(side, "side", 1, SVT_INT, SR_GetInt, SR_PutInt),
104 	SF(player, "player", 1, SVT_INDEX("players"),
105 		SR_MobjGetPlayer, SR_MobjPutPlayer),
106 	SF(spawnpoint, "spawnpoint", 1, SVT_STRUCT("spawnpoint_t"),
107 		SR_MobjGetSpawnPoint, SR_MobjPutSpawnPoint),
108 	SF(origheight, "origheight", 1, SVT_FLOAT, SR_GetFloat, SR_PutFloat),
109 	SF(visibility, "visibility", 1, SVT_FLOAT, SR_GetFloat, SR_PutFloat),
110 	SF(vis_target, "vis_target", 1, SVT_FLOAT, SR_GetFloat, SR_PutFloat),
111 	SF(vertangle, "vertangle", 1, SVT_FLOAT, SR_GetAngleFromSlope, SR_PutAngleToSlope),
112 	SF(spreadcount, "spreadcount", 1, SVT_INT, SR_GetInt, SR_PutInt),
113 	SF(currentattack, "currentattack", 1, SVT_STRING, SR_MobjGetAttack, SR_MobjPutAttack),
114 	SF(source, "source", 1, SVT_INDEX("mobjs"), SR_MobjGetMobj, SR_MobjPutMobj),
115 	SF(target, "target", 1, SVT_INDEX("mobjs"), SR_MobjGetMobj, SR_MobjPutMobj),
116 	SF(tracer, "tracer", 1, SVT_INDEX("mobjs"), SR_MobjGetMobj, SR_MobjPutMobj),
117 	SF(supportobj, "supportobj", 1, SVT_INDEX("mobjs"), SR_MobjGetMobj, SR_MobjPutMobj),
118 	SF(above_mo, "above_mo", 1, SVT_INDEX("mobjs"), SR_MobjGetMobj, SR_MobjPutMobj),
119 	SF(below_mo, "below_mo", 1, SVT_INDEX("mobjs"), SR_MobjGetMobj, SR_MobjPutMobj),
120 	SF(ride_dx, "ride_dx", 1, SVT_FLOAT, SR_GetFloat, SR_PutFloat),
121 	SF(ride_dy, "ride_dy", 1, SVT_FLOAT, SR_GetFloat, SR_PutFloat),
122 	SF(on_ladder, "on_ladder", 1, SVT_INT, SR_GetInt, SR_PutInt),
123 	SF(path_trigger, "path_trigger", 1, SVT_STRING,
124 	    SR_TriggerGetScript, SR_TriggerPutScript),
125 	SF(dlight.r, "dlight_qty", 1, SVT_FLOAT, SR_GetFloat, SR_PutFloat),
126 	SF(dlight.target, "dlight_target", 1, SVT_FLOAT, SR_GetFloat, SR_PutFloat),
127 	SF(dlight.color, "dlight_color", 1, SVT_RGBCOL, SR_GetRGB, SR_PutRGB),
128 	SF(shot_count, "shot_count", 1, SVT_INT, SR_GetInt, SR_PutInt),
129 
130 	// NOT HERE:
131 	//   subsector & region: these are regenerated.
132 	//   next,prev,snext,sprev,bnext,bprev: links are regenerated.
133 	//   tunnel_hash: would be meaningless, and not important.
134 	//   lastlookup: being reset to zero won't hurt.
135 	//   ...
136 
137 	SVFIELD_END
138 };
139 
140 savestruct_t sv_struct_mobj =
141 {
142 	NULL,            // link in list
143 	"mobj_t",        // structure name
144 	"mobj",          // start marker
145 	sv_fields_mobj,  // field descriptions
146 	SVDUMMY,         // dummy base
147 	true,            // define_me
148 	NULL             // pointer to known struct
149 };
150 
151 #undef SV_F_BASE
152 
153 savearray_t sv_array_mobj =
154 {
155 	NULL,             // link in list
156 	"mobjs",          // array name
157 	&sv_struct_mobj,  // array type
158 	true,             // define_me
159 	true,             // allow_hub
160 
161 	SV_MobjCountElems,     // count routine
162 	SV_MobjGetElem,        // index routine
163 	SV_MobjCreateElems,    // creation routine
164 	SV_MobjFinaliseElems,  // finalisation routine
165 
166 	NULL,     // pointer to known array
167 	0         // loaded size
168 };
169 
170 
171 //----------------------------------------------------------------------------
172 //
173 //  SPAWNPOINT STRUCTURE
174 //
175 static spawnpoint_t sv_dummy_spawnpoint;
176 
177 #define SV_F_BASE  sv_dummy_spawnpoint
178 
179 static savefield_t sv_fields_spawnpoint[] =
180 {
181 	SF(x, "x", 1, SVT_FLOAT, SR_GetFloat, SR_PutFloat),
182 	SF(y, "y", 1, SVT_FLOAT, SR_GetFloat, SR_PutFloat),
183 	SF(z, "z", 1, SVT_FLOAT, SR_GetFloat, SR_PutFloat),
184 	SF(angle, "angle", 1, SVT_ANGLE, SR_GetAngle, SR_PutAngle),
185 	SF(vertangle, "slope", 1, SVT_FLOAT, SR_GetAngleFromSlope, SR_PutAngleToSlope),
186 	SF(info, "info", 1, SVT_STRING, SR_MobjGetType, SR_MobjPutType),
187 	SF(flags, "flags", 1, SVT_INT, SR_GetInt, SR_PutInt),
188 
189 	SVFIELD_END
190 };
191 
192 savestruct_t sv_struct_spawnpoint =
193 {
194 	NULL,                 // link in list
195 	"spawnpoint_t",       // structure name
196 	"spwn",               // start marker
197 	sv_fields_spawnpoint, // field descriptions
198 	SVDUMMY,              // dummy base
199 	true,                 // define_me
200 	NULL                  // pointer to known struct
201 };
202 
203 #undef SV_F_BASE
204 
205 
206 //----------------------------------------------------------------------------
207 //
208 //  ITEMINQUE STRUCTURE AND ARRAY
209 //
210 static iteminque_t sv_dummy_iteminque;
211 
212 #define SV_F_BASE  sv_dummy_iteminque
213 
214 static savefield_t sv_fields_iteminque[] =
215 {
216 	SF(spawnpoint, "spawnpoint", 1, SVT_STRUCT("spawnpoint_t"),
217 		SR_MobjGetSpawnPoint, SR_MobjPutSpawnPoint),
218 	SF(time, "time", 1, SVT_INT, SR_GetInt, SR_PutInt),
219 
220 	// NOT HERE:
221 	//   next,prev: links are regenerated.
222 
223 	SVFIELD_END
224 };
225 
226 savestruct_t sv_struct_iteminque =
227 {
228 	NULL,                 // link in list
229 	"iteminque_t",        // structure name
230 	"itmq",               // start marker
231 	sv_fields_iteminque,  // field descriptions
232 	SVDUMMY,              // dummy base
233 	true,                 // define_me
234 	NULL                  // pointer to known struct
235 };
236 
237 #undef SV_F_BASE
238 
239 savearray_t sv_array_iteminque =
240 {
241 	NULL,                  // link in list
242 	"itemquehead",         // array name
243 	&sv_struct_iteminque,  // array type
244 	true,                  // define_me
245 	true,             // allow_hub
246 
247 	SV_ItemqCountElems,     // count routine
248 	SV_ItemqGetElem,        // index routine
249 	SV_ItemqCreateElems,    // creation routine
250 	SV_ItemqFinaliseElems,  // finalisation routine
251 
252 	NULL,  // pointer to known array
253 	0      // loaded size
254 };
255 
256 
257 //----------------------------------------------------------------------------
258 
259 //
260 // SV_MobjCountElems
261 //
SV_MobjCountElems(void)262 int SV_MobjCountElems(void)
263 {
264 	mobj_t *cur;
265 	int count=0;
266 
267 	for (cur=mobjlisthead; cur; cur=cur->next)
268 		count++;
269 
270 	return count;
271 }
272 
273 //
274 // SV_MobjGetElem
275 //
276 // The index here starts at 0.
277 //
SV_MobjGetElem(int index)278 void *SV_MobjGetElem(int index)
279 {
280 	mobj_t *cur;
281 
282 	for (cur=mobjlisthead; cur && index > 0; cur=cur->next)
283 		index--;
284 
285 	if (!cur)
286 		I_Error("LOADGAME: Invalid Mobj: %d\n", index);
287 
288 	SYS_ASSERT(index == 0);
289 
290 	return cur;
291 }
292 
293 //
294 // SV_MobjFindElem
295 //
296 // Returns the index number (starts at 0 here).
297 //
SV_MobjFindElem(mobj_t * elem)298 int SV_MobjFindElem(mobj_t *elem)
299 {
300 	mobj_t *cur;
301 	int index;
302 
303 	for (cur=mobjlisthead, index=0; cur && cur != elem; cur=cur->next)
304 		index++;
305 
306 	if (!cur)
307 		I_Error("LOADGAME: No such MobjPtr: %p\n", elem);
308 
309 	return index;
310 }
311 
312 //
313 // SV_MobjCreateElems
314 //
SV_MobjCreateElems(int num_elems)315 void SV_MobjCreateElems(int num_elems)
316 {
317 	// free existing mobjs
318 	if (mobjlisthead)
319 		P_RemoveAllMobjs();
320 
321 	SYS_ASSERT(mobjlisthead == NULL);
322 
323 	for (; num_elems > 0; num_elems--)
324 	{
325 		mobj_t *cur = Z_New(mobj_t, 1);
326 
327 		Z_Clear(cur, mobj_t, 1);
328 
329 		cur->next = mobjlisthead;
330 		cur->prev = NULL;
331 
332 		if (mobjlisthead)
333 			mobjlisthead->prev = cur;
334 
335 		mobjlisthead = cur;
336 
337 		// initialise defaults
338 		cur->info  = NULL;
339 		cur->state = cur->next_state = states+1;
340 
341 		cur->model_skin = 1;
342 		cur->model_last_frame = -1;
343 	}
344 }
345 
346 //
347 // SV_MobjFinaliseElems
348 //
SV_MobjFinaliseElems(void)349 void SV_MobjFinaliseElems(void)
350 {
351 	mobj_t *mo;
352 
353 	for (mo=mobjlisthead; mo; mo=mo->next)
354 	{
355 		if (! mo->info)
356 			mo->info = mobjtypes.Lookup(0);  // template
357 
358 		P_SetThingPosition(mo);
359 
360 		// handle reference counts
361 
362 #define REF_COUNT_FIELD(field)  \
363 	if (mo->field) mo->field->refcount++;
364 
365 		REF_COUNT_FIELD(tracer);
366 		REF_COUNT_FIELD(source);
367 		REF_COUNT_FIELD(target);
368 		REF_COUNT_FIELD(supportobj);
369 		REF_COUNT_FIELD(above_mo);
370 		REF_COUNT_FIELD(below_mo);
371 
372 #undef REF_COUNT_FIELD
373 
374 		// sanity checks
375 	}
376 }
377 
378 
379 //----------------------------------------------------------------------------
380 
381 //
382 // SV_ItemqCountElems
383 //
SV_ItemqCountElems(void)384 int SV_ItemqCountElems(void)
385 {
386 	iteminque_t *cur;
387 	int count=0;
388 
389 	for (cur=itemquehead; cur; cur=cur->next)
390 		count++;
391 
392 	return count;
393 }
394 
395 //
396 // SV_ItemqGetElem
397 //
398 // The index value starts at 0.
399 //
SV_ItemqGetElem(int index)400 void *SV_ItemqGetElem(int index)
401 {
402 	iteminque_t *cur;
403 
404 	for (cur=itemquehead; cur && index > 0; cur=cur->next)
405 		index--;
406 
407 	if (!cur)
408 		I_Error("LOADGAME: Invalid ItemInQue: %d\n", index);
409 
410 	SYS_ASSERT(index == 0);
411 	return cur;
412 }
413 
414 //
415 // SV_ItemqFindElem
416 //
417 // Returns the index number (starts at 0 here).
418 //
SV_ItemqFindElem(iteminque_t * elem)419 int SV_ItemqFindElem(iteminque_t *elem)
420 {
421 	iteminque_t *cur;
422 	int index;
423 
424 	for (cur=itemquehead, index=0; cur && cur != elem; cur=cur->next)
425 		index++;
426 
427 	if (!cur)
428 		I_Error("LOADGAME: No such ItemInQue ptr: %p\n", elem);
429 
430 	return index;
431 }
432 
433 //
434 // SV_ItemqCreateElems
435 //
SV_ItemqCreateElems(int num_elems)436 void SV_ItemqCreateElems(int num_elems)
437 {
438 	P_RemoveItemsInQue();
439 
440 	itemquehead = NULL;
441 
442 	for (; num_elems > 0; num_elems--)
443 	{
444 		iteminque_t *cur = Z_New(iteminque_t, 1);
445 
446 		Z_Clear(cur, iteminque_t, 1);
447 
448 		cur->next = itemquehead;
449 		cur->prev = NULL;
450 
451 		if (itemquehead)
452 			itemquehead->prev = cur;
453 
454 		itemquehead = cur;
455 
456 		// initialise defaults: leave blank
457 	}
458 }
459 
460 //
461 // SV_ItemqFinaliseElems
462 //
SV_ItemqFinaliseElems(void)463 void SV_ItemqFinaliseElems(void)
464 {
465 	iteminque_t *cur, *next;
466 
467 	// remove any dead wood
468 	for (cur = itemquehead; cur; cur = next)
469 	{
470 		next = cur->next;
471 
472 		if (cur->spawnpoint.info)
473 			continue;
474 
475 		I_Warning("LOADGAME: discarding empty ItemInQue\n");
476 
477 		if (next)
478 			next->prev = cur->prev;
479 
480 		if (cur->prev)
481 			cur->prev->next = next;
482 		else
483 			itemquehead = next;
484 
485 		Z_Free(cur);
486 	}
487 }
488 
489 
490 //----------------------------------------------------------------------------
491 
SR_MobjGetPlayer(void * storage,int index,void * extra)492 bool SR_MobjGetPlayer(void *storage, int index, void *extra)
493 {
494 	player_t ** dest = (player_t **)storage + index;
495 
496 	int swizzle = SV_GetInt();
497 
498 	*dest = (swizzle == 0) ? NULL : (player_t*)SV_PlayerGetElem(swizzle - 1);
499 	return true;
500 }
501 
SR_MobjPutPlayer(void * storage,int index,void * extra)502 void SR_MobjPutPlayer(void *storage, int index, void *extra)
503 {
504 	player_t *elem = ((player_t **)storage)[index];
505 
506 	int swizzle = (elem == NULL) ? 0 : SV_PlayerFindElem(elem) + 1;
507 
508 	SV_PutInt(swizzle);
509 }
510 
SR_MobjGetMobj(void * storage,int index,void * extra)511 bool SR_MobjGetMobj(void *storage, int index, void *extra)
512 {
513 	mobj_t ** dest = (mobj_t **)storage + index;
514 
515 	int swizzle = SV_GetInt();
516 
517 	*dest = (swizzle == 0) ? NULL : (mobj_t*)SV_MobjGetElem(swizzle - 1);
518 	return true;
519 }
520 
SR_MobjPutMobj(void * storage,int index,void * extra)521 void SR_MobjPutMobj(void *storage, int index, void *extra)
522 {
523 	mobj_t *elem = ((mobj_t **)storage)[index];
524 
525 	int swizzle;
526 
527 	swizzle = (elem == NULL) ? 0 : SV_MobjFindElem(elem) + 1;
528 	SV_PutInt(swizzle);
529 }
530 
SR_MobjGetType(void * storage,int index,void * extra)531 bool SR_MobjGetType(void *storage, int index, void *extra)
532 {
533 	mobjtype_c ** dest = (mobjtype_c **)storage + index;
534 
535 	const char *name = SV_GetString();
536 
537 	if (! name)
538 	{
539 		*dest = NULL;
540 		return true;
541 	}
542 
543 	// special handling for projectiles (attacks)
544 	if (strncmp(name, "atk:", 4) == 0)
545 	{
546 		const atkdef_c *atk = atkdefs.Lookup(name+4);
547 
548 		if (atk)
549 			*dest = (mobjtype_c *)atk->atk_mobj;
550 	}
551 	else
552 		*dest = (mobjtype_c *)mobjtypes.Lookup(name);
553 
554 	if (! *dest)
555 	{
556 		// Note: a missing 'info' field will be fixed up later
557 		I_Warning("LOADGAME: no such thing type '%s'\n",  name);
558 	}
559 
560 	SV_FreeString(name);
561 	return true;
562 }
563 
SR_MobjPutType(void * storage,int index,void * extra)564 void SR_MobjPutType(void *storage, int index, void *extra)
565 {
566 	mobjtype_c *info = ((mobjtype_c **)storage)[index];
567 
568 	SV_PutString((info == NULL) ? NULL : info->name.c_str());
569 }
570 
SR_MobjGetSpawnPoint(void * storage,int index,void * extra)571 bool SR_MobjGetSpawnPoint(void *storage, int index, void *extra)
572 {
573 	spawnpoint_t *dest = (spawnpoint_t *)storage + index;
574 
575 	if (sv_struct_spawnpoint.counterpart)
576 		return SV_LoadStruct(dest, sv_struct_spawnpoint.counterpart);
577 
578 	return true;  // presumably
579 }
580 
SR_MobjPutSpawnPoint(void * storage,int index,void * extra)581 void SR_MobjPutSpawnPoint(void *storage, int index, void *extra)
582 {
583 	spawnpoint_t *src = (spawnpoint_t *)storage + index;
584 
585 	SV_SaveStruct(src, &sv_struct_spawnpoint);
586 }
587 
SR_MobjGetAttack(void * storage,int index,void * extra)588 bool SR_MobjGetAttack(void *storage, int index, void *extra)
589 {
590 	atkdef_c ** dest = (atkdef_c **)storage + index;
591 
592 	const char *name = SV_GetString();
593 
594 	// Intentional Const Override
595 	*dest = (name == NULL) ? NULL : (atkdef_c *)atkdefs.Lookup(name);
596 
597 	SV_FreeString(name);
598 	return true;
599 }
600 
SR_MobjPutAttack(void * storage,int index,void * extra)601 void SR_MobjPutAttack(void *storage, int index, void *extra)
602 {
603 	atkdef_c *info = ((atkdef_c **)storage)[index];
604 
605 	SV_PutString((info == NULL) ? NULL : info->name.c_str());
606 }
607 
608 
609 //----------------------------------------------------------------------------
610 
611 //
612 // SR_MobjGetState
613 //
SR_MobjGetState(void * storage,int index,void * extra)614 bool SR_MobjGetState(void *storage, int index, void *extra)
615 {
616 	state_t ** dest = (state_t **)storage + index;
617 
618 	char buffer[256];
619 	char *base_p, *off_p;
620 	int base, offset;
621 
622 	const char *swizzle;
623 	const mobj_t *mo = (mobj_t *) sv_current_elem;
624 	const mobjtype_c *actual;
625 
626 	SYS_ASSERT(mo);
627 
628 	swizzle = SV_GetString();
629 
630 	if (!swizzle || !mo->info)
631 	{
632 		*dest = NULL;
633 		return true;
634 	}
635 
636 	Z_StrNCpy(buffer, swizzle, 256-1);
637 	SV_FreeString(swizzle);
638 
639 	// separate string at `:' characters
640 
641 	base_p = strchr(buffer, ':');
642 
643 	if (base_p == NULL || base_p[0] == 0)
644 		I_Error("Corrupt savegame: bad state 1/2: `%s'\n", buffer);
645 
646 	*base_p++ = 0;
647 
648 	off_p = strchr(base_p, ':');
649 
650 	if (off_p == NULL || off_p[0] == 0)
651 		I_Error("Corrupt savegame: bad state 2/2: `%s'\n", base_p);
652 
653 	*off_p++ = 0;
654 
655 	// find thing that contains the state
656 	actual = mo->info;
657 
658 	if (buffer[0] != '*')
659 	{
660 		// Do we care about those in the disabled group?
661 		actual = mobjtypes.Lookup(buffer);
662 		if (!actual)
663 			I_Error("LOADGAME: no such thing %s for state %s:%s\n",
664 			buffer, base_p, off_p);
665 	}
666 
667 	// find base state
668 	offset = strtol(off_p, NULL, 0) - 1;
669 
670 	base = DDF_StateFindLabel(actual->state_grp, base_p, true /* quiet */);
671 
672 	if (! base)
673 	{
674 		I_Warning("LOADGAME: no such label `%s' for state.\n", base_p);
675 		offset = 0;
676 
677 		if (actual->idle_state)
678 			base = actual->idle_state;
679 		else if (actual->spawn_state)
680 			base = actual->spawn_state;
681 		else if (actual->meander_state)
682 			base = actual->meander_state;
683 		else if (actual->state_grp.size() > 0)
684 			base = actual->state_grp[0].first;
685 		else
686 			base = 1;
687 	}
688 
689 #if 0
690 	L_WriteDebug("Unswizzled state `%s:%s:%s' -> %d\n",
691 		buffer, base_p, off_p, base + offset);
692 #endif
693 
694 	*dest = states + base + offset;
695 
696 	return true;
697 }
698 
699 //
700 // SR_MobjPutState
701 //
702 // The format of the string is:
703 //
704 //    THING `:' BASE `:' OFFSET
705 //
706 // where THING is usually just "*" for the current thing, but can
707 // refer to another ddf thing (e.g. "IMP").  BASE is the nearest
708 // labelled state (e.g. "SPAWN"), or "*" as offset from the thing's
709 // first state (unlikely to be needed).  OFFSET is the integer offset
710 // from the base state (e.g. "5"), which BTW starts at 1 (like the ddf
711 // format).
712 //
713 // Alternatively, the string can be NULL, which means the state
714 // pointer should be NULL.
715 //
716 // P.S: we go to all this trouble to try and get reasonable behaviour
717 // when loading with different DDF files than what we saved with.
718 // Typical example: a new item, monster or weapon gets added to our
719 // DDF files causing all state numbers to be shifted upwards.
720 //
SR_MobjPutState(void * storage,int index,void * extra)721 void SR_MobjPutState(void *storage, int index, void *extra)
722 {
723 	state_t *S = ((state_t **)storage)[index];
724 
725 	char swizzle[256];
726 
727 	int s_num, base;
728 
729 	const mobj_t *mo = (mobj_t *) sv_current_elem;
730 	const mobjtype_c *actual;
731 
732 	SYS_ASSERT(mo);
733 
734 	if (S == NULL || !mo->info)
735 	{
736 		SV_PutString(NULL);
737 		return;
738 	}
739 
740 	// object has no states ?
741 	if (mo->info->state_grp.empty())
742 	{
743 		I_Warning("SAVEGAME: object [%s] has no states !!\n", mo->info->name.c_str());
744 		SV_PutString(NULL);
745 		return;
746 	}
747 
748 	// get state number, check if valid
749 	s_num = (int)(S - states);
750 
751 	if (s_num < 0 || s_num >= num_states)
752 	{
753 		I_Warning("SAVEGAME: object [%s] is in invalid state %d\n",
754 			mo->info->name.c_str(), s_num);
755 
756 		if (mo->info->idle_state)
757 			s_num = mo->info->idle_state;
758 		else if (mo->info->spawn_state)
759 			s_num = mo->info->spawn_state;
760 		else if (mo->info->meander_state)
761 			s_num = mo->info->meander_state;
762 		else
763 		{
764 			SV_PutString("*:*:1");
765 			return;
766 		}
767 	}
768 
769 	// state gone AWOL into another object ?
770 	actual = mo->info;
771 
772 	if (! DDF_StateGroupHasState(actual->state_grp, s_num))
773 	{
774 		I_Warning("SAVEGAME: object [%s] is in AWOL state %d\n",
775 			mo->info->name.c_str(), s_num);
776 
777 		epi::array_iterator_c it;
778 
779 		// look for real object
780 		for (it = mobjtypes.GetBaseIterator(); it.IsValid(); it++)
781 		{
782 			actual = ITERATOR_TO_TYPE(it, mobjtype_c*);
783 
784 			if (DDF_StateGroupHasState(actual->state_grp, s_num))
785 				break;
786 		}
787 
788 		if (it.IsValid())
789 		{
790 			I_Warning("-- ARGH: state %d cannot be found !!\n", s_num);
791 			SV_PutString("*:*:1");
792 			return;
793 		}
794 
795 		if (! actual->name)
796 		{
797 			I_Warning("-- OOPS: state %d found in unnamed object !!\n", s_num);
798 			SV_PutString("*:*:1");
799 			return;
800 		}
801 	}
802 
803 	// find the nearest base state
804 	base = s_num;
805 
806 	while (! states[base].label &&
807 	       DDF_StateGroupHasState(actual->state_grp, base-1))
808 	{
809 		base--;
810 	}
811 
812 	sprintf(swizzle, "%s:%s:%d",
813 		(actual == mo->info) ? "*" : actual->name.c_str(),
814 		states[base].label ? states[base].label : "*",
815 		1 + s_num - base);
816 
817 #if 0
818 	L_WriteDebug("Swizzled state %d of [%s] -> `%s'\n",
819 		s_num, mo->info->name, swizzle);
820 #endif
821 
822 	SV_PutString(swizzle);
823 }
824 
825 
826 //--- editor settings ---
827 // vi:ts=4:sw=4:noexpandtab
828