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