1 /* $Id $ */
2 /*
3 THE COMPUTER CODE CONTAINED HEREIN IS THE SOLE PROPERTY OF PARALLAX
4 SOFTWARE CORPORATION ("PARALLAX").  PARALLAX, IN DISTRIBUTING THE CODE TO
5 END-USERS, AND SUBJECT TO ALL OF THE TERMS AND CONDITIONS HEREIN, GRANTS A
6 ROYALTY-FREE, PERPETUAL LICENSE TO SUCH END-USERS FOR USE BY SUCH END-USERS
7 IN USING, DISPLAYING,  AND CREATING DERIVATIVE WORKS THEREOF, SO LONG AS
8 SUCH USE, DISPLAY OR CREATION IS FOR NON-COMMERCIAL, ROYALTY OR REVENUE
9 FREE PURPOSES.  IN NO EVENT SHALL THE END-USER USE THE COMPUTER CODE
10 CONTAINED HEREIN FOR REVENUE-BEARING PURPOSES.  THE END-USER UNDERSTANDS
11 AND AGREES TO THE TERMS HEREIN AND ACCEPTS THE SAME BY USE OF THIS FILE.
12 COPYRIGHT 1993-1999 PARALLAX SOFTWARE CORPORATION.  ALL RIGHTS RESERVED.
13 */
14 
15 /*
16  *
17  * Save game information
18  *
19  *
20  */
21 
22 #ifdef HAVE_CONFIG_H
23 #include <conf.h>
24 #endif
25 
26 #ifdef RCS
27 char gamesave_rcsid[] = "$Id: gamesave.c,v 1.19 2003/04/03 07:15:43 btb Exp $";
28 #endif
29 
30 #include <stdio.h>
31 #include <string.h>
32 
33 #include "pstypes.h"
34 #include "strutil.h"
35 #include "mono.h"
36 #include "key.h"
37 #include "gr.h"
38 #include "palette.h"
39 #include "newmenu.h"
40 
41 #include "inferno.h"
42 #ifdef EDITOR
43 #include "editor/editor.h"
44 #endif
45 #include "error.h"
46 #include "object.h"
47 #include "game.h"
48 #include "screens.h"
49 #include "wall.h"
50 #include "gamemine.h"
51 #include "robot.h"
52 
53 
54 #include "cfile.h"
55 #include "bm.h"
56 #include "menu.h"
57 #include "switch.h"
58 #include "fuelcen.h"
59 #include "cntrlcen.h"
60 #include "powerup.h"
61 #include "weapon.h"
62 #include "newdemo.h"
63 #include "gameseq.h"
64 #include "automap.h"
65 #include "polyobj.h"
66 #include "text.h"
67 #include "gamefont.h"
68 #include "gamesave.h"
69 #include "gamepal.h"
70 #include "laser.h"
71 #include "byteswap.h"
72 #include "multi.h"
73 #include "makesig.h"
74 
75 char Gamesave_current_filename[128];
76 
77 int Gamesave_current_version;
78 
79 #define GAME_VERSION            32
80 #define GAME_COMPATIBLE_VERSION 22
81 
82 //version 28->29  add delta light support
83 //version 27->28  controlcen id now is reactor number, not model number
84 //version 28->29  ??
85 //version 29->30  changed trigger structure
86 //version 30->31  changed trigger structure some more
87 //version 31->32  change segment structure, make it 512 bytes w/o editor, add Segment2s array.
88 
89 #define MENU_CURSOR_X_MIN       MENU_X
90 #define MENU_CURSOR_X_MAX       MENU_X+6
91 
92 struct {
93 	ushort  fileinfo_signature;
94 	ushort  fileinfo_version;
95 	int     fileinfo_sizeof;
96 } game_top_fileinfo;    // Should be same as first two fields below...
97 
98 struct {
99 	ushort  fileinfo_signature;
100 	ushort  fileinfo_version;
101 	int     fileinfo_sizeof;
102 	char    mine_filename[15];
103 	int     level;
104 	int     player_offset;              // Player info
105 	int     player_sizeof;
106 	int     object_offset;              // Object info
107 	int     object_howmany;
108 	int     object_sizeof;
109 	int     walls_offset;
110 	int     walls_howmany;
111 	int     walls_sizeof;
112 	int     doors_offset;
113 	int     doors_howmany;
114 	int     doors_sizeof;
115 	int     triggers_offset;
116 	int     triggers_howmany;
117 	int     triggers_sizeof;
118 	int     links_offset;
119 	int     links_howmany;
120 	int     links_sizeof;
121 	int     control_offset;
122 	int     control_howmany;
123 	int     control_sizeof;
124 	int     matcen_offset;
125 	int     matcen_howmany;
126 	int     matcen_sizeof;
127 	int     dl_indices_offset;
128 	int     dl_indices_howmany;
129 	int     dl_indices_sizeof;
130 	int     delta_light_offset;
131 	int     delta_light_howmany;
132 	int     delta_light_sizeof;
133 } game_fileinfo;
134 
135 //  LINT: adding function prototypes
136 void read_object(object *obj, CFILE *f, int version);
137 void write_object(object *obj, FILE *f);
138 void do_load_save_levels(int save);
139 void dump_mine_info(void);
140 
141 extern char MaxPowerupsAllowed[MAX_POWERUP_TYPES];
142 extern char PowerupsInMine[MAX_POWERUP_TYPES];
143 
144 #ifdef EDITOR
145 extern char mine_filename[];
146 extern int save_mine_data_compiled(FILE * SaveFile);
147 //--unused-- #else
148 //--unused-- char mine_filename[128];
149 #endif
150 
151 int Gamesave_num_org_robots = 0;
152 //--unused-- grs_bitmap * Gamesave_saved_bitmap = NULL;
153 
154 #ifdef EDITOR
155 // Return true if this level has a name of the form "level??"
156 // Note that a pathspec can appear at the beginning of the filename.
is_real_level(char * filename)157 int is_real_level(char *filename)
158 {
159 	int len = strlen(filename);
160 
161 	if (len < 6)
162 		return 0;
163 
164 	//mprintf((0, "String = [%s]\n", &filename[len-11]));
165 	return !strnicmp(&filename[len-11], "level", 5);
166 
167 }
168 #endif
169 
change_filename_extension(char * dest,char * src,char * new_ext)170 void change_filename_extension( char *dest, char *src, char *new_ext )
171 {
172 	int i;
173 
174 	strcpy (dest, src);
175 
176 	if (new_ext[0]=='.')
177 		new_ext++;
178 
179 	for (i=1; i<strlen(dest); i++ )
180 		if (dest[i]=='.'||dest[i]==' '||dest[i]==0)
181 			break;
182 
183 	if (i < 123) {
184 		dest[i]='.';
185 		dest[i+1]=new_ext[0];
186 		dest[i+2]=new_ext[1];
187 		dest[i+3]=new_ext[2];
188 		dest[i+4]=0;
189 		return;
190 	}
191 }
192 
193 //--unused-- vms_angvec zero_angles={0,0,0};
194 
195 #define vm_angvec_zero(v) do {(v)->p=(v)->b=(v)->h=0;} while (0)
196 
197 int Gamesave_num_players=0;
198 
199 int N_save_pof_names;
200 char Save_pof_names[MAX_POLYGON_MODELS][FILENAME_LEN];
201 
202 void check_and_fix_matrix(vms_matrix *m);
203 
verify_object(object * obj)204 void verify_object( object * obj )	{
205 
206 	obj->lifeleft = IMMORTAL_TIME;		//all loaded object are immortal, for now
207 
208 	if ( obj->type == OBJ_ROBOT )	{
209 		Gamesave_num_org_robots++;
210 
211 		// Make sure valid id...
212 		if ( obj->id >= N_robot_types )
213 			obj->id = obj->id % N_robot_types;
214 
215 		// Make sure model number & size are correct...
216 		if ( obj->render_type == RT_POLYOBJ ) {
217 			Assert(Robot_info[obj->id].model_num != -1);
218 				//if you fail this assert, it means that a robot in this level
219 				//hasn't been loaded, possibly because he's marked as
220 				//non-shareware.  To see what robot number, print obj->id.
221 
222 			Assert(Robot_info[obj->id].always_0xabcd == 0xabcd);
223 				//if you fail this assert, it means that the robot_ai for
224 				//a robot in this level hasn't been loaded, possibly because
225 				//it's marked as non-shareware.  To see what robot number,
226 				//print obj->id.
227 
228 			obj->rtype.pobj_info.model_num = Robot_info[obj->id].model_num;
229 			obj->size = Polygon_models[obj->rtype.pobj_info.model_num].rad;
230 
231 			//@@Took out this ugly hack 1/12/96, because Mike has added code
232 			//@@that should fix it in a better way.
233 			//@@//this is a super-ugly hack.  Since the baby stripe robots have
234 			//@@//their firing point on their bounding sphere, the firing points
235 			//@@//can poke through a wall if the robots are very close to it. So
236 			//@@//we make their radii bigger so the guns can't get too close to
237 			//@@//the walls
238 			//@@if (Robot_info[obj->id].flags & RIF_BIG_RADIUS)
239 			//@@	obj->size = (obj->size*3)/2;
240 
241 			//@@if (obj->control_type==CT_AI && Robot_info[obj->id].attack_type)
242 			//@@	obj->size = obj->size*3/4;
243 		}
244 
245 		if (obj->id == 65)						//special "reactor" robots
246 			obj->movement_type = MT_NONE;
247 
248 		if (obj->movement_type == MT_PHYSICS) {
249 			obj->mtype.phys_info.mass = Robot_info[obj->id].mass;
250 			obj->mtype.phys_info.drag = Robot_info[obj->id].drag;
251 		}
252 	}
253 	else {		//Robots taken care of above
254 
255 		if ( obj->render_type == RT_POLYOBJ ) {
256 			int i;
257 			char *name = Save_pof_names[obj->rtype.pobj_info.model_num];
258 
259 			for (i=0;i<N_polygon_models;i++)
260 				if (!stricmp(Pof_names[i],name)) {		//found it!
261 					// mprintf((0,"Mapping <%s> to %d (was %d)\n",name,i,obj->rtype.pobj_info.model_num));
262 					obj->rtype.pobj_info.model_num = i;
263 					break;
264 				}
265 		}
266 	}
267 
268 	if ( obj->type == OBJ_POWERUP ) {
269 		if ( obj->id >= N_powerup_types )	{
270 			obj->id = 0;
271 			Assert( obj->render_type != RT_POLYOBJ );
272 		}
273 		obj->control_type = CT_POWERUP;
274 		obj->size = Powerup_info[obj->id].size;
275 		obj->ctype.powerup_info.creation_time = 0;
276 
277 #ifdef NETWORK
278 		if (Game_mode & GM_NETWORK)
279 			{
280 			  if (multi_powerup_is_4pack(obj->id))
281 				{
282 				 PowerupsInMine[obj->id-1]+=4;
283 			 	 MaxPowerupsAllowed[obj->id-1]+=4;
284 				}
285 			  PowerupsInMine[obj->id]++;
286 		     MaxPowerupsAllowed[obj->id]++;
287 			  mprintf ((0,"PowerupLimiter: ID=%d\n",obj->id));
288 			  if (obj->id>MAX_POWERUP_TYPES)
289 				mprintf ((1,"POWERUP: Overwriting array bounds!! Get JL!\n"));
290 		 	}
291 #endif
292 
293 	}
294 
295 	if ( obj->type == OBJ_WEAPON )	{
296 		if ( obj->id >= N_weapon_types )	{
297 			obj->id = 0;
298 			Assert( obj->render_type != RT_POLYOBJ );
299 		}
300 
301 		if (obj->id == PMINE_ID) {		//make sure pmines have correct values
302 
303 			obj->mtype.phys_info.mass = Weapon_info[obj->id].mass;
304 			obj->mtype.phys_info.drag = Weapon_info[obj->id].drag;
305 			obj->mtype.phys_info.flags |= PF_FREE_SPINNING;
306 
307 			// Make sure model number & size are correct...
308 			Assert( obj->render_type == RT_POLYOBJ );
309 
310 			obj->rtype.pobj_info.model_num = Weapon_info[obj->id].model_num;
311 			obj->size = Polygon_models[obj->rtype.pobj_info.model_num].rad;
312 		}
313 	}
314 
315 	if ( obj->type == OBJ_CNTRLCEN ) {
316 
317 		obj->render_type = RT_POLYOBJ;
318 		obj->control_type = CT_CNTRLCEN;
319 
320 		if (Gamesave_current_version <= 1) { // descent 1 reactor
321 			obj->id = 0;                         // used to be only one kind of reactor
322 			obj->rtype.pobj_info.model_num = Reactors[0].model_num;// descent 1 reactor
323 		}
324 		//@@// Make model number is correct...
325 		//@@for (i=0; i<Num_total_object_types; i++ )
326 		//@@	if ( ObjType[i] == OL_CONTROL_CENTER ) {
327 		//@@		obj->rtype.pobj_info.model_num = ObjId[i];
328 		//@@		obj->shields = ObjStrength[i];
329 		//@@		break;
330 		//@@	}
331 
332 		#ifdef EDITOR
333 		{
334 		int i;
335 		// Check, and set, strength of reactor
336 		for (i=0; i<Num_total_object_types; i++ )
337 			if ( ObjType[i]==OL_CONTROL_CENTER && ObjId[i] == obj->id ) {
338 				obj->shields = ObjStrength[i];
339 				break;
340 			}
341 		Assert(i < Num_total_object_types);		//make sure we found it
342 		}
343 		#endif
344 	}
345 
346 	if ( obj->type == OBJ_PLAYER )	{
347 		//int i;
348 
349 		//Assert(obj == Player);
350 
351 		if ( obj == ConsoleObject )
352 			init_player_object();
353 		else
354 			if (obj->render_type == RT_POLYOBJ)	//recover from Matt's pof file matchup bug
355 				obj->rtype.pobj_info.model_num = Player_ship->model_num;
356 
357 		//Make sure orient matrix is orthogonal
358 		check_and_fix_matrix(&obj->orient);
359 
360 		obj->id = Gamesave_num_players++;
361 	}
362 
363 	if (obj->type == OBJ_HOSTAGE) {
364 
365 		//@@if (obj->id > N_hostage_types)
366 		//@@	obj->id = 0;
367 
368 		obj->render_type = RT_HOSTAGE;
369 		obj->control_type = CT_POWERUP;
370 	}
371 
372 }
373 
374 //static gs_skip(int len,CFILE *file)
375 //{
376 //
377 //	cfseek(file,len,SEEK_CUR);
378 //}
379 
380 #ifdef EDITOR
gs_write_int(int i,FILE * file)381 static void gs_write_int(int i,FILE *file)
382 {
383 	if (fwrite( &i, sizeof(i), 1, file) != 1)
384 		Error( "Error reading int in gamesave.c" );
385 
386 }
387 
gs_write_fix(fix f,FILE * file)388 static void gs_write_fix(fix f,FILE *file)
389 {
390 	if (fwrite( &f, sizeof(f), 1, file) != 1)
391 		Error( "Error reading fix in gamesave.c" );
392 
393 }
394 
gs_write_short(short s,FILE * file)395 static void gs_write_short(short s,FILE *file)
396 {
397 	if (fwrite( &s, sizeof(s), 1, file) != 1)
398 		Error( "Error reading short in gamesave.c" );
399 
400 }
401 
gs_write_fixang(fixang f,FILE * file)402 static void gs_write_fixang(fixang f,FILE *file)
403 {
404 	if (fwrite( &f, sizeof(f), 1, file) != 1)
405 		Error( "Error reading fixang in gamesave.c" );
406 
407 }
408 
gs_write_byte(byte b,FILE * file)409 static void gs_write_byte(byte b,FILE *file)
410 {
411 	if (fwrite( &b, sizeof(b), 1, file) != 1)
412 		Error( "Error reading byte in gamesave.c" );
413 
414 }
415 
gr_write_vector(vms_vector * v,FILE * file)416 static void gr_write_vector(vms_vector *v,FILE *file)
417 {
418 	gs_write_fix(v->x,file);
419 	gs_write_fix(v->y,file);
420 	gs_write_fix(v->z,file);
421 }
422 
gs_write_matrix(vms_matrix * m,FILE * file)423 static void gs_write_matrix(vms_matrix *m,FILE *file)
424 {
425 	gr_write_vector(&m->rvec,file);
426 	gr_write_vector(&m->uvec,file);
427 	gr_write_vector(&m->fvec,file);
428 }
429 
gs_write_angvec(vms_angvec * v,FILE * file)430 static void gs_write_angvec(vms_angvec *v,FILE *file)
431 {
432 	gs_write_fixang(v->p,file);
433 	gs_write_fixang(v->b,file);
434 	gs_write_fixang(v->h,file);
435 }
436 
437 #endif
438 
439 
440 extern int multi_powerup_is_4pack(int);
441 //reads one object of the given version from the given file
read_object(object * obj,CFILE * f,int version)442 void read_object(object *obj,CFILE *f,int version)
443 {
444 
445 	obj->type           = cfile_read_byte(f);
446 	obj->id             = cfile_read_byte(f);
447 
448 	obj->control_type   = cfile_read_byte(f);
449 	obj->movement_type  = cfile_read_byte(f);
450 	obj->render_type    = cfile_read_byte(f);
451 	obj->flags          = cfile_read_byte(f);
452 
453 	obj->segnum         = cfile_read_short(f);
454 	obj->attached_obj   = -1;
455 
456 	cfile_read_vector(&obj->pos,f);
457 	cfile_read_matrix(&obj->orient,f);
458 
459 	obj->size           = cfile_read_fix(f);
460 	obj->shields        = cfile_read_fix(f);
461 
462 	cfile_read_vector(&obj->last_pos,f);
463 
464 	obj->contains_type  = cfile_read_byte(f);
465 	obj->contains_id    = cfile_read_byte(f);
466 	obj->contains_count = cfile_read_byte(f);
467 
468 	switch (obj->movement_type) {
469 
470 		case MT_PHYSICS:
471 
472 			cfile_read_vector(&obj->mtype.phys_info.velocity,f);
473 			cfile_read_vector(&obj->mtype.phys_info.thrust,f);
474 
475 			obj->mtype.phys_info.mass		= cfile_read_fix(f);
476 			obj->mtype.phys_info.drag		= cfile_read_fix(f);
477 			obj->mtype.phys_info.brakes	= cfile_read_fix(f);
478 
479 			cfile_read_vector(&obj->mtype.phys_info.rotvel,f);
480 			cfile_read_vector(&obj->mtype.phys_info.rotthrust,f);
481 
482 			obj->mtype.phys_info.turnroll	= cfile_read_fixang(f);
483 			obj->mtype.phys_info.flags		= cfile_read_short(f);
484 
485 			break;
486 
487 		case MT_SPINNING:
488 
489 			cfile_read_vector(&obj->mtype.spin_rate,f);
490 			break;
491 
492 		case MT_NONE:
493 			break;
494 
495 		default:
496 			Int3();
497 	}
498 
499 	switch (obj->control_type) {
500 
501 		case CT_AI: {
502 			int i;
503 
504 			obj->ctype.ai_info.behavior				= cfile_read_byte(f);
505 
506 			for (i=0;i<MAX_AI_FLAGS;i++)
507 				obj->ctype.ai_info.flags[i]			= cfile_read_byte(f);
508 
509 			obj->ctype.ai_info.hide_segment			= cfile_read_short(f);
510 			obj->ctype.ai_info.hide_index			= cfile_read_short(f);
511 			obj->ctype.ai_info.path_length			= cfile_read_short(f);
512 			obj->ctype.ai_info.cur_path_index		= cfile_read_short(f);
513 
514 			if (version <= 25) {
515 				cfile_read_short(f);	//				obj->ctype.ai_info.follow_path_start_seg	=
516 				cfile_read_short(f);	//				obj->ctype.ai_info.follow_path_end_seg		=
517 			}
518 
519 			break;
520 		}
521 
522 		case CT_EXPLOSION:
523 
524 			obj->ctype.expl_info.spawn_time		= cfile_read_fix(f);
525 			obj->ctype.expl_info.delete_time		= cfile_read_fix(f);
526 			obj->ctype.expl_info.delete_objnum	= cfile_read_short(f);
527 			obj->ctype.expl_info.next_attach = obj->ctype.expl_info.prev_attach = obj->ctype.expl_info.attach_parent = -1;
528 
529 			break;
530 
531 		case CT_WEAPON:
532 
533 			//do I really need to read these?  Are they even saved to disk?
534 
535 			obj->ctype.laser_info.parent_type		= cfile_read_short(f);
536 			obj->ctype.laser_info.parent_num		= cfile_read_short(f);
537 			obj->ctype.laser_info.parent_signature	= cfile_read_int(f);
538 
539 			break;
540 
541 		case CT_LIGHT:
542 
543 			obj->ctype.light_info.intensity = cfile_read_fix(f);
544 			break;
545 
546 		case CT_POWERUP:
547 
548 			if (version >= 25)
549 				obj->ctype.powerup_info.count = cfile_read_int(f);
550 			else
551 				obj->ctype.powerup_info.count = 1;
552 
553 			if (obj->id == POW_VULCAN_WEAPON)
554 					obj->ctype.powerup_info.count = VULCAN_WEAPON_AMMO_AMOUNT;
555 
556 			if (obj->id == POW_GAUSS_WEAPON)
557 					obj->ctype.powerup_info.count = VULCAN_WEAPON_AMMO_AMOUNT;
558 
559 			if (obj->id == POW_OMEGA_WEAPON)
560 					obj->ctype.powerup_info.count = MAX_OMEGA_CHARGE;
561 
562 			break;
563 
564 
565 		case CT_NONE:
566 		case CT_FLYING:
567 		case CT_DEBRIS:
568 			break;
569 
570 		case CT_SLEW:		//the player is generally saved as slew
571 			break;
572 
573 		case CT_CNTRLCEN:
574 			break;
575 
576 		case CT_MORPH:
577 		case CT_FLYTHROUGH:
578 		case CT_REPAIRCEN:
579 		default:
580 			Int3();
581 
582 	}
583 
584 	switch (obj->render_type) {
585 
586 		case RT_NONE:
587 			break;
588 
589 		case RT_MORPH:
590 		case RT_POLYOBJ: {
591 			int i,tmo;
592 
593 			obj->rtype.pobj_info.model_num		= cfile_read_int(f);
594 
595 			for (i=0;i<MAX_SUBMODELS;i++)
596 				cfile_read_angvec(&obj->rtype.pobj_info.anim_angles[i],f);
597 
598 			obj->rtype.pobj_info.subobj_flags	= cfile_read_int(f);
599 
600 			tmo = cfile_read_int(f);
601 
602 			#ifndef EDITOR
603 			obj->rtype.pobj_info.tmap_override	= tmo;
604 			#else
605 			if (tmo==-1)
606 				obj->rtype.pobj_info.tmap_override	= -1;
607 			else {
608 				int xlated_tmo = tmap_xlate_table[tmo];
609 				if (xlated_tmo < 0)	{
610 					mprintf( (0, "Couldn't find texture for demo object, model_num = %d\n", obj->rtype.pobj_info.model_num));
611 					Int3();
612 					xlated_tmo = 0;
613 				}
614 				obj->rtype.pobj_info.tmap_override	= xlated_tmo;
615 			}
616 			#endif
617 
618 			obj->rtype.pobj_info.alt_textures	= 0;
619 
620 			break;
621 		}
622 
623 		case RT_WEAPON_VCLIP:
624 		case RT_HOSTAGE:
625 		case RT_POWERUP:
626 		case RT_FIREBALL:
627 
628 			obj->rtype.vclip_info.vclip_num	= cfile_read_int(f);
629 			obj->rtype.vclip_info.frametime	= cfile_read_fix(f);
630 			obj->rtype.vclip_info.framenum	= cfile_read_byte(f);
631 
632 			break;
633 
634 		case RT_LASER:
635 			break;
636 
637 		default:
638 			Int3();
639 
640 	}
641 
642 }
643 
644 #ifdef EDITOR
645 
646 //writes one object to the given file
write_object(object * obj,FILE * f)647 void write_object(object *obj,FILE *f)
648 {
649 	gs_write_byte(obj->type,f);
650 	gs_write_byte(obj->id,f);
651 
652 	gs_write_byte(obj->control_type,f);
653 	gs_write_byte(obj->movement_type,f);
654 	gs_write_byte(obj->render_type,f);
655 	gs_write_byte(obj->flags,f);
656 
657 	gs_write_short(obj->segnum,f);
658 
659 	gr_write_vector(&obj->pos,f);
660 	gs_write_matrix(&obj->orient,f);
661 
662 	gs_write_fix(obj->size,f);
663 	gs_write_fix(obj->shields,f);
664 
665 	gr_write_vector(&obj->last_pos,f);
666 
667 	gs_write_byte(obj->contains_type,f);
668 	gs_write_byte(obj->contains_id,f);
669 	gs_write_byte(obj->contains_count,f);
670 
671 	switch (obj->movement_type) {
672 
673 		case MT_PHYSICS:
674 
675 	 		gr_write_vector(&obj->mtype.phys_info.velocity,f);
676 			gr_write_vector(&obj->mtype.phys_info.thrust,f);
677 
678 			gs_write_fix(obj->mtype.phys_info.mass,f);
679 			gs_write_fix(obj->mtype.phys_info.drag,f);
680 			gs_write_fix(obj->mtype.phys_info.brakes,f);
681 
682 			gr_write_vector(&obj->mtype.phys_info.rotvel,f);
683 			gr_write_vector(&obj->mtype.phys_info.rotthrust,f);
684 
685 			gs_write_fixang(obj->mtype.phys_info.turnroll,f);
686 			gs_write_short(obj->mtype.phys_info.flags,f);
687 
688 			break;
689 
690 		case MT_SPINNING:
691 
692 			gr_write_vector(&obj->mtype.spin_rate,f);
693 			break;
694 
695 		case MT_NONE:
696 			break;
697 
698 		default:
699 			Int3();
700 	}
701 
702 	switch (obj->control_type) {
703 
704 		case CT_AI: {
705 			int i;
706 
707 			gs_write_byte(obj->ctype.ai_info.behavior,f);
708 
709 			for (i=0;i<MAX_AI_FLAGS;i++)
710 				gs_write_byte(obj->ctype.ai_info.flags[i],f);
711 
712 			gs_write_short(obj->ctype.ai_info.hide_segment,f);
713 			gs_write_short(obj->ctype.ai_info.hide_index,f);
714 			gs_write_short(obj->ctype.ai_info.path_length,f);
715 			gs_write_short(obj->ctype.ai_info.cur_path_index,f);
716 
717 			// -- unused! mk, 08/13/95 -- gs_write_short(obj->ctype.ai_info.follow_path_start_seg,f);
718 			// -- unused! mk, 08/13/95 -- gs_write_short(obj->ctype.ai_info.follow_path_end_seg,f);
719 
720 			break;
721 		}
722 
723 		case CT_EXPLOSION:
724 
725 			gs_write_fix(obj->ctype.expl_info.spawn_time,f);
726 			gs_write_fix(obj->ctype.expl_info.delete_time,f);
727 			gs_write_short(obj->ctype.expl_info.delete_objnum,f);
728 
729 			break;
730 
731 		case CT_WEAPON:
732 
733 			//do I really need to write these objects?
734 
735 			gs_write_short(obj->ctype.laser_info.parent_type,f);
736 			gs_write_short(obj->ctype.laser_info.parent_num,f);
737 			gs_write_int(obj->ctype.laser_info.parent_signature,f);
738 
739 			break;
740 
741 		case CT_LIGHT:
742 
743 			gs_write_fix(obj->ctype.light_info.intensity,f);
744 			break;
745 
746 		case CT_POWERUP:
747 
748 			gs_write_int(obj->ctype.powerup_info.count,f);
749 			break;
750 
751 		case CT_NONE:
752 		case CT_FLYING:
753 		case CT_DEBRIS:
754 			break;
755 
756 		case CT_SLEW:		//the player is generally saved as slew
757 			break;
758 
759 		case CT_CNTRLCEN:
760 			break;			//control center object.
761 
762 		case CT_MORPH:
763 		case CT_REPAIRCEN:
764 		case CT_FLYTHROUGH:
765 		default:
766 			Int3();
767 
768 	}
769 
770 	switch (obj->render_type) {
771 
772 		case RT_NONE:
773 			break;
774 
775 		case RT_MORPH:
776 		case RT_POLYOBJ: {
777 			int i;
778 
779 			gs_write_int(obj->rtype.pobj_info.model_num,f);
780 
781 			for (i=0;i<MAX_SUBMODELS;i++)
782 				gs_write_angvec(&obj->rtype.pobj_info.anim_angles[i],f);
783 
784 			gs_write_int(obj->rtype.pobj_info.subobj_flags,f);
785 
786 			gs_write_int(obj->rtype.pobj_info.tmap_override,f);
787 
788 			break;
789 		}
790 
791 		case RT_WEAPON_VCLIP:
792 		case RT_HOSTAGE:
793 		case RT_POWERUP:
794 		case RT_FIREBALL:
795 
796 			gs_write_int(obj->rtype.vclip_info.vclip_num,f);
797 			gs_write_fix(obj->rtype.vclip_info.frametime,f);
798 			gs_write_byte(obj->rtype.vclip_info.framenum,f);
799 
800 			break;
801 
802 		case RT_LASER:
803 			break;
804 
805 		default:
806 			Int3();
807 
808 	}
809 
810 }
811 #endif
812 
813 extern int remove_trigger_num(int trigger_num);
814 
815 // -----------------------------------------------------------------------------
816 // Load game
817 // Loads all the relevant data for a level.
818 // If level != -1, it loads the filename with extension changed to .min
819 // Otherwise it loads the appropriate level mine.
820 // returns 0=everything ok, 1=old version, -1=error
load_game_data(CFILE * LoadFile)821 int load_game_data(CFILE *LoadFile)
822 {
823 	int i,j;
824 	int start_offset;
825 
826 	start_offset = cftell(LoadFile);
827 
828 	//===================== READ FILE INFO ========================
829 
830 	// Set default values
831 	game_fileinfo.level					=	-1;
832 	game_fileinfo.player_offset		=	-1;
833 	game_fileinfo.player_sizeof		=	sizeof(player);
834  	game_fileinfo.object_offset		=	-1;
835 	game_fileinfo.object_howmany		=	0;
836 	game_fileinfo.object_sizeof		=	sizeof(object);
837 	game_fileinfo.walls_offset			=	-1;
838 	game_fileinfo.walls_howmany		=	0;
839 	game_fileinfo.walls_sizeof			=	sizeof(wall);
840 	game_fileinfo.doors_offset			=	-1;
841 	game_fileinfo.doors_howmany		=	0;
842 	game_fileinfo.doors_sizeof			=	sizeof(active_door);
843 	game_fileinfo.triggers_offset		=	-1;
844 	game_fileinfo.triggers_howmany	=	0;
845 	game_fileinfo.triggers_sizeof		=	sizeof(trigger);
846 	game_fileinfo.control_offset		=	-1;
847 	game_fileinfo.control_howmany		=	0;
848 	game_fileinfo.control_sizeof		=	sizeof(control_center_triggers);
849 	game_fileinfo.matcen_offset		=	-1;
850 	game_fileinfo.matcen_howmany		=	0;
851 	game_fileinfo.matcen_sizeof		=	sizeof(matcen_info);
852 
853 	game_fileinfo.dl_indices_offset		=	-1;
854 	game_fileinfo.dl_indices_howmany		=	0;
855 	game_fileinfo.dl_indices_sizeof		=	sizeof(dl_index);
856 
857 	game_fileinfo.delta_light_offset		=	-1;
858 	game_fileinfo.delta_light_howmany		=	0;
859 	game_fileinfo.delta_light_sizeof		=	sizeof(delta_light);
860 
861 	// Read in game_top_fileinfo to get size of saved fileinfo.
862 
863 	if (cfseek( LoadFile, start_offset, SEEK_SET ))
864 		Error( "Error seeking in gamesave.c" );
865 
866 //	if (cfread( &game_top_fileinfo, sizeof(game_top_fileinfo), 1, LoadFile) != 1)
867 //		Error( "Error reading game_top_fileinfo in gamesave.c" );
868 
869 	game_top_fileinfo.fileinfo_signature = cfile_read_short(LoadFile);
870 	game_top_fileinfo.fileinfo_version = cfile_read_short(LoadFile);
871 	game_top_fileinfo.fileinfo_sizeof = cfile_read_int(LoadFile);
872 
873 	// Check signature
874 	if (game_top_fileinfo.fileinfo_signature != 0x6705)
875 		return -1;
876 
877 	// Check version number
878 	if (game_top_fileinfo.fileinfo_version < GAME_COMPATIBLE_VERSION )
879 		return -1;
880 
881 	// Now, Read in the fileinfo
882 	if (cfseek( LoadFile, start_offset, SEEK_SET ))
883 		Error( "Error seeking to game_fileinfo in gamesave.c" );
884 
885 //	if (cfread( &game_fileinfo, game_top_fileinfo.fileinfo_sizeof, 1, LoadFile )!=1)
886 //		Error( "Error reading game_fileinfo in gamesave.c" );
887 
888 	game_fileinfo.fileinfo_signature = cfile_read_short(LoadFile);
889 	game_fileinfo.fileinfo_version = cfile_read_short(LoadFile);
890 	game_fileinfo.fileinfo_sizeof = cfile_read_int(LoadFile);
891 	for(i=0; i<15; i++)
892 		game_fileinfo.mine_filename[i] = cfile_read_byte(LoadFile);
893 	game_fileinfo.level = cfile_read_int(LoadFile);
894 	game_fileinfo.player_offset = cfile_read_int(LoadFile);				// Player info
895 	game_fileinfo.player_sizeof = cfile_read_int(LoadFile);
896 	game_fileinfo.object_offset = cfile_read_int(LoadFile);				// Object info
897 	game_fileinfo.object_howmany = cfile_read_int(LoadFile);
898 	game_fileinfo.object_sizeof = cfile_read_int(LoadFile);
899 	game_fileinfo.walls_offset = cfile_read_int(LoadFile);
900 	game_fileinfo.walls_howmany = cfile_read_int(LoadFile);
901 	game_fileinfo.walls_sizeof = cfile_read_int(LoadFile);
902 	game_fileinfo.doors_offset = cfile_read_int(LoadFile);
903 	game_fileinfo.doors_howmany = cfile_read_int(LoadFile);
904 	game_fileinfo.doors_sizeof = cfile_read_int(LoadFile);
905 	game_fileinfo.triggers_offset = cfile_read_int(LoadFile);
906 	game_fileinfo.triggers_howmany = cfile_read_int(LoadFile);
907 	game_fileinfo.triggers_sizeof = cfile_read_int(LoadFile);
908 	game_fileinfo.links_offset = cfile_read_int(LoadFile);
909 	game_fileinfo.links_howmany = cfile_read_int(LoadFile);
910 	game_fileinfo.links_sizeof = cfile_read_int(LoadFile);
911 	game_fileinfo.control_offset = cfile_read_int(LoadFile);
912 	game_fileinfo.control_howmany = cfile_read_int(LoadFile);
913 	game_fileinfo.control_sizeof = cfile_read_int(LoadFile);
914 	game_fileinfo.matcen_offset = cfile_read_int(LoadFile);
915 	game_fileinfo.matcen_howmany = cfile_read_int(LoadFile);
916 	game_fileinfo.matcen_sizeof = cfile_read_int(LoadFile);
917 
918 	if (game_top_fileinfo.fileinfo_version >= 29) {
919 		game_fileinfo.dl_indices_offset = cfile_read_int(LoadFile);
920 		game_fileinfo.dl_indices_howmany = cfile_read_int(LoadFile);
921 		game_fileinfo.dl_indices_sizeof = cfile_read_int(LoadFile);
922 
923 		game_fileinfo.delta_light_offset = cfile_read_int(LoadFile);
924 		game_fileinfo.delta_light_howmany = cfile_read_int(LoadFile);
925 		game_fileinfo.delta_light_sizeof = cfile_read_int(LoadFile);
926 	}
927 
928 	if (game_top_fileinfo.fileinfo_version >= 31) { //load mine filename
929 		// read newline-terminated string, not sure what version this changed.
930 		cfgets(Current_level_name,sizeof(Current_level_name),LoadFile);
931 
932 		if (Current_level_name[strlen(Current_level_name)-1] == '\n')
933 			Current_level_name[strlen(Current_level_name)-1] = 0;
934 	}
935 	else if (game_top_fileinfo.fileinfo_version >= 14) { //load mine filename
936 		// read null-terminated string
937 		char *p=Current_level_name;
938 		//must do read one char at a time, since no cfgets()
939 		do *p = cfgetc(LoadFile); while (*p++!=0);
940 	}
941 	else
942 		Current_level_name[0]=0;
943 
944 	if (game_top_fileinfo.fileinfo_version >= 19) {	//load pof names
945 		N_save_pof_names = cfile_read_short(LoadFile);
946 		if (N_save_pof_names != 0x614d && N_save_pof_names != 0x5547) { // "Ma"de w/DMB beta/"GU"ILE
947 			Assert(N_save_pof_names < MAX_POLYGON_MODELS);
948 			cfread(Save_pof_names,N_save_pof_names,FILENAME_LEN,LoadFile);
949 		}
950 	}
951 
952 	//===================== READ PLAYER INFO ==========================
953 	Object_next_signature = 0;
954 
955 	//===================== READ OBJECT INFO ==========================
956 
957 	Gamesave_num_org_robots = 0;
958 	Gamesave_num_players = 0;
959 
960 	if (game_fileinfo.object_offset > -1) {
961 		if (cfseek( LoadFile, game_fileinfo.object_offset, SEEK_SET ))
962 			Error( "Error seeking to object_offset in gamesave.c" );
963 
964 		for (i=0;i<game_fileinfo.object_howmany;i++) {
965 
966 			read_object(&Objects[i], LoadFile, game_top_fileinfo.fileinfo_version);
967 
968 			Objects[i].signature = Object_next_signature++;
969 			verify_object( &Objects[i] );
970 		}
971 
972 	}
973 
974 	//===================== READ WALL INFO ============================
975 
976 	if (game_fileinfo.walls_offset > -1)
977 	{
978 
979 		if (!cfseek( LoadFile, game_fileinfo.walls_offset,SEEK_SET ))	{
980 			for (i=0;i<game_fileinfo.walls_howmany;i++) {
981 
982 				if (game_top_fileinfo.fileinfo_version >= 20)
983 					wall_read(&Walls[i], LoadFile); // v20 walls and up.
984 				else if (game_top_fileinfo.fileinfo_version >= 17) {
985 					v19_wall w;
986 
987 					v19_wall_read(&w, LoadFile);
988 
989 					Walls[i].segnum	        = w.segnum;
990 					Walls[i].sidenum		= w.sidenum;
991 					Walls[i].linked_wall	= w.linked_wall;
992 
993 					Walls[i].type			= w.type;
994 					Walls[i].flags			= w.flags;
995 					Walls[i].hps			= w.hps;
996 					Walls[i].trigger		= w.trigger;
997 					Walls[i].clip_num		= w.clip_num;
998 					Walls[i].keys			= w.keys;
999 
1000 					Walls[i].state			= WALL_DOOR_CLOSED;
1001 				} else {
1002 					v16_wall w;
1003 
1004 					v16_wall_read(&w, LoadFile);
1005 
1006 					Walls[i].segnum = Walls[i].sidenum = Walls[i].linked_wall = -1;
1007 
1008 					Walls[i].type		= w.type;
1009 					Walls[i].flags		= w.flags;
1010 					Walls[i].hps		= w.hps;
1011 					Walls[i].trigger	= w.trigger;
1012 					Walls[i].clip_num	= w.clip_num;
1013 					Walls[i].keys		= w.keys;
1014 				}
1015 
1016 			}
1017 		}
1018 	}
1019 
1020 	//===================== READ DOOR INFO ============================
1021 
1022 	if (game_fileinfo.doors_offset > -1)
1023 	{
1024 		if (!cfseek( LoadFile, game_fileinfo.doors_offset,SEEK_SET ))	{
1025 
1026 			for (i=0;i<game_fileinfo.doors_howmany;i++) {
1027 
1028 				if (game_top_fileinfo.fileinfo_version >= 20)
1029 					active_door_read(&ActiveDoors[i], LoadFile); // version 20 and up
1030 				else {
1031 					v19_door d;
1032 					int p;
1033 
1034 					v19_door_read(&d, LoadFile);
1035 
1036 					ActiveDoors[i].n_parts = d.n_parts;
1037 
1038 					for (p=0;p<d.n_parts;p++) {
1039 						int cseg,cside;
1040 
1041 						cseg = Segments[d.seg[p]].children[d.side[p]];
1042 						cside = find_connect_side(&Segments[d.seg[p]],&Segments[cseg]);
1043 
1044 						ActiveDoors[i].front_wallnum[p] = Segments[d.seg[p]].sides[d.side[p]].wall_num;
1045 						ActiveDoors[i].back_wallnum[p] = Segments[cseg].sides[cside].wall_num;
1046 					}
1047 				}
1048 
1049 			}
1050 		}
1051 	}
1052 
1053 	//==================== READ TRIGGER INFO ==========================
1054 
1055 
1056 // for MACINTOSH -- assume all triggers >= verion 31 triggers.
1057 
1058 	if (game_fileinfo.triggers_offset > -1)
1059 	{
1060 		if (!cfseek( LoadFile, game_fileinfo.triggers_offset,SEEK_SET ))	{
1061 			for (i=0;i<game_fileinfo.triggers_howmany;i++)
1062 				if (game_top_fileinfo.fileinfo_version < 31) {
1063 					v30_trigger trig;
1064 					int t,type;
1065 
1066 					type=0;
1067 
1068 					if (game_top_fileinfo.fileinfo_version < 30) {
1069 						v29_trigger trig29;
1070 						int t;
1071 
1072 						v29_trigger_read(&trig29, LoadFile);
1073 
1074 						trig.flags		= trig29.flags;
1075 						trig.num_links	= trig29.num_links;
1076 						trig.num_links	= trig29.num_links;
1077 						trig.value		= trig29.value;
1078 						trig.time		= trig29.time;
1079 
1080 						for (t=0;t<trig.num_links;t++) {
1081 							trig.seg[t]  = trig29.seg[t];
1082 							trig.side[t] = trig29.side[t];
1083 						}
1084 					}
1085 					else
1086 						v30_trigger_read(&trig, LoadFile);
1087 
1088 					//Assert(trig.flags & TRIGGER_ON);
1089 					trig.flags &= ~TRIGGER_ON;
1090 
1091 					if (trig.flags & TRIGGER_CONTROL_DOORS)
1092 						type = TT_OPEN_DOOR;
1093 					else if (trig.flags & TRIGGER_SHIELD_DAMAGE)
1094 						Int3();
1095 					else if (trig.flags & TRIGGER_ENERGY_DRAIN)
1096 						Int3();
1097 					else if (trig.flags & TRIGGER_EXIT)
1098 						type = TT_EXIT;
1099 					else if (trig.flags & TRIGGER_ONE_SHOT)
1100 						Int3();
1101 					else if (trig.flags & TRIGGER_MATCEN)
1102 						type = TT_MATCEN;
1103 					else if (trig.flags & TRIGGER_ILLUSION_OFF)
1104 						type = TT_ILLUSION_OFF;
1105 					else if (trig.flags & TRIGGER_SECRET_EXIT)
1106 						type = TT_SECRET_EXIT;
1107 					else if (trig.flags & TRIGGER_ILLUSION_ON)
1108 						type = TT_ILLUSION_ON;
1109 					else if (trig.flags & TRIGGER_UNLOCK_DOORS)
1110 						type = TT_UNLOCK_DOOR;
1111 					else if (trig.flags & TRIGGER_OPEN_WALL)
1112 						type = TT_OPEN_WALL;
1113 					else if (trig.flags & TRIGGER_CLOSE_WALL)
1114 						type = TT_CLOSE_WALL;
1115 					else if (trig.flags & TRIGGER_ILLUSORY_WALL)
1116 						type = TT_ILLUSORY_WALL;
1117 					else
1118 						Int3();
1119 
1120 					Triggers[i].type        = type;
1121 					Triggers[i].flags       = 0;
1122 					Triggers[i].num_links   = trig.num_links;
1123 					Triggers[i].num_links   = trig.num_links;
1124 					Triggers[i].value       = trig.value;
1125 					Triggers[i].time        = trig.time;
1126 
1127 					for (t=0;t<trig.num_links;t++) {
1128 						Triggers[i].seg[t] = trig.seg[t];
1129 						Triggers[i].side[t] = trig.side[t];
1130 					}
1131 				}
1132 				else
1133 					trigger_read(&Triggers[i], LoadFile);
1134 		}
1135 	}
1136 
1137 	//================ READ CONTROL CENTER TRIGGER INFO ===============
1138 
1139 	if (game_fileinfo.control_offset > -1)
1140 		if (!cfseek(LoadFile, game_fileinfo.control_offset, SEEK_SET))
1141 		{
1142 			Assert(game_fileinfo.control_sizeof == sizeof(control_center_triggers));
1143 			control_center_triggers_read_n(&ControlCenterTriggers, game_fileinfo.control_howmany, LoadFile);
1144 		}
1145 
1146 	//================ READ MATERIALOGRIFIZATIONATORS INFO ===============
1147 
1148 	if (game_fileinfo.matcen_offset > -1)
1149 	{	int	j;
1150 
1151 		if (!cfseek( LoadFile, game_fileinfo.matcen_offset,SEEK_SET ))	{
1152 			// mprintf((0, "Reading %i materialization centers.\n", game_fileinfo.matcen_howmany));
1153 			for (i=0;i<game_fileinfo.matcen_howmany;i++) {
1154 				if (game_top_fileinfo.fileinfo_version < 27) {
1155 					old_matcen_info m;
1156 
1157 					old_matcen_info_read(&m, LoadFile);
1158 
1159 					RobotCenters[i].robot_flags[0] = m.robot_flags;
1160 					RobotCenters[i].robot_flags[1] = 0;
1161 					RobotCenters[i].hit_points = m.hit_points;
1162 					RobotCenters[i].interval = m.interval;
1163 					RobotCenters[i].segnum = m.segnum;
1164 					RobotCenters[i].fuelcen_num = m.fuelcen_num;
1165 				}
1166 				else
1167 					matcen_info_read(&RobotCenters[i], LoadFile);
1168 
1169 				//	Set links in RobotCenters to Station array
1170 
1171 				for (j=0; j<=Highest_segment_index; j++)
1172 					if (Segment2s[j].special == SEGMENT_IS_ROBOTMAKER)
1173 						if (Segment2s[j].matcen_num == i)
1174 							RobotCenters[i].fuelcen_num = Segment2s[j].value;
1175 
1176 				// mprintf((0, "   %i: flags = %08x\n", i, RobotCenters[i].robot_flags));
1177 			}
1178 		}
1179 	}
1180 
1181 
1182 	//================ READ DL_INDICES INFO ===============
1183 
1184 	Num_static_lights = 0;
1185 
1186 	if (game_fileinfo.dl_indices_offset > -1) {
1187 		int	i;
1188 
1189 		if (!cfseek( LoadFile, game_fileinfo.dl_indices_offset, SEEK_SET ))	{
1190 			Num_static_lights = game_fileinfo.dl_indices_howmany;
1191 			for (i=0; i<game_fileinfo.dl_indices_howmany; i++) {
1192 				if (game_top_fileinfo.fileinfo_version < 29) {
1193 					mprintf((0, "Warning: Old mine version.  Not reading Dl_indices info.\n"));
1194 					Int3();	//shouldn't be here!!!
1195 				} else
1196 					dl_index_read(&Dl_indices[i], LoadFile);
1197 			}
1198 		}
1199 	}
1200 
1201 	//	Indicate that no light has been subtracted from any vertices.
1202 	clear_light_subtracted();
1203 
1204 	//================ READ DELTA LIGHT INFO ===============
1205 
1206 	if (game_fileinfo.delta_light_offset > -1) {
1207 		int	i;
1208 
1209 		if (!cfseek( LoadFile, game_fileinfo.delta_light_offset, SEEK_SET ))	{
1210 			for (i=0; i<game_fileinfo.delta_light_howmany; i++) {
1211 				if (game_top_fileinfo.fileinfo_version < 29) {
1212 					mprintf((0, "Warning: Old mine version.  Not reading delta light info.\n"));
1213 				} else
1214 					delta_light_read(&Delta_lights[i], LoadFile);
1215 			}
1216 		}
1217 	}
1218 
1219 	//========================= UPDATE VARIABLES ======================
1220 
1221 	reset_objects(game_fileinfo.object_howmany);
1222 
1223 	for (i=0; i<MAX_OBJECTS; i++) {
1224 		Objects[i].next = Objects[i].prev = -1;
1225 		if (Objects[i].type != OBJ_NONE) {
1226 			int objsegnum = Objects[i].segnum;
1227 
1228 			if (objsegnum > Highest_segment_index)		//bogus object
1229 				Objects[i].type = OBJ_NONE;
1230 			else {
1231 				Objects[i].segnum = -1;			//avoid Assert()
1232 				obj_link(i,objsegnum);
1233 			}
1234 		}
1235 	}
1236 
1237 	clear_transient_objects(1);		//1 means clear proximity bombs
1238 
1239 	// Make sure non-transparent doors are set correctly.
1240 	for (i=0; i< Num_segments; i++)
1241 		for (j=0;j<MAX_SIDES_PER_SEGMENT;j++) {
1242 			side	*sidep = &Segments[i].sides[j];
1243 			if ((sidep->wall_num != -1) && (Walls[sidep->wall_num].clip_num != -1)) {
1244 				//mprintf((0, "Checking Wall %d\n", Segments[i].sides[j].wall_num));
1245 				if (WallAnims[Walls[sidep->wall_num].clip_num].flags & WCF_TMAP1) {
1246 					//mprintf((0, "Fixing non-transparent door.\n"));
1247 					sidep->tmap_num = WallAnims[Walls[sidep->wall_num].clip_num].frames[0];
1248 					sidep->tmap_num2 = 0;
1249 				}
1250 			}
1251 		}
1252 
1253 
1254 	Num_walls = game_fileinfo.walls_howmany;
1255 	reset_walls();
1256 
1257 	Num_open_doors = game_fileinfo.doors_howmany;
1258 	Num_triggers = game_fileinfo.triggers_howmany;
1259 
1260 	//go through all walls, killing references to invalid triggers
1261 	for (i=0;i<Num_walls;i++)
1262 		if (Walls[i].trigger >= Num_triggers) {
1263 			mprintf((0,"Removing reference to invalid trigger %d from wall %d\n",Walls[i].trigger,i));
1264 			Walls[i].trigger = -1;	//kill trigger
1265 		}
1266 
1267 	//go through all triggers, killing unused ones
1268 	for (i=0;i<Num_triggers;) {
1269 		int w;
1270 
1271 		//	Find which wall this trigger is connected to.
1272 		for (w=0; w<Num_walls; w++)
1273 			if (Walls[w].trigger == i)
1274 				break;
1275 
1276 	#ifdef EDITOR
1277 		if (w == Num_walls) {
1278 			mprintf((0,"Removing unreferenced trigger %d\n",i));
1279 			remove_trigger_num(i);
1280 		}
1281 		else
1282 	#endif
1283 			i++;
1284 	}
1285 
1286 	//	MK, 10/17/95: Make walls point back at the triggers that control them.
1287 	//	Go through all triggers, stuffing controlling_trigger field in Walls.
1288 	{	int t;
1289 
1290 	for (i=0; i<Num_walls; i++)
1291 		Walls[i].controlling_trigger = -1;
1292 
1293 	for (t=0; t<Num_triggers; t++) {
1294 		int	l;
1295 		for (l=0; l<Triggers[t].num_links; l++) {
1296 			int	seg_num, side_num, wall_num;
1297 
1298 			seg_num = Triggers[t].seg[l];
1299 			side_num = Triggers[t].side[l];
1300 			wall_num = Segments[seg_num].sides[side_num].wall_num;
1301 
1302 			// -- if (Walls[wall_num].controlling_trigger != -1)
1303 			// -- 	Int3();
1304 
1305 			//check to see that if a trigger requires a wall that it has one,
1306 			//and if it requires a matcen that it has one
1307 
1308 			if (Triggers[t].type == TT_MATCEN) {
1309 				if (Segment2s[seg_num].special != SEGMENT_IS_ROBOTMAKER)
1310 					Int3();		//matcen trigger doesn't point to matcen
1311 			}
1312 			else if (Triggers[t].type != TT_LIGHT_OFF && Triggers[t].type != TT_LIGHT_ON) {	//light triggers don't require walls
1313 				if (wall_num == -1)
1314 					Int3();	//	This is illegal.  This trigger requires a wall
1315 				else
1316 					Walls[wall_num].controlling_trigger = t;
1317 			}
1318 		}
1319 	}
1320 	}
1321 
1322 	Num_robot_centers = game_fileinfo.matcen_howmany;
1323 
1324 	//fix old wall structs
1325 	if (game_top_fileinfo.fileinfo_version < 17) {
1326 		int segnum,sidenum,wallnum;
1327 
1328 		for (segnum=0; segnum<=Highest_segment_index; segnum++)
1329 			for (sidenum=0;sidenum<6;sidenum++)
1330 				if ((wallnum=Segments[segnum].sides[sidenum].wall_num) != -1) {
1331 					Walls[wallnum].segnum = segnum;
1332 					Walls[wallnum].sidenum = sidenum;
1333 				}
1334 	}
1335 
1336 	#ifndef NDEBUG
1337 	{
1338 		int	sidenum;
1339 		for (sidenum=0; sidenum<6; sidenum++) {
1340 			int	wallnum = Segments[Highest_segment_index].sides[sidenum].wall_num;
1341 			if (wallnum != -1)
1342 				if ((Walls[wallnum].segnum != Highest_segment_index) || (Walls[wallnum].sidenum != sidenum))
1343 					Int3();	//	Error.  Bogus walls in this segment.
1344 								// Consult Yuan or Mike.
1345 		}
1346 	}
1347 	#endif
1348 
1349 	//create_local_segment_data();
1350 
1351 	fix_object_segs();
1352 
1353 	#ifndef NDEBUG
1354 	dump_mine_info();
1355 	#endif
1356 
1357 	if (game_top_fileinfo.fileinfo_version < GAME_VERSION && !(game_top_fileinfo.fileinfo_version==25 && GAME_VERSION==26))
1358 		return 1;		//means old version
1359 	else
1360 		return 0;
1361 }
1362 
1363 
1364 int check_segment_connections(void);
1365 
1366 extern void	set_ambient_sound_flags(void);
1367 
1368 // ----------------------------------------------------------------------------
1369 
1370 #define LEVEL_FILE_VERSION      8
1371 //1 -> 2  add palette name
1372 //2 -> 3  add control center explosion time
1373 //3 -> 4  add reactor strength
1374 //4 -> 5  killed hostage text stuff
1375 //5 -> 6  added Secret_return_segment and Secret_return_orient
1376 //6 -> 7  added flickering lights
1377 //7 -> 8  made version 8 to be not compatible with D2 1.0 & 1.1
1378 
1379 #ifndef RELEASE
1380 char *Level_being_loaded=NULL;
1381 #endif
1382 
1383 #ifdef COMPACT_SEGS
1384 extern void ncache_flush();
1385 #endif
1386 
1387 extern int Slide_segs_computed;
1388 
1389 int no_old_level_file_error=0;
1390 
1391 //loads a level (.LVL) file from disk
1392 //returns 0 if success, else error code
load_level(char * filename_passed)1393 int load_level(char * filename_passed)
1394 {
1395 #ifdef EDITOR
1396 	int use_compiled_level=1;
1397 #endif
1398 	CFILE * LoadFile;
1399 	char filename[128];
1400 	int sig, minedata_offset, gamedata_offset;
1401 	int mine_err, game_err;
1402 #ifdef NETWORK
1403 	int i;
1404 #endif
1405 
1406 	Slide_segs_computed = 0;
1407 
1408 #ifdef NETWORK
1409    if (Game_mode & GM_NETWORK)
1410 	 {
1411 	  for (i=0;i<MAX_POWERUP_TYPES;i++)
1412 		{
1413 			MaxPowerupsAllowed[i]=0;
1414 			PowerupsInMine[i]=0;
1415 		}
1416 	 }
1417 #endif
1418 
1419 	#ifdef COMPACT_SEGS
1420 	ncache_flush();
1421 	#endif
1422 
1423 	#ifndef RELEASE
1424 	Level_being_loaded = filename_passed;
1425 	#endif
1426 
1427 	strcpy(filename,filename_passed);
1428 
1429 	#ifdef EDITOR
1430 		//if we have the editor, try the LVL first, no matter what was passed.
1431 		//if we don't have an LVL, try RDL
1432 		//if we don't have the editor, we just use what was passed
1433 
1434 		change_filename_extension(filename,filename_passed,".lvl");
1435 		use_compiled_level = 0;
1436 
1437 		if (!cfexist(filename))	{
1438 			change_filename_extension(filename,filename,".rl2");
1439 			use_compiled_level = 1;
1440 		}
1441 	#endif
1442 
1443 	LoadFile = cfopen( filename, "rb" );
1444 
1445 	if (!LoadFile)	{
1446 		#ifdef EDITOR
1447 			mprintf((0,"Can't open level file <%s>\n", filename));
1448 			return 1;
1449 		#else
1450 			Error("Can't open file <%s>\n",filename);
1451 		#endif
1452 	}
1453 
1454 	strcpy( Gamesave_current_filename, filename );
1455 
1456 //	#ifdef NEWDEMO
1457 //	if ( Newdemo_state == ND_STATE_RECORDING )
1458 //		newdemo_record_start_demo();
1459 //	#endif
1460 
1461 	sig                      = cfile_read_int(LoadFile);
1462 	Gamesave_current_version = cfile_read_int(LoadFile);
1463 	mprintf((0, "Gamesave_current_version = %d\n", Gamesave_current_version));
1464 	minedata_offset          = cfile_read_int(LoadFile);
1465 	gamedata_offset          = cfile_read_int(LoadFile);
1466 
1467 	Assert(sig == MAKE_SIG('P','L','V','L'));
1468 
1469 	if (Gamesave_current_version >= 8) {    //read dummy data
1470 		cfile_read_int(LoadFile);
1471 		cfile_read_short(LoadFile);
1472 		cfile_read_byte(LoadFile);
1473 	}
1474 
1475 	if (Gamesave_current_version < 5)
1476 		cfile_read_int(LoadFile);       //was hostagetext_offset
1477 
1478 	if (Gamesave_current_version > 1) {
1479 		cfgets(Current_level_palette,sizeof(Current_level_palette),LoadFile);
1480 		if (Current_level_palette[strlen(Current_level_palette)-1] == '\n')
1481 			Current_level_palette[strlen(Current_level_palette)-1] = 0;
1482 	}
1483 	if (Gamesave_current_version <= 1 || Current_level_palette[0]==0) // descent 1 level
1484 		strcpy(Current_level_palette, DEFAULT_LEVEL_PALETTE);
1485 
1486 	if (Gamesave_current_version >= 3)
1487 		Base_control_center_explosion_time = cfile_read_int(LoadFile);
1488 	else
1489 		Base_control_center_explosion_time = DEFAULT_CONTROL_CENTER_EXPLOSION_TIME;
1490 
1491 	if (Gamesave_current_version >= 4)
1492 		Reactor_strength = cfile_read_int(LoadFile);
1493 	else
1494 		Reactor_strength = -1;  //use old defaults
1495 
1496 	if (Gamesave_current_version >= 7) {
1497 		int i;
1498 
1499 		Num_flickering_lights = cfile_read_int(LoadFile);
1500 		Assert((Num_flickering_lights >= 0) && (Num_flickering_lights < MAX_FLICKERING_LIGHTS));
1501 		for (i = 0; i < Num_flickering_lights; i++)
1502 			flickering_light_read(&Flickering_lights[i], LoadFile);
1503 	}
1504 	else
1505 		Num_flickering_lights = 0;
1506 
1507 	if (Gamesave_current_version < 6) {
1508 		Secret_return_segment = 0;
1509 		Secret_return_orient.rvec.x = F1_0;
1510 		Secret_return_orient.rvec.y = 0;
1511 		Secret_return_orient.rvec.z = 0;
1512 		Secret_return_orient.fvec.x = 0;
1513 		Secret_return_orient.fvec.y = F1_0;
1514 		Secret_return_orient.fvec.z = 0;
1515 		Secret_return_orient.uvec.x = 0;
1516 		Secret_return_orient.uvec.y = 0;
1517 		Secret_return_orient.uvec.z = F1_0;
1518 	} else {
1519 		Secret_return_segment = cfile_read_int(LoadFile);
1520 		Secret_return_orient.rvec.x = cfile_read_int(LoadFile);
1521 		Secret_return_orient.rvec.y = cfile_read_int(LoadFile);
1522 		Secret_return_orient.rvec.z = cfile_read_int(LoadFile);
1523 		Secret_return_orient.fvec.x = cfile_read_int(LoadFile);
1524 		Secret_return_orient.fvec.y = cfile_read_int(LoadFile);
1525 		Secret_return_orient.fvec.z = cfile_read_int(LoadFile);
1526 		Secret_return_orient.uvec.x = cfile_read_int(LoadFile);
1527 		Secret_return_orient.uvec.y = cfile_read_int(LoadFile);
1528 		Secret_return_orient.uvec.z = cfile_read_int(LoadFile);
1529 	}
1530 
1531 	cfseek(LoadFile,minedata_offset,SEEK_SET);
1532 	#ifdef EDITOR
1533 	if (!use_compiled_level) {
1534 		mine_err = load_mine_data(LoadFile);
1535 #if 0 // get from d1src if needed
1536 		// Compress all uv coordinates in mine, improves texmap precision. --MK, 02/19/96
1537 		compress_uv_coordinates_all();
1538 #endif
1539 	} else
1540 	#endif
1541 		//NOTE LINK TO ABOVE!!
1542 		mine_err = load_mine_data_compiled(LoadFile);
1543 
1544 	if (mine_err == -1) {   //error!!
1545 		cfclose(LoadFile);
1546 		return 2;
1547 	}
1548 
1549 	cfseek(LoadFile,gamedata_offset,SEEK_SET);
1550 	game_err = load_game_data(LoadFile);
1551 
1552 	if (game_err == -1) {   //error!!
1553 		cfclose(LoadFile);
1554 		return 3;
1555 	}
1556 
1557 	//======================== CLOSE FILE =============================
1558 
1559 	cfclose( LoadFile );
1560 
1561 	set_ambient_sound_flags();
1562 
1563 	#ifdef EDITOR
1564 	write_game_text_file(filename);
1565 	if (Errors_in_mine) {
1566 		if (is_real_level(filename)) {
1567 			char  ErrorMessage[200];
1568 
1569 			sprintf( ErrorMessage, "Warning: %i errors in %s!\n", Errors_in_mine, Level_being_loaded );
1570 			stop_time();
1571 			gr_palette_load(gr_palette);
1572 			nm_messagebox( NULL, 1, "Continue", ErrorMessage );
1573 			start_time();
1574 		} else
1575 			mprintf((1, "Error: %i errors in %s.\n", Errors_in_mine, Level_being_loaded));
1576 	}
1577 	#endif
1578 
1579 	#ifdef EDITOR
1580 	//If an old version, ask the use if he wants to save as new version
1581 	if (!no_old_level_file_error && (Function_mode == FMODE_EDITOR) && (((LEVEL_FILE_VERSION > 3) && Gamesave_current_version < LEVEL_FILE_VERSION) || mine_err == 1 || game_err == 1)) {
1582 		char  ErrorMessage[200];
1583 
1584 		sprintf( ErrorMessage,
1585 					"You just loaded a old version\n"
1586 					"level.  Would you like to save\n"
1587 					"it as a current version level?");
1588 
1589 		stop_time();
1590 		gr_palette_load(gr_palette);
1591 		if (nm_messagebox( NULL, 2, "Don't Save", "Save", ErrorMessage )==1)
1592 			save_level(filename);
1593 		start_time();
1594 	}
1595 	#endif
1596 
1597 	#ifdef EDITOR
1598 	if (Function_mode == FMODE_EDITOR)
1599 		editor_status("Loaded NEW mine %s, \"%s\"",filename,Current_level_name);
1600 	#endif
1601 
1602 	#ifdef EDITOR
1603 	if (check_segment_connections())
1604 		nm_messagebox( "ERROR", 1, "Ok",
1605 				"Connectivity errors detected in\n"
1606 				"mine.  See monochrome screen for\n"
1607 				"details, and contact Matt or Mike." );
1608 	#endif
1609 
1610 	return 0;
1611 }
1612 
1613 #ifdef EDITOR
get_level_name()1614 void get_level_name()
1615 {
1616 //NO_UI!!!	UI_WINDOW 				*NameWindow = NULL;
1617 //NO_UI!!!	UI_GADGET_INPUTBOX	*NameText;
1618 //NO_UI!!!	UI_GADGET_BUTTON 		*QuitButton;
1619 //NO_UI!!!
1620 //NO_UI!!!	// Open a window with a quit button
1621 //NO_UI!!!	NameWindow = ui_open_window( 20, 20, 300, 110, WIN_DIALOG );
1622 //NO_UI!!!	QuitButton = ui_add_gadget_button( NameWindow, 150-24, 60, 48, 40, "Done", NULL );
1623 //NO_UI!!!
1624 //NO_UI!!!	ui_wprintf_at( NameWindow, 10, 12,"Please enter a name for this mine:" );
1625 //NO_UI!!!	NameText = ui_add_gadget_inputbox( NameWindow, 10, 30, LEVEL_NAME_LEN, LEVEL_NAME_LEN, Current_level_name );
1626 //NO_UI!!!
1627 //NO_UI!!!	NameWindow->keyboard_focus_gadget = (UI_GADGET *)NameText;
1628 //NO_UI!!!	QuitButton->hotkey = KEY_ENTER;
1629 //NO_UI!!!
1630 //NO_UI!!!	ui_gadget_calc_keys(NameWindow);
1631 //NO_UI!!!
1632 //NO_UI!!!	while (!QuitButton->pressed && last_keypress!=KEY_ENTER) {
1633 //NO_UI!!!		ui_mega_process();
1634 //NO_UI!!!		ui_window_do_gadgets(NameWindow);
1635 //NO_UI!!!	}
1636 //NO_UI!!!
1637 //NO_UI!!!	strcpy( Current_level_name, NameText->text );
1638 //NO_UI!!!
1639 //NO_UI!!!	if ( NameWindow!=NULL )	{
1640 //NO_UI!!!		ui_close_window( NameWindow );
1641 //NO_UI!!!		NameWindow = NULL;
1642 //NO_UI!!!	}
1643 //NO_UI!!!
1644 
1645 	newmenu_item m[2];
1646 
1647 	m[0].type = NM_TYPE_TEXT; m[0].text = "Please enter a name for this mine:";
1648 	m[1].type = NM_TYPE_INPUT; m[1].text = Current_level_name; m[1].text_len = LEVEL_NAME_LEN;
1649 
1650 	newmenu_do( NULL, "Enter mine name", 2, m, NULL );
1651 
1652 }
1653 #endif
1654 
1655 
1656 #ifdef EDITOR
1657 
1658 int	Errors_in_mine;
1659 
1660 // -----------------------------------------------------------------------------
compute_num_delta_light_records(void)1661 int compute_num_delta_light_records(void)
1662 {
1663 	int	i;
1664 	int	total = 0;
1665 
1666 	for (i=0; i<Num_static_lights; i++) {
1667 		total += Dl_indices[i].count;
1668 	}
1669 
1670 	return total;
1671 
1672 }
1673 
1674 // -----------------------------------------------------------------------------
1675 // Save game
save_game_data(FILE * SaveFile)1676 int save_game_data(FILE * SaveFile)
1677 {
1678 	int  player_offset, object_offset, walls_offset, doors_offset, triggers_offset, control_offset, matcen_offset; //, links_offset;
1679 	int	dl_indices_offset, delta_light_offset;
1680 	int start_offset,end_offset;
1681 
1682 	start_offset = ftell(SaveFile);
1683 
1684 	//===================== SAVE FILE INFO ========================
1685 
1686 	game_fileinfo.fileinfo_signature =	0x6705;
1687 	game_fileinfo.fileinfo_version	=	GAME_VERSION;
1688 	game_fileinfo.level					=  Current_level_num;
1689 	game_fileinfo.fileinfo_sizeof		=	sizeof(game_fileinfo);
1690 	game_fileinfo.player_offset		=	-1;
1691 	game_fileinfo.player_sizeof		=	sizeof(player);
1692 	game_fileinfo.object_offset		=	-1;
1693 	game_fileinfo.object_howmany		=	Highest_object_index+1;
1694 	game_fileinfo.object_sizeof		=	sizeof(object);
1695 	game_fileinfo.walls_offset			=	-1;
1696 	game_fileinfo.walls_howmany		=	Num_walls;
1697 	game_fileinfo.walls_sizeof			=	sizeof(wall);
1698 	game_fileinfo.doors_offset			=	-1;
1699 	game_fileinfo.doors_howmany		=	Num_open_doors;
1700 	game_fileinfo.doors_sizeof			=	sizeof(active_door);
1701 	game_fileinfo.triggers_offset		=	-1;
1702 	game_fileinfo.triggers_howmany	=	Num_triggers;
1703 	game_fileinfo.triggers_sizeof		=	sizeof(trigger);
1704 	game_fileinfo.control_offset		=	-1;
1705 	game_fileinfo.control_howmany		=  1;
1706 	game_fileinfo.control_sizeof		=  sizeof(control_center_triggers);
1707  	game_fileinfo.matcen_offset		=	-1;
1708 	game_fileinfo.matcen_howmany		=	Num_robot_centers;
1709 	game_fileinfo.matcen_sizeof		=	sizeof(matcen_info);
1710 
1711  	game_fileinfo.dl_indices_offset		=	-1;
1712 	game_fileinfo.dl_indices_howmany		=	Num_static_lights;
1713 	game_fileinfo.dl_indices_sizeof		=	sizeof(dl_index);
1714 
1715  	game_fileinfo.delta_light_offset		=	-1;
1716 	game_fileinfo.delta_light_howmany	=	compute_num_delta_light_records();
1717 	game_fileinfo.delta_light_sizeof		=	sizeof(delta_light);
1718 
1719 	// Write the fileinfo
1720 	fwrite( &game_fileinfo, sizeof(game_fileinfo), 1, SaveFile );
1721 
1722 	// Write the mine name
1723 	fprintf(SaveFile,"%s\n",Current_level_name);
1724 
1725 	fwrite(&N_polygon_models,2,1,SaveFile);
1726 	fwrite(Pof_names,N_polygon_models,sizeof(*Pof_names),SaveFile);
1727 
1728 	//==================== SAVE PLAYER INFO ===========================
1729 
1730 	player_offset = ftell(SaveFile);
1731 	fwrite( &Players[Player_num], sizeof(player), 1, SaveFile );
1732 
1733 	//==================== SAVE OBJECT INFO ===========================
1734 
1735 	object_offset = ftell(SaveFile);
1736 	//fwrite( &Objects, sizeof(object), game_fileinfo.object_howmany, SaveFile );
1737 	{
1738 		int i;
1739 		for (i=0;i<game_fileinfo.object_howmany;i++)
1740 			write_object(&Objects[i],SaveFile);
1741 	}
1742 
1743 	//==================== SAVE WALL INFO =============================
1744 
1745 	walls_offset = ftell(SaveFile);
1746 	fwrite( Walls, sizeof(wall), game_fileinfo.walls_howmany, SaveFile );
1747 
1748 	//==================== SAVE DOOR INFO =============================
1749 
1750 	doors_offset = ftell(SaveFile);
1751 	fwrite( ActiveDoors, sizeof(active_door), game_fileinfo.doors_howmany, SaveFile );
1752 
1753 	//==================== SAVE TRIGGER INFO =============================
1754 
1755 	triggers_offset = ftell(SaveFile);
1756 	fwrite( Triggers, sizeof(trigger), game_fileinfo.triggers_howmany, SaveFile );
1757 
1758 	//================ SAVE CONTROL CENTER TRIGGER INFO ===============
1759 
1760 	control_offset = ftell(SaveFile);
1761 	fwrite( &ControlCenterTriggers, sizeof(control_center_triggers), 1, SaveFile );
1762 
1763 
1764 	//================ SAVE MATERIALIZATION CENTER TRIGGER INFO ===============
1765 
1766 	matcen_offset = ftell(SaveFile);
1767 	// mprintf((0, "Writing %i materialization centers\n", game_fileinfo.matcen_howmany));
1768 	// { int i;
1769 	// for (i=0; i<game_fileinfo.matcen_howmany; i++)
1770 	// 	mprintf((0, "   %i: robot_flags = %08x\n", i, RobotCenters[i].robot_flags));
1771 	// }
1772 	fwrite( RobotCenters, sizeof(matcen_info), game_fileinfo.matcen_howmany, SaveFile );
1773 
1774 	//================ SAVE DELTA LIGHT INFO ===============
1775 	dl_indices_offset = ftell(SaveFile);
1776 	fwrite( Dl_indices, sizeof(dl_index), game_fileinfo.dl_indices_howmany, SaveFile );
1777 
1778 	delta_light_offset = ftell(SaveFile);
1779 	fwrite( Delta_lights, sizeof(delta_light), game_fileinfo.delta_light_howmany, SaveFile );
1780 
1781 	//============= REWRITE FILE INFO, TO SAVE OFFSETS ===============
1782 
1783 	// Update the offset fields
1784 	game_fileinfo.player_offset		=	player_offset;
1785 	game_fileinfo.object_offset		=	object_offset;
1786 	game_fileinfo.walls_offset			=	walls_offset;
1787 	game_fileinfo.doors_offset			=	doors_offset;
1788 	game_fileinfo.triggers_offset		=	triggers_offset;
1789 	game_fileinfo.control_offset		=	control_offset;
1790 	game_fileinfo.matcen_offset		=	matcen_offset;
1791 	game_fileinfo.dl_indices_offset	=	dl_indices_offset;
1792 	game_fileinfo.delta_light_offset	=	delta_light_offset;
1793 
1794 
1795 	end_offset = ftell(SaveFile);
1796 
1797 	// Write the fileinfo
1798 	fseek(  SaveFile, start_offset, SEEK_SET );  // Move to TOF
1799 	fwrite( &game_fileinfo, sizeof(game_fileinfo), 1, SaveFile );
1800 
1801 	// Go back to end of data
1802 	fseek(SaveFile,end_offset,SEEK_SET);
1803 
1804 	return 0;
1805 }
1806 
1807 int save_mine_data(FILE * SaveFile);
1808 
1809 // -----------------------------------------------------------------------------
1810 // Save game
save_level_sub(char * filename,int compiled_version)1811 int save_level_sub(char * filename, int compiled_version)
1812 {
1813 	FILE * SaveFile;
1814 	char temp_filename[128];
1815 	int sig = MAKE_SIG('P','L','V','L'),version=LEVEL_FILE_VERSION;
1816 	int minedata_offset=0,gamedata_offset=0;
1817 
1818 	if ( !compiled_version )	{
1819 		write_game_text_file(filename);
1820 
1821 		if (Errors_in_mine) {
1822 			if (is_real_level(filename)) {
1823 				char  ErrorMessage[200];
1824 
1825 				sprintf( ErrorMessage, "Warning: %i errors in this mine!\n", Errors_in_mine );
1826 				stop_time();
1827 				gr_palette_load(gr_palette);
1828 
1829 				if (nm_messagebox( NULL, 2, "Cancel Save", "Save", ErrorMessage )!=1)	{
1830 					start_time();
1831 					return 1;
1832 				}
1833 				start_time();
1834 			} else
1835 				mprintf((1, "Error: %i errors in this mine.  See the 'txm' file.\n", Errors_in_mine));
1836 		}
1837 		change_filename_extension(temp_filename,filename,".LVL");
1838 	}
1839 	else
1840 	{
1841 		// macs are using the regular hog/rl2 files for shareware
1842 		#if defined(SHAREWARE) && !defined(MACINTOSH)
1843 			change_filename_extension(temp_filename,filename,".SL2");
1844 		#else
1845 			change_filename_extension(temp_filename,filename,".RL2");
1846 		#endif
1847 	}
1848 
1849 	SaveFile = fopen( temp_filename, "wb" );
1850 	if (!SaveFile)
1851 	{
1852 		char ErrorMessage[256];
1853 
1854 		char fname[20];
1855 		_splitpath( temp_filename, NULL, NULL, fname, NULL );
1856 
1857 		sprintf( ErrorMessage, \
1858 			"ERROR: Cannot write to '%s'.\nYou probably need to check out a locked\nversion of the file. You should save\nthis under a different filename, and then\ncheck out a locked copy by typing\n\'co -l %s.lvl'\nat the DOS prompt.\n"
1859 			, temp_filename, fname );
1860 		stop_time();
1861 		gr_palette_load(gr_palette);
1862 		nm_messagebox( NULL, 1, "Ok", ErrorMessage );
1863 		start_time();
1864 		return 1;
1865 	}
1866 
1867 	if (Current_level_name[0] == 0)
1868 		strcpy(Current_level_name,"Untitled");
1869 
1870 	clear_transient_objects(1);		//1 means clear proximity bombs
1871 
1872 	compress_objects();		//after this, Highest_object_index == num objects
1873 
1874 	//make sure player is in a segment
1875 	if (update_object_seg(&Objects[Players[0].objnum]) == 0) {
1876 		if (ConsoleObject->segnum > Highest_segment_index)
1877 			ConsoleObject->segnum = 0;
1878 		compute_segment_center(&ConsoleObject->pos,&(Segments[ConsoleObject->segnum]));
1879 	}
1880 
1881 	fix_object_segs();
1882 
1883 	//Write the header
1884 
1885 	gs_write_int(sig,SaveFile);
1886 	gs_write_int(version,SaveFile);
1887 
1888 	//save placeholders
1889 	gs_write_int(minedata_offset,SaveFile);
1890 	gs_write_int(gamedata_offset,SaveFile);
1891 
1892 	//Now write the damn data
1893 
1894 	//write the version 8 data (to make file unreadable by 1.0 & 1.1)
1895 	gs_write_int(GameTime,SaveFile);
1896 	gs_write_short(FrameCount,SaveFile);
1897 	gs_write_byte(FrameTime,SaveFile);
1898 
1899 	// Write the palette file name
1900 	fprintf(SaveFile,"%s\n",Current_level_palette);
1901 
1902 	gs_write_int(Base_control_center_explosion_time,SaveFile);
1903 	gs_write_int(Reactor_strength,SaveFile);
1904 
1905 	gs_write_int(Num_flickering_lights,SaveFile);
1906 	fwrite(Flickering_lights,sizeof(*Flickering_lights),Num_flickering_lights,SaveFile);
1907 
1908 	gs_write_int(Secret_return_segment, SaveFile);
1909 	gs_write_int(Secret_return_orient.rvec.x, SaveFile);
1910 	gs_write_int(Secret_return_orient.rvec.y, SaveFile);
1911 	gs_write_int(Secret_return_orient.rvec.z, SaveFile);
1912 	gs_write_int(Secret_return_orient.fvec.x, SaveFile);
1913 	gs_write_int(Secret_return_orient.fvec.y, SaveFile);
1914 	gs_write_int(Secret_return_orient.fvec.z, SaveFile);
1915 	gs_write_int(Secret_return_orient.uvec.x, SaveFile);
1916 	gs_write_int(Secret_return_orient.uvec.y, SaveFile);
1917 	gs_write_int(Secret_return_orient.uvec.z, SaveFile);
1918 
1919 	minedata_offset = ftell(SaveFile);
1920 	if ( !compiled_version )
1921 		save_mine_data(SaveFile);
1922 	else
1923 		save_mine_data_compiled(SaveFile);
1924 	gamedata_offset = ftell(SaveFile);
1925 	save_game_data(SaveFile);
1926 
1927 	fseek(SaveFile,sizeof(sig)+sizeof(version),SEEK_SET);
1928 	gs_write_int(minedata_offset,SaveFile);
1929 	gs_write_int(gamedata_offset,SaveFile);
1930 
1931 	//==================== CLOSE THE FILE =============================
1932 	fclose(SaveFile);
1933 
1934 	if ( !compiled_version )	{
1935 		if (Function_mode == FMODE_EDITOR)
1936 			editor_status("Saved mine %s, \"%s\"",filename,Current_level_name);
1937 	}
1938 
1939 	return 0;
1940 
1941 }
1942 
1943 #if 0 //dunno - 3rd party stuff?
1944 extern void compress_uv_coordinates_all(void);
1945 #endif
1946 
save_level(char * filename)1947 int save_level(char * filename)
1948 {
1949 	int r1;
1950 
1951 	// Save normal version...
1952 	r1 = save_level_sub(filename, 0);
1953 
1954 	// Save compiled version...
1955 	save_level_sub(filename, 1);
1956 
1957 	return r1;
1958 }
1959 
1960 #endif	//EDITOR
1961 
1962 #ifndef NDEBUG
dump_mine_info(void)1963 void dump_mine_info(void)
1964 {
1965 	int	segnum, sidenum;
1966 	fix	min_u, max_u, min_v, max_v, min_l, max_l, max_sl;
1967 
1968 	min_u = F1_0*1000;
1969 	min_v = min_u;
1970 	min_l = min_u;
1971 
1972 	max_u = -min_u;
1973 	max_v = max_u;
1974 	max_l = max_u;
1975 
1976 	max_sl = 0;
1977 
1978 	for (segnum=0; segnum<=Highest_segment_index; segnum++) {
1979 		for (sidenum=0; sidenum<MAX_SIDES_PER_SEGMENT; sidenum++) {
1980 			int	vertnum;
1981 			side	*sidep = &Segments[segnum].sides[sidenum];
1982 
1983 			if (Segment2s[segnum].static_light > max_sl)
1984 				max_sl = Segment2s[segnum].static_light;
1985 
1986 			for (vertnum=0; vertnum<4; vertnum++) {
1987 				if (sidep->uvls[vertnum].u < min_u)
1988 					min_u = sidep->uvls[vertnum].u;
1989 				else if (sidep->uvls[vertnum].u > max_u)
1990 					max_u = sidep->uvls[vertnum].u;
1991 
1992 				if (sidep->uvls[vertnum].v < min_v)
1993 					min_v = sidep->uvls[vertnum].v;
1994 				else if (sidep->uvls[vertnum].v > max_v)
1995 					max_v = sidep->uvls[vertnum].v;
1996 
1997 				if (sidep->uvls[vertnum].l < min_l)
1998 					min_l = sidep->uvls[vertnum].l;
1999 				else if (sidep->uvls[vertnum].l > max_l)
2000 					max_l = sidep->uvls[vertnum].l;
2001 			}
2002 
2003 		}
2004 	}
2005 
2006 //	mprintf((0, "Smallest uvl = %7.3f %7.3f %7.3f.  Largest uvl = %7.3f %7.3f %7.3f\n", f2fl(min_u), f2fl(min_v), f2fl(min_l), f2fl(max_u), f2fl(max_v), f2fl(max_l)));
2007 //	mprintf((0, "Static light maximum = %7.3f\n", f2fl(max_sl)));
2008 //	mprintf((0, "Number of walls: %i\n", Num_walls));
2009 
2010 }
2011 
2012 #endif
2013 
2014 #ifdef EDITOR
2015 
2016 //read in every level in mission and save out compiled version
save_all_compiled_levels(void)2017 void save_all_compiled_levels(void)
2018 {
2019 	do_load_save_levels(1);
2020 }
2021 
2022 //read in every level in mission
load_all_levels(void)2023 void load_all_levels(void)
2024 {
2025 	do_load_save_levels(0);
2026 }
2027 
2028 
do_load_save_levels(int save)2029 void do_load_save_levels(int save)
2030 {
2031 	int level_num;
2032 
2033 	if (! SafetyCheck())
2034 		return;
2035 
2036 	no_old_level_file_error=1;
2037 
2038 	for (level_num=1;level_num<=Last_level;level_num++) {
2039 		load_level(Level_names[level_num-1]);
2040 		load_palette(Current_level_palette,1,1);		//don't change screen
2041 		if (save)
2042 			save_level_sub(Level_names[level_num-1],1);
2043 	}
2044 
2045 	for (level_num=-1;level_num>=Last_secret_level;level_num--) {
2046 		load_level(Secret_level_names[-level_num-1]);
2047 		load_palette(Current_level_palette,1,1);		//don't change screen
2048 		if (save)
2049 			save_level_sub(Secret_level_names[-level_num-1],1);
2050 	}
2051 
2052 	no_old_level_file_error=0;
2053 
2054 }
2055 
2056 #endif
2057