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