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