]> icculus.org git repositories - taylor/freespace2.git/blob - src/fred2/missionsave.cpp
Initial revision
[taylor/freespace2.git] / src / fred2 / missionsave.cpp
1 /*
2  * $Logfile: /Freespace2/code/Fred2/MissionSave.cpp $
3  * $Revision$
4  * $Date$
5  * $Author$
6  *
7  * Mission saving in Fred.
8  *
9  * $Log$
10  * Revision 1.1  2002/05/03 03:28:08  root
11  * Initial revision
12  *
13  * 
14  * 33    8/28/99 7:29p Dave
15  * Fixed wingmen persona messaging. Make sure locked turrets don't count
16  * towards the # attacking a player.
17  * 
18  * 32    8/27/99 12:04a Dave
19  * Campaign loop screen.
20  * 
21  * 31    8/26/99 8:52p Dave
22  * Gave multiplayer TvT messaging a heavy dose of sanity. Cheat codes.
23  * 
24  * 30    8/17/99 5:20p Andsager
25  * Control campaign editor bug.
26  * 
27  * 29    8/16/99 3:53p Andsager
28  * Add special warp in interface in Fred and saving / reading.
29  * 
30  * 28    7/02/99 4:30p Dave
31  * Much more sophisticated lightning support.
32  * 
33  * 27    6/10/99 11:06a Andsager
34  * Mission designed selection of asteroid types.
35  * 
36  * 26    6/03/99 6:37p Dave
37  * More TNT fun. Made perspective bitmaps more flexible.
38  * 
39  * 25    5/20/99 6:59p Dave
40  * Added alternate type names for ships. Changed swarm missile table
41  * entries.
42  * 
43  * 24    5/09/99 6:00p Dave
44  * Lots of cool new effects. E3 build tweaks.
45  * 
46  * 23    4/26/99 8:47p Dave
47  * Made all pof related nebula stuff customizable through Fred.
48  * 
49  * 22    4/26/99 12:49p Andsager
50  * Add protect object from beam support to Fred
51  * 
52  * 21    4/16/99 2:34p Andsager
53  * Second pass on debris fields
54  * 
55  * 20    4/15/99 5:00p Andsager
56  * Frist pass on Debris field
57  * 
58  * 19    4/07/99 6:21p Dave
59  * Fred and Freespace support for multiple background bitmaps and suns.
60  * Fixed link errors on all subprojects. Moved encrypt_init() to
61  * cfile_init() and lcl_init(), since its safe to call twice.
62  * 
63  * 18    3/31/99 9:50a Andsager
64  * Interface for generalization of asteroid field (debris field)
65  * 
66  * 17    3/24/99 4:05p Dave
67  * Put in support for assigning the player to a specific squadron with a
68  * specific logo. Preliminary work for doing pos/orient checksumming in
69  * multiplayer to reduce bandwidth.
70  * 
71  * 16    3/01/99 7:39p Dave
72  * Added prioritizing ship respawns. Also fixed respawns in TvT so teams
73  * don't mix respawn points.
74  * 
75  * 15    2/26/99 6:01p Andsager
76  * Add sexp has-been-tagged-delay and cap-subsys-cargo-known-delay
77  * 
78  * 14    2/17/99 2:11p Dave
79  * First full run of squad war. All freespace and tracker side stuff
80  * works.
81  * 
82  * 13    2/11/99 2:15p Andsager
83  * Add ship explosion modification to FRED
84  * 
85  * 12    2/07/99 8:51p Andsager
86  * Add inner bound to asteroid field.  Inner bound tries to stay astroid
87  * free.  Wrap when within and don't throw at ships inside.
88  * 
89  * 11    2/03/99 12:42p Andsager
90  * Add escort priority.  Modify ship_flags_dlg to include field.  Save and
91  * Load.  Add escort priority field to ship.
92  * 
93  * 10    1/25/99 5:03a Dave
94  * First run of stealth, AWACS and TAG missile support. New mission type
95  * :)
96  * 
97  * 9     1/19/99 3:57p Andsager
98  * Round 2 of variables
99  * 
100  * 8     1/07/99 1:52p Andsager
101  * Initial check in of Sexp_variables
102  * 
103  * 7     12/17/98 2:43p Andsager
104  * Modify fred campaign save file to include optional mission loops
105  * 
106  * 6     11/14/98 5:37p Dave
107  * Put in support for full nebulas.
108  * 
109  * 5     11/05/98 4:18p Dave
110  * Modified mission file format.
111  * 
112  * 4     10/29/98 9:22p Dave
113  * Removed minor bug concering externalization of campaign files.
114  * 
115  * 3     10/29/98 6:49p Dave
116  * Finished up Fred support for externalizing mission and campaign files.
117  * 
118  * 2     10/07/98 6:28p Dave
119  * Initial checkin. Renamed all relevant stuff to be Fred2 instead of
120  * Fred. Globalized mission and campaign file extensions. Removed Silent
121  * Threat specific code.
122  * 
123  * 1     10/07/98 3:00p Dave
124  * 
125  * 207   9/16/98 10:42a Hoffoss
126  * Added sexp node counting to fsm files for end user designers.
127  * 
128  * 206   9/15/98 7:24p Dave
129  * Minor UI changes. Localized bunch of new text.
130  * 
131  * 205   9/15/98 11:44a Dave
132  * Renamed builtin ships and wepaons appropriately in FRED. Put in scoring
133  * scale factors. Fixed standalone filtering of MD missions to non-MD
134  * hosts.
135  * 
136  * 204   9/10/98 1:17p Dave
137  * Put in code to flag missions and campaigns as being MD or not in Fred
138  * and Freespace. Put in multiplayer support for filtering out MD
139  * missions. Put in multiplayer popups for warning of non-valid missions.
140  * 
141  * 203   5/21/98 12:58a Hoffoss
142  * Fixed warnings optimized build turned up.
143  * 
144  * 202   5/20/98 1:04p Hoffoss
145  * Made credits screen use new artwork and removed rating field usage from
146  * Fred (a goal struct member).
147  * 
148  * 201   5/19/98 1:19p Allender
149  * new low level reliable socket reading code.  Make all missions/campaign
150  * load/save to data missions folder (i.e. we are rid of the player
151  * missions folder)
152  * 
153  * 200   5/13/98 5:13p Allender
154  * red alert support to go back to previous mission
155  * 
156  *
157  * $NoKeywords: $
158  */
159
160 #include "stdafx.h"
161 #include "freespace.h"
162 #include "missionsave.h"
163 #include "missiongoals.h"
164 #include "missionmessage.h"
165 #include "missionparse.h"
166 #include "fredrender.h"
167 #include "aigoals.h"
168 #include "starfield.h"
169 #include "lighting.h"
170 #include "linklist.h"
171 #include "weapon.h"
172 #include "missioncampaign.h"
173 #include "campaigntreewnd.h"
174 #include "campaigntreeview.h"
175 #include "campaigneditordlg.h"
176 #include "sexp.h"
177 #include "missionbriefcommon.h"
178 #include "management.h"
179 #include "eventmusic.h"
180 #include "nebula.h"
181 #include "asteroid.h"
182 #include "missioncmdbrief.h"
183 #include "jumpnode.h"
184 #include "mainfrm.h"
185 #include "fhash.h"
186 #include "neb.h"
187 #include "osapi.h"
188
189 int CFred_mission_save::save_mission_file(char *pathname)
190 {
191         char backup_name[256], savepath[MAX_PATH_LEN], *p;
192         CTime t;
193
194         t = CTime::GetCurrentTime();
195         strcpy(The_mission.modified, t.Format("%x at %X"));
196
197         strcpy(savepath, "");
198         p = strrchr(pathname, '\\');
199         if ( p ) {
200                 *p = '\0';
201                 strcpy(savepath, pathname);
202                 *p = '\\';
203                 strcat(savepath, "\\");
204         }
205         strcat(savepath, "saving.xxx");
206
207         reset_parse();
208         fred_parse_flag = 0;
209         fp = cfopen(savepath, "wt", CFILE_NORMAL);
210         if (!fp)        {
211                 nprintf(("Error", "Can't open mission file to save.\n"));
212                 return -1;
213         }       
214
215         if (save_mission_info())
216                 err = -2;
217         else if (save_plot_info())
218                 err = -3;
219         else if (save_variables())
220                 err = -3;
221 //      else if (save_briefing_info())
222 //              err = -4;
223         else if (save_cmd_briefs())
224                 err = -4;
225         else if (save_briefing())
226                 err = -4;
227         else if (save_debriefing())
228                 err = -5;
229         else if (save_players())
230                 err = -6;
231         else if (save_objects())
232                 err = -7;
233         else if (save_wings())
234                 err = -8;
235         else if (save_events())
236                 err = -9;
237         else if (save_goals())
238                 err = -10;
239         else if (save_waypoints())
240                 err = -11;
241         else if (save_messages())
242                 err = -12;
243         else if (save_reinforcements())
244                 err = -13;
245         else if (save_bitmaps())
246                 err = -14;
247         else if (save_asteroid_fields())
248                 err = -15;
249         else if (save_music())
250                 err = -16;
251         else {
252                 required_string_fred("#End");
253                 parse_comments(2);
254                 token_found = NULL;
255                 parse_comments();
256                 fout("\n");
257         }
258
259         cfclose(fp);
260         if (err) {
261                 mprintf(("Mission saving error code #%d", err));
262
263         } else {
264                 strcpy(backup_name, pathname);
265                 if (backup_name[strlen(backup_name) - 4] == '.')
266                         backup_name[strlen(backup_name) - 4] = 0;
267
268                 strcat(backup_name, ".bak");
269                 cf_attrib(pathname, 0, FILE_ATTRIBUTE_READONLY, CF_TYPE_MISSIONS);
270                 cf_delete(backup_name, CF_TYPE_MISSIONS);
271                 cf_rename(pathname, backup_name, CF_TYPE_MISSIONS);
272                 cf_rename(savepath, pathname, CF_TYPE_MISSIONS);
273         }
274
275         return err;
276 }
277
278 int CFred_mission_save::autosave_mission_file(char *pathname)
279 {
280         char backup_name[256], name2[256];
281         int i, len;
282         CTime t;
283         
284         t = CTime::GetCurrentTime();
285         strcpy(The_mission.modified, t.Format("%x at %X"));
286
287         len = strlen(pathname);
288         strcpy(backup_name, pathname);
289         strcpy(name2, pathname);
290         sprintf(backup_name + len, ".%.3d", BACKUP_DEPTH);
291         cf_delete(backup_name, CF_TYPE_MISSIONS);
292         for (i=BACKUP_DEPTH; i>1; i--) {
293                 sprintf(backup_name + len, ".%.3d", i - 1);
294                 sprintf(name2 + len, ".%.3d", i);
295                 cf_rename(backup_name, name2, CF_TYPE_MISSIONS);
296         }
297         
298         strcpy(backup_name + len, ".001");
299         reset_parse();
300         fred_parse_flag = 0;
301         fp = cfopen(backup_name, "wt", CFILE_NORMAL, CF_TYPE_MISSIONS);
302         if (!fp)        {
303                 nprintf(("Error", "Can't open mission file to save.\n"));
304                 return -1;
305         }
306
307         if (save_mission_info())
308                 err = -2;
309         else if (save_plot_info())
310                 err = -3;
311         else if (save_variables())
312                 err = -3;
313 //      else if (save_briefing_info())
314 //              err = -4;
315         else if (save_cmd_briefs())
316                 err = -4;
317         else if (save_briefing())
318                 err = -4;
319         else if (save_debriefing())
320                 err = -5;
321         else if (save_players())
322                 err = -6;
323         else if (save_objects())
324                 err = -7;
325         else if (save_wings())
326                 err = -8;
327         else if (save_events())
328                 err = -9;
329         else if (save_goals())
330                 err = -10;
331         else if (save_waypoints())
332                 err = -11;
333         else if (save_messages())
334                 err = -12;
335         else if (save_reinforcements())
336                 err = -13;
337         else if (save_bitmaps())
338                 err = -14;
339         else if (save_asteroid_fields())
340                 err = -15;
341         else if (save_music())
342                 err = -16;
343         else {
344                 required_string_fred("#End");
345                 parse_comments(2);
346                 token_found = NULL;
347                 parse_comments();
348                 fout("\n");
349         }
350
351         cfclose(fp);
352         if (err)
353                 mprintf(("Mission saving error code #%d", err));
354
355         return err;
356 }
357
358 int CFred_mission_save::save_mission_info()
359 {
360         // int f = count_free_sexp_nodes();
361         // fout("// Of %d total sexp nodes, %d free, %d used\n\n",MAX_SEXP_NODES, f, MAX_SEXP_NODES - f);
362
363         required_string_fred("#Mission Info");
364         parse_comments(0);
365
366         required_string_fred("$Version:");
367         parse_comments(2);
368         fout(" %.2f", FRED_MISSION_VERSION);
369
370         // XSTR
371         required_string_fred("$Name:");
372         parse_comments();
373         fout(" ");
374         fout_ext("%s", The_mission.name);
375
376         required_string_fred("$Author:");
377         parse_comments();
378         fout(" %s", The_mission.author);
379
380         required_string_fred("$Created:");
381         parse_comments();
382         fout(" %s", The_mission.created);
383
384         required_string_fred("$Modified:");
385         parse_comments();
386         fout(" %s", The_mission.modified);
387
388         required_string_fred("$Notes:");
389         parse_comments();
390         fout("\n%s", The_mission.notes);
391
392         required_string_fred("$End Notes:");
393         parse_comments(0);
394
395         // XSTR
396         required_string_fred("$Mission Desc:");
397         parse_comments(2);
398         fout("\n");
399         fout_ext("%s", The_mission.mission_desc);
400         fout("\n");
401
402         required_string_fred("$end_multi_text");
403         parse_comments(0);
404
405 #if 0
406         if (optional_string_fred("+Game Type:"))
407                 parse_comments(2);
408         else
409                 fout("\n\n+Game Type:");
410         fout("\n%s", Game_types[The_mission.game_type]);        
411 #endif          
412
413         if ( optional_string_fred("+Game Type Flags:")){
414                 parse_comments(2);
415         } else {
416                 fout("\n+Game Type Flags:");
417         }       
418
419         fout(" %d", The_mission.game_type);
420
421         if (optional_string_fred("+Flags:")){
422                 parse_comments(2);
423         } else {
424                 fout("\n+Flags:");
425         }
426
427         fout(" %d", The_mission.flags);
428
429         // maybe write out Nebula intensity
430         if(The_mission.flags & MISSION_FLAG_FULLNEB){
431                 Assert(Neb2_awacs > 0.0f);
432                 fout("\n+NebAwacs: %f\n", Neb2_awacs);
433
434                 // storm name
435                 fout("\n+Storm: %s\n", Mission_parse_storm_name);
436         }
437
438         // For multiplayer missions -- write out the number of player starts and number of respawns
439         if (The_mission.game_type & MISSION_TYPE_MULTI) {
440                 if (optional_string_fred("+Num Players:"))
441                         parse_comments(2);
442                 else
443                         fout("\n+Num Players:");
444
445                 fout(" %d", Player_starts);
446
447                 if (optional_string_fred("+Num Respawns:"))
448                         parse_comments(2);
449                 else
450                         fout("\n+Num Respawns:");
451
452                 fout(" %d", The_mission.num_respawns);
453         }
454         
455         if ( optional_string_fred("+Red Alert:")){
456                 parse_comments(2);
457         } else {
458                 fout("\n+Red Alert:");
459         }
460         fout(" %d", The_mission.red_alert );
461         
462         if ( optional_string_fred("+Scramble:")){
463                 parse_comments(2);
464         } else {
465                 fout("\n+Scramble:");
466         }
467         fout(" %d", The_mission.scramble);
468
469         if ( optional_string_fred("+Disallow Support:")){
470                 parse_comments(2);
471         } else {
472                 fout("\n+Disallow Support:");
473         }
474
475         fout(" %d", The_mission.disallow_support);
476
477         if (Mission_all_attack) {
478                 if (optional_string_fred("+All Teams Attack")){
479                         parse_comments();
480                 } else {
481                         fout("\n+All Teams Attack");
482                 }
483         }
484
485         if (Entry_delay_time) {
486                 if (optional_string_fred("+Player Entry Delay:"))
487                         parse_comments(2);
488                 else
489                         fout("\n\n+Player Entry Delay:");
490
491                 fout("\n%f", f2fl(Entry_delay_time));
492         }
493
494         if (optional_string_fred("+Viewer pos:")){
495                 parse_comments(2);
496         } else {
497                 fout("\n\n+Viewer pos:");
498         }
499
500         save_vector(view_pos);
501
502         if (optional_string_fred("+Viewer orient:")){
503                 parse_comments();
504         } else {
505                 fout("\n+Viewer orient:");
506         }
507
508         save_matrix(view_orient);
509
510         // squadron info
511         if(!(The_mission.game_type & MISSION_TYPE_MULTI) && (strlen(The_mission.squad_name) > 0)){
512                 // squad name
513                 fout("\n+SquadReassignName: %s", The_mission.squad_name);
514
515                 // maybe squad logo
516                 if(strlen(The_mission.squad_filename) > 0){
517                         fout("\n+SquadReassignLogo: %s", The_mission.squad_filename);
518                 }
519         }
520
521         return err;
522 }
523
524 int CFred_mission_save::save_plot_info()
525 {
526         required_string_fred("#Plot Info");
527         parse_comments(2);
528
529         // XSTR
530         required_string_fred("$Tour:");
531         parse_comments(2);
532         fout(" ");
533         fout_ext("%s", The_mission.tour_name);
534
535         required_string_fred("$Pre-Briefing Cutscene:");
536         parse_comments();
537         fout(" %s", The_mission.pre_briefing_cutscene);
538
539         required_string_fred("$Pre-Mission Cutscene:");
540         parse_comments();
541         fout(" %s", The_mission.pre_mission_cutscene);
542
543         required_string_fred("$Next Mission Success:");
544         parse_comments();
545         fout(" %s", The_mission.next_mission_success);
546
547         required_string_fred("$Next Mission Partial:");
548         parse_comments();
549         fout(" %s", The_mission.next_mission_partial);
550
551         required_string_fred("$Next Mission Failure:");
552         parse_comments();
553         fout(" %s", The_mission.next_mission_failure);
554
555         return err;
556 }
557
558 int CFred_mission_save::save_cmd_brief()
559 {
560         int stage;
561
562         stage = 0;
563         required_string_fred("#Command Briefing");
564         parse_comments(2);
565
566         if (The_mission.game_type & MISSION_TYPE_MULTI)
567                 return err;  // no command briefings allowed in multiplayer missions.
568
569         for (stage=0; stage<Cur_cmd_brief->num_stages; stage++) {
570                 required_string_fred("$Stage Text:");
571                 parse_comments(2);
572
573                 // XSTR
574                 fout("\n");
575                 fout_ext("%s", Cur_cmd_brief->stage[stage].text);
576
577                 required_string_fred("$end_multi_text", "$Stage Text:");
578                 parse_comments();
579
580                 required_string_fred("$Ani Filename:");
581                 parse_comments();
582                 fout(" %s", Cur_cmd_brief->stage[stage].ani_filename);
583
584                 required_string_fred("+Wave Filename:", "$Ani Filename:");
585                 parse_comments();
586                 fout(" %s", Cur_cmd_brief->stage[stage].wave_filename);
587         }
588
589         return err;
590 }
591
592 int CFred_mission_save::save_cmd_briefs()
593 {
594         int i;
595
596         for (i=0; i<Num_teams; i++) {
597                 Cur_cmd_brief = &Cmd_briefs[i];
598                 save_cmd_brief();
599         }
600
601         return err;
602 }
603
604 int CFred_mission_save::save_briefing()
605 {
606         int                     i,j,k, nb;
607         char                    out[4096];
608         brief_stage     *bs;
609         brief_icon      *bi;
610
611         for ( nb = 0; nb < Num_teams; nb++ ) {
612
613                 required_string_fred("#Briefing");
614                 parse_comments(2);
615
616                 required_string_fred("$start_briefing");
617                 parse_comments();
618
619                 Assert(Briefings[nb].num_stages <= MAX_BRIEF_STAGES);
620                 required_string_fred("$num_stages:");
621                 parse_comments();
622                 fout(" %d", Briefings[nb].num_stages);
623
624                 for (i=0; i<Briefings[nb].num_stages; i++) {
625                         bs = &Briefings[nb].stages[i];
626
627                         required_string_fred("$start_stage");
628                         parse_comments();
629
630                         required_string_fred("$multi_text");
631                         parse_comments();
632
633                         // XSTR
634                         fout("\n");
635                         sprintf(out,"%s", bs->new_text);
636                         fout_ext(out);
637
638                         required_string_fred("$end_multi_text", "$start_stage");
639                         parse_comments();
640
641                         if (!drop_white_space(bs->voice)[0]){
642                                 strcpy(bs->voice, "None");
643                         }
644
645                         required_string_fred("$voice:");
646                         parse_comments();
647                         fout(" %s", bs->voice);
648
649                         required_string_fred("$camera_pos:");
650                         parse_comments();
651                         save_vector(bs->camera_pos);
652                         
653                         required_string_fred("$camera_orient:");
654                         parse_comments();
655                         save_matrix(bs->camera_orient);
656
657                         required_string_fred("$camera_time:");
658                         parse_comments();
659                         fout(" %d", bs->camera_time);
660
661                         required_string_fred("$num_lines:");
662                         parse_comments();
663                         fout(" %d", bs->num_lines);
664
665                         for (k=0; k<bs->num_lines; k++) {
666                                 required_string_fred("$line_start:");
667                                 parse_comments();
668                                 fout(" %d", bs->lines[k].start_icon);
669
670                                 required_string_fred("$line_end:");
671                                 parse_comments();
672                                 fout(" %d", bs->lines[k].end_icon);
673                         }
674
675                         required_string_fred("$num_icons:");
676                         parse_comments();
677                         Assert(bs->num_icons <= MAX_STAGE_ICONS );
678                         fout(" %d", bs->num_icons);
679
680                         required_string_fred("$Flags:");
681                         parse_comments();
682                         fout(" %d", bs->flags);
683
684                         required_string_fred("$Formula:");
685                         parse_comments();
686                         convert_sexp_to_string(bs->formula, out, SEXP_SAVE_MODE);
687                         fout(" %s", out);
688
689                         for ( j = 0; j < bs->num_icons; j++ ) {
690                                 bi = &bs->icons[j];
691
692                                 required_string_fred("$start_icon");
693                                 parse_comments();
694
695                                 required_string_fred("$type:");
696                                 parse_comments();
697                                 fout(" %d", bi->type);
698
699                                 required_string_fred("$team:");
700                                 parse_comments();
701                                 fout(" %s", Team_names[bitmask_2_bitnum(bi->team)]);
702
703                                 required_string_fred("$class:");
704                                 parse_comments();
705                                 if (bi->ship_class < 0)
706                                         bi->ship_class = 0;
707
708                                 fout(" %s", Ship_info[bi->ship_class].name);
709
710                                 required_string_fred("$pos:");
711                                 parse_comments();
712                                 save_vector(bi->pos);
713
714                                 if (drop_white_space(bi->label)[0]) {
715                                         if (optional_string_fred("$label:"))
716                                                 parse_comments();
717                                         else
718                                                 fout("\n$label:");
719
720                                         fout(" %s", bi->label);
721                                 }
722
723                                 if (optional_string_fred("+id:"))
724                                         parse_comments(); 
725                                 else
726                                         fout("\n+id:");
727
728                                 fout(" %d", bi->id);
729
730                                 required_string_fred("$hlight:");
731                                 parse_comments();
732                                 fout(" %d", (bi->flags & BI_HIGHLIGHT)?1:0 );
733
734                                 required_string_fred("$multi_text");
735                                 parse_comments();
736
737 //                              sprintf(out,"\n%s", bi->text);
738 //                              fout(out);
739
740                                 required_string_fred("$end_multi_text");
741                                 parse_comments();
742
743                                 required_string_fred("$end_icon");
744                                 parse_comments();
745                         }
746
747                         required_string_fred("$end_stage");
748                         parse_comments();
749                 }
750                 required_string_fred("$end_briefing");
751                 parse_comments();
752         }
753
754         return err;
755 }
756
757 int CFred_mission_save::save_debriefing()
758 {
759         int j, i;
760         char out[8192];
761
762         for ( j = 0; j < Num_teams; j++ ) {
763
764                 Debriefing = &Debriefings[j];
765
766                 required_string_fred("#Debriefing_info");
767                 parse_comments(2);
768
769                 required_string_fred("$Num stages:");
770                 parse_comments(2);
771                 fout(" %d", Debriefing->num_stages);
772
773                 for (i=0; i<Debriefing->num_stages; i++) {
774                         required_string_fred("$Formula:");
775                         parse_comments(2);
776                         convert_sexp_to_string(Debriefing->stages[i].formula, out, SEXP_SAVE_MODE);
777                         fout(" %s", out);
778
779                         // XSTR
780                         required_string_fred("$Multi text");
781                         parse_comments();
782                         fout("\n   ");
783                         fout_ext("%s", Debriefing->stages[i].new_text);
784
785                         required_string_fred("$end_multi_text");
786                         parse_comments();
787
788                         if (!drop_white_space(Debriefing->stages[i].voice)[0]){
789                                 strcpy(Debriefing->stages[i].voice, "None");
790                         }
791
792                         required_string_fred("$Voice:");
793                         parse_comments();
794                         fout(" %s", Debriefing->stages[i].voice);
795
796                         // XSTR
797                         required_string_fred("$Recommendation text:");
798                         parse_comments();
799                         fout("\n   ");
800                         fout_ext("%s", Debriefing->stages[i].new_recommendation_text);
801
802                         required_string_fred("$end_multi_text");
803                         parse_comments();
804                 }
805         }
806
807         return err;
808 }
809
810 int sexp_variable_block_count();
811 // save variables
812 int CFred_mission_save::save_variables()
813 {
814         char *type;
815         char number[] = "number";
816         char string[] = "string";
817         char block[] = "block";
818         int i;
819
820         // sort sexp_variables
821         sexp_variable_sort();
822
823         // get count
824         int num_variables = sexp_variable_count();
825         int num_block_vars = sexp_variable_block_count();
826         int total_variables = num_variables + num_block_vars;
827
828         if (total_variables > 0) {
829
830                 // write 'em out
831                 required_string_fred("#Sexp_variables");
832                 parse_comments(2);
833
834                 required_string_fred("$Variables:");
835                 parse_comments(2);
836
837                 fout ("\n(");
838 //              parse_comments();
839
840                 for (i=0; i<num_variables; i++) {
841                         if (Sexp_variables[i].type & SEXP_VARIABLE_NUMBER) {
842                                 type = number;
843                         } else {
844                                 type = string;
845                         }
846                         // index "var name" "default" "type"
847                         fout("\n\t\t%d\t\t\"%s\"\t\t\"%s\"\t\t\"%s\"", i, Sexp_variables[i].variable_name, Sexp_variables[i].text, type);
848 //                      parse_comments();
849                 }
850
851                 for (i=MAX_SEXP_VARIABLES-num_block_vars; i<MAX_SEXP_VARIABLES; i++) {
852                         type = block;
853                         fout("\n\t\t%d\t\t\"%s\"\t\t\"%s\"\t\t\"%s\"", i, Sexp_variables[i].variable_name, Sexp_variables[i].text, type);
854                 }
855
856                 fout("\n)");
857         }
858
859         return err;
860 }
861
862
863 int CFred_mission_save::save_players()
864 {
865         int i, j;
866         int used_pool[MAX_WEAPON_TYPES];
867
868         // write out alternate name list
869         if(Mission_alt_type_count > 0){
870                 fout("\n\n#Alternate Types:\n");
871
872                 // write them all out
873                 for(i=0; i<Mission_alt_type_count; i++){
874                         fout("$Alt: %s\n", Mission_alt_types[i]);
875                 }
876
877                 // end
878                 fout("\n#end\n");
879         }
880
881         required_string_fred("#Players");
882         parse_comments(2);
883         fout("\t\t;! %d total\n", Player_starts);
884
885         for (i=0; i<Num_teams; i++) {
886                 required_string_fred("$Starting Shipname:");
887                 parse_comments();
888                 Assert(Player_start_shipnum >= 0);
889                 fout(" %s", Ships[Player_start_shipnum].ship_name);
890                 
891                 required_string_fred("$Ship Choices:");
892                 parse_comments();
893                 fout(" (\n");
894
895                 for (j=0; j<Team_data[i].number_choices; j++)
896                         fout("\t\"%s\"\t%d\n", Ship_info[Team_data[i].ship_list[j]].name,
897                                 Team_data[i].ship_count[j]);
898
899                 fout(")");
900
901                 if (optional_string_fred("+Weaponry Pool:", "$Starting Shipname:")){
902                         parse_comments(2);
903                 } else {
904                         fout("\n\n+Weaponry Pool:");
905                 }
906
907                 fout(" (\n");
908                 generate_weaponry_usage_list(used_pool);
909                 for (j=0; j<Num_weapon_types; j++){
910                         if (Team_data[i].weaponry_pool[j] + used_pool[j] > 0){
911                                 fout("\t\"%s\"\t%d\n", Weapon_info[j].name, Team_data[i].weaponry_pool[j] + used_pool[j]);
912                         }
913                 }
914
915                 fout(")");
916         }
917
918         return err;
919 }
920
921 int CFred_mission_save::save_objects()
922 {
923         char out[4096];
924         int i, j, z;
925         ai_info *aip;
926         object *objp;
927         ship_info *sip;
928         
929         required_string_fred("#Objects");
930         parse_comments(2);
931         fout("\t\t;! %d total\n", ship_get_num_ships() );
932
933         for (i=z=0; i<MAX_SHIPS; i++) {
934                 if (Ships[i].objnum < 0){
935                         continue;
936                 }
937
938                 j = Objects[Ships[i].objnum].type;
939                 if ((j != OBJ_SHIP) && (j != OBJ_START)){
940                         continue;
941                 }
942
943                 objp = &Objects[Ships[i].objnum];
944                 sip = &Ship_info[Ships[i].ship_info_index];
945                 required_string_either_fred("$Name:", "#Wings");
946                 required_string_fred("$Name:");
947                 parse_comments(z ? 2 : 1);
948                 fout(" %s\t\t;! Object #%d\n", Ships[i].ship_name, i);
949
950                 required_string_fred("$Class:");
951                 parse_comments(0);
952                 fout(" %s", Ship_info[Ships[i].ship_info_index].name);
953
954                 // optional alternate type name
955                 if(strlen(Fred_alt_names[i])){
956                         fout("\n$Alt: %s\n", Fred_alt_names[i]);
957                 }
958
959                 required_string_fred("$Team:");
960                 parse_comments();
961                 fout(" %s", Team_names[bitmask_2_bitnum(Ships[i].team)]);
962
963                 required_string_fred("$Location:");
964                 parse_comments();
965                 save_vector(Objects[Ships[i].objnum].pos);
966
967                 required_string_fred("$Orientation:");
968                 parse_comments();
969                 save_matrix(Objects[Ships[i].objnum].orient);
970
971                 required_string_fred("$IFF:");
972                 parse_comments();
973                 fout(" %s", Iff_names[0]);
974
975                 Assert(Ships[i].ai_index >= 0);
976                 aip = &Ai_info[Ships[i].ai_index];
977
978                 required_string_fred("$AI Behavior:");
979                 parse_comments();
980                 fout(" %s", Ai_behavior_names[aip->behavior]);
981
982                 if (Ships[i].weapons.ai_class != Ship_info[Ships[i].ship_info_index].ai_class) {
983                         if (optional_string_fred("+AI Class:", "$Name:"))
984                                 parse_comments();
985                         else
986                                 fout("\n+AI Class:");
987
988                         fout(" %s", Ai_class_names[Ships[i].weapons.ai_class]);
989                 }
990
991                 save_ai_goals(Ai_info[Ships[i].ai_index].goals, i);
992
993                 // XSTR
994                 required_string_fred("$Cargo 1:");
995                 parse_comments();
996                 fout(" ");
997                 fout_ext("%s", Cargo_names[Ships[i].cargo1]);
998
999                 save_common_object_data(&Objects[Ships[i].objnum], &Ships[i]);
1000
1001                 if (Ships[i].wingnum >= 0){
1002                         Ships[i].arrival_location = ARRIVE_AT_LOCATION;
1003                 }
1004
1005                 required_string_fred("$Arrival Location:");
1006                 parse_comments();
1007                 fout(" %s", Arrival_location_names[Ships[i].arrival_location]);
1008
1009                 if (Ships[i].arrival_location != ARRIVE_AT_LOCATION) {
1010                         if (optional_string_fred("+Arrival Distance:", "$Name:")){
1011                                 parse_comments();
1012                         } else {
1013                                 fout("\n+Arrival Distance:");
1014                         }
1015
1016                         fout(" %d", Ships[i].arrival_distance);
1017                         if (optional_string_fred("$Arrival Anchor:", "$Name:")){
1018                                 parse_comments();
1019                         } else {
1020                                 fout("\n$Arrival Anchor:");
1021                         }
1022
1023                         z = Ships[i].arrival_anchor;
1024                         if (z >= SPECIAL_ARRIVAL_ANCHORS_OFFSET){
1025                                 fout(" %s", Special_arrival_anchor_names[z - SPECIAL_ARRIVAL_ANCHORS_OFFSET]);
1026                         } else if (z >= 0) {
1027                                 fout(" %s", Ships[z].ship_name);
1028                         } else {
1029                                 fout(" <error>");
1030                         }
1031                 }
1032
1033                 if (Ships[i].arrival_delay) {
1034                         if (optional_string_fred("+Arrival Delay:", "$Name:")){
1035                                 parse_comments();
1036                         } else {
1037                                 fout("\n+Arrival Delay:");
1038                         }
1039
1040                         fout(" %d", Ships[i].arrival_delay);
1041                 }
1042
1043                 required_string_fred("$Arrival Cue:");
1044                 parse_comments();
1045                 convert_sexp_to_string(Ships[i].arrival_cue, out, SEXP_SAVE_MODE);
1046                 fout(" %s", out);
1047
1048                 required_string_fred("$Departure Location:");
1049                 parse_comments();
1050                 fout(" %s", Departure_location_names[Ships[i].departure_location]);
1051
1052
1053                 if ( Ships[i].departure_location == DEPART_AT_DOCK_BAY ) {
1054                         required_string_fred("$Departure Anchor:");
1055                         parse_comments();
1056                         
1057                         if ( Ships[i].departure_anchor >= 0 ){
1058                                 fout(" %s", Ships[Ships[i].departure_anchor].ship_name );
1059                         } else {
1060                                 fout(" <error>");
1061                         }
1062                 }
1063
1064                 if (Ships[i].departure_delay) {
1065                         if (optional_string_fred("+Departure delay:", "$Name:")){
1066                                 parse_comments();
1067                         } else {
1068                                 fout("\n+Departure delay:");
1069                         }
1070
1071                         fout(" %d", Ships[i].departure_delay);
1072                 }
1073
1074                 required_string_fred("$Departure Cue:");
1075                 parse_comments();
1076                 convert_sexp_to_string(Ships[i].departure_cue, out, SEXP_SAVE_MODE);
1077                 fout(" %s", out);
1078
1079                 required_string_fred("$Determination:");
1080                 parse_comments();
1081                 fout(" %d", Ships[i].determination);
1082
1083                 if (optional_string_fred("+Flags:", "$Name:")) {
1084                         parse_comments();
1085                         fout (" (");
1086                 } else
1087                         fout("\n+Flags: (");
1088
1089                 if (Ships[i].flags & SF_CARGO_REVEALED)
1090                         fout(" \"cargo-known\"");
1091                 if (Ships[i].flags & SF_IGNORE_COUNT)
1092                         fout(" \"ignore-count\"");
1093                 if (objp->flags & OF_PROTECTED)
1094                         fout(" \"protect-ship\"");
1095                 if (objp->flags & OF_BEAM_PROTECTED)
1096                         fout(" \"beam-protect-ship\"");
1097                 if (Ships[i].flags & SF_REINFORCEMENT)
1098                         fout(" \"reinforcement\"");
1099                 if (objp->flags & OF_NO_SHIELDS)
1100                         fout(" \"no-shields\"");
1101                 if (Ships[i].flags & SF_ESCORT)
1102                         fout(" \"escort\"");
1103                 if (objp->type == OBJ_START)
1104                         fout(" \"player-start\"");
1105                 if (Ships[i].flags & SF_NO_ARRIVAL_MUSIC)
1106                         fout(" \"no-arrival-music\"");
1107                 if (Ships[i].flags & SF_NO_ARRIVAL_WARP)
1108                         fout(" \"no-arrival-warp\"");
1109                 if (Ships[i].flags & SF_NO_DEPARTURE_WARP)
1110                         fout(" \"no-departure-warp\"");
1111                 if (Ships[i].flags & SF_LOCKED)
1112                         fout(" \"locked\"");
1113                 if (Ships[i].flags & SF_INVULNERABLE)
1114                         fout(" \"invulnerable\"");
1115                 if (Ships[i].flags & SF_HIDDEN_FROM_SENSORS)
1116                         fout(" \"hidden-from-sensors\"");
1117                 if ( Ships[i].flags & SF_SCANNABLE )
1118                         fout(" \"scannable\"");
1119                 if ( Ai_info[Ships[i].ai_index].ai_flags & AIF_KAMIKAZE )
1120                         fout(" \"kamikaze\"");
1121                 if ( Ai_info[Ships[i].ai_index].ai_flags & AIF_NO_DYNAMIC )
1122                         fout(" \"no-dynamic\"");
1123                 if ( Ships[i].flags & SF_RED_ALERT_STORE_STATUS )
1124                         fout(" \"red-alert-carry\"");
1125                 if ( objp->flags & OF_SPECIAL_WARP )
1126                         fout(" \"special-warp\"");
1127                 fout(" )");
1128
1129                 fout("+Respawn priority: %d\n", Ships[i].respawn_priority);
1130
1131                 if (Ships[i].flags & SF_ESCORT) {
1132                         if (optional_string_fred("+Escort priority:", "$Name:")) {
1133                                 parse_comments();
1134                         } else {
1135                                 fout("\n+Escort priority:");
1136                         }
1137
1138                         fout(" %d", Ships[i].escort_priority);
1139                 }
1140
1141                 if (Ships[i].special_exp_index != -1) {
1142                         if (optional_string_fred("+Special Exp index:", "$Name:")) {
1143                                 parse_comments();
1144                         } else {
1145                                 fout("+Special Exp index:");
1146                         }
1147
1148                         fout(" %d", Ships[i].special_exp_index);
1149                 }
1150
1151
1152                 if ( Ai_info[Ships[i].ai_index].ai_flags & AIF_KAMIKAZE ) {
1153                         if ( optional_string_fred("+Kamikaze Damage:", "$Name:")){
1154                                 parse_comments();
1155                         } else {
1156                                 fout("\n+Kamikaze Damage:");
1157                         }
1158
1159                         fout(" %d", (int)(Ai_info[Ships[i].ai_index].kamikaze_damage) );
1160                 }
1161
1162                 if (Ships[i].hotkey != -1) {
1163                         if (optional_string_fred("+Hotkey:", "$Name:")){
1164                                 parse_comments();
1165                         } else {
1166                                 fout("\n+Hotkey:");
1167                         }
1168
1169                         fout(" %d", Ships[i].hotkey);
1170                 }
1171
1172                 // mwa -- new code to save off information about initially docked ships.
1173                 if ( Ships[i].flags & SF_INITIALLY_DOCKED ) {
1174                         Assert ( aip->dock_objnum != -1 );              // ge allender if you hit this
1175
1176                         if (optional_string_fred("+Docked With:", "$Name:"))
1177                                 parse_comments();
1178                         else
1179                                 fout("\n+Docked With:");
1180
1181                         switch (Objects[aip->dock_objnum].type) {
1182                                 case OBJ_SHIP:
1183                                 case OBJ_START:
1184                                         j = Objects[aip->dock_objnum].instance;
1185                                         break;
1186
1187                                 default:
1188                                         Int3();
1189                         }
1190
1191                         fout(" %s", Ships[j].ship_name);
1192
1193                         required_string_fred("$Docker Point:", "$Name:");
1194                         parse_comments();
1195                         fout(" %s", model_get_dock_name(Ships[j].modelnum, aip->dockee_index));
1196
1197                         required_string_fred("$Dockee Point:", "$Name:");
1198                         parse_comments();
1199                         fout(" %s", model_get_dock_name(Ships[i].modelnum, aip->dock_index));
1200
1201                 }
1202
1203                 // check the ship flag about killing off the ship before a missino starts.  Write out the appropriate
1204                 // variable if necessary
1205                 if ( Ships[i].flags & SF_KILL_BEFORE_MISSION ) {
1206                         if ( optional_string_fred("+Destroy At:", "$Name:"))
1207                                 parse_comments();
1208                         else
1209                                 fout ("\n+Destroy At: ");
1210
1211                         fout(" %d", Ships[i].final_death_time );
1212                 }
1213
1214                 // possibly write out the orders that this ship will accept.  We'll only do it if the orders
1215                 // are not the default set of orders
1216                 if ( Ships[i].orders_accepted != ship_get_default_orders_accepted( &Ship_info[Ships[i].ship_info_index]) ) {
1217                         if ( optional_string_fred("+Orders Accepted:", "$Name:") )
1218                                 parse_comments();
1219                         else
1220                                 fout("\n+Orders Accepted:");
1221
1222                         fout(" %d\t\t;! note that this is a bitfield!!!", Ships[i].orders_accepted);
1223                 }
1224
1225                 if (Ships[i].group >= 0) {
1226                         if (optional_string_fred("+Group:", "$Name:"))
1227                                 parse_comments();
1228                         else
1229                                 fout("\n+Group:");
1230
1231                         fout(" %d", Ships[i].group);
1232                 }
1233
1234                 if (Ships[i].score) {
1235                         if (optional_string_fred("+Score:", "$Name:"))
1236                                 parse_comments();
1237                         else
1238                                 fout("\n+Score:");
1239
1240                         fout(" %d", Ships[i].score);
1241                 }
1242
1243                 // deal with the persona for this ship as well.
1244                 if ( Ships[i].persona_index != -1 ) {
1245                         if (optional_string_fred("+Persona Index:", "$Name:"))
1246                                 parse_comments();
1247                         else
1248                                 fout("\n+Persona Index:");
1249
1250                         fout(" %d", Ships[i].persona_index);
1251                 }
1252
1253                 z++;
1254         }
1255
1256         return err;
1257 }
1258
1259 int CFred_mission_save::save_common_object_data(object *objp, ship *shipp)
1260 {
1261         int j, z;
1262         ship_subsys *ptr;
1263         ship_info *sip;
1264         ship_weapon *wp;
1265
1266         sip = &Ship_info[shipp->ship_info_index];
1267         if ((int) objp->phys_info.speed) {
1268                 if (optional_string_fred("+Initial Velocity:", "$Name:", "+Subsystem:"))
1269                         parse_comments();
1270                 else
1271                         fout("\n+Initial Velocity:");
1272
1273                 fout(" %d", (int) objp->phys_info.speed);
1274         }
1275
1276         if ((int) objp->hull_strength != (int) sip->initial_hull_strength) {
1277                 if (optional_string_fred("+Initial Hull:", "$Name:", "+Subsystem:"))
1278                         parse_comments();
1279                 else
1280                         fout("\n+Initial Hull:");
1281
1282                 fout(" %d", (int) objp->hull_strength);
1283         }
1284
1285         if ((int) get_shield_strength(objp) != 100) {
1286                 if (optional_string_fred("+Initial Shields:", "$Name:", "+Subsystem:"))
1287                         parse_comments();
1288                 else
1289                         fout("\n+Initial Shields:");
1290
1291                 fout(" %d", (int) objp->shields[0]);
1292         }
1293
1294         // save normal ship weapons info
1295         required_string_fred("+Subsystem:", "$Name:");
1296         parse_comments();
1297         fout(" Pilot");
1298
1299         wp = &shipp->weapons;
1300         z = 0;
1301         j = wp->num_primary_banks;
1302         while (j--)
1303                 if (wp->primary_bank_weapons[j] != sip->primary_bank_weapons[j])
1304                         z = 1;
1305
1306         if (z) {
1307                 if (optional_string_fred("+Primary Banks:", "$Name:", "+Subsystem:"))
1308                         parse_comments();
1309                 else
1310                         fout("\n+Primary Banks:");
1311
1312                 fout(" ( ");
1313                 for (j=0; j<wp->num_primary_banks; j++)
1314                         fout("\"%s\" ", Weapon_info[wp->primary_bank_weapons[j]].name);
1315
1316                 fout(")");
1317         }
1318
1319         z = 0;
1320         j = wp->num_secondary_banks;
1321         while (j--)
1322                 if (wp->secondary_bank_weapons[j] != sip->secondary_bank_weapons[j])
1323                         z = 1;
1324
1325         if (z) {
1326                 if (optional_string_fred("+Secondary Banks:", "$Name:", "+Subsystem:"))
1327                         parse_comments();
1328                 else
1329                         fout("\n+Secondary Banks:");
1330
1331                 fout(" ( ");
1332                 for (j=0; j<wp->num_secondary_banks; j++)
1333                         fout("\"%s\" ", Weapon_info[wp->secondary_bank_weapons[j]].name);
1334
1335                 fout(")");
1336         }
1337
1338         z = 0;
1339         j = wp->num_secondary_banks;
1340         while (j--)
1341                 if (wp->secondary_bank_ammo[j] != 100)
1342                         z = 1;
1343
1344         if (z) {
1345                 if (optional_string_fred("+Sbank Ammo:", "$Name:", "+Subsystem:"))
1346                         parse_comments();
1347                 else
1348                         fout("\n+Sbank Ammo:");
1349
1350                 fout(" ( ");
1351                 for (j=0; j<wp->num_secondary_banks; j++)
1352                         fout("%d ", wp->secondary_bank_ammo[j]);
1353
1354                 fout(")");
1355         }
1356
1357         ptr = GET_FIRST(&shipp->subsys_list);
1358         while (ptr != END_OF_LIST(&shipp->subsys_list)) {
1359                 if ( (ptr->current_hits) || (ptr->system_info->type == SUBSYSTEM_TURRET) || (ptr->subsys_cargo_name != -1)) {
1360                         if (optional_string_fred("+Subsystem:", "$Name:"))
1361                                 parse_comments();
1362                         else
1363                                 fout("\n+Subsystem:");
1364
1365                         fout(" %s", ptr->system_info->subobj_name);
1366                 }
1367
1368                 if (ptr->current_hits) {
1369                         if (optional_string_fred("$Damage:", "$Name:", "+Subsystem:"))
1370                                 parse_comments();
1371                         else
1372                                 fout("\n$Damage:");
1373
1374                         fout(" %d", (int) ptr->current_hits);
1375                 }
1376
1377                 if (ptr->subsys_cargo_name != -1) {
1378                         if (optional_string_fred("+Cargo Name:", "$Name:", "+Subsystem:"))
1379                                 parse_comments();
1380                         else
1381                                 fout("\n+Cargo Name:");
1382
1383                         fout_ext("%s", Cargo_names[ptr->subsys_cargo_name]);
1384                 }
1385
1386                 if (ptr->system_info->type == SUBSYSTEM_TURRET)
1387                         save_turret_info(ptr, shipp - Ships);
1388
1389                 ptr = GET_NEXT(ptr);
1390         }
1391
1392 /*      for (j=0; j<shipp->status_count; j++) {
1393                 required_string_fred("$Status Description:");
1394                 parse_comments(-1);
1395                 fout(" %s", Status_desc_names[shipp->status_type[j]]);
1396
1397                 required_string_fred("$Status:");
1398                 parse_comments(-1);
1399                 fout(" %s", Status_type_names[shipp->status[j]]);
1400
1401                 required_string_fred("$Target:");
1402                 parse_comments(-1);
1403                 fout(" %s", Status_target_names[shipp->target[j]]);
1404         }*/
1405
1406         return err;
1407 }
1408
1409 int CFred_mission_save::save_wings()
1410 {
1411         char out[4096];
1412         int i, j, z, ship, count = 0;
1413
1414         fred_parse_flag = 0;
1415         required_string_fred("#Wings");
1416         parse_comments(2);
1417         fout("\t\t;! %d total", num_wings);
1418
1419         for (i=0; i<MAX_WINGS; i++) {
1420                 if (!Wings[i].wave_count)
1421                         continue;
1422
1423                 count++;
1424                 required_string_either_fred("$Name:", "#Events");
1425                 required_string_fred("$Name:");
1426                 parse_comments(2);
1427                 fout(" %s", Wings[i].name);
1428
1429                 required_string_fred("$Waves:");
1430                 parse_comments();
1431                 fout(" %d", Wings[i].num_waves);
1432
1433                 required_string_fred("$Wave Threshold:");
1434                 parse_comments();
1435                 fout(" %d", Wings[i].threshold);
1436
1437                 required_string_fred("$Special Ship:");
1438                 parse_comments();
1439                 fout(" %d\t\t;! %s\n", Wings[i].special_ship,
1440                         Ships[Wings[i].ship_index[Wings[i].special_ship]].ship_name);
1441
1442                 required_string_fred("$Arrival Location:");
1443                 parse_comments();
1444                 fout(" %s", Arrival_location_names[Wings[i].arrival_location]);
1445
1446                 if (Wings[i].arrival_location != ARRIVE_AT_LOCATION) {
1447                         if (optional_string_fred("+Arrival Distance:", "$Name:"))
1448                                 parse_comments();
1449                         else
1450                                 fout("\n+Arrival Distance:");
1451
1452                         fout(" %d", Wings[i].arrival_distance);
1453                         if (optional_string_fred("$Arrival Anchor:", "$Name:"))
1454                                 parse_comments();
1455                         else
1456                                 fout("\n$Arrival Anchor:");
1457
1458                         z = Wings[i].arrival_anchor;
1459                         if (z >= SPECIAL_ARRIVAL_ANCHORS_OFFSET)
1460                                 fout(" %s", Special_arrival_anchor_names[z - SPECIAL_ARRIVAL_ANCHORS_OFFSET]);
1461                         else if (z >= 0)
1462                                 fout(" %s", Ships[z].ship_name);
1463                         else
1464                                 fout(" <error>");
1465                 }
1466
1467                 if (Wings[i].arrival_delay) {
1468                         if (optional_string_fred("+Arrival delay:", "$Name:"))
1469                                 parse_comments();
1470                         else
1471                                 fout("\n+Arrival delay:");
1472
1473                         fout(" %d", Wings[i].arrival_delay);
1474                 }
1475
1476                 required_string_fred("$Arrival Cue:");
1477                 parse_comments();
1478                 convert_sexp_to_string(Wings[i].arrival_cue, out, SEXP_SAVE_MODE);
1479                 fout(" %s", out);
1480
1481                 required_string_fred("$Departure Location:");
1482                 parse_comments();
1483                 fout(" %s", Departure_location_names[Wings[i].departure_location]);
1484
1485                 if ( Wings[i].departure_location == DEPART_AT_DOCK_BAY ) {
1486                         required_string_fred("$Departure Anchor:");
1487                         parse_comments();
1488
1489                         if ( Wings[i].departure_anchor >= 0 )
1490                                 fout(" %s", Ships[Wings[i].departure_anchor].ship_name );
1491                         else
1492                                 fout(" <error>");
1493                 }
1494
1495                 if (Wings[i].departure_delay) {
1496                         if (optional_string_fred("+Departure delay:", "$Name:"))
1497                                 parse_comments();
1498                         else
1499                                 fout("\n+Departure delay:");
1500
1501                         fout(" %d", Wings[i].departure_delay);
1502                 }
1503
1504                 required_string_fred("$Departure Cue:");
1505                 parse_comments();
1506                 convert_sexp_to_string(Wings[i].departure_cue, out, SEXP_SAVE_MODE);
1507                 fout(" %s", out);
1508
1509                 required_string_fred("$Ships:");
1510                 parse_comments();
1511                 fout(" (\t\t;! %d total\n", Wings[i].wave_count);
1512
1513                 for (j=0; j<Wings[i].wave_count; j++) {
1514                         ship = Wings[i].ship_index[j];
1515 //                      if (Objects[Ships[ship].objnum].type == OBJ_START)
1516 //                              fout("\t\"Player 1\"\n");
1517 //                      else
1518                                 fout("\t\"%s\"\n", Ships[Wings[i].ship_index[j]].ship_name);
1519                 }
1520
1521                 fout(")");
1522                 save_ai_goals(Wings[i].ai_goals, -1);
1523
1524                 if (Wings[i].hotkey != -1) {
1525                         if (optional_string_fred("+Hotkey:", "$Name:"))
1526                                 parse_comments();
1527                         else
1528                                 fout("\n+Hotkey:");
1529
1530                         fout(" %d", Wings[i].hotkey);
1531                 }
1532
1533                 if ( optional_string_fred("+Flags:", "$Name:")) {
1534                         parse_comments();
1535                         fout( "(" );
1536                 } else 
1537                         fout("\n+Flags: (");
1538
1539                 if ( Wings[i].flags & WF_IGNORE_COUNT )
1540                         fout(" \"ignore-count\"");
1541                 if ( Wings[i].flags & WF_REINFORCEMENT )
1542                         fout(" \"reinforcement\"");
1543                 if ( Wings[i].flags & WF_NO_ARRIVAL_MUSIC )
1544                         fout(" \"no-arrival-music\"");
1545                 if ( Wings[i].flags & WF_NO_ARRIVAL_MESSAGE )
1546                         fout(" \"no-arrival-message\"");
1547                 if ( Wings[i].flags & WF_NO_ARRIVAL_WARP )
1548                         fout(" \"no-arrival-warp\"");
1549                 if ( Wings[i].flags & WF_NO_DEPARTURE_WARP )
1550                         fout(" \"no-departure-warp\"");
1551                 if ( Wings[i].flags & WF_NO_DYNAMIC )
1552                         fout( " \"no-dynamic\"" );
1553
1554                 fout(" )");
1555
1556                 if (Wings[i].wave_delay_min) {
1557                         if (optional_string_fred("+Wave Delay Min:", "$Name:"))
1558                                 parse_comments();
1559                         else
1560                                 fout("\n+Wave Delay Min:");
1561
1562                         fout(" %d", Wings[i].wave_delay_min);
1563                 }
1564
1565                 if (Wings[i].wave_delay_max) {
1566                         if (optional_string_fred("+Wave Delay Max:", "$Name:"))
1567                                 parse_comments();
1568                         else
1569                                 fout("\n+Wave Delay Max:");
1570
1571                         fout(" %d", Wings[i].wave_delay_max);
1572                 }
1573         }
1574
1575         Assert(count == num_wings);
1576         return err;
1577 }
1578
1579 int CFred_mission_save::save_goals()
1580 {
1581         char out[4096];
1582         int i;
1583
1584         fred_parse_flag = 0;
1585         required_string_fred("#Goals");
1586         parse_comments(2);
1587         fout("\t\t;! %d total\n", Num_goals);
1588
1589         for (i=0; i<Num_goals; i++) {
1590                 int type;
1591
1592                 required_string_either_fred("$Type:", "#Waypoints");
1593                 required_string_fred("$Type:");
1594                 parse_comments(i ? 2 : 1);
1595
1596                 type = Mission_goals[i].type & GOAL_TYPE_MASK;
1597                 fout(" %s", Goal_type_names[type]);
1598
1599                 if (*Mission_goals[i].name) {
1600                         if (optional_string_fred("+Name:", "$Type:"))
1601                                 parse_comments();
1602                         else
1603                                 fout("\n+Name:");
1604
1605                         fout(" %s", Mission_goals[i].name);
1606                 }
1607
1608                 // XSTR
1609                 required_string_fred("$MessageNew:");
1610                 parse_comments();
1611                 fout(" ");
1612                 fout_ext("%s", Mission_goals[i].message);
1613                 fout("\n");
1614                 required_string_fred("$end_multi_text");
1615                 parse_comments(0);
1616
1617                 required_string_fred("$Formula:");
1618                 parse_comments();
1619                 convert_sexp_to_string(Mission_goals[i].formula, out, SEXP_SAVE_MODE);
1620                 fout(" %s", out);
1621
1622                 if ( Mission_goals[i].type & INVALID_GOAL ) {
1623                         if (optional_string_fred("+Invalid", "$Type:"))
1624                                 parse_comments();
1625                         else
1626                                 fout("\n+Invalid");
1627                 }
1628
1629                 if ( Mission_goals[i].flags & MGF_NO_MUSIC ) {
1630                         if (optional_string_fred("+No music", "$Type:"))
1631                                 parse_comments();
1632                         else
1633                                 fout("\n+No music");
1634                 }
1635
1636                 if ( Mission_goals[i].score != 0 ) {
1637                         if ( optional_string_fred("+Score:", "$Type:"))
1638                                 parse_comments();
1639                         else
1640                                 fout("\n+Score:");
1641                         fout(" %d", Mission_goals[i].score );
1642                 }
1643
1644                 if ( The_mission.game_type & MISSION_TYPE_MULTI_TEAMS ) {
1645                         if ( optional_string_fred("+Team:", "$Type:"))
1646                                 parse_comments();
1647                         else
1648                                 fout("\n+Team:");
1649                         fout(" %d", Mission_goals[i].team );
1650                 }
1651         }
1652
1653         return err;
1654 }
1655
1656 int CFred_mission_save::save_waypoints()
1657 {
1658         int i;
1659         object *ptr;
1660
1661         fred_parse_flag = 0;
1662         required_string_fred("#Waypoints");
1663         parse_comments(2);
1664         fout("\t\t;! %d lists total\n", Num_waypoint_lists);
1665
1666         for (i=0; i<Num_jump_nodes; i++) {
1667                 ptr = GET_FIRST(&obj_used_list);
1668                 while (ptr != END_OF_LIST(&obj_used_list)) {
1669                         if ((ptr->type == OBJ_JUMP_NODE) && (ptr->instance == i))
1670                                 break;
1671
1672                         ptr = GET_NEXT(ptr);
1673                 }
1674
1675                 Assert(ptr != END_OF_LIST(&obj_used_list));
1676
1677                 required_string_fred("$Jump Node:", "$Jump Node Name:");
1678                 parse_comments(2);
1679                 save_vector(ptr->pos);
1680
1681                 required_string_fred("$Jump Node Name:", "$Jump Node:");
1682                 parse_comments();
1683                 fout(" %s", Jump_nodes[i].name);
1684         }
1685
1686         for (i=0; i<Num_waypoint_lists; i++) {
1687                 required_string_either_fred("$Name:", "#Messages");
1688                 required_string_fred("$Name:");
1689                 parse_comments(i ? 2 : 1);
1690                 fout(" %s", Waypoint_lists[i].name);
1691
1692                 required_string_fred("$List:");
1693                 parse_comments();
1694                 fout(" (\t\t;! %d points in list\n", Waypoint_lists[i].count);
1695
1696                 save_waypoint_list(Waypoint_lists[i]);
1697                 fout(")");
1698         }
1699
1700         return err;
1701 }
1702
1703 int CFred_mission_save::save_waypoint_list(waypoint_list &w)
1704 {
1705         int i;
1706
1707         for (i=0; i<w.count; i++)
1708                 fout("\t( %f, %f, %f )\n", w.waypoints[i].x, w.waypoints[i].y, w.waypoints[i].z);
1709
1710         return 0;
1711 }
1712
1713 int CFred_mission_save::save_messages()
1714 {
1715         int i;
1716
1717         fred_parse_flag = 0;
1718         required_string_fred("#Messages");
1719         parse_comments(2);
1720         fout("\t\t;! %d total\n", Num_messages);
1721
1722         for (i=Num_builtin_messages; i<Num_messages; i++) {
1723                 required_string_either_fred("$Name:", "#Reinforcements");
1724                 required_string_fred("$Name:");
1725                 parse_comments(i ? 2 : 1);
1726                 fout(" %s", Messages[i].name);
1727
1728                 // team
1729                 required_string_fred("$Team:");
1730                 parse_comments(i ? 2 : 1);
1731                 if((Messages[i].multi_team < 0) || (Messages[i].multi_team >= 2)){
1732                         fout(" %d", -1);
1733                 } else {
1734                         fout(" %d", Messages[i].multi_team);
1735                 }
1736
1737                 // XSTR
1738                 required_string_fred("$MessageNew:");
1739                 parse_comments();
1740                 fout(" ");
1741                 fout_ext("%s", Messages[i].message);
1742                 fout("\n");
1743                 required_string_fred("$end_multi_text");
1744                 parse_comments(0);
1745
1746                 if ( Messages[i].persona_index != -1 ) {
1747                         if ( optional_string_fred("+Persona:", "$Name:"))
1748                                 parse_comments();
1749                         else
1750                                 fout("\n+Persona:");
1751
1752                         fout(" %s", Personas[Messages[i].persona_index].name );
1753                 }
1754
1755                 if (Messages[i].avi_info.name) {
1756                         if (optional_string_fred("+AVI Name:", "$Name:"))
1757                                 parse_comments();
1758                         else
1759                                 fout("\n+AVI Name:");
1760
1761                         fout(" %s", Messages[i].avi_info.name);
1762                 }
1763
1764                 if (Messages[i].wave_info.name) {
1765                         if (optional_string_fred("+Wave Name:", "$Name:"))
1766                                 parse_comments();
1767                         else
1768                                 fout("\n+Wave Name:");
1769
1770                         fout(" %s", Messages[i].wave_info.name);
1771                 }
1772         }
1773
1774         return err;
1775 }
1776
1777 int CFred_mission_save::save_vector(vector &v)
1778 {
1779         fout(" %f, %f, %f", v.x, v.y, v.z);
1780         return 0;
1781 }
1782
1783 int CFred_mission_save::save_matrix(matrix &m)
1784 {
1785         fout("\n\t%f, %f, %f,\n", m.rvec.x, m.rvec.y, m.rvec.z);
1786         fout("\t%f, %f, %f,\n", m.uvec.x, m.uvec.y, m.uvec.z);
1787         fout("\t%f, %f, %f", m.fvec.x, m.fvec.y, m.fvec.z);
1788         return 0;
1789 }
1790
1791 // saves comments from previous campaign/mission file
1792 void CFred_mission_save::parse_comments(int newlines)
1793 {
1794         char *comment_start = NULL;
1795         int state = 0, same_line = 0, first_comment = 1, tab = 0, flag = 0;
1796
1797         if (newlines < 0) {
1798                 newlines = -newlines;
1799                 tab = 1;
1800         }
1801
1802         if (newlines)
1803                 same_line = 1;
1804
1805         if (fred_parse_flag || !Token_found_flag || !token_found || (token_found && (*Mission_text_raw == EOF_CHAR))) {
1806                 while (newlines-- > 0)
1807                         fout("\n");
1808
1809                 if (tab)
1810                         fout("\t");
1811
1812                 if (token_found)
1813                         fout("%s", token_found);
1814
1815                 return;
1816         }
1817
1818         while (*raw_ptr != EOF_CHAR) {
1819                 if (!state) {
1820                         if (token_found && (*raw_ptr == *token_found))
1821                                 if (!strnicmp(raw_ptr, token_found, strlen(token_found))) {
1822                                         same_line = newlines - 1 + same_line;
1823                                         while (same_line-- > 0)
1824                                                 fout("\n");
1825                                         
1826                                         if (tab)
1827                                                 fout("\t");
1828
1829                                         fout("%s", token_found);
1830                                         return;
1831                                 }
1832
1833                         if ((*raw_ptr == '/') && (raw_ptr[1] == '*')) {
1834                                 comment_start = raw_ptr;
1835                                 state = 1;
1836                         }
1837
1838                         if ((*raw_ptr == ';') && (raw_ptr[1] != '!')) {
1839                                 comment_start = raw_ptr;
1840                                 state = 2;
1841                         }
1842
1843                         if ((*raw_ptr == '/') && (raw_ptr[1] == '/')) {
1844                                 comment_start = raw_ptr;
1845                                 state = 2;
1846                         }
1847
1848                         if (*raw_ptr == '\n')
1849                                 flag = 1;
1850
1851                         if (state && flag)
1852                                 fout("\n");
1853
1854                 } else {
1855                         if ((*raw_ptr == '\n') && (state == 2)) {
1856                                 if (first_comment && !flag)
1857                                         fout("\t\t");
1858
1859                                 *raw_ptr = 0;
1860                                 fout("%s\n", comment_start);
1861                                 *raw_ptr = '\n';
1862                                 state = first_comment = same_line = flag = 0;
1863                         }
1864
1865                         if ((*raw_ptr == '*') && (raw_ptr[1] == '/') && (state == 1)) {
1866                                 if (first_comment && !flag)
1867                                         fout("\t\t");
1868
1869                                 state = raw_ptr[2];
1870                                 raw_ptr[2] = 0;
1871                                 fout("%s", comment_start);
1872                                 raw_ptr[2] = (char)state;
1873                                 state = first_comment = flag = 0;
1874                         }
1875                 }
1876
1877                 raw_ptr++;
1878         }
1879
1880         return;
1881 }
1882
1883 int CFred_mission_save::fout(char *format, ...)
1884 {
1885         char str[16384];
1886         va_list args;
1887         
1888         if (err){
1889                 return err;
1890         }
1891
1892         va_start(args, format);
1893         vsprintf(str, format, args);
1894         va_end(args);
1895         Assert(strlen(str) < 16384);
1896
1897         cfputs(str, fp);
1898         return 0;
1899 }
1900
1901 int CFred_mission_save::fout_ext(char *format, ...)
1902 {
1903         char str[16384];
1904         char str_out[16384] = "";
1905         va_list args;
1906         int str_id;
1907         
1908         if (err){
1909                 return err;
1910         }
1911
1912         va_start(args, format);
1913         vsprintf(str, format, args);
1914         va_end(args);
1915         Assert(strlen(str) < 16384);
1916
1917         // lookup the string in the hash table
1918         str_id = fhash_string_exists(str);
1919         // doesn't exist, so assign it an ID of -1 and stick it in the table
1920         if(str_id <= -2){
1921                 sprintf(str_out, " XSTR(\"%s\", -1)", str);
1922
1923                 // add the string to the table          
1924                 fhash_add_str(str, -1);
1925         }
1926         // _does_ exist, so just write it out as it is
1927         else {
1928                 sprintf(str_out, " XSTR(\"%s\", %d)", str, str_id);
1929         }
1930
1931         cfputs(str_out, fp);
1932         return 0;
1933 }
1934
1935 void CFred_mission_save::save_ai_goals(ai_goals *goalp, int ship)
1936 {
1937         char *str = NULL, buf[80];
1938         int i, valid, flag = 1;
1939
1940         for (i=0; i<MAX_AI_GOALS; i++) {
1941                 if (goalp[i].ai_mode == AI_GOAL_NONE)
1942                         continue;
1943
1944                 if (flag) {
1945                         if (optional_string_fred("$AI Goals:", "$Name:"))
1946                                 parse_comments();
1947                         else
1948                                 fout("\n$AI Goals:");
1949
1950                         fout(" ( goals ");
1951                         flag = 0;
1952                 }
1953
1954                 if (goalp[i].ai_mode == AI_GOAL_CHASE_ANY) {
1955                         fout("( ai-chase-any %d ) ", goalp[i].priority);
1956
1957                 } else if (goalp[i].ai_mode == AI_GOAL_UNDOCK) {
1958                         fout("( ai-undock %d ) ", goalp[i].priority);
1959
1960                 } else if (goalp[i].ai_mode == AI_GOAL_KEEP_SAFE_DISTANCE) {
1961                         fout("( ai-keep-safe-distance %d ) ", goalp[i].priority);
1962                 
1963                 } else if (goalp[i].ai_mode == AI_GOAL_PLAY_DEAD) {
1964                         fout("( ai-play-dead %d ) ", goalp[i].priority);
1965
1966                 } else if (goalp[i].ai_mode == AI_GOAL_WARP) {
1967                         fout("( ai-warp-out %d ) ", goalp[i].priority);
1968                 
1969                 } else {
1970                         valid = 1;
1971                         if (!goalp[i].ship_name) {
1972                                 Warning(LOCATION, "Ai goal has no target where one is required");
1973
1974                         } else {
1975                                 sprintf(buf, "\"%s\"", goalp[i].ship_name);
1976                                 switch (goalp[i].ai_mode) {
1977                                         case AI_GOAL_WAYPOINTS:
1978                                                 str = "ai-waypoints";
1979                                                 break;
1980
1981                                         case AI_GOAL_WAYPOINTS_ONCE:
1982                                                 str = "ai-waypoints-once";
1983                                                 break;
1984
1985                                         case AI_GOAL_DESTROY_SUBSYSTEM:
1986                                                 if (goalp[i].docker.index == -1 || !goalp[i].docker.index) {
1987                                                         valid = 0;
1988                                                         Warning(LOCATION, "AI destroy subsystem goal invalid subsystem name\n");
1989
1990                                                 } else {
1991                                                         sprintf(buf, "\"%s\" \"%s\"", goalp[i].ship_name, goalp[i].docker.name);
1992                                                         str = "ai-destroy-subsystem";
1993                                                 }
1994
1995                                                 break;
1996
1997                                         case AI_GOAL_DOCK:
1998                                                 if (ship < 0) {
1999                                                         valid = 0;
2000                                                         Warning(LOCATION, "Wings aren't allowed to have a docking goal\n");
2001                                                         
2002                                                 } else if (goalp[i].docker.index == -1 || !goalp[i].docker.index) {
2003                                                         valid = 0;
2004                                                         Warning(LOCATION, "AI dock goal for \"%s\" has invalid docker point "
2005                                                                 "(docking with \"%s\")\n", Ships[ship].ship_name, goalp[i].ship_name);
2006
2007                                                 } else if (goalp[i].dockee.index == -1 || !goalp[i].dockee.index) {
2008                                                         valid = 0;
2009                                                         Warning(LOCATION, "AI dock goal for \"%s\" has invalid dockee point "
2010                                                                 "(docking with \"%s\")\n", Ships[ship].ship_name, goalp[i].ship_name);
2011
2012                                                 } else {
2013                                                         sprintf(buf, "\"%s\" \"%s\" \"%s\"", goalp[i].ship_name,
2014                                                                 goalp[i].docker.name, goalp[i].dockee.name);
2015
2016                                                         str = "ai-dock";
2017                                                 }
2018                                                 break;
2019
2020                                         case AI_GOAL_CHASE:
2021                                                 str = "ai-chase";
2022                                                 break;
2023
2024                                         case AI_GOAL_CHASE_WING:
2025                                                 str = "ai-chase-wing";
2026                                                 break;
2027
2028                                         case AI_GOAL_GUARD:
2029                                                 str = "ai-guard";
2030                                                 break;
2031
2032                                         case AI_GOAL_GUARD_WING:
2033                                                 str = "ai-guard-wing";
2034                                                 break;
2035
2036                                         case AI_GOAL_DISABLE_SHIP:
2037                                                 str = "ai-disable-ship";
2038                                                 break;
2039
2040                                         case AI_GOAL_DISARM_SHIP:
2041                                                 str = "ai-disarm-ship";
2042                                                 break;
2043
2044                                         case AI_GOAL_IGNORE:
2045                                                 str = "ai-ignore";
2046                                                 break;
2047
2048                                         case AI_GOAL_EVADE_SHIP:
2049                                                 str = "ai-evade-ship";
2050                                                 break;
2051
2052                                         case AI_GOAL_STAY_NEAR_SHIP:
2053                                                 str = "ai-stay-near-ship";
2054                                                 break;
2055
2056                                         case AI_GOAL_STAY_STILL:
2057                                                 str = "ai-stay-still";
2058                                                 break;
2059
2060                                         default:
2061                                                 Assert(0);
2062                                 }
2063
2064                                 if (valid)
2065                                         fout("( %s %s %d ) ", str, buf, goalp[i].priority);
2066                         }
2067                 }
2068         }
2069
2070         if (!flag)
2071                 fout(")");
2072 }
2073
2074 int CFred_mission_save::save_events()
2075 {
2076         char out[4096];
2077         int i;
2078
2079         fred_parse_flag = 0;
2080         required_string_fred("#Events");
2081         parse_comments(2);
2082         fout("\t\t;! %d total\n", Num_mission_events);
2083
2084         for (i=0; i<Num_mission_events; i++) {
2085                 required_string_either_fred("$Formula:", "#Goals");
2086                 required_string_fred("$Formula:");
2087                 parse_comments(i ? 2 : 1);
2088                 convert_sexp_to_string(Mission_events[i].formula, out, SEXP_SAVE_MODE);
2089                 fout(" %s", out);
2090
2091                 if (*Mission_events[i].name) {
2092                         if (optional_string_fred("+Name:", "$Formula:")){
2093                                 parse_comments();
2094                         } else {
2095                                 fout("\n+Name:");
2096                         }
2097
2098                         fout(" %s", Mission_events[i].name);
2099                 }
2100
2101                 if ( optional_string_fred("+Repeat Count:", "$Formula:")){
2102                         parse_comments();
2103                 } else {
2104                         fout("\n+Repeat Count:");
2105                 }
2106
2107                 fout(" %d", Mission_events[i].repeat_count);
2108
2109                 if ( optional_string_fred("+Interval:", "$Formula:")){
2110                         parse_comments();
2111                 } else {
2112                         fout("\n+Interval:");
2113                 }
2114
2115                 fout(" %d", Mission_events[i].interval);
2116
2117                 if ( Mission_events[i].score != 0 ) {
2118                         if ( optional_string_fred("+Score:", "$Formula:")){
2119                                 parse_comments();
2120                         } else {
2121                                 fout("\n+Score:");
2122                         }
2123                         fout(" %d", Mission_events[i].score);
2124                 }
2125
2126                 if ( Mission_events[i].chain_delay >= 0 ) {
2127                         if ( optional_string_fred("+Chained:", "$Formula:")){
2128                                 parse_comments();
2129                         } else {
2130                                 fout("\n+Chained:");
2131                         }
2132
2133                         fout(" %d", Mission_events[i].chain_delay);
2134                 }
2135
2136                 //XSTR
2137                 if (Mission_events[i].objective_text) {
2138                         if (optional_string_fred("+Objective:", "$Formula:")){
2139                                 parse_comments();
2140                         } else {
2141                                 fout("\n+Objective:");
2142                         }
2143
2144                         fout(" ");
2145                         fout_ext("%s", Mission_events[i].objective_text);
2146                 }
2147
2148                 //XSTR
2149                 if (Mission_events[i].objective_key_text) {
2150                         if (optional_string_fred("+Objective key:", "$Formula:")){
2151                                 parse_comments();
2152                         } else {
2153                                 fout("\n+Objective key:");
2154                         }
2155
2156                         fout(" ");
2157                         fout_ext("%s", Mission_events[i].objective_key_text);
2158                 }
2159
2160                 // save team
2161                 if (Mission_events[i].team >= 0){
2162                         if (optional_string_fred("+Team:")){
2163                                 parse_comments();
2164                         } else {
2165                                 fout("\n+Team:");
2166                         } 
2167                         fout(" ");
2168                         fout("%d", Mission_events[i].team);
2169                 }
2170         }
2171
2172         return err;
2173 }
2174
2175 int CFred_mission_save::save_reinforcements()
2176 {
2177         int i, j, type;
2178
2179         fred_parse_flag = 0;
2180         required_string_fred("#Reinforcements");
2181         parse_comments(2);
2182         fout("\t\t;! %d total\n", Num_reinforcements);
2183
2184         for (i=0; i<Num_reinforcements; i++) {
2185                 required_string_either_fred("$Name:", "#Background bitmaps");
2186                 required_string_fred("$Name:");
2187                 parse_comments(i ? 2 : 1);
2188                 fout(" %s", Reinforcements[i].name);
2189
2190                 type = TYPE_ATTACK_PROTECT;
2191                 for (j=0; j<MAX_SHIPS; j++)
2192                         if ((Ships[j].objnum != -1) && !stricmp(Ships[j].ship_name, Reinforcements[i].name)) {
2193                                 if (Ship_info[Ships[j].ship_info_index].flags & SIF_SUPPORT)
2194                                         type = TYPE_REPAIR_REARM;
2195                                 break;
2196                         }
2197
2198                 required_string_fred("$Type:");
2199                 parse_comments();
2200                 fout(" %s", Reinforcement_type_names[type]);
2201
2202                 required_string_fred("$Num times:");
2203                 parse_comments();
2204                 fout(" %d", Reinforcements[i].uses);
2205
2206                 if ( optional_string_fred("+Arrival Delay:", "$Name:"))
2207                         parse_comments();
2208                 else
2209                         fout("\n+Arrival Delay:");
2210                 fout(" %d", Reinforcements[i].arrival_delay );
2211
2212                 if (optional_string_fred("+No Messages:", "$Name:"))
2213                         parse_comments();
2214                 else
2215                         fout("\n+No Messages:");
2216                 fout(" (");
2217                 for (j = 0; j < MAX_REINFORCEMENT_MESSAGES; j++) {
2218                         if ( strlen(Reinforcements[i].no_messages[j]) )
2219                                 fout(" \"%s\"", Reinforcements[i].no_messages[j]);
2220                 }
2221                 fout(" )");
2222
2223                 if (optional_string_fred("+Yes Messages:", "$Name:"))
2224                         parse_comments();
2225                 else
2226                         fout("\n+Yes Messages:");
2227                 fout(" (");
2228                 for (j = 0; j < MAX_REINFORCEMENT_MESSAGES; j++) {
2229                         if ( strlen(Reinforcements[i].yes_messages[j]) )
2230                                 fout(" \"%s\"", Reinforcements[i].yes_messages[j]);
2231                 }
2232                 fout(" )");
2233
2234         }
2235
2236         return err;
2237 }
2238
2239 int CFred_mission_save::save_bitmaps()
2240 {       
2241         int idx;
2242
2243         fred_parse_flag = 0;
2244         required_string_fred("#Background bitmaps");
2245         parse_comments(2);
2246         fout("\t\t;! %d total\n", Num_starfield_bitmaps);
2247
2248         required_string_fred("$Num stars:");
2249         parse_comments();
2250         fout(" %d", Num_stars);
2251
2252         float Ambient_light_level = 1.0f;       // JAS: Should this be set to something?
2253         required_string_fred("$Ambient light level:");
2254         parse_comments();
2255         fout(" %d", Ambient_light_level);
2256
2257         // neb2 stuff
2258         if(The_mission.flags & MISSION_FLAG_FULLNEB){
2259                 required_string_fred("+Neb2:");
2260                 parse_comments();
2261                 fout(" %s\n", Neb2_texture_name);
2262
2263                 required_string_fred("+Neb2Flags:");
2264                 parse_comments();
2265                 fout(" %d\n", Neb2_poof_flags);
2266         }
2267         // neb 1 stuff
2268         else {
2269                 if (Nebula_index >= 0) {
2270                         if (optional_string_fred("+Nebula:")){
2271                                 parse_comments();
2272                         } else {
2273                                 fout("\n+Nebula:");
2274                         }               
2275                         fout(" %s", Nebula_filenames[Nebula_index]);            
2276
2277                         required_string_fred("+Color:");
2278                         parse_comments();
2279                         fout(" %s", Nebula_colors[Mission_palette]);
2280
2281                         required_string_fred("+Pitch:");
2282                         parse_comments();
2283                         fout(" %d", Nebula_pitch);
2284
2285                         required_string_fred("+Bank:");
2286                         parse_comments();
2287                         fout(" %d", Nebula_bank);
2288
2289                         required_string_fred("+Heading:");
2290                         parse_comments();
2291                         fout(" %d", Nebula_heading);
2292                 }
2293         }
2294
2295         // save suns by sun bitmap filename
2296         for(idx=0; idx<Num_suns; idx++){
2297                 // sun name, angles and scale
2298                 required_string_fred("$Sun:");
2299                 parse_comments();
2300                 fout(" %s", Suns[idx].filename);
2301
2302                 required_string_fred("+Angles:");
2303                 parse_comments();
2304                 fout(" %f %f %f", Suns[idx].ang.p, Suns[idx].ang.b, Suns[idx].ang.h);
2305
2306                 required_string_fred("+Scale:");
2307                 parse_comments();
2308                 fout(" %f", Suns[idx].scale_x);
2309         }
2310
2311         // save background bitmaps by filename
2312         for(idx=0; idx<Num_starfield_bitmaps; idx++){
2313                 // sun name, angles and scale
2314                 required_string_fred("$Starbitmap:");
2315                 parse_comments();
2316                 fout(" %s", Starfield_bitmap_instance[idx].filename);
2317
2318                 required_string_fred("+Angles:");
2319                 parse_comments();
2320                 fout(" %f %f %f", Starfield_bitmap_instance[idx].ang.p, Starfield_bitmap_instance[idx].ang.b, Starfield_bitmap_instance[idx].ang.h);
2321
2322                 required_string_fred("+ScaleX:");
2323                 parse_comments();
2324                 fout(" %f", Starfield_bitmap_instance[idx].scale_x);
2325
2326                 required_string_fred("+ScaleY:");
2327                 parse_comments();
2328                 fout(" %f", Starfield_bitmap_instance[idx].scale_y);
2329
2330                 required_string_fred("+DivX:");
2331                 parse_comments();
2332                 fout(" %d", Starfield_bitmap_instance[idx].div_x);
2333
2334                 required_string_fred("+DivY:");
2335                 parse_comments();
2336                 fout(" %d", Starfield_bitmap_instance[idx].div_y);
2337         }
2338
2339         return err;
2340 }
2341
2342 int CFred_mission_save::save_asteroid_fields()
2343 {
2344         int i, idx;
2345
2346         fred_parse_flag = 0;
2347         required_string_fred("#Asteroid Fields");
2348         parse_comments(2);
2349
2350         for (i=0; i<1 /*MAX_ASTEROID_FIELDS*/; i++) {
2351                 if (!Asteroid_field.num_initial_asteroids)
2352                         continue;
2353
2354                 required_string_fred("$Density:");
2355                 parse_comments(2);
2356                 fout(" %d", Asteroid_field.num_initial_asteroids);
2357
2358                 // field type
2359                 if (optional_string_fred("+Field Type:")){
2360                         parse_comments();
2361                 } else {
2362                         fout("\n+Field Type:");
2363                 }
2364                 fout(" %d", Asteroid_field.field_type);
2365
2366                 // debris type
2367                 if (optional_string_fred("+Debris Genre:")){
2368                         parse_comments();
2369                 } else {
2370                         fout("\n+Debris Genre:");
2371                 }
2372                 fout(" %d", Asteroid_field.debris_genre);
2373
2374                 // field_debris_type (only if ship genre)
2375                 if (Asteroid_field.debris_genre == DG_SHIP) {
2376                         for (int idx=0; idx<3; idx++) {
2377                                 if (Asteroid_field.field_debris_type[idx] != -1) {
2378                                         if (optional_string_fred("+Field Debris Type:")){
2379                                                 parse_comments();
2380                                         } else {
2381                                                 fout("\n+Field Debris Type:");
2382                                         }
2383                                         fout(" %d", Asteroid_field.field_debris_type[idx]);
2384                                 }
2385                         }
2386                 } else {
2387                         // asteroid subtypes stored in field_debris_type as -1 or 1
2388                         for (idx=0; idx<3; idx++) {
2389                                 if (Asteroid_field.field_debris_type[idx] != -1) {
2390                                         if (optional_string_fred("+Field Debris Type:")){
2391                                                 parse_comments();
2392                                         } else {
2393                                                 fout("\n+Field Debris Type:");
2394                                         }
2395                                         fout(" %d", idx);
2396                                 }
2397                         }
2398                 }
2399
2400
2401                 required_string_fred("$Average Speed:");
2402                 parse_comments();
2403                 fout(" %f", vm_vec_mag(&Asteroid_field.vel));
2404
2405                 required_string_fred("$Minimum:");
2406                 parse_comments();
2407                 save_vector(Asteroid_field.min_bound);
2408
2409                 required_string_fred("$Maximum:");
2410                 parse_comments();
2411                 save_vector(Asteroid_field.max_bound);
2412
2413                 if (Asteroid_field.has_inner_bound == 1) {
2414                         if (optional_string_fred("+Inner Bound:")){
2415                                 parse_comments();
2416                         } else {
2417                                 fout("\n+Inner Bound:");
2418                         }
2419
2420                         required_string_fred("$Minimum:");
2421                         parse_comments();
2422                         save_vector(Asteroid_field.inner_min_bound);
2423
2424                         required_string_fred("$Maximum:");
2425                         parse_comments();
2426                         save_vector(Asteroid_field.inner_max_bound);
2427                 }
2428         }
2429
2430         return err;
2431 }
2432
2433 int CFred_mission_save::save_music()
2434 {
2435         required_string_fred("#Music");
2436         parse_comments(2);
2437
2438         required_string_fred("$Event Music:");
2439         parse_comments(2);
2440         if (Current_soundtrack_num < 0)
2441                 fout(" None");
2442         else
2443                 fout(" %s", Soundtracks[Current_soundtrack_num].name);
2444
2445         required_string_fred("$Briefing Music:");
2446         parse_comments();
2447         if (Mission_music[SCORE_BRIEFING] < 0)
2448                 fout(" None");
2449         else
2450                 fout(" %s", Spooled_music[Mission_music[SCORE_BRIEFING]].name);
2451
2452         return err;
2453 }
2454
2455 void CFred_mission_save::save_turret_info(ship_subsys *ptr, int ship)
2456 {
2457         int i, z;
2458         ship_weapon *wp = &ptr->weapons;
2459
2460         if (wp->ai_class != Ship_info[Ships[ship].ship_info_index].ai_class) {
2461                 if (optional_string_fred("+AI Class:", "$Name:", "+Subsystem:"))
2462                         parse_comments();
2463                 else
2464                         fout("\n+AI Class:");
2465
2466                 fout(" %s", Ai_class_names[wp->ai_class]);
2467         }
2468
2469         z = 0;
2470         i = wp->num_primary_banks;
2471         while (i--)
2472                 if (wp->primary_bank_weapons[i] != ptr->system_info->primary_banks[i])
2473                         z = 1;
2474
2475         if (z) {
2476                 if (optional_string_fred("+Primary Banks:", "$Name:", "+Subsystem:"))
2477                         parse_comments();
2478                 else
2479                         fout("\n+Primary Banks:");
2480
2481                 fout(" ( ");
2482                 for (i=0; i<wp->num_primary_banks; i++)
2483                         fout("\"%s\" ", Weapon_info[wp->primary_bank_weapons[i]].name);
2484
2485                 fout(")");
2486         }
2487
2488         z = 0;
2489         i = wp->num_secondary_banks;
2490         while (i--)
2491                 if (wp->secondary_bank_weapons[i] != ptr->system_info->secondary_banks[i])
2492                         z = 1;
2493
2494         if (z) {
2495                 if (optional_string_fred("+Secondary Banks:", "$Name:", "+Subsystem:"))
2496                         parse_comments();
2497                 else
2498                         fout("\n+Secondary Banks:");
2499
2500                 fout(" ( ");
2501                 for (i=0; i<wp->num_secondary_banks; i++)
2502                         fout("\"%s\" ", Weapon_info[wp->secondary_bank_weapons[i]].name);
2503
2504                 fout(")");
2505         }
2506
2507         z = 0;
2508         i = wp->num_secondary_banks;
2509         while (i--)
2510                 if (wp->secondary_bank_ammo[i] != 100)
2511                         z = 1;
2512
2513         if (z) {
2514                 if (optional_string_fred("+Sbank Ammo:", "$Name:", "+Subsystem:"))
2515                         parse_comments();
2516                 else
2517                         fout("\n+Sbank Ammo:");
2518
2519                 fout(" ( ");
2520                 for (i=0; i<wp->num_secondary_banks; i++)
2521                         fout("%d ", wp->secondary_bank_ammo[i]);
2522
2523                 fout(")");
2524         }
2525 }
2526
2527 int CFred_mission_save::save_campaign_file(char *pathname)
2528 {
2529         int i, j, m, flag;
2530
2531         Campaign_tree_formp->save_tree();  // flush all changes so they get saved.
2532         Campaign_tree_viewp->sort_elements();
2533         reset_parse();
2534         fred_parse_flag = 0;
2535
2536         pathname = cf_add_ext(pathname, FS_CAMPAIGN_FILE_EXT);
2537         fp = cfopen(pathname, "wt", CFILE_NORMAL, CF_TYPE_MISSIONS);
2538         if (!fp)        {
2539                 nprintf(("Error", "Can't open campaign file to save.\n"));
2540                 return -1;
2541         }
2542
2543         required_string_fred("$Name:");
2544         parse_comments(0);
2545         fout(" %s", Campaign.name);
2546
2547         Assert((Campaign.type >= 0) && (Campaign.type < MAX_CAMPAIGN_TYPES));
2548         required_string_fred("$Type:");
2549         parse_comments();
2550         fout(" %s", campaign_types[Campaign.type]);
2551
2552         // XSTR
2553         if (Campaign.desc) {
2554                 required_string_fred("+Description:");
2555                 parse_comments();
2556                 fout("\n");
2557                 fout_ext("%s", Campaign.desc);
2558                 fout("\n$end_multi_text");
2559         }
2560
2561         if ( Campaign.type != CAMPAIGN_TYPE_SINGLE ) {
2562                 required_string_fred("+Num Players:");
2563                 parse_comments();
2564                 fout(" %d", Campaign.num_players);
2565         }       
2566
2567         // write out the ships and weapons which the player can start the campaign with
2568         optional_string_fred("+Starting Ships: (");
2569         parse_comments(2);
2570         for (i = 0; i < MAX_SHIP_TYPES; i++ ) {
2571                 if ( Campaign.ships_allowed[i] )
2572                         fout(" \"%s\" ", Ship_info[i].name );
2573         }
2574         fout( ")\n" );
2575
2576         optional_string_fred("+Starting Weapons: (");
2577         parse_comments();
2578         for (i = 0; i < MAX_WEAPON_TYPES; i++ ) {
2579                 if ( Campaign.weapons_allowed[i] )
2580                         fout(" \"%s\" ", Weapon_info[i].name );
2581         }
2582         fout( ")\n" );
2583
2584         fred_parse_flag = 0;
2585         for (i=0; i<Campaign.num_missions; i++) {
2586                 m = Sorted[i];
2587                 required_string_either_fred("$Mission:", "#End");
2588                 required_string_fred("$Mission:");
2589                 parse_comments(2);
2590                 fout(" %s", Campaign.missions[m].name);
2591
2592                 if ( strlen(Campaign.missions[i].briefing_cutscene) ) {
2593                         if (optional_string_fred("+Briefing Cutscene:", "$Mission"))
2594                                 parse_comments();
2595                         else
2596                                 fout("\n+Briefing Cutscene:");
2597
2598                         fout( " %s", Campaign.missions[i].briefing_cutscene );
2599                 }
2600
2601                 required_string_fred("+Flags:", "$Mission:");
2602                 parse_comments();
2603                 fout(" %d", Campaign.missions[m].flags);
2604
2605                 // save campaign link sexp
2606                 bool mission_loop = false;
2607                 flag = 0;
2608                 for (j=0; j<Total_links; j++) {
2609                         if (Links[j].from == m) {
2610                                 if (!flag) {
2611                                         if (optional_string_fred("+Formula:", "$Mission:"))
2612                                                 parse_comments();
2613                                         else
2614                                                 fout("\n+Formula:");
2615
2616                                         fout(" ( cond\n");
2617                                         flag = 1;
2618                                 }
2619
2620                                 //save_campaign_sexp(Links[j].sexp, Campaign.missions[Links[j].to].name);
2621                                 if (Links[j].mission_loop) {
2622                                         mission_loop = true;
2623                                 } else {
2624                                         save_campaign_sexp(Links[j].sexp, Links[j].to);
2625                                 }
2626                         }
2627                 }
2628
2629                 if (flag) {
2630                         fout(")");
2631                 }
2632
2633                 // now save campaign loop sexp
2634                 if (mission_loop) {
2635                         required_string_fred("\n+Mission Loop:");
2636                         parse_comments();
2637
2638                         int num_mission_loop = 0;
2639                         for (j=0; j<Total_links; j++) {
2640                                 if ( (Links[j].from == m) && (Links[j].mission_loop) ) {
2641
2642                                         num_mission_loop++;
2643
2644                                         // maybe write out mission loop descript
2645                                         if ((num_mission_loop == 1) && Links[j].mission_loop_txt) {
2646                                                 required_string_fred("+Mission Loop Text:");
2647                                                 parse_comments();
2648                                                 fout("\n");
2649                                                 fout_ext("%s", Links[j].mission_loop_txt);
2650                                                 fout("\n$end_multi_text");
2651                                         }
2652
2653                                         // maybe write out mission loop descript
2654                                         if ((num_mission_loop == 1) && Links[j].mission_loop_brief_anim) {
2655                                                 required_string_fred("+Mission Loop Brief Anim:");
2656                                                 parse_comments();
2657                                                 fout("\n");
2658                                                 fout_ext("%s", Links[j].mission_loop_brief_anim);
2659                                                 fout("\n$end_multi_text");
2660                                         }
2661
2662                                         // maybe write out mission loop descript
2663                                         if ((num_mission_loop == 1) && Links[j].mission_loop_brief_sound) {
2664                                                 required_string_fred("+Mission Loop Brief Sound:");
2665                                                 parse_comments();
2666                                                 fout("\n");
2667                                                 fout_ext("%s", Links[j].mission_loop_brief_sound);
2668                                                 fout("\n$end_multi_text");
2669                                         }
2670
2671                                         if (num_mission_loop == 1) {
2672                                                 // write out mission loop formula
2673                                                 fout("\n+Formula:");
2674                                                 fout(" ( cond\n");
2675                                                 save_campaign_sexp(Links[j].sexp, Links[j].to);
2676                                                 fout(")");
2677                                         }
2678                                 }
2679                         }
2680                         if (num_mission_loop > 1) {
2681                                 char buffer[1024];
2682                                 sprintf(buffer, "Multiple branching loop error from mission %s\nEdit campaign for *at most* 1 loop from each mission.", Campaign.missions[m].name);
2683                                 MessageBox((HWND)os_get_window(), buffer, "Error", MB_OK);
2684                         }
2685                 }
2686
2687                 if (optional_string_fred("+Level:", "$Mission:")){
2688                         parse_comments();
2689                 } else {
2690                         fout("\n\n+Level:");
2691                 }
2692
2693                 fout(" %d", Campaign.missions[m].level);
2694
2695                 if (optional_string_fred("+Position:", "$Mission:")){
2696                         parse_comments();
2697                 } else {
2698                         fout("\n+Position:");
2699                 }
2700
2701                 fout(" %d", Campaign.missions[m].pos);
2702         }
2703
2704         required_string_fred("#End");
2705         parse_comments(2);
2706         token_found = NULL;
2707         parse_comments();
2708         fout("\n");
2709
2710         cfclose(fp);
2711         if (err)
2712                 mprintf(("Campaign saving error code #%d", err));
2713         else
2714                 Campaign_wnd->error_checker();
2715
2716         return err;
2717 }
2718
2719 void CFred_mission_save::save_campaign_sexp(int node, int link_num)
2720 {
2721         char out[4096];
2722
2723         Sexp_string = out;
2724         *out = 0;
2725         Assert(node >= 0);
2726
2727         // if the link num is -1, then this is a end-of-campaign location
2728         if ( link_num != -1 ) {
2729                 if (build_sexp_string(node, 2, SEXP_SAVE_MODE))
2730                         fout("   (\n      %s\n      ( next-mission \"%s\" )\n   )\n", out, Campaign.missions[link_num].name);
2731                 else
2732                         fout("   ( %s( next-mission \"%s\" ) )\n", out, Campaign.missions[link_num].name);
2733         } else {
2734                 if (build_sexp_string(node, 2, SEXP_SAVE_MODE)){
2735                         fout("   (\n      %s\n      ( end-of-campaign )\n   )\n", out);
2736                 } else {
2737                         fout("   ( %s( end-of-campaign ) )\n", out );
2738                 }
2739         }
2740 }
2741
2742