1+ #include " campaignsave.h"
2+
3+ #include " globalincs/linklist.h"
4+ #include " globalincs/version.h"
5+
6+ #include " mission/missioncampaign.h"
7+ #include " parse/parselo.h"
8+
9+ #include < freespace.h>
10+
11+ // Bodies copied from the old implementations in missionsave.cpp,
12+ // unchanged except for the new class name and include.
13+ int Fred_campaign_save::save_campaign_file (const char * pathname)
14+ {
15+ // This is original FRED code. These were moved to the call sites as the data should be fully
16+ // prepared before calling this function.
17+ // TODO check QtFRED's tree is sorted and saved before calling this function.
18+ // Campaign_tree_formp->save_tree(); // flush all changes so they get saved.
19+ // Campaign_tree_viewp->sort_elements();
20+
21+ reset_parse ();
22+ raw_ptr = Parse_text_raw;
23+ fred_parse_flag = 0 ;
24+
25+ pathname = cf_add_ext (pathname, FS_CAMPAIGN_FILE_EXT);
26+ fp = cfopen (pathname, " wt" , CF_TYPE_MISSIONS);
27+ if (!fp) {
28+ nprintf ((" Error" , " Can't open campaign file to save.\n " ));
29+ return -1 ;
30+ }
31+
32+ required_string_fred (" $Name:" );
33+ parse_comments (0 );
34+ fout (" %s" , Campaign.name );
35+
36+ Assert ((Campaign.type >= 0 ) && (Campaign.type < MAX_CAMPAIGN_TYPES));
37+ required_string_fred (" $Type:" );
38+ parse_comments ();
39+ fout (" %s" , campaign_types[Campaign.type ]);
40+
41+ // XSTR
42+ if (Campaign.desc ) {
43+ required_string_fred (" +Description:" );
44+ parse_comments ();
45+ fout_ext (" \n " , " %s" , Campaign.desc );
46+ fout (" \n $end_multi_text" );
47+ }
48+
49+ if (Campaign.type != CAMPAIGN_TYPE_SINGLE) {
50+ required_string_fred (" +Num Players:" );
51+ parse_comments ();
52+ fout (" %d" , Campaign.num_players );
53+ }
54+
55+ // campaign flags - Goober5000
56+ if (save_config.save_format != MissionFormat::RETAIL) {
57+ optional_string_fred (" $Flags:" );
58+ parse_comments ();
59+ fout (" %d\n " , Campaign.flags );
60+ }
61+
62+ if (save_config.save_format != MissionFormat::RETAIL && !Campaign.custom_data .empty ()) {
63+ if (optional_string_fred (" $begin_custom_data_map" )) {
64+ parse_comments (2 );
65+ } else {
66+ fout (" \n $begin_custom_data_map" );
67+ }
68+
69+ for (const auto & pair : Campaign.custom_data ) {
70+ fout (" \n +Val: %s %s" , pair.first .c_str (), pair.second .c_str ());
71+ }
72+
73+ if (optional_string_fred (" $end_custom_data_map" )) {
74+ parse_comments ();
75+ } else {
76+ fout (" \n $end_custom_data_map" );
77+ }
78+ }
79+
80+ // write out the ships and weapons which the player can start the campaign with
81+ optional_string_fred (" +Starting Ships: (" );
82+ parse_comments (2 );
83+ for (int i = 0 ; i < ship_info_size (); i++) {
84+ if (Campaign.ships_allowed [i])
85+ fout (" \" %s\" " , Ship_info[i].name );
86+ }
87+ fout (" )\n " );
88+
89+ optional_string_fred (" +Starting Weapons: (" );
90+ parse_comments ();
91+ for (int i = 0 ; i < weapon_info_size (); i++) {
92+ if (Campaign.weapons_allowed [i])
93+ fout (" \" %s\" " , Weapon_info[i].name );
94+ }
95+ fout (" )\n " );
96+
97+ fred_parse_flag = 0 ;
98+ for (int i = 0 ; i < Campaign.num_missions ; i++) {
99+ // Expect to get Campaign.missions ordered from FRED
100+ cmission& cm = Campaign.missions [i];
101+
102+ required_string_either_fred (" $Mission:" , " #End" );
103+ required_string_fred (" $Mission:" );
104+ parse_comments (2 );
105+ fout (" %s" , cm.name );
106+
107+ if (strlen (cm.briefing_cutscene )) {
108+ if (optional_string_fred (" +Briefing Cutscene:" , " $Mission" ))
109+ parse_comments ();
110+ else
111+ fout (" \n +Briefing Cutscene:" );
112+
113+ fout (" %s" , cm.briefing_cutscene );
114+ }
115+
116+ required_string_fred (" +Flags:" , " $Mission:" );
117+ parse_comments ();
118+
119+ // don't save any internal flags
120+ auto flags_to_save = cm.flags & CMISSION_EXTERNAL_FLAG_MASK;
121+
122+ // Goober5000
123+ if (save_config.save_format != MissionFormat::RETAIL) {
124+ // don't save Bastion flag
125+ fout (" %d" , flags_to_save & ~CMISSION_FLAG_BASTION);
126+
127+ // new main hall stuff
128+ if (optional_string_fred (" +Main Hall:" , " $Mission:" ))
129+ parse_comments ();
130+ else
131+ fout (" \n +Main Hall:" );
132+
133+ fout (" %s" , cm.main_hall .c_str ());
134+ } else {
135+ // save Bastion flag properly
136+ fout (" %d" , flags_to_save | ((cm.main_hall != " " ) ? CMISSION_FLAG_BASTION : 0 ));
137+ }
138+
139+ if (!cm.substitute_main_hall .empty ()) {
140+ fso_comment_push (" ;;FSO 3.7.2;;" );
141+ if (optional_string_fred (" +Substitute Main Hall:" )) {
142+ parse_comments (1 );
143+ fout (" %s" , cm.substitute_main_hall .c_str ());
144+ } else {
145+ fout_version (" \n +Substitute Main Hall: %s" , cm.substitute_main_hall .c_str ());
146+ }
147+ fso_comment_pop ();
148+ } else {
149+ bypass_comment (" ;;FSO 3.7.2;; +Substitute Main Hall:" );
150+ }
151+
152+ if (cm.debrief_persona_index > 0 ) {
153+ fso_comment_push (" ;;FSO 3.6.8;;" );
154+ if (optional_string_fred (" +Debriefing Persona Index:" )) {
155+ parse_comments (1 );
156+ fout (" %d" , cm.debrief_persona_index );
157+ } else {
158+ fout_version (" \n +Debriefing Persona Index: %d" , cm.debrief_persona_index );
159+ }
160+ fso_comment_pop ();
161+ } else {
162+ bypass_comment (" ;;FSO 3.6.8;; +Debriefing Persona Index:" );
163+ }
164+
165+ // new save cmission sexps
166+ if (optional_string_fred (" +Formula:" , " $Mission:" )) {
167+ parse_comments ();
168+ } else {
169+ fout (" \n +Formula:" );
170+ }
171+
172+ {
173+ SCP_string sexp_out{};
174+ convert_sexp_to_string (sexp_out, cm.formula , SEXP_SAVE_MODE);
175+ fout (" %s" , sexp_out.c_str ());
176+ }
177+
178+ bool mission_loop = cm.flags & CMISSION_FLAG_HAS_LOOP;
179+
180+ Assertion (cm.flags ^ CMISSION_FLAG_HAS_FORK,
181+ " scpFork campaigns cannot be saved, use axemFork.\n Should be detected on load." );
182+
183+ if (mission_loop) {
184+ required_string_fred (" \n +Mission Loop:" );
185+ parse_comments ();
186+
187+ if (cm.mission_branch_desc ) {
188+ required_string_fred (" +Mission Loop Text:" );
189+ parse_comments ();
190+ fout_ext (" \n " , " %s" , cm.mission_branch_desc );
191+ fout (" \n $end_multi_text" );
192+ }
193+
194+ if (cm.mission_branch_brief_anim ) {
195+ required_string_fred (" +Mission Loop Brief Anim:" );
196+ parse_comments ();
197+ fout_ext (" \n " , " %s" , cm.mission_branch_brief_anim );
198+ fout (" \n $end_multi_text" );
199+ }
200+
201+ if (cm.mission_branch_brief_sound ) {
202+ required_string_fred (" +Mission Loop Brief Sound:" );
203+ parse_comments ();
204+ fout_ext (" \n " , " %s" , cm.mission_branch_brief_sound );
205+ fout (" \n $end_multi_text" );
206+ }
207+
208+ // write out mission loop formula
209+ fout (" \n +Formula:" );
210+ {
211+ SCP_string sexp_out{};
212+ convert_sexp_to_string (sexp_out, cm.mission_loop_formula , SEXP_SAVE_MODE);
213+ fout (" %s" , sexp_out.c_str ());
214+ }
215+ }
216+
217+ if (optional_string_fred (" +Level:" , " $Mission:" )) {
218+ parse_comments ();
219+ } else {
220+ fout (" \n\n +Level:" );
221+ }
222+
223+ fout (" %d" , cm.level );
224+
225+ if (optional_string_fred (" +Position:" , " $Mission:" )) {
226+ parse_comments ();
227+ } else {
228+ fout (" \n +Position:" );
229+ }
230+
231+ fout (" %d" , cm.pos );
232+
233+ fso_comment_pop ();
234+ }
235+
236+ required_string_fred (" #End" );
237+ parse_comments (2 );
238+ token_found = NULL ;
239+ parse_comments ();
240+ fout (" \n " );
241+
242+ cfclose (fp);
243+ fso_comment_pop (true );
244+
245+ // Mission editor should run the error checker *before* calling the save function
246+ Assertion (!err, " Nothing in here should have a side effect to raise the mission error saving flag." );
247+
248+ return err;
249+ }
250+
251+ void Fred_campaign_save::save_campaign_sexp (int node, int link_num)
252+ {
253+ SCP_string sexp_out;
254+ Assert (node >= 0 );
255+
256+ // if the link num is -1, then this is a end-of-campaign location
257+ if (link_num != -1 ) {
258+ if (build_sexp_string (sexp_out, node, 2 , SEXP_SAVE_MODE)) {
259+ fout (" (\n %s\n ( next-mission \" %s\" )\n )\n " ,
260+ sexp_out.c_str (),
261+ Campaign.missions [link_num].name );
262+ } else {
263+ fout (" ( %s( next-mission \" %s\" ) )\n " , sexp_out.c_str (), Campaign.missions [link_num].name );
264+ }
265+ } else {
266+ if (build_sexp_string (sexp_out, node, 2 , SEXP_SAVE_MODE)) {
267+ fout (" (\n %s\n ( end-of-campaign )\n )\n " , sexp_out.c_str ());
268+ } else {
269+ fout (" ( %s( end-of-campaign ) )\n " , sexp_out.c_str ());
270+ }
271+ }
272+ }
0 commit comments