1 /***********************************************************************
2  Freeciv - Copyright (C) 1996 - A Kjeldberg, L Gregersen, P Unold
3    This program is free software; you can redistribute it and/or modify
4    it under the terms of the GNU General Public License as published by
5    the Free Software Foundation; either version 2, or (at your option)
6    any later version.
7 
8    This program is distributed in the hope that it will be useful,
9    but WITHOUT ANY WARRANTY; without even the implied warranty of
10    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11    GNU General Public License for more details.
12 ***********************************************************************/
13 
14 #ifdef HAVE_CONFIG_H
15 #include <fc_config.h>
16 #endif
17 
18 /* utility */
19 #include "registry.h"
20 #include "string_vector.h"
21 
22 /* common */
23 #include "achievements.h"
24 #include "game.h"
25 #include "government.h"
26 #include "map.h"
27 #include "movement.h"
28 #include "multipliers.h"
29 #include "specialist.h"
30 #include "style.h"
31 #include "unittype.h"
32 #include "version.h"
33 
34 /* server */
35 #include "ruleset.h"
36 #include "settings.h"
37 
38 #include "rulesave.h"
39 
40 /* Ruleset format version */
41 #define FORMAT_VERSION 1
42 
43 /**************************************************************************
44   Create new ruleset section file with common header.
45 **************************************************************************/
create_ruleset_file(const char * rsname,const char * rstype)46 static struct section_file *create_ruleset_file(const char *rsname,
47                                                 const char *rstype)
48 {
49   struct section_file *sfile = secfile_new(TRUE);
50   char buf[500];
51 
52   if (rsname != NULL && rsname[0] != '\0') {
53     fc_snprintf(buf, sizeof(buf), "%s %s data for Freeciv", rsname, rstype);
54   } else {
55     fc_snprintf(buf, sizeof(buf), "Template %s data for Freeciv", rstype);
56   }
57 
58   secfile_insert_str(sfile, buf, "datafile.description");
59   secfile_insert_str(sfile, freeciv_datafile_version(), "datafile.ruledit");
60   secfile_insert_str(sfile, RULESET_CAPABILITIES, "datafile.options");
61   secfile_insert_int(sfile, FORMAT_VERSION, "datafile.format_version");
62 
63   return sfile;
64 }
65 
66 /**************************************************************************
67   Save int value that has default applied upon loading.
68 **************************************************************************/
save_default_int(struct section_file * sfile,int value,int default_value,const char * path,const char * entry)69 static bool save_default_int(struct section_file *sfile, int value,
70                              int default_value, const char *path, const char *entry)
71 {
72   if (value != default_value) {
73     if (entry != NULL) {
74       secfile_insert_int(sfile, value,
75                          "%s.%s", path, entry);
76     } else {
77       secfile_insert_int(sfile, value,
78                          "%s", path);
79     }
80   }
81 
82   return TRUE;
83 }
84 
85 /**************************************************************************
86   Save bool value that has default applied upon loading.
87 **************************************************************************/
save_default_bool(struct section_file * sfile,bool value,bool default_value,const char * path,const char * entry)88 static bool save_default_bool(struct section_file *sfile, bool value,
89                               bool default_value, const char *path, const char *entry)
90 {
91   if ((value && !default_value)
92       || (!value && default_value)) {
93     if (entry != NULL) {
94       secfile_insert_bool(sfile, value,
95                           "%s.%s", path, entry);
96     } else {
97       secfile_insert_bool(sfile, value,
98                           "%s", path);
99     }
100   }
101 
102   return TRUE;
103 }
104 
105 /**************************************************************************
106   Save name of the object.
107 **************************************************************************/
save_name_translation(struct section_file * sfile,struct name_translation * name,const char * path)108 static bool save_name_translation(struct section_file *sfile,
109                                   struct name_translation *name,
110                                   const char *path)
111 {
112   struct entry *mod_entry;
113 
114   mod_entry = secfile_insert_str(sfile,
115                                  untranslated_name(name),
116                                  "%s.name", path);
117   entry_str_set_gt_marking(mod_entry, TRUE);
118   if (strcmp(skip_intl_qualifier_prefix(untranslated_name(name)),
119              rule_name_get(name))) {
120     secfile_insert_str(sfile,
121                        rule_name_get(name),
122                        "%s.rule_name", path);
123   }
124 
125   return TRUE;
126 }
127 
128 /**************************************************************************
129   Save vector of requirements
130 **************************************************************************/
save_reqs_vector(struct section_file * sfile,const struct requirement_vector * reqs,const char * path,const char * entry)131 static bool save_reqs_vector(struct section_file *sfile,
132                              const struct requirement_vector *reqs,
133                              const char *path, const char *entry)
134 {
135   int i;
136   bool includes_negated = FALSE;
137   bool includes_surviving = FALSE;
138   bool includes_quiet = FALSE;
139 
140   requirement_vector_iterate(reqs, preq) {
141     if (!preq->present) {
142       includes_negated = TRUE;
143     }
144     if (preq->survives) {
145       includes_surviving = TRUE;
146     }
147     if (preq->quiet) {
148       includes_quiet = TRUE;
149     }
150   } requirement_vector_iterate_end;
151 
152   i = 0;
153   requirement_vector_iterate(reqs, preq) {
154     secfile_insert_str(sfile,
155                        universals_n_name(preq->source.kind),
156                        "%s.%s%d.type", path, entry, i);
157     secfile_insert_str(sfile,
158                        universal_rule_name(&(preq->source)),
159                        "%s.%s%d.name", path, entry, i);
160     secfile_insert_str(sfile,
161                        req_range_name(preq->range),
162                        "%s.%s%d.range", path, entry, i);
163 
164     if (includes_surviving) {
165       secfile_insert_bool(sfile,
166                           preq->survives,
167                           "%s.%s%d.survives", path, entry, i);
168     }
169 
170     if (includes_negated) {
171       secfile_insert_bool(sfile,
172                           preq->present,
173                           "%s.%s%d.present", path, entry, i);
174     }
175 
176     if (includes_quiet) {
177       secfile_insert_bool(sfile,
178                           preq->quiet,
179                           "%s.%s%d.quiet", path, entry, i);
180     }
181 
182     i++;
183   } requirement_vector_iterate_end;
184 
185   return TRUE;
186 }
187 
188 /**************************************************************************
189   Save techs vector. Input is A_LAST terminated array of techs to save.
190 **************************************************************************/
save_tech_list(struct section_file * sfile,int * input,const char * path,const char * entry)191 static bool save_tech_list(struct section_file *sfile, int *input,
192                            const char *path, const char *entry)
193 {
194   const char *tech_names[MAX_NUM_TECH_LIST];
195   int set_count;
196   int i;
197 
198   set_count = 0;
199   for (i = 0; input[i] != A_LAST && i < MAX_NUM_TECH_LIST; i++) {
200     tech_names[set_count++] = advance_rule_name(advance_by_number(input[i]));
201   }
202 
203   if (set_count > 0) {
204     secfile_insert_str_vec(sfile, tech_names, set_count,
205                            "%s.%s", path, entry);
206   }
207 
208   return TRUE;
209 }
210 
211 /**************************************************************************
212   Save tech reference
213 **************************************************************************/
save_tech_ref(struct section_file * sfile,const struct advance * padv,const char * path,const char * entry)214 static bool save_tech_ref(struct section_file *sfile,
215                           const struct advance *padv,
216                           const char *path, const char *entry)
217 {
218    if (padv == A_NEVER) {
219      secfile_insert_str(sfile, "Never", "%s.%s", path, entry);
220    } else {
221      secfile_insert_str(sfile, advance_rule_name(padv),
222                         "%s.%s", path, entry);
223    }
224 
225    return TRUE;
226 }
227 
228 /**************************************************************************
229   Save terrain reference
230 **************************************************************************/
save_terrain_ref(struct section_file * sfile,const struct terrain * save,const struct terrain * pthis,const char * path,const char * entry)231 static bool save_terrain_ref(struct section_file *sfile,
232                              const struct terrain *save,
233                              const struct terrain *pthis,
234                              const char *path, const char *entry)
235 {
236   if (save == NULL) {
237     secfile_insert_str(sfile, "none", "%s.%s", path, entry);
238   } else if (save == pthis) {
239     secfile_insert_str(sfile, "yes", "%s.%s", path, entry);
240   } else {
241     secfile_insert_str(sfile, terrain_rule_name(save),
242                         "%s.%s", path, entry);
243   }
244 
245   return TRUE;
246 }
247 
248 /**************************************************************************
249   Save government reference
250 **************************************************************************/
save_gov_ref(struct section_file * sfile,const struct government * gov,const char * path,const char * entry)251 static bool save_gov_ref(struct section_file *sfile,
252                          const struct government *gov,
253                          const char *path, const char *entry)
254 {
255   secfile_insert_str(sfile, government_rule_name(gov), "%s.%s", path, entry);
256 
257   return TRUE;
258 }
259 
260 /**************************************************************************
261   Save buildings vector. Input is B_LAST terminated array of buildings
262   to save.
263 **************************************************************************/
save_building_list(struct section_file * sfile,int * input,const char * path,const char * entry)264 static bool save_building_list(struct section_file *sfile, int *input,
265                                const char *path, const char *entry)
266 {
267   const char *building_names[MAX_NUM_BUILDING_LIST];
268   int set_count;
269   int i;
270 
271   set_count = 0;
272   for (i = 0; input[i] != B_LAST && i < MAX_NUM_BUILDING_LIST; i++) {
273     building_names[set_count++] = improvement_rule_name(improvement_by_number(input[i]));
274   }
275 
276   if (set_count > 0) {
277     secfile_insert_str_vec(sfile, building_names, set_count,
278                            "%s.%s", path, entry);
279   }
280 
281   return TRUE;
282 }
283 
284 /**************************************************************************
285   Save units vector. Input is NULL terminated array of units
286   to save.
287 **************************************************************************/
save_unit_list(struct section_file * sfile,struct unit_type ** input,const char * path,const char * entry)288 static bool save_unit_list(struct section_file *sfile, struct unit_type **input,
289                            const char *path, const char *entry)
290 {
291   const char *unit_names[MAX_NUM_UNIT_LIST];
292   int set_count;
293   int i;
294 
295   set_count = 0;
296   for (i = 0; input[i] != NULL && i < MAX_NUM_UNIT_LIST; i++) {
297     unit_names[set_count++] = utype_rule_name(input[i]);
298   }
299 
300   if (set_count > 0) {
301     secfile_insert_str_vec(sfile, unit_names, set_count,
302                            "%s.%s", path, entry);
303   }
304 
305   return TRUE;
306 }
307 
308 /**************************************************************************
309   Save vector of unit class names based on bitvector bits
310 **************************************************************************/
save_uclass_vec(struct section_file * sfile,bv_unit_classes * bits,const char * path,const char * entry,bool unreachable_only)311 static bool save_uclass_vec(struct section_file *sfile,
312                             bv_unit_classes *bits,
313                             const char *path, const char *entry,
314                             bool unreachable_only)
315 {
316   const char *class_names[UCL_LAST];
317   int classes = 0;
318 
319   unit_class_iterate(pcargo) {
320     if (BV_ISSET(*(bits), uclass_index(pcargo))
321         && (uclass_has_flag(pcargo, UCF_UNREACHABLE)
322             || !unreachable_only)) {
323       class_names[classes++] = uclass_rule_name(pcargo);
324     }
325   } unit_class_iterate_end;
326 
327   if (classes > 0) {
328     secfile_insert_str_vec(sfile, class_names, classes,
329                            "%s.%s", path, entry);
330   }
331 
332   return TRUE;
333 }
334 
335 /**************************************************************************
336   Save strvec as ruleset vector of strings
337 **************************************************************************/
save_strvec(struct section_file * sfile,struct strvec * to_save,const char * path,const char * entry)338 static bool save_strvec(struct section_file *sfile,
339                         struct strvec *to_save,
340                         const char *path, const char *entry)
341 {
342   if (to_save != NULL) {
343     int sect_count = strvec_size(to_save);
344     const char *sections[sect_count];
345     int i;
346 
347     for (i = 0; i < sect_count; i++) {
348       sections[i] = strvec_get(to_save, i);
349     }
350 
351     secfile_insert_str_vec(sfile, sections, sect_count, "%s.%s", path, entry);
352   }
353 
354   return TRUE;
355 }
356 
357 /**************************************************************************
358   Save ruleset file.
359 **************************************************************************/
save_ruleset_file(struct section_file * sfile,const char * filename)360 static bool save_ruleset_file(struct section_file *sfile, const char *filename)
361 {
362   return secfile_save(sfile, filename, 0, FZ_PLAIN);
363 }
364 
365 /**************************************************************************
366   Save buildings.ruleset
367 **************************************************************************/
save_buildings_ruleset(const char * filename,const char * name)368 static bool save_buildings_ruleset(const char *filename, const char *name)
369 {
370   struct section_file *sfile = create_ruleset_file(name, "building");
371   int sect_idx;
372 
373   if (sfile == NULL) {
374     return FALSE;
375   }
376 
377   sect_idx = 0;
378   improvement_active_iterate(pb) {
379     if (!pb->disabled) {
380       char path[512];
381       const char *flag_names[IF_COUNT];
382       int set_count;
383       int flagi;
384 
385       fc_snprintf(path, sizeof(path), "building_%d", sect_idx++);
386 
387       save_name_translation(sfile, &(pb->name), path);
388 
389       secfile_insert_str(sfile, impr_genus_id_name(pb->genus),
390                          "%s.genus", path);
391 
392       if (strcmp(pb->graphic_str, "-")) {
393         secfile_insert_str(sfile, pb->graphic_str, "%s.graphic", path);
394       }
395       if (strcmp(pb->graphic_alt, "-")) {
396         secfile_insert_str(sfile, pb->graphic_alt, "%s.graphic_alt", path);
397       }
398       if (strcmp(pb->soundtag, "-")) {
399         secfile_insert_str(sfile, pb->soundtag, "%s.sound", path);
400       }
401       if (strcmp(pb->soundtag_alt, "-")) {
402         secfile_insert_str(sfile, pb->soundtag_alt, "%s.sound_alt", path);
403       }
404 
405       save_reqs_vector(sfile, &(pb->reqs), path, "reqs");
406       save_reqs_vector(sfile, &(pb->obsolete_by), path, "obsolete_by");
407 
408       secfile_insert_int(sfile, pb->build_cost, "%s.build_cost", path);
409       secfile_insert_int(sfile, pb->upkeep, "%s.upkeep", path);
410       secfile_insert_int(sfile, pb->sabotage, "%s.sabotage", path);
411 
412       set_count = 0;
413       for (flagi = 0; flagi < IF_COUNT; flagi++) {
414         if (improvement_has_flag(pb, flagi)) {
415           flag_names[set_count++] = impr_flag_id_name(flagi);
416         }
417       }
418 
419       if (set_count > 0) {
420         secfile_insert_str_vec(sfile, flag_names, set_count,
421                                "%s.flags", path);
422       }
423 
424       save_strvec(sfile, pb->helptext, path, "helptext");
425     }
426   } improvement_active_iterate_end;
427 
428   return save_ruleset_file(sfile, filename);
429 }
430 
431 /**************************************************************************
432   Save styles.ruleset
433 **************************************************************************/
save_styles_ruleset(const char * filename,const char * name)434 static bool save_styles_ruleset(const char *filename, const char *name)
435 {
436   struct section_file *sfile = create_ruleset_file(name, "styles");
437   int sect_idx;
438   int i;
439 
440   if (sfile == NULL) {
441     return FALSE;
442   }
443 
444   sect_idx = 0;
445   styles_iterate(pstyle) {
446     char path[512];
447 
448     fc_snprintf(path, sizeof(path), "style_%d", sect_idx++);
449 
450     save_name_translation(sfile, &(pstyle->name), path);
451   } styles_iterate_end;
452 
453   sect_idx = 0;
454   for (i = 0; i < game.control.styles_count; i++) {
455     char path[512];
456 
457     fc_snprintf(path, sizeof(path), "citystyle_%d", sect_idx++);
458 
459     save_name_translation(sfile, &(city_styles[i].name), path);
460 
461     secfile_insert_str(sfile, city_styles[i].graphic, "%s.graphic", path);
462     secfile_insert_str(sfile, city_styles[i].graphic_alt, "%s.graphic_alt", path);
463     if (strcmp(city_styles[i].citizens_graphic, "-")) {
464       secfile_insert_str(sfile, city_styles[i].citizens_graphic,
465                          "%s.citizens_graphic", path);
466     }
467     if (strcmp(city_styles[i].citizens_graphic_alt, "generic")) {
468       secfile_insert_str(sfile, city_styles[i].citizens_graphic_alt,
469                          "%s.citizens_graphic_alt", path);
470     }
471 
472     save_reqs_vector(sfile, &(city_styles[i].reqs), path, "reqs");
473   }
474 
475   sect_idx = 0;
476   music_styles_iterate(pmus) {
477     char path[512];
478 
479     fc_snprintf(path, sizeof(path), "musicstyle_%d", sect_idx++);
480 
481     secfile_insert_str(sfile, pmus->music_peaceful, "%s.music_peaceful", path);
482     secfile_insert_str(sfile, pmus->music_combat, "%s.music_combat", path);
483 
484     save_reqs_vector(sfile, &(pmus->reqs), path, "reqs");
485   } music_styles_iterate_end;
486 
487   return save_ruleset_file(sfile, filename);
488 }
489 
490 /**************************************************************************
491   Save cities.ruleset
492 **************************************************************************/
save_cities_ruleset(const char * filename,const char * name)493 static bool save_cities_ruleset(const char *filename, const char *name)
494 {
495   struct section_file *sfile = create_ruleset_file(name, "cities");
496   int sect_idx;
497 
498   if (sfile == NULL) {
499     return FALSE;
500   }
501 
502   sect_idx = 0;
503   specialist_type_iterate(sp) {
504     struct specialist *s = specialist_by_number(sp);
505     char path[512];
506 
507     fc_snprintf(path, sizeof(path), "specialist_%d", sect_idx++);
508 
509     save_name_translation(sfile, &(s->name), path);
510 
511     if (strcmp(rule_name_get(&s->name), rule_name_get(&s->abbreviation))) {
512       secfile_insert_str(sfile, rule_name_get(&s->abbreviation),
513                          "%s.short_name", path);
514     }
515 
516     save_reqs_vector(sfile, &(s->reqs), path, "reqs");
517 
518     if (strcmp(s->graphic_alt, "-")) {
519       secfile_insert_str(sfile, s->graphic_alt, "%s.graphic_alt", path);
520     }
521 
522     save_strvec(sfile, s->helptext, path, "helptext");
523 
524   } specialist_type_iterate_end;
525 
526   if (game.info.celebratesize != GAME_DEFAULT_CELEBRATESIZE) {
527     secfile_insert_int(sfile, game.info.celebratesize,
528                        "parameters.celebrate_size_limit");
529   }
530   if (game.info.add_to_size_limit != GAME_DEFAULT_ADDTOSIZE) {
531     secfile_insert_int(sfile, game.info.add_to_size_limit,
532                        "parameters.add_to_size_limit");
533   }
534   if (game.info.angrycitizen != GAME_DEFAULT_ANGRYCITIZEN) {
535     secfile_insert_bool(sfile, game.info.angrycitizen,
536                        "parameters.angry_citizens");
537   }
538   if (game.info.changable_tax != GAME_DEFAULT_CHANGABLE_TAX) {
539     secfile_insert_bool(sfile, game.info.changable_tax,
540                        "parameters.changable_tax");
541   }
542   if (game.info.forced_science != 0) {
543     secfile_insert_int(sfile, game.info.forced_science,
544                        "parameters.forced_science");
545   }
546   if (game.info.forced_luxury != 100) {
547     secfile_insert_int(sfile, game.info.forced_luxury,
548                        "parameters.forced_luxury");
549   }
550   if (game.info.forced_gold != 0) {
551     secfile_insert_int(sfile, game.info.forced_gold,
552                        "parameters.forced_gold");
553   }
554   if (game.server.vision_reveal_tiles != GAME_DEFAULT_VISION_REVEAL_TILES) {
555     secfile_insert_bool(sfile, game.server.vision_reveal_tiles,
556                        "parameters.vision_reveal_tiles");
557   }
558   if (game.info.pop_report_zeroes != 1) {
559     secfile_insert_int(sfile, game.info.pop_report_zeroes,
560                        "parameters.pop_report_zeroes");
561   }
562   if (game.info.citizen_nationality != GAME_DEFAULT_NATIONALITY) {
563     secfile_insert_bool(sfile, game.info.citizen_nationality,
564                        "citizen.nationality");
565   }
566   if (game.info.citizen_convert_speed != GAME_DEFAULT_CONVERT_SPEED) {
567     secfile_insert_int(sfile, game.info.citizen_convert_speed,
568                        "citizen.convert_speed");
569   }
570   if (game.info.citizen_partisans_pct != 0) {
571     secfile_insert_int(sfile, game.info.citizen_partisans_pct,
572                        "citizen.partisans_pct");
573   }
574 
575   return save_ruleset_file(sfile, filename);
576 }
577 
578 /**************************************************************************
579   Effect saving callback data structure.
580 **************************************************************************/
581 typedef struct {
582   int idx;
583   struct section_file *sfile;
584 } effect_cb_data;
585 
586 /**************************************************************************
587   Save one effect. Callback called for each effect in cache.
588 **************************************************************************/
effect_save(const struct effect * peffect,void * data)589 static bool effect_save(const struct effect *peffect, void *data)
590 {
591   effect_cb_data *cbdata = (effect_cb_data *)data;
592   char path[512];
593 
594   fc_snprintf(path, sizeof(path), "effect_%d", cbdata->idx++);
595 
596   secfile_insert_str(cbdata->sfile,
597                      effect_type_name(peffect->type),
598                      "%s.type", path);
599   secfile_insert_int(cbdata->sfile, peffect->value, "%s.value", path);
600 
601   save_reqs_vector(cbdata->sfile, &peffect->reqs, path, "reqs");
602 
603   return TRUE;
604 }
605 
606 /**************************************************************************
607   Save effects.ruleset
608 **************************************************************************/
save_effects_ruleset(const char * filename,const char * name)609 static bool save_effects_ruleset(const char *filename, const char *name)
610 {
611   struct section_file *sfile = create_ruleset_file(name, "effect");
612   effect_cb_data data;
613 
614   if (sfile == NULL) {
615     return FALSE;
616   }
617 
618   data.idx = 0;
619   data.sfile = sfile;
620 
621   if (!iterate_effect_cache(effect_save, &data)) {
622     return FALSE;
623   }
624 
625   return save_ruleset_file(sfile, filename);
626 }
627 
628 /**************************************************************************
629   Save game.ruleset
630 **************************************************************************/
save_game_ruleset(const char * filename,const char * name)631 static bool save_game_ruleset(const char *filename, const char *name)
632 {
633   struct section_file *sfile = create_ruleset_file(name, "game");
634   int sect_idx;
635   int col_idx;
636   int set_count;
637   enum gameloss_style gs;
638   const char *style_names[32]; /* FIXME: Should determine max length automatically.
639                                 * currently it's 3 (bits 0,1, and 2) so there's plenty of
640                                 * safety margin here. */
641   const char *tnames[game.server.ruledit.named_teams];
642   enum trade_route_type trt;
643   int i;
644   enum gen_action quiet_actions[ACTION_COUNT];
645   bool locks;
646 
647   if (sfile == NULL) {
648     return FALSE;
649   }
650 
651   if (game.server.ruledit.description_file != NULL) {
652     secfile_insert_str(sfile, game.server.ruledit.description_file,
653                        "ruledit.description_file");
654   }
655 
656   if (game.control.preferred_tileset[0] != '\0') {
657     secfile_insert_str(sfile, game.control.preferred_tileset,
658                        "tileset.preferred");
659   }
660   if (game.control.preferred_soundset[0] != '\0') {
661     secfile_insert_str(sfile, game.control.preferred_soundset,
662                        "soundset.preferred");
663   }
664   if (game.control.preferred_musicset[0] != '\0') {
665     secfile_insert_str(sfile, game.control.preferred_musicset,
666                        "musicset.preferred");
667   }
668 
669   secfile_insert_str(sfile, game.control.name, "about.name");
670   secfile_insert_str(sfile, game.control.version, "about.version");
671 
672   if (game.ruleset_summary != NULL) {
673     struct entry *mod_entry;
674 
675     mod_entry = secfile_insert_str(sfile, game.ruleset_summary,
676                                    "about.summary");
677     entry_str_set_gt_marking(mod_entry, TRUE);
678   }
679 
680   if (game.ruleset_description != NULL) {
681     if (game.server.ruledit.description_file == NULL) {
682       secfile_insert_str(sfile, game.ruleset_description,
683                          "about.description");
684     } else {
685       secfile_insert_filereference(sfile, game.server.ruledit.description_file,
686                                    "about.description");
687     }
688   }
689 
690   save_tech_list(sfile, game.rgame.global_init_techs,
691                  "options", "global_init_techs");
692   save_building_list(sfile, game.rgame.global_init_buildings,
693                      "options", "global_init_buildings");
694 
695   save_default_bool(sfile, game.control.popup_tech_help,
696                     FALSE,
697                     "options.popup_tech_help", NULL);
698   save_default_int(sfile, game.info.base_pollution,
699                    RS_DEFAULT_BASE_POLLUTION,
700                    "civstyle.base_pollution", NULL);
701 
702   set_count = 0;
703   for (gs = gameloss_style_begin(); gs != gameloss_style_end(); gs = gameloss_style_next(gs)) {
704     if (game.info.gameloss_style & gs) {
705       style_names[set_count++] = gameloss_style_name(gs);
706     }
707   }
708 
709   if (set_count > 0) {
710     secfile_insert_str_vec(sfile, style_names, set_count,
711                            "civstyle.gameloss_style");
712   }
713 
714   save_default_int(sfile, game.info.happy_cost,
715                    RS_DEFAULT_HAPPY_COST,
716                    "civstyle.happy_cost", NULL);
717   save_default_int(sfile, game.info.food_cost,
718                    RS_DEFAULT_FOOD_COST,
719                    "civstyle.food_cost", NULL);
720   save_default_bool(sfile, game.info.civil_war_enabled,
721                     TRUE,
722                     "civstyle.civil_war_enabled", NULL);
723   save_default_bool(sfile, game.info.paradrop_to_transport,
724                     FALSE,
725                     "civstyle.paradrop_to_transport", NULL);
726   save_default_int(sfile, game.info.base_bribe_cost,
727                    RS_DEFAULT_BASE_BRIBE_COST,
728                    "civstyle.base_bribe_cost", NULL);
729   save_default_int(sfile, game.server.ransom_gold,
730                    RS_DEFAULT_RANSOM_GOLD,
731                    "civstyle.ransom_gold", NULL);
732   save_default_bool(sfile, game.info.pillage_select,
733                     RS_DEFAULT_PILLAGE_SELECT,
734                     "civstyle.pillage_select", NULL);
735   save_default_bool(sfile, game.info.tech_steal_allow_holes,
736                     RS_DEFAULT_TECH_STEAL_HOLES,
737                     "civstyle.tech_steal_allow_holes", NULL);
738   save_default_bool(sfile, game.info.tech_trade_allow_holes,
739                     RS_DEFAULT_TECH_TRADE_HOLES,
740                     "civstyle.tech_trade_allow_holes", NULL);
741   save_default_bool(sfile, game.info.tech_trade_loss_allow_holes,
742                     RS_DEFAULT_TECH_TRADE_LOSS_HOLES,
743                     "civstyle.tech_trade_loss_allow_holes", NULL);
744   save_default_bool(sfile, game.info.tech_parasite_allow_holes,
745                     RS_DEFAULT_TECH_PARASITE_HOLES,
746                     "civstyle.tech_parasite_allow_holes", NULL);
747   save_default_bool(sfile, game.info.tech_loss_allow_holes,
748                     RS_DEFAULT_TECH_LOSS_HOLES,
749                     "civstyle.tech_loss_allow_holes", NULL);
750   save_default_int(sfile, game.server.upgrade_veteran_loss,
751                    RS_DEFAULT_UPGRADE_VETERAN_LOSS,
752                    "civstyle.upgrade_veteran_loss", NULL);
753   save_default_int(sfile, game.server.autoupgrade_veteran_loss,
754                    RS_DEFAULT_UPGRADE_VETERAN_LOSS,
755                    "civstyle.autoupgrade_veteran_loss", NULL);
756 
757   secfile_insert_int_vec(sfile, game.info.granary_food_ini,
758                          game.info.granary_num_inis,
759                          "civstyle.granary_food_ini");
760 
761   save_default_int(sfile, game.info.granary_food_inc,
762                    RS_DEFAULT_GRANARY_FOOD_INC,
763                    "civstyle.granary_food_inc", NULL);
764 
765   output_type_iterate(o) {
766     char buffer[256];
767 
768     fc_snprintf(buffer, sizeof(buffer),
769                 "civstyle.min_city_center_%s",
770                 get_output_identifier(o));
771 
772     save_default_int(sfile, game.info.min_city_center_output[o],
773                      RS_DEFAULT_CITY_CENTER_OUTPUT,
774                      buffer, NULL);
775   } output_type_iterate_end;
776 
777   save_default_int(sfile, game.server.init_vis_radius_sq,
778                    RS_DEFAULT_VIS_RADIUS_SQ,
779                    "civstyle.init_vis_radius_sq", NULL);
780   save_default_int(sfile, game.info.init_city_radius_sq,
781                    RS_DEFAULT_CITY_RADIUS_SQ,
782                    "civstyle.init_city_radius_sq", NULL);
783   if (0 != fc_strcasecmp(gold_upkeep_style_name(game.info.gold_upkeep_style),
784                          RS_DEFAULT_GOLD_UPKEEP_STYLE)) {
785     secfile_insert_str(sfile,
786                        gold_upkeep_style_name(game.info.gold_upkeep_style),
787                        "civstyle.gold_upkeep_style");
788   }
789   save_default_bool(sfile, game.info.illness_on,
790                     RS_DEFAULT_ILLNESS_ON,
791                     "illness.illness_on", NULL);
792   save_default_int(sfile, game.info.illness_base_factor,
793                    RS_DEFAULT_ILLNESS_BASE_FACTOR,
794                    "illness.illness_base_factor", NULL);
795   save_default_int(sfile, game.info.illness_min_size,
796                    RS_DEFAULT_ILLNESS_MIN_SIZE,
797                    "illness.illness_min_size", NULL);
798   save_default_int(sfile, game.info.illness_trade_infection,
799                    RS_DEFAULT_ILLNESS_TRADE_INFECTION_PCT,
800                    "illness.illness_trade_infection", NULL);
801   save_default_int(sfile, game.info.illness_pollution_factor,
802                    RS_DEFAULT_ILLNESS_POLLUTION_PCT,
803                    "illness.illness_pollution_factor", NULL);
804   save_default_int(sfile, game.server.base_incite_cost,
805                    RS_DEFAULT_INCITE_BASE_COST,
806                    "incite_cost.base_incite_cost", NULL);
807   save_default_int(sfile, game.server.incite_improvement_factor,
808                    RS_DEFAULT_INCITE_IMPROVEMENT_FCT,
809                    "incite_cost.improvement_factor", NULL);
810   save_default_int(sfile, game.server.incite_unit_factor,
811                    RS_DEFAULT_INCITE_UNIT_FCT,
812                    "incite_cost.unit_factor", NULL);
813   save_default_int(sfile, game.server.incite_total_factor,
814                    RS_DEFAULT_INCITE_TOTAL_FCT,
815                    "incite_cost.total_factor", NULL);
816   save_default_bool(sfile, game.info.slow_invasions,
817                     RS_DEFAULT_SLOW_INVASIONS,
818                     "global_unit_options.slow_invasions", NULL);
819 
820   save_default_bool(sfile, game.info.force_trade_route,
821                     RS_DEFAULT_FORCE_TRADE_ROUTE,
822                     "actions.force_trade_route", NULL);
823 
824   secfile_insert_str(sfile,
825                      action_by_number(ACTION_SPY_POISON)->ui_name,
826                      "actions.ui_name_poison_city");
827   secfile_insert_str(sfile,
828                      action_by_number(ACTION_SPY_SABOTAGE_UNIT)->ui_name,
829                      "actions.ui_name_sabotage_unit");
830   secfile_insert_str(sfile,
831                      action_by_number(ACTION_SPY_BRIBE_UNIT)->ui_name,
832                      "actions.ui_name_bribe_unit");
833   secfile_insert_str(sfile,
834                      action_by_number(ACTION_SPY_SABOTAGE_CITY)->ui_name,
835                      "actions.ui_name_sabotage_city");
836   secfile_insert_str(sfile,
837                      action_by_number(ACTION_SPY_TARGETED_SABOTAGE_CITY)->ui_name,
838                      "actions.ui_name_targeted_sabotage_city");
839   secfile_insert_str(sfile,
840                      action_by_number(ACTION_SPY_INCITE_CITY)->ui_name,
841                      "actions.ui_name_incite_city");
842   secfile_insert_str(sfile,
843                      action_by_number(ACTION_ESTABLISH_EMBASSY)->ui_name,
844                      "actions.ui_name_establish_embassy");
845   secfile_insert_str(sfile,
846                      action_by_number(ACTION_SPY_STEAL_TECH)->ui_name,
847                      "actions.ui_name_steal_tech");
848   secfile_insert_str(sfile,
849                      action_by_number(ACTION_SPY_TARGETED_STEAL_TECH)->ui_name,
850                      "actions.ui_name_targeted_steal_tech");
851   secfile_insert_str(sfile,
852                      action_by_number(ACTION_SPY_INVESTIGATE_CITY)->ui_name,
853                      "actions.ui_name_investigate_city");
854   secfile_insert_str(sfile,
855                      action_by_number(ACTION_SPY_STEAL_GOLD)->ui_name,
856                      "actions.ui_name_steal_gold");
857   secfile_insert_str(sfile,
858                      action_by_number(ACTION_TRADE_ROUTE)->ui_name,
859                      "actions.ui_name_establish_trade_route");
860   secfile_insert_str(sfile,
861                      action_by_number(ACTION_MARKETPLACE)->ui_name,
862                      "actions.ui_name_enter_marketplace");
863   secfile_insert_str(sfile,
864                      action_by_number(ACTION_HELP_WONDER)->ui_name,
865                      "actions.ui_name_help_wonder");
866 
867   i = 0;
868   action_iterate(act) {
869     if (action_by_number(act)->quiet) {
870       quiet_actions[i] = act;
871       i++;
872     }
873   } action_iterate_end;
874 
875   if (secfile_insert_enum_vec(sfile, &quiet_actions, i, gen_action,
876                               "actions.quiet_actions") != i) {
877     log_error("Didn't save all quiet actions.");
878 
879     return FALSE;
880   }
881 
882   sect_idx = 0;
883   action_enablers_iterate(pae) {
884     char path[512];
885 
886     fc_snprintf(path, sizeof(path), "actionenabler_%d", sect_idx++);
887 
888     secfile_insert_str(sfile, gen_action_name(pae->action),
889                        "%s.action", path);
890 
891     save_reqs_vector(sfile, &(pae->actor_reqs), path, "actor_reqs");
892     save_reqs_vector(sfile, &(pae->target_reqs), path, "target_reqs");
893   } action_enablers_iterate_end;
894 
895   save_default_bool(sfile, game.info.tired_attack,
896                     RS_DEFAULT_TIRED_ATTACK,
897                     "combat_rules.tired_attack", NULL);
898   save_default_int(sfile, game.info.border_city_radius_sq,
899                    RS_DEFAULT_BORDER_RADIUS_SQ_CITY,
900                    "borders.radius_sq_city", NULL);
901   save_default_int(sfile, game.info.border_size_effect,
902                    RS_DEFAULT_BORDER_SIZE_EFFECT,
903                    "borders.size_effect", NULL);
904   save_default_int(sfile, game.info.border_city_permanent_radius_sq,
905                    RS_DEFAULT_BORDER_RADIUS_SQ_CITY_PERMANENT,
906                    "borders.radius_sq_city_permanent", NULL);
907   secfile_insert_str(sfile, tech_cost_style_name(game.info.tech_cost_style),
908                      "research.tech_cost_style");
909   save_default_int(sfile, game.info.base_tech_cost,
910                    RS_DEFAULT_BASE_TECH_COST,
911                    "research.base_tech_cost", NULL);
912   secfile_insert_str(sfile, tech_leakage_style_name(game.info.tech_leakage),
913                      "research.tech_leakage");
914   secfile_insert_str(sfile, tech_upkeep_style_name(game.info.tech_upkeep_style),
915                      "research.tech_upkeep_style");
916   save_default_int(sfile, game.info.tech_upkeep_divider,
917                    RS_DEFAULT_TECH_UPKEEP_DIVIDER,
918                    "research.tech_upkeep_divider", NULL);
919   secfile_insert_str(sfile, free_tech_method_name(game.info.free_tech_method),
920                      "research.free_tech_method");
921 
922   save_default_int(sfile, game.info.culture_vic_points,
923                    RS_DEFAULT_CULTURE_VIC_POINTS,
924                    "culture.victory_min_points", NULL);
925   save_default_int(sfile, game.info.culture_vic_lead,
926                    RS_DEFAULT_CULTURE_VIC_LEAD,
927                    "culture.victory_lead_pct", NULL);
928   save_default_int(sfile, game.info.culture_migration_pml,
929                    RS_DEFAULT_CULTURE_MIGRATION_PML,
930                    "culture.migration_pml", NULL);
931 
932   save_default_bool(sfile, game.info.calendar_skip_0,
933                     RS_DEFAULT_CALENDAR_SKIP_0,
934                     "calendar.skip_year_0", NULL);
935   save_default_int(sfile, game.server.start_year,
936                    GAME_DEFAULT_START_YEAR,
937                    "calendar.start_year", NULL);
938   save_default_int(sfile, game.info.calendar_fragments,
939                    0, "calendar.fragments", NULL);
940 
941   for (i = 0; i < MAX_CALENDAR_FRAGMENTS; i++) {
942     if (game.info.calendar_fragment_name[i][0] != '\0') {
943       secfile_insert_str(sfile, game.info.calendar_fragment_name[i],
944                          "calendar.fragment_name%d", i);
945     }
946   }
947 
948   if (strcmp(game.info.positive_year_label, RS_DEFAULT_POS_YEAR_LABEL)) {
949     secfile_insert_str(sfile, game.info.positive_year_label,
950                        "calendar.positive_label");
951   }
952   if (strcmp(game.info.negative_year_label, RS_DEFAULT_NEG_YEAR_LABEL)) {
953     secfile_insert_str(sfile, game.info.negative_year_label,
954                        "calendar.negative_label");
955   }
956 
957   if (game.plr_bg_color != NULL) {
958     rgbcolor_save(sfile, game.plr_bg_color, "playercolors.background");
959   }
960 
961   col_idx = 0;
962   rgbcolor_list_iterate(game.server.plr_colors, pcol) {
963     rgbcolor_save(sfile, pcol, "playercolors.colorlist%d", col_idx++);
964   } rgbcolor_list_iterate_end;
965 
966 
967   if (game.server.ruledit.named_teams > 0) {
968     for (i = 0; i < game.server.ruledit.named_teams; i++) {
969       tnames[i] = team_slot_rule_name(team_slot_by_number(i));
970     }
971 
972     secfile_insert_str_vec(sfile, tnames,
973                            game.server.ruledit.named_teams,
974                            "teams.names");
975   }
976 
977   sect_idx = 0;
978   disaster_type_iterate(pd) {
979     char path[512];
980     enum disaster_effect_id de;
981     const char *effect_names[DE_COUNT];
982 
983     fc_snprintf(path, sizeof(path), "disaster_%d", sect_idx++);
984 
985     save_name_translation(sfile, &(pd->name), path);
986     save_reqs_vector(sfile, &(pd->reqs), path, "reqs");
987     if (pd->frequency != GAME_DEFAULT_DISASTER_FREQ) {
988       secfile_insert_int(sfile, pd->frequency,
989                          "%s.frequency", path);
990     }
991 
992     set_count = 0;
993     for (de = disaster_effect_id_begin();
994          de != disaster_effect_id_end();
995          de = disaster_effect_id_next(de)) {
996       if (BV_ISSET(pd->effects, de)) {
997         effect_names[set_count++] = disaster_effect_id_name(de);
998       }
999     }
1000 
1001     if (set_count > 0) {
1002       secfile_insert_str_vec(sfile, effect_names, set_count,
1003                              "%s.effects", path);
1004     }
1005   } disaster_type_iterate_end;
1006 
1007   sect_idx = 0;
1008   achievements_iterate(pach) {
1009     char path[512];
1010 
1011     fc_snprintf(path, sizeof(path), "achievement_%d", sect_idx++);
1012 
1013     save_name_translation(sfile, &(pach->name), path);
1014 
1015     secfile_insert_str(sfile, achievement_type_name(pach->type),
1016                        "%s.type", path);
1017 
1018     save_default_bool(sfile, pach->unique,
1019                       GAME_DEFAULT_ACH_UNIQUE,
1020                       path, "unique");
1021     save_default_int(sfile, pach->value,
1022                      GAME_DEFAULT_ACH_VALUE,
1023                      path, "value");
1024     save_default_int(sfile, pach->culture,
1025                      0, path, "culture");
1026 
1027     secfile_insert_str(sfile, pach->first_msg, "%s.first_msg", path);
1028     if (pach->cons_msg != NULL) {
1029       secfile_insert_str(sfile, pach->cons_msg, "%s.cons_msg", path);
1030     }
1031 
1032   } achievements_iterate_end;
1033 
1034   set_count = 0;
1035   for (trt = 0; trt < TRT_LAST; trt++) {
1036     struct trade_route_settings *set = trade_route_settings_by_type(trt);
1037     const char *cancelling = traderoute_cancelling_type_name(set->cancelling);
1038 
1039     if (set->trade_pct != 100 || strcmp(cancelling, "Active")) {
1040       char path[256];
1041 
1042       fc_snprintf(path, sizeof(path),
1043                   "trade.settings%d", set_count++);
1044 
1045       secfile_insert_str(sfile, trade_route_type_name(trt),
1046                          "%s.type", path);
1047       secfile_insert_int(sfile, set->trade_pct,
1048                          "%s.pct", path);
1049       secfile_insert_str(sfile, cancelling,
1050                          "%s.cancelling", path);
1051       secfile_insert_str(sfile, traderoute_bonus_type_name(set->bonus_type),
1052                          "%s.bonus", path);
1053     }
1054   }
1055 
1056   locks = FALSE;
1057   settings_iterate(SSET_ALL, pset) {
1058     if (setting_locked(pset)) {
1059       locks = TRUE;
1060       break;
1061     }
1062   } settings_iterate_end;
1063 
1064   set_count = 0;
1065   settings_iterate(SSET_ALL, pset) {
1066     if (setting_get_setdef(pset) == SETDEF_RULESET || setting_locked(pset)) {
1067       secfile_insert_str(sfile, setting_name(pset),
1068                          "settings.set%d.name", set_count);
1069       switch (setting_type(pset)) {
1070       case SSET_BOOL:
1071         secfile_insert_bool(sfile, setting_bool_get(pset),
1072                             "settings.set%d.value", set_count);
1073         break;
1074       case SSET_INT:
1075         secfile_insert_int(sfile, setting_int_get(pset),
1076                            "settings.set%d.value", set_count);
1077         break;
1078       case SSET_STRING:
1079         secfile_insert_str(sfile, setting_str_get(pset),
1080                            "settings.set%d.value", set_count);
1081         break;
1082       case SSET_ENUM:
1083         secfile_insert_enum_data(sfile, read_enum_value(pset), FALSE,
1084                                  setting_enum_secfile_str, pset,
1085                                  "settings.set%d.value", set_count);
1086         break;
1087       case SSET_BITWISE:
1088         secfile_insert_enum_data(sfile, setting_bitwise_get(pset), TRUE,
1089                                  setting_bitwise_secfile_str, pset,
1090                                  "settings.set%d.value", set_count);
1091         break;
1092       }
1093 
1094       if (locks) {
1095         secfile_insert_bool(sfile, setting_locked(pset),
1096                             "settings.set%d.lock", set_count);
1097       }
1098 
1099       set_count++;
1100     }
1101   } settings_iterate_end;
1102 
1103   return save_ruleset_file(sfile, filename);
1104 }
1105 
1106 /**************************************************************************
1107   Save governments.ruleset
1108 **************************************************************************/
save_governments_ruleset(const char * filename,const char * name)1109 static bool save_governments_ruleset(const char *filename, const char *name)
1110 {
1111   struct section_file *sfile = create_ruleset_file(name, "government");
1112   int sect_idx;
1113 
1114   if (sfile == NULL) {
1115     return FALSE;
1116   }
1117 
1118   save_gov_ref(sfile, game.government_during_revolution, "governments",
1119                "during_revolution");
1120 
1121   sect_idx = 0;
1122   governments_iterate(pg) {
1123     char path[512];
1124     struct ruler_title *prtitle;
1125 
1126     fc_snprintf(path, sizeof(path), "government_%d", sect_idx++);
1127 
1128     save_name_translation(sfile, &(pg->name), path);
1129 
1130     secfile_insert_str(sfile, pg->graphic_str, "%s.graphic", path);
1131     secfile_insert_str(sfile, pg->graphic_alt, "%s.graphic_alt", path);
1132 
1133     save_reqs_vector(sfile, &(pg->reqs), path, "reqs");
1134 
1135     if (pg->ai.better != NULL) {
1136       save_gov_ref(sfile, pg->ai.better, path,
1137                    "ai_better");
1138     }
1139 
1140     ruler_title_hash_lookup(pg->ruler_titles, NULL,
1141                             &prtitle);
1142     if (prtitle != NULL) {
1143       const char *title;
1144 
1145       title = ruler_title_male_untranslated_name(prtitle);
1146       if (title != NULL) {
1147         secfile_insert_str(sfile, title,
1148                            "%s.ruler_male_title", path);
1149       }
1150 
1151       title = ruler_title_female_untranslated_name(prtitle);
1152       if (title != NULL) {
1153         secfile_insert_str(sfile, title,
1154                            "%s.ruler_female_title", path);
1155       }
1156     }
1157 
1158     save_strvec(sfile, pg->helptext, path, "helptext");
1159 
1160   } governments_iterate_end;
1161 
1162   sect_idx = 0;
1163   multipliers_iterate(pmul) {
1164     char path[512];
1165 
1166     fc_snprintf(path, sizeof(path), "multiplier_%d", sect_idx++);
1167 
1168     save_name_translation(sfile, &(pmul->name), path);
1169 
1170     secfile_insert_int(sfile, pmul->start, "%s.start", path);
1171     secfile_insert_int(sfile, pmul->stop, "%s.stop", path);
1172     secfile_insert_int(sfile, pmul->step, "%s.step", path);
1173     secfile_insert_int(sfile, pmul->def, "%s.default", path);
1174 
1175     save_default_int(sfile, pmul->offset, 0, path, "offset");
1176     save_default_int(sfile, pmul->factor, 100, path, "factor");
1177 
1178     save_strvec(sfile, pmul->helptext, path, "helptext");
1179   } multipliers_iterate_end;
1180 
1181   return save_ruleset_file(sfile, filename);
1182 }
1183 
1184 /**************************************************************************
1185   Save list of AI traits
1186 **************************************************************************/
save_traits(struct trait_limits * traits,struct trait_limits * default_traits,struct section_file * sfile,const char * secname,const char * field_prefix)1187 static bool save_traits(struct trait_limits *traits,
1188                         struct trait_limits *default_traits,
1189                         struct section_file *sfile,
1190                         const char *secname, const char *field_prefix)
1191 {
1192   enum trait tr;
1193 
1194  /* FIXME: Use specenum trait names without duplicating them here.
1195    *        Just needs to take care of case. */
1196   const char *trait_names[] = {
1197     "expansionist",
1198     "trader",
1199     "aggressive",
1200     NULL
1201   };
1202 
1203   for (tr = trait_begin(); tr != trait_end() && trait_names[tr] != NULL;
1204        tr = trait_next(tr)) {
1205     int default_default;
1206 
1207     default_default = (traits[tr].min + traits[tr].max) / 2;
1208 
1209     if ((default_traits == NULL && traits[tr].min != TRAIT_DEFAULT_VALUE)
1210         || (default_traits != NULL && traits[tr].min != default_traits[tr].min)) {
1211       secfile_insert_int(sfile, traits[tr].min, "%s.%s%s_min", secname, field_prefix,
1212                          trait_names[tr]);
1213     }
1214     if ((default_traits == NULL && traits[tr].max != TRAIT_DEFAULT_VALUE)
1215         || (default_traits != NULL && traits[tr].max != default_traits[tr].max)) {
1216       secfile_insert_int(sfile, traits[tr].max, "%s.%s%s_max", secname, field_prefix,
1217                          trait_names[tr]);
1218     }
1219     if (default_default != traits[tr].fixed) {
1220       secfile_insert_int(sfile, traits[tr].fixed, "%s.%s%s_default", secname, field_prefix,
1221                          trait_names[tr]);
1222     }
1223   }
1224 
1225   return TRUE;
1226 }
1227 
1228 /**************************************************************************
1229   Save a single nation.
1230 **************************************************************************/
save_nation(struct section_file * sfile,struct nation_type * pnat,int sect_idx)1231 static bool save_nation(struct section_file *sfile, struct nation_type *pnat,
1232                         int sect_idx)
1233 {
1234   char path[512];
1235   int max_items = nation_city_list_size(pnat->server.default_cities);
1236   char *city_str[max_items];
1237   max_items = MAX(max_items, MAX_NUM_NATION_SETS + MAX_NUM_NATION_GROUPS);
1238   max_items = MAX(max_items, game.control.nation_count);
1239   const char *list_items[max_items];
1240   int set_count;
1241   int subsect_idx;
1242 
1243   fc_snprintf(path, sizeof(path), "nation_%d", sect_idx++);
1244 
1245   if (pnat->translation_domain == NULL) {
1246     secfile_insert_str(sfile, "freeciv", "%s.translation_domain", path);
1247   } else {
1248     secfile_insert_str(sfile, pnat->translation_domain, "%s.translation_domain", path);
1249   }
1250 
1251   save_name_translation(sfile, &(pnat->adjective), path);
1252   secfile_insert_str(sfile, untranslated_name(&(pnat->noun_plural)), "%s.plural", path);
1253 
1254   set_count = 0;
1255   nation_sets_iterate(pset) {
1256     if (nation_is_in_set(pnat, pset)) {
1257       list_items[set_count++] = nation_set_rule_name(pset);
1258     }
1259   } nation_sets_iterate_end;
1260   nation_groups_iterate(pgroup) {
1261     if (nation_is_in_group(pnat, pgroup)) {
1262       list_items[set_count++] = nation_group_rule_name(pgroup);
1263     }
1264   } nation_groups_iterate_end;
1265 
1266   if (set_count > 0) {
1267     secfile_insert_str_vec(sfile, list_items, set_count, "%s.groups", path);
1268   }
1269 
1270   set_count = 0;
1271   nation_list_iterate(pnat->server.conflicts_with, pconfl) {
1272     list_items[set_count++] = nation_rule_name(pconfl);
1273   } nation_list_iterate_end;
1274   if (set_count > 0) {
1275     secfile_insert_str_vec(sfile, list_items, set_count, "%s.conflicts_with", path);
1276   }
1277 
1278   subsect_idx = 0;
1279   nation_leader_list_iterate(pnat->leaders, pleader) {
1280     secfile_insert_str(sfile, nation_leader_name(pleader), "%s.leaders%d.name",
1281                        path, subsect_idx);
1282     secfile_insert_str(sfile, nation_leader_is_male(pleader) ? "Male" : "Female",
1283                        "%s.leaders%d.sex", path, subsect_idx++);
1284   } nation_leader_list_iterate_end;
1285 
1286   if (pnat->server.rgb != NULL) {
1287     rgbcolor_save(sfile, pnat->server.rgb, "%s.color", path);
1288   }
1289 
1290   save_traits(pnat->server.traits, game.server.default_traits,
1291               sfile, path, "trait_");
1292 
1293   if (!pnat->is_playable) {
1294     secfile_insert_bool(sfile, pnat->is_playable, "%s.is_playable", path);
1295   }
1296 
1297   if (pnat->barb_type != NOT_A_BARBARIAN) {
1298     secfile_insert_str(sfile, barbarian_type_name(pnat->barb_type),
1299                        "%s.barbarian_type", path);
1300   }
1301 
1302   if (strcmp(pnat->flag_graphic_str, "-")) {
1303     secfile_insert_str(sfile, pnat->flag_graphic_str, "%s.flag", path);
1304   }
1305   if (strcmp(pnat->flag_graphic_alt, "-")) {
1306     secfile_insert_str(sfile, pnat->flag_graphic_alt, "%s.flag_alt", path);
1307   }
1308 
1309   subsect_idx = 0;
1310   governments_iterate(pgov) {
1311     struct ruler_title *prtitle;
1312 
1313     if (ruler_title_hash_lookup(pgov->ruler_titles, pnat, &prtitle)) {
1314       secfile_insert_str(sfile, government_rule_name(pgov),
1315                          "%s.ruler_titles%d.government", path, subsect_idx);
1316       secfile_insert_str(sfile, ruler_title_male_untranslated_name(prtitle),
1317                          "%s.ruler_titles%d.male_title", path, subsect_idx);
1318       secfile_insert_str(sfile, ruler_title_female_untranslated_name(prtitle),
1319                          "%s.ruler_titles%d.female_title", path, subsect_idx++);
1320     }
1321   } governments_iterate_end;
1322 
1323   secfile_insert_str(sfile, style_rule_name(pnat->style), "%s.style", path);
1324 
1325   set_count = 0;
1326   nation_list_iterate(pnat->server.civilwar_nations, pconfl) {
1327     list_items[set_count++] = nation_rule_name(pconfl);
1328   } nation_list_iterate_end;
1329   if (set_count > 0) {
1330     secfile_insert_str_vec(sfile, list_items, set_count, "%s.civilwar_nations", path);
1331   }
1332 
1333   save_tech_list(sfile, pnat->init_techs, path, "init_techs");
1334   save_building_list(sfile, pnat->init_buildings, path, "init_buildings");
1335   save_unit_list(sfile, pnat->init_units, path, "init_units");
1336 
1337   if (pnat->init_government) {
1338     secfile_insert_str(sfile, government_rule_name(pnat->init_government),
1339                        "%s.init_government", path);
1340   }
1341 
1342   set_count = 0;
1343   nation_city_list_iterate(pnat->server.default_cities, pncity) {
1344     bool list_started = FALSE;
1345 
1346     city_str[set_count] = fc_malloc(strlen(nation_city_name(pncity)) + strlen(" (!river")
1347                                     + strlen(")")
1348                                     + MAX_NUM_TERRAINS * (strlen(", ") + MAX_LEN_NAME));
1349 
1350     strcpy(city_str[set_count], nation_city_name(pncity));
1351     switch(nation_city_river_preference(pncity)) {
1352     case NCP_DISLIKE:
1353       strcat(city_str[set_count], " (!river");
1354       list_started = TRUE;
1355       break;
1356     case NCP_LIKE:
1357       strcat(city_str[set_count], " (river");
1358       list_started = TRUE;
1359       break;
1360     case NCP_NONE:
1361       break;
1362     }
1363 
1364     terrain_type_iterate(pterr) {
1365       const char *pref = NULL;
1366 
1367       switch(nation_city_terrain_preference(pncity, pterr)) {
1368       case NCP_DISLIKE:
1369         pref = "!";
1370         break;
1371       case NCP_LIKE:
1372         pref = "";
1373         break;
1374       case NCP_NONE:
1375         pref = NULL;
1376         break;
1377       }
1378 
1379       if (pref != NULL) {
1380         if (list_started) {
1381           strcat(city_str[set_count], ", ");
1382         } else {
1383           strcat(city_str[set_count], " (");
1384           list_started = TRUE;
1385         }
1386         strcat(city_str[set_count], pref);
1387         strcat(city_str[set_count], terrain_rule_name(pterr));
1388       }
1389 
1390     } terrain_type_iterate_end;
1391 
1392     if (list_started) {
1393       strcat(city_str[set_count], ")");
1394     }
1395 
1396     list_items[set_count] = city_str[set_count];
1397     set_count++;
1398   } nation_city_list_iterate_end;
1399   if (set_count > 0) {
1400     int i;
1401 
1402     secfile_insert_str_vec(sfile, list_items, set_count, "%s.cities", path);
1403 
1404     for (i = 0; i < set_count; i++) {
1405       FC_FREE(city_str[i]);
1406     }
1407   }
1408 
1409   secfile_insert_str(sfile, pnat->legend, "%s.legend", path);
1410 
1411   return TRUE;
1412 }
1413 
1414 /**************************************************************************
1415   Save nations.ruleset
1416 **************************************************************************/
save_nations_ruleset(const char * filename,const char * name,struct rule_data * data)1417 static bool save_nations_ruleset(const char *filename, const char *name,
1418                                  struct rule_data *data)
1419 {
1420   struct section_file *sfile = create_ruleset_file(name, "nation");
1421 
1422   if (sfile == NULL) {
1423     return FALSE;
1424   }
1425 
1426   if (data->nationlist != NULL) {
1427     secfile_insert_str(sfile, data->nationlist, "ruledit.nationlist");
1428   }
1429   if (game.server.ruledit.embedded_nations != NULL) {
1430     int i;
1431     const char **tmp = fc_malloc(game.server.ruledit.embedded_nations_count * sizeof(char *));
1432 
1433     /* Dance around the secfile_insert_str_vec() parameter type (requires extra const)
1434      * resrictions */
1435     for (i = 0; i < game.server.ruledit.embedded_nations_count; i++) {
1436       tmp[i] = game.server.ruledit.embedded_nations[i];
1437     }
1438 
1439     secfile_insert_str_vec(sfile, tmp,
1440                            game.server.ruledit.embedded_nations_count,
1441                            "ruledit.embedded_nations");
1442     free(tmp);
1443   }
1444 
1445   save_traits(game.server.default_traits, NULL, sfile,
1446               "default_traits", "");
1447 
1448   if (data->nationlist == NULL) {
1449     if (game.server.ruledit.allowed_govs != NULL) {
1450       secfile_insert_str_vec(sfile, game.server.ruledit.allowed_govs,
1451                              game.server.ruledit.ag_count,
1452                              "compatibility.allowed_govs");
1453     }
1454     if (game.server.ruledit.allowed_terrains != NULL) {
1455       secfile_insert_str_vec(sfile, game.server.ruledit.allowed_terrains,
1456                              game.server.ruledit.at_count,
1457                              "compatibility.allowed_terrains");
1458     }
1459     if (game.server.ruledit.allowed_styles != NULL) {
1460       secfile_insert_str_vec(sfile, game.server.ruledit.allowed_styles,
1461                              game.server.ruledit.as_count,
1462                              "compatibility.allowed_styles");
1463     }
1464   }
1465 
1466   if (game.default_government != NULL) {
1467     secfile_insert_str(sfile, government_rule_name(game.default_government),
1468                        "compatibility.default_government");
1469   }
1470 
1471   if (data->nationlist != NULL) {
1472     secfile_insert_include(sfile, data->nationlist);
1473 
1474     if (game.server.ruledit.embedded_nations != NULL) {
1475       int sect_idx;
1476 
1477       for (sect_idx = 0; sect_idx < game.server.ruledit.embedded_nations_count;
1478            sect_idx++) {
1479         struct nation_type *pnat
1480           = nation_by_rule_name(game.server.ruledit.embedded_nations[sect_idx]);
1481 
1482         if (pnat == NULL) {
1483           log_error("Embedded nation \"%s\" not found!",
1484                     game.server.ruledit.embedded_nations[sect_idx]);
1485         } else {
1486           save_nation(sfile, pnat, sect_idx);
1487         }
1488       }
1489     }
1490   } else {
1491     int sect_idx = 0;
1492 
1493     nation_sets_iterate(pset) {
1494       char path[512];
1495 
1496       fc_snprintf(path, sizeof(path), "nset_%d", sect_idx++);
1497 
1498       /* We don't use save_name_translation() for this as name and rule_name must
1499        * always be saved separately */
1500       secfile_insert_str(sfile, nation_set_untranslated_name(pset), "%s.name", path);
1501       secfile_insert_str(sfile, nation_set_rule_name(pset), "%s.rule_name", path);
1502       secfile_insert_str(sfile, nation_set_description(pset), "%s.description", path);
1503     } nation_sets_iterate_end;
1504 
1505     sect_idx = 0;
1506     nation_groups_iterate(pgroup) {
1507       char path[512];
1508 
1509       fc_snprintf(path, sizeof(path), "ngroup_%d", sect_idx++);
1510 
1511       save_name_translation(sfile, &(pgroup->name), path);
1512 
1513       secfile_insert_int(sfile, pgroup->server.match, "%s.match", path);
1514       if (pgroup->hidden) {
1515         secfile_insert_bool(sfile, pgroup->hidden, "%s.hidden", path);
1516       }
1517     } nation_groups_iterate_end;
1518 
1519     sect_idx = 0;
1520     nations_iterate(pnat) {
1521       save_nation(sfile, pnat, sect_idx++);
1522     } nations_iterate_end;
1523   }
1524 
1525   return save_ruleset_file(sfile, filename);
1526 }
1527 
1528 /**************************************************************************
1529   Save techs.ruleset
1530 **************************************************************************/
save_techs_ruleset(const char * filename,const char * name)1531 static bool save_techs_ruleset(const char *filename, const char *name)
1532 {
1533   struct section_file *sfile = create_ruleset_file(name, "tech");
1534   int i;
1535   int sect_idx;
1536   struct advance *a_none = advance_by_number(A_NONE);
1537 
1538 
1539   if (sfile == NULL) {
1540     return FALSE;
1541   }
1542 
1543   for (i = 0; i < MAX_NUM_USER_TECH_FLAGS; i++) {
1544     const char *flagname = tech_flag_id_name_cb(i + TECH_USER_1);
1545     const char *helptxt = tech_flag_helptxt(i + TECH_USER_1);
1546 
1547     if (flagname != NULL) {
1548       secfile_insert_str(sfile, flagname, "control.flags%d.name", i);
1549 
1550       /* Save the user flag help text even when it is undefined. That makes
1551        * the formatting code happy. The resulting "" is ignored when the
1552        * ruleset is loaded. */
1553       secfile_insert_str(sfile, helptxt, "control.flags%d.helptxt", i);
1554     }
1555   }
1556 
1557   sect_idx = 0;
1558   advance_active_iterate(pa) {
1559     if (pa->require[AR_ONE] != A_NEVER) {
1560       char path[512];
1561       const char *flag_names[TF_COUNT];
1562       int set_count;
1563       int flagi;
1564 
1565       fc_snprintf(path, sizeof(path), "advance_%d", sect_idx++);
1566 
1567       save_name_translation(sfile, &(pa->name), path);
1568 
1569       save_tech_ref(sfile, pa->require[AR_ONE], path, "req1");
1570       save_tech_ref(sfile, pa->require[AR_TWO], path, "req2");
1571       if (pa->require[AR_ROOT] != a_none && !pa->inherited_root_req) {
1572         save_tech_ref(sfile, pa->require[AR_ROOT], path, "root_req");
1573       }
1574 
1575       secfile_insert_str(sfile, pa->graphic_str, "%s.graphic", path);
1576       if (strcmp("-", pa->graphic_alt)) {
1577         secfile_insert_str(sfile, pa->graphic_alt, "%s.graphic_alt", path);
1578       }
1579       if (pa->bonus_message != NULL) {
1580         secfile_insert_str(sfile, pa->bonus_message, "%s.bonus_message", path);
1581       }
1582 
1583       set_count = 0;
1584       for (flagi = 0; flagi < TF_COUNT; flagi++) {
1585         if (advance_has_flag(advance_index(pa), flagi)) {
1586           flag_names[set_count++] = tech_flag_id_name(flagi);
1587         }
1588       }
1589 
1590       if (set_count > 0) {
1591         secfile_insert_str_vec(sfile, flag_names, set_count,
1592                                "%s.flags", path);
1593       }
1594       if (pa->cost >= 0) {
1595         secfile_insert_int(sfile, pa->cost, "%s.cost", path);
1596       }
1597 
1598       save_strvec(sfile, pa->helptext, path, "helptext");
1599     }
1600 
1601   } advance_active_iterate_end;
1602 
1603   return save_ruleset_file(sfile, filename);
1604 }
1605 
1606 /**************************************************************************
1607   Save terrain.ruleset
1608 **************************************************************************/
save_terrain_ruleset(const char * filename,const char * name)1609 static bool save_terrain_ruleset(const char *filename, const char *name)
1610 {
1611   struct section_file *sfile = create_ruleset_file(name, "terrain");
1612   int sect_idx;
1613   int i;
1614 
1615   if (sfile == NULL) {
1616     return FALSE;
1617   }
1618 
1619   for (i = 0; i < MAX_NUM_USER_TER_FLAGS; i++) {
1620     const char *flagname = terrain_flag_id_name_cb(i + TER_USER_1);
1621     const char *helptxt = terrain_flag_helptxt(i + TER_USER_1);
1622 
1623     if (flagname != NULL) {
1624       secfile_insert_str(sfile, flagname, "control.flags%d.name", i);
1625 
1626       /* Save the user flag help text even when it is undefined. That makes
1627        * the formatting code happy. The resulting "" is ignored when the
1628        * ruleset is loaded. */
1629       secfile_insert_str(sfile, helptxt, "control.flags%d.helptxt", i);
1630     }
1631   }
1632 
1633   if (terrain_control.ocean_reclaim_requirement_pct <= 100) {
1634     secfile_insert_int(sfile, terrain_control.ocean_reclaim_requirement_pct,
1635                        "parameters.ocean_reclaim_requirement");
1636   }
1637   if (terrain_control.land_channel_requirement_pct <= 100) {
1638     secfile_insert_int(sfile, terrain_control.land_channel_requirement_pct,
1639                        "parameters.land_channel_requirement");
1640   }
1641   if (terrain_control.terrain_thaw_requirement_pct <= 100) {
1642     secfile_insert_int(sfile, terrain_control.terrain_thaw_requirement_pct,
1643                        "parameters.thaw_requirement");
1644   }
1645   if (terrain_control.terrain_freeze_requirement_pct <= 100) {
1646     secfile_insert_int(sfile, terrain_control.terrain_freeze_requirement_pct,
1647                        "parameters.freeze_requirement");
1648   }
1649   if (terrain_control.lake_max_size != 0) {
1650     secfile_insert_int(sfile, terrain_control.lake_max_size,
1651                        "parameters.lake_max_size");
1652   }
1653   if (terrain_control.min_start_native_area != 0) {
1654     secfile_insert_int(sfile, terrain_control.min_start_native_area,
1655                        "parameters.min_start_native_area");
1656   }
1657   if (terrain_control.move_fragments != 3) {
1658     secfile_insert_int(sfile, terrain_control.move_fragments,
1659                        "parameters.move_fragments");
1660   }
1661   if (terrain_control.igter_cost != 1) {
1662     secfile_insert_int(sfile, terrain_control.igter_cost,
1663                        "parameters.igter_cost");
1664   }
1665   if (terrain_control.pythagorean_diagonal != RS_DEFAULT_PYTHAGOREAN_DIAGONAL) {
1666     secfile_insert_bool(sfile, terrain_control.pythagorean_diagonal,
1667                         "parameters.pythagorean_diagonal");
1668   }
1669   if (game.map.server.ocean_resources) {
1670     secfile_insert_bool(sfile, TRUE,
1671                        "parameters.ocean_resources");
1672   }
1673 
1674   sect_idx = 0;
1675   terrain_type_iterate(pterr) {
1676     char path[512];
1677     char identifier[2];
1678     int r;
1679     const char *flag_names[TER_USER_LAST];
1680     const char *puc_names[UCL_LAST];
1681     int flagi;
1682     int set_count;
1683 
1684     fc_snprintf(path, sizeof(path), "terrain_%d", sect_idx++);
1685 
1686     save_name_translation(sfile, &(pterr->name), path);
1687 
1688     secfile_insert_str(sfile, pterr->graphic_str, "%s.graphic", path);
1689     secfile_insert_str(sfile, pterr->graphic_alt, "%s.graphic_alt", path);
1690     identifier[0] = pterr->identifier;
1691     identifier[1] = '\0';
1692     secfile_insert_str(sfile, identifier, "%s.identifier", path);
1693 
1694     secfile_insert_str(sfile, terrain_class_name(pterr->tclass),
1695                        "%s.class", path);
1696 
1697     secfile_insert_int(sfile, pterr->movement_cost, "%s.movement_cost", path);
1698     secfile_insert_int(sfile, pterr->defense_bonus, "%s.defense_bonus", path);
1699 
1700     output_type_iterate(o) {
1701       if (pterr->output[o] != 0) {
1702         secfile_insert_int(sfile, pterr->output[o], "%s.%s", path,
1703                            get_output_identifier(o));
1704       }
1705     } output_type_iterate_end;
1706 
1707     /* Check resource count */
1708     for (r = 0; pterr->resources[r] != NULL; r++) {
1709       /* Just increasing r as long as there is resources */
1710     }
1711 
1712     {
1713       const char *resource_names[r];
1714 
1715       for (r = 0; pterr->resources[r] != NULL; r++) {
1716         resource_names[r] = resource_rule_name(pterr->resources[r]);
1717       }
1718 
1719       secfile_insert_str_vec(sfile, resource_names, r,
1720                              "%s.resources", path);
1721     }
1722 
1723     output_type_iterate(o) {
1724       if (pterr->road_output_incr_pct[o] != 0) {
1725         secfile_insert_int(sfile, pterr->road_output_incr_pct[o],
1726                            "%s.road_%s_incr_pct", path,
1727                            get_output_identifier(o));
1728       }
1729     } output_type_iterate_end;
1730 
1731     secfile_insert_int(sfile, pterr->base_time, "%s.base_time", path);
1732     secfile_insert_int(sfile, pterr->road_time, "%s.road_time", path);
1733 
1734     save_terrain_ref(sfile, pterr->irrigation_result, pterr, path,
1735                      "irrigation_result");
1736     secfile_insert_int(sfile, pterr->irrigation_food_incr,
1737                        "%s.irrigation_food_incr", path);
1738     secfile_insert_int(sfile, pterr->irrigation_time,
1739                        "%s.irrigation_time", path);
1740 
1741     save_terrain_ref(sfile, pterr->mining_result, pterr, path,
1742                      "mining_result");
1743     secfile_insert_int(sfile, pterr->mining_shield_incr,
1744                        "%s.mining_shield_incr", path);
1745     secfile_insert_int(sfile, pterr->mining_time,
1746                        "%s.mining_time", path);
1747 
1748     save_terrain_ref(sfile, pterr->transform_result, pterr, path,
1749                      "transform_result");
1750     secfile_insert_int(sfile, pterr->transform_time,
1751                        "%s.transform_time", path);
1752 
1753     if (pterr->animal != NULL) {
1754       secfile_insert_str(sfile, utype_rule_name(pterr->animal),
1755                          "%s.animal", path);
1756     } else {
1757       secfile_insert_str(sfile, "None",
1758                          "%s.animal", path);
1759     }
1760 
1761     secfile_insert_int(sfile, pterr->pillage_time,
1762                        "%s.pillage_time", path);
1763     secfile_insert_int(sfile, pterr->clean_pollution_time,
1764                        "%s.clean_pollution_time", path);
1765     secfile_insert_int(sfile, pterr->clean_fallout_time,
1766                        "%s.clean_fallout_time", path);
1767 
1768     save_terrain_ref(sfile, pterr->warmer_wetter_result, pterr, path,
1769                      "warmer_wetter_result");
1770     save_terrain_ref(sfile, pterr->warmer_drier_result, pterr, path,
1771                      "warmer_drier_result");
1772     save_terrain_ref(sfile, pterr->cooler_wetter_result, pterr, path,
1773                      "cooler_wetter_result");
1774     save_terrain_ref(sfile, pterr->cooler_drier_result, pterr, path,
1775                      "cooler_drier_result");
1776 
1777     set_count = 0;
1778     for (flagi = 0; flagi < TER_USER_LAST; flagi++) {
1779       if (terrain_has_flag(pterr, flagi)) {
1780         flag_names[set_count++] = terrain_flag_id_name(flagi);
1781       }
1782     }
1783 
1784     if (set_count > 0) {
1785       secfile_insert_str_vec(sfile, flag_names, set_count,
1786                              "%s.flags", path);
1787     }
1788 
1789     {
1790       enum mapgen_terrain_property mtp;
1791 
1792       for (mtp = mapgen_terrain_property_begin();
1793            mtp != mapgen_terrain_property_end();
1794            mtp = mapgen_terrain_property_next(mtp)) {
1795         if (pterr->property[mtp] != 0) {
1796           secfile_insert_int(sfile, pterr->property[mtp],
1797                              "%s.property_%s", path,
1798                              mapgen_terrain_property_name(mtp));
1799         }
1800       }
1801     }
1802 
1803     set_count = 0;
1804     unit_class_iterate(puc) {
1805       if (BV_ISSET(pterr->native_to, uclass_index(puc))) {
1806         puc_names[set_count++] = uclass_rule_name(puc);
1807       }
1808     } unit_class_iterate_end;
1809 
1810     if (set_count > 0) {
1811       secfile_insert_str_vec(sfile, puc_names, set_count,
1812                              "%s.native_to", path);
1813     }
1814 
1815     rgbcolor_save(sfile, pterr->rgb, "%s.color", path);
1816 
1817     save_strvec(sfile, pterr->helptext, path, "helptext");
1818 
1819   } terrain_type_iterate_end;
1820 
1821   sect_idx = 0;
1822   resource_type_iterate(pres) {
1823     char path[512];
1824     char identifier[2];
1825 
1826     fc_snprintf(path, sizeof(path), "resource_%d", sect_idx++);
1827 
1828     save_name_translation(sfile, &(pres->name), path);
1829 
1830     output_type_iterate(o) {
1831       if (pres->output[o] != 0) {
1832         secfile_insert_int(sfile, pres->output[o], "%s.%s",
1833                            path, get_output_identifier(o));
1834       }
1835     } output_type_iterate_end;
1836 
1837     secfile_insert_str(sfile, pres->graphic_str, "%s.graphic", path);
1838     secfile_insert_str(sfile, pres->graphic_alt, "%s.graphic_alt", path);
1839     identifier[0] = pres->identifier;
1840     identifier[1] = '\0';
1841     secfile_insert_str(sfile, identifier, "%s.identifier", path);
1842   } resource_type_iterate_end;
1843 
1844   secfile_insert_str(sfile, terrain_control.gui_type_base0,
1845                      "extraui.ui_name_base_fortress");
1846   secfile_insert_str(sfile, terrain_control.gui_type_base1,
1847                      "extraui.ui_name_base_airbase");
1848 
1849   sect_idx = 0;
1850   extra_type_iterate(pextra) {
1851     char path[512];
1852     const char *flag_names[EF_COUNT];
1853     const char *cause_names[EC_COUNT];
1854     const char *puc_names[UCL_LAST];
1855     const char *extra_names[MAX_EXTRA_TYPES];
1856     int flagi;
1857     int causei;
1858     int set_count;
1859 
1860     fc_snprintf(path, sizeof(path), "extra_%d", sect_idx++);
1861 
1862     save_name_translation(sfile, &(pextra->name), path);
1863 
1864     secfile_insert_str(sfile, extra_category_name(pextra->category),
1865                        "%s.category", path);
1866 
1867     set_count = 0;
1868     for (causei = 0; causei < EC_COUNT; causei++) {
1869       if (is_extra_caused_by(pextra, causei)) {
1870         cause_names[set_count++] = extra_cause_name(causei);
1871       }
1872     }
1873 
1874     if (set_count > 0) {
1875       secfile_insert_str_vec(sfile, cause_names, set_count,
1876                              "%s.causes", path);
1877     }
1878 
1879     set_count = 0;
1880     for (causei = 0; causei < ERM_COUNT; causei++) {
1881       if (is_extra_removed_by(pextra, causei)) {
1882         cause_names[set_count++] = extra_rmcause_name(causei);
1883       }
1884     }
1885 
1886     if (set_count > 0) {
1887       secfile_insert_str_vec(sfile, cause_names, set_count,
1888                              "%s.rmcauses", path);
1889     }
1890 
1891     if (strcmp(pextra->graphic_str, "-")) {
1892       secfile_insert_str(sfile, pextra->graphic_str, "%s.graphic", path);
1893     }
1894     if (strcmp(pextra->graphic_alt, "-")) {
1895       secfile_insert_str(sfile, pextra->graphic_alt, "%s.graphic_alt", path);
1896     }
1897     if (strcmp(pextra->activity_gfx, "-")) {
1898       secfile_insert_str(sfile, pextra->activity_gfx, "%s.activity_gfx", path);
1899     }
1900     if (strcmp(pextra->act_gfx_alt, "-")) {
1901       secfile_insert_str(sfile, pextra->act_gfx_alt, "%s.act_gfx_alt", path);
1902     }
1903     if (strcmp(pextra->act_gfx_alt2, "-")) {
1904       secfile_insert_str(sfile, pextra->act_gfx_alt2, "%s.act_gfx_alt2", path);
1905     }
1906     if (strcmp(pextra->rmact_gfx, "-")) {
1907       secfile_insert_str(sfile, pextra->rmact_gfx, "%s.rmact_gfx", path);
1908     }
1909     if (strcmp(pextra->rmact_gfx_alt, "-")) {
1910       secfile_insert_str(sfile, pextra->rmact_gfx_alt, "%s.rmact_gfx_alt", path);
1911     }
1912 
1913     save_reqs_vector(sfile, &(pextra->reqs), path, "reqs");
1914     save_reqs_vector(sfile, &(pextra->rmreqs), path, "rmreqs");
1915 
1916     if (!pextra->buildable) {
1917       secfile_insert_bool(sfile, pextra->buildable, "%s.buildable", path);
1918     }
1919     secfile_insert_int(sfile, pextra->build_time, "%s.build_time", path);
1920     secfile_insert_int(sfile, pextra->removal_time, "%s.removal_time", path);
1921     if (pextra->build_time_factor != 1) {
1922       secfile_insert_int(sfile, pextra->build_time_factor, "%s.build_time_factor", path);
1923     }
1924     if (pextra->removal_time_factor != 1) {
1925       secfile_insert_int(sfile, pextra->removal_time_factor, "%s.removal_time_factor", path);
1926     }
1927     if (pextra->defense_bonus != 0) {
1928       secfile_insert_int(sfile, pextra->defense_bonus, "%s.defense_bonus", path);
1929     }
1930 
1931     set_count = 0;
1932     unit_class_iterate(puc) {
1933       if (BV_ISSET(pextra->native_to, uclass_index(puc))) {
1934         puc_names[set_count++] = uclass_rule_name(puc);
1935       }
1936     } unit_class_iterate_end;
1937 
1938     if (set_count > 0) {
1939       secfile_insert_str_vec(sfile, puc_names, set_count,
1940                              "%s.native_to", path);
1941     }
1942 
1943     set_count = 0;
1944     for (flagi = 0; flagi < EF_COUNT; flagi++) {
1945       if (extra_has_flag(pextra, flagi)) {
1946         flag_names[set_count++] = extra_flag_id_name(flagi);
1947       }
1948     }
1949 
1950     if (set_count > 0) {
1951       secfile_insert_str_vec(sfile, flag_names, set_count,
1952                              "%s.flags", path);
1953     }
1954 
1955     set_count = 0;
1956     extra_type_iterate(confl) {
1957       if (!can_extras_coexist(pextra, confl)) {
1958         extra_names[set_count++] = extra_rule_name(confl);
1959       }
1960     } extra_type_iterate_end;
1961 
1962     if (set_count > 0) {
1963       secfile_insert_str_vec(sfile, extra_names, set_count,
1964                              "%s.conflicts", path);
1965     }
1966 
1967     set_count = 0;
1968     extra_type_iterate(top) {
1969       if (BV_ISSET(pextra->hidden_by, extra_index(top))) {
1970         extra_names[set_count++] = extra_rule_name(top);
1971       }
1972     } extra_type_iterate_end;
1973 
1974     if (set_count > 0) {
1975       secfile_insert_str_vec(sfile, extra_names, set_count,
1976                              "%s.hidden_by", path);
1977     }
1978 
1979     save_strvec(sfile, pextra->helptext, path, "helptext");
1980 
1981   } extra_type_iterate_end;
1982 
1983   sect_idx = 0;
1984   extra_type_by_cause_iterate(EC_BASE, pextra) {
1985     char path[512];
1986     struct base_type *pbase = extra_base_get(pextra);
1987     const char *flag_names[BF_COUNT];
1988     int flagi;
1989     int set_count;
1990 
1991     fc_snprintf(path, sizeof(path), "base_%d", sect_idx++);
1992 
1993     secfile_insert_str(sfile, extra_rule_name(pextra),
1994                        "%s.extra", path);
1995 
1996     secfile_insert_str(sfile, base_gui_type_name(pbase->gui_type),
1997                        "%s.gui_type", path);
1998 
1999     if (pbase->border_sq >= 0) {
2000       secfile_insert_int(sfile, pbase->border_sq, "%s.border_sq", path);
2001     }
2002     if (pbase->vision_main_sq >= 0) {
2003       secfile_insert_int(sfile, pbase->vision_main_sq, "%s.vision_main_sq", path);
2004     }
2005     if (pbase->vision_invis_sq >= 0) {
2006       secfile_insert_int(sfile, pbase->vision_invis_sq, "%s.vision_invis_sq", path);
2007     }
2008 
2009     set_count = 0;
2010     for (flagi = 0; flagi < BF_COUNT; flagi++) {
2011       if (base_has_flag(pbase, flagi)) {
2012         flag_names[set_count++] = base_flag_id_name(flagi);
2013       }
2014     }
2015 
2016     if (set_count > 0) {
2017       secfile_insert_str_vec(sfile, flag_names, set_count,
2018                              "%s.flags", path);
2019     }
2020   } extra_type_by_cause_iterate_end;
2021 
2022   sect_idx = 0;
2023   extra_type_by_cause_iterate(EC_ROAD, pextra) {
2024     struct road_type *proad = extra_road_get(pextra);
2025     char path[512];
2026     const char *flag_names[RF_COUNT];
2027     int flagi;
2028     int set_count;
2029 
2030     fc_snprintf(path, sizeof(path), "road_%d", sect_idx++);
2031 
2032     secfile_insert_str(sfile, extra_rule_name(pextra),
2033                        "%s.extra", path);
2034 
2035     secfile_insert_int(sfile, proad->move_cost, "%s.move_cost", path);
2036 
2037     if (proad->move_mode != RMM_FAST_ALWAYS) {
2038       secfile_insert_str(sfile, road_move_mode_name(proad->move_mode),
2039                          "%s.move_mode", path);
2040     }
2041 
2042     output_type_iterate(o) {
2043       if (proad->tile_incr_const[o] != 0) {
2044         secfile_insert_int(sfile, proad->tile_incr_const[o],
2045                            "%s.%s_incr_const", path, get_output_identifier(o));
2046       }
2047       if (proad->tile_incr[o] != 0) {
2048         secfile_insert_int(sfile, proad->tile_incr[o],
2049                            "%s.%s_incr", path, get_output_identifier(o));
2050       }
2051       if (proad->tile_bonus[o] != 0) {
2052         secfile_insert_int(sfile, proad->tile_bonus[o],
2053                            "%s.%s_bonus", path, get_output_identifier(o));
2054       }
2055     } output_type_iterate_end;
2056 
2057     switch (proad->compat) {
2058     case ROCO_ROAD:
2059       secfile_insert_str(sfile, "Road", "%s.compat_special", path);
2060       break;
2061     case ROCO_RAILROAD:
2062       secfile_insert_str(sfile, "Railroad", "%s.compat_special", path);
2063       break;
2064     case ROCO_RIVER:
2065       secfile_insert_str(sfile, "River", "%s.compat_special", path);
2066       break;
2067     case ROCO_NONE:
2068       secfile_insert_str(sfile, "None", "%s.compat_special", path);
2069       break;
2070     }
2071 
2072     set_count = 0;
2073     for (flagi = 0; flagi < RF_COUNT; flagi++) {
2074       if (road_has_flag(proad, flagi)) {
2075         flag_names[set_count++] = road_flag_id_name(flagi);
2076       }
2077     }
2078 
2079     if (set_count > 0) {
2080       secfile_insert_str_vec(sfile, flag_names, set_count,
2081                              "%s.flags", path);
2082     }
2083   } extra_type_by_cause_iterate_end;
2084 
2085   return save_ruleset_file(sfile, filename);
2086 }
2087 
2088 /**************************************************************************
2089   Save one veteran system.
2090 **************************************************************************/
save_veteran_system(struct section_file * sfile,const char * path,struct veteran_system * vsystem)2091 static bool save_veteran_system(struct section_file *sfile, const char *path,
2092                                 struct veteran_system *vsystem)
2093 {
2094   const char *vlist_name[vsystem->levels];
2095   int vlist_power[vsystem->levels];
2096   int vlist_raise[vsystem->levels];
2097   int vlist_wraise[vsystem->levels];
2098   int vlist_move[vsystem->levels];
2099   int i;
2100 
2101   for (i = 0; i < vsystem->levels; i++) {
2102     vlist_name[i] = rule_name_get(&(vsystem->definitions[i].name));
2103     vlist_power[i] = vsystem->definitions[i].power_fact;
2104     vlist_raise[i] = vsystem->definitions[i].raise_chance;
2105     vlist_wraise[i] = vsystem->definitions[i].work_raise_chance;
2106     vlist_move[i] = vsystem->definitions[i].move_bonus;
2107   }
2108 
2109   secfile_insert_str_vec(sfile, vlist_name, vsystem->levels,
2110                          "%s.veteran_names", path);
2111   secfile_insert_int_vec(sfile, vlist_power, vsystem->levels,
2112                          "%s.veteran_power_fact", path);
2113   secfile_insert_int_vec(sfile, vlist_raise, vsystem->levels,
2114                          "%s.veteran_raise_chance", path);
2115   secfile_insert_int_vec(sfile, vlist_wraise, vsystem->levels,
2116                          "%s.veteran_work_raise_chance", path);
2117   secfile_insert_int_vec(sfile, vlist_move, vsystem->levels,
2118                          "%s.veteran_move_bonus", path);
2119 
2120   return TRUE;
2121 }
2122 
2123 /**************************************************************************
2124   Save unit combat bonuses list.
2125 **************************************************************************/
save_combat_bonuses(struct section_file * sfile,struct unit_type * put,char * path)2126 static bool save_combat_bonuses(struct section_file *sfile,
2127                                 struct unit_type *put,
2128                                 char *path)
2129 {
2130   int i;
2131   bool has_quiet = FALSE;
2132 
2133   combat_bonus_list_iterate(put->bonuses, pbonus) {
2134     if (pbonus->quiet) {
2135       has_quiet = TRUE;
2136     }
2137   } combat_bonus_list_iterate_end;
2138 
2139   i = 0;
2140 
2141   combat_bonus_list_iterate(put->bonuses, pbonus) {
2142     secfile_insert_str(sfile, unit_type_flag_id_name(pbonus->flag),
2143                        "%s.bonuses%d.flag", path, i);
2144     secfile_insert_str(sfile, combat_bonus_type_name(pbonus->type),
2145                        "%s.bonuses%d.type", path, i);
2146     secfile_insert_int(sfile, pbonus->value,
2147                        "%s.bonuses%d.value", path, i);
2148 
2149     if (has_quiet) {
2150       secfile_insert_bool(sfile, pbonus->quiet,
2151                           "%s.bonuses%d.quiet", path, i);
2152     }
2153 
2154     i++;
2155   } combat_bonus_list_iterate_end;
2156 
2157   return TRUE;
2158 }
2159 
2160 /**************************************************************************
2161   Save units.ruleset
2162 **************************************************************************/
save_units_ruleset(const char * filename,const char * name)2163 static bool save_units_ruleset(const char *filename, const char *name)
2164 {
2165   struct section_file *sfile = create_ruleset_file(name, "unit");
2166   int i;
2167   int sect_idx;
2168 
2169   if (sfile == NULL) {
2170     return FALSE;
2171   }
2172 
2173   for (i = 0; i < MAX_NUM_USER_UNIT_FLAGS; i++) {
2174     const char *flagname = unit_type_flag_id_name_cb(i + UTYF_USER_FLAG_1);
2175     const char *helptxt = unit_type_flag_helptxt(i + UTYF_USER_FLAG_1);
2176 
2177     if (flagname != NULL) {
2178       secfile_insert_str(sfile, flagname, "control.flags%d.name", i);
2179 
2180       /* Save the user flag help text even when it is undefined. That makes
2181        * the formatting code happy. The resulting "" is ignored when the
2182        * ruleset is loaded. */
2183       secfile_insert_str(sfile, helptxt, "control.flags%d.helptxt", i);
2184     }
2185   }
2186 
2187   save_veteran_system(sfile, "veteran_system", game.veteran);
2188 
2189   sect_idx = 0;
2190   unit_class_iterate(puc) {
2191     char path[512];
2192     char *hut_str = NULL;
2193     const char *flag_names[UCF_COUNT];
2194     int flagi;
2195     int set_count;
2196 
2197     fc_snprintf(path, sizeof(path), "unitclass_%d", sect_idx++);
2198 
2199     save_name_translation(sfile, &(puc->name), path);
2200 
2201     secfile_insert_int(sfile, puc->min_speed / SINGLE_MOVE,
2202                        "%s.min_speed", path);
2203     secfile_insert_int(sfile, puc->hp_loss_pct, "%s.hp_loss_pct", path);
2204     if (puc->non_native_def_pct != 100) {
2205       secfile_insert_int(sfile, puc->non_native_def_pct,
2206                          "%s.non_native_def_pct", path);
2207     }
2208     if (puc->hut_behavior != HUT_NORMAL) {
2209       switch (puc->hut_behavior) {
2210       case HUT_NORMAL:
2211         hut_str = "Normal";
2212         break;
2213       case HUT_NOTHING:
2214         hut_str = "Nothing";
2215         break;
2216       case HUT_FRIGHTEN:
2217         hut_str = "Frighten";
2218         break;
2219       }
2220       fc_assert(hut_str != NULL);
2221       secfile_insert_str(sfile, hut_str, "%s.hut_behavior", path);
2222     }
2223 
2224     set_count = 0;
2225     for (flagi = 0; flagi < UCF_COUNT; flagi++) {
2226       if (uclass_has_flag(puc, flagi)) {
2227         flag_names[set_count++] = unit_class_flag_id_name(flagi);
2228       }
2229     }
2230 
2231     if (set_count > 0) {
2232       secfile_insert_str_vec(sfile, flag_names, set_count,
2233                              "%s.flags", path);
2234     }
2235 
2236     save_strvec(sfile, puc->helptext, path, "helptext");
2237 
2238   } unit_class_iterate_end;
2239 
2240   sect_idx = 0;
2241   unit_active_type_iterate(put) {
2242     if (!put->disabled) {
2243       char path[512];
2244       const char *flag_names[UTYF_LAST_USER_FLAG + 1];
2245       int flagi;
2246       int set_count;
2247 
2248       fc_snprintf(path, sizeof(path), "unit_%d", sect_idx++);
2249 
2250       save_name_translation(sfile, &(put->name), path);
2251 
2252       secfile_insert_str(sfile, uclass_rule_name(put->uclass),
2253                          "%s.class", path);
2254 
2255       save_tech_ref(sfile, put->require_advance, path, "tech_req");
2256 
2257       if (put->need_government != NULL) {
2258         secfile_insert_str(sfile, government_rule_name(put->need_government),
2259                            "%s.gov_req", path);
2260       }
2261 
2262       if (put->need_improvement != NULL) {
2263         secfile_insert_str(sfile, improvement_rule_name(put->need_improvement),
2264                            "%s.impr_req", path);
2265       }
2266 
2267       if (put->obsoleted_by != NULL) {
2268         secfile_insert_str(sfile, utype_rule_name(put->obsoleted_by),
2269                            "%s.obsolete_by", path);
2270       }
2271 
2272       secfile_insert_str(sfile, put->graphic_str, "%s.graphic", path);
2273       if (strcmp("-", put->graphic_alt)) {
2274         secfile_insert_str(sfile, put->graphic_alt, "%s.graphic_alt", path);
2275       }
2276       if (strcmp("-", put->sound_move)) {
2277         secfile_insert_str(sfile, put->sound_move, "%s.sound_move", path);
2278       }
2279       if (strcmp("-", put->sound_move_alt)) {
2280         secfile_insert_str(sfile, put->sound_move_alt, "%s.sound_move_alt", path);
2281       }
2282       if (strcmp("-", put->sound_fight)) {
2283         secfile_insert_str(sfile, put->sound_fight, "%s.sound_fight", path);
2284       }
2285       if (strcmp("-", put->sound_fight_alt)) {
2286         secfile_insert_str(sfile, put->sound_fight_alt, "%s.sound_fight_alt", path);
2287       }
2288 
2289       secfile_insert_int(sfile, put->build_cost, "%s.build_cost", path);
2290       secfile_insert_int(sfile, put->pop_cost, "%s.pop_cost", path);
2291       secfile_insert_int(sfile, put->attack_strength, "%s.attack", path);
2292       secfile_insert_int(sfile, put->defense_strength, "%s.defense", path);
2293       secfile_insert_int(sfile, put->move_rate / SINGLE_MOVE, "%s.move_rate", path);
2294       secfile_insert_int(sfile, put->vision_radius_sq, "%s.vision_radius_sq", path);
2295       secfile_insert_int(sfile, put->transport_capacity, "%s.transport_cap", path);
2296 
2297       save_uclass_vec(sfile, &(put->cargo), path, "cargo", FALSE);
2298       save_uclass_vec(sfile, &(put->embarks), path, "embarks", TRUE);
2299       save_uclass_vec(sfile, &(put->disembarks), path, "disembarks", TRUE);
2300 
2301       secfile_insert_int(sfile, put->hp, "%s.hitpoints", path);
2302       secfile_insert_int(sfile, put->firepower, "%s.firepower", path);
2303       secfile_insert_int(sfile, put->fuel, "%s.fuel", path);
2304       secfile_insert_int(sfile, put->happy_cost, "%s.uk_happy", path);
2305 
2306       output_type_iterate(o) {
2307         if (put->upkeep[o] != 0) {
2308           secfile_insert_int(sfile, put->upkeep[o], "%s.uk_%s",
2309                              path, get_output_identifier(o));
2310         }
2311       } output_type_iterate_end;
2312 
2313       if (put->converted_to != NULL) {
2314         secfile_insert_str(sfile, utype_rule_name(put->converted_to),
2315                            "%s.convert_to", path);
2316       }
2317       if (put->convert_time != 1) {
2318         secfile_insert_int(sfile, put->convert_time, "%s.convert_time", path);
2319       }
2320 
2321       save_combat_bonuses(sfile, put, path);
2322       save_uclass_vec(sfile, &(put->targets), path, "targets", TRUE);
2323 
2324       if (put->veteran != NULL) {
2325         save_veteran_system(sfile, path, put->veteran);
2326       }
2327 
2328       if (put->paratroopers_range != 0) {
2329         secfile_insert_int(sfile, put->paratroopers_range,
2330                            "%s.paratroopers_range", path);
2331         secfile_insert_int(sfile, put->paratroopers_mr_req / SINGLE_MOVE,
2332                            "%s.paratroopers_mr_req", path);
2333         secfile_insert_int(sfile, put->paratroopers_mr_sub / SINGLE_MOVE,
2334                            "%s.paratroopers_mr_sub", path);
2335       }
2336       if (put->bombard_rate != 0) {
2337         secfile_insert_int(sfile, put->bombard_rate,
2338                            "%s.bombard_rate", path);
2339       }
2340       if (put->city_size != 1) {
2341         secfile_insert_int(sfile, put->city_size,
2342                            "%s.city_size", path);
2343       }
2344 
2345       set_count = 0;
2346       for (flagi = 0; flagi <= UTYF_LAST_USER_FLAG; flagi++) {
2347         if (utype_has_flag(put, flagi)) {
2348           flag_names[set_count++] = unit_type_flag_id_name(flagi);
2349         }
2350       }
2351 
2352       if (set_count > 0) {
2353         secfile_insert_str_vec(sfile, flag_names, set_count,
2354                                "%s.flags", path);
2355       }
2356 
2357       set_count = 0;
2358       for (flagi = L_FIRST; flagi < L_LAST; flagi++) {
2359         if (utype_has_role(put, flagi)) {
2360           flag_names[set_count++] = unit_role_id_name(flagi);
2361         }
2362       }
2363 
2364       if (set_count > 0) {
2365         secfile_insert_str_vec(sfile, flag_names, set_count,
2366                                "%s.roles", path);
2367       }
2368 
2369       save_strvec(sfile, put->helptext, path, "helptext");
2370     }
2371   } unit_active_type_iterate_end;
2372 
2373   return save_ruleset_file(sfile, filename);
2374 }
2375 
2376 /**************************************************************************
2377   Save script.lua
2378 **************************************************************************/
save_script_lua(const char * filename,const char * name)2379 static bool save_script_lua(const char *filename, const char *name)
2380 {
2381   char *buffer = get_script_buffer();
2382 
2383   if (buffer != NULL) {
2384     FILE *ffile = fc_fopen(filename, "w");
2385     int full_len = strlen(buffer);
2386     int len;
2387 
2388     if (ffile != NULL) {
2389       len = fwrite(buffer, 1, full_len, ffile);
2390 
2391       if (len != full_len) {
2392         return FALSE;
2393       }
2394 
2395       fclose(ffile);
2396     } else {
2397       return FALSE;
2398     }
2399   }
2400 
2401   return TRUE;
2402 }
2403 
2404 /**************************************************************************
2405   Save ruleset to directory given.
2406 **************************************************************************/
save_ruleset(const char * path,const char * name,struct rule_data * data)2407 bool save_ruleset(const char *path, const char *name, struct rule_data *data)
2408 {
2409   if (make_dir(path)) {
2410     bool success = TRUE;
2411     char filename[500];
2412 
2413     if (success) {
2414       fc_snprintf(filename, sizeof(filename), "%s/buildings.ruleset", path);
2415       success = save_buildings_ruleset(filename, name);
2416     }
2417 
2418     if (success) {
2419       fc_snprintf(filename, sizeof(filename), "%s/styles.ruleset", path);
2420       success = save_styles_ruleset(filename, name);
2421     }
2422 
2423     if (success) {
2424       fc_snprintf(filename, sizeof(filename), "%s/cities.ruleset", path);
2425       success = save_cities_ruleset(filename, name);
2426     }
2427 
2428     if (success) {
2429       fc_snprintf(filename, sizeof(filename), "%s/effects.ruleset", path);
2430       success = save_effects_ruleset(filename, name);
2431     }
2432 
2433     if (success) {
2434       fc_snprintf(filename, sizeof(filename), "%s/game.ruleset", path);
2435       success = save_game_ruleset(filename, name);
2436     }
2437 
2438     if (success) {
2439       fc_snprintf(filename, sizeof(filename), "%s/governments.ruleset", path);
2440       success = save_governments_ruleset(filename, name);
2441     }
2442 
2443     if (success) {
2444       fc_snprintf(filename, sizeof(filename), "%s/nations.ruleset", path);
2445       success = save_nations_ruleset(filename, name, data);
2446     }
2447 
2448     if (success) {
2449       fc_snprintf(filename, sizeof(filename), "%s/techs.ruleset", path);
2450       success = save_techs_ruleset(filename, name);
2451     }
2452 
2453     if (success) {
2454       fc_snprintf(filename, sizeof(filename), "%s/terrain.ruleset", path);
2455       success = save_terrain_ruleset(filename, name);
2456     }
2457 
2458     if (success) {
2459       fc_snprintf(filename, sizeof(filename), "%s/units.ruleset", path);
2460       success = save_units_ruleset(filename, name);
2461     }
2462 
2463     if (success) {
2464       fc_snprintf(filename, sizeof(filename), "%s/script.lua", path);
2465       success = save_script_lua(filename, name);
2466     }
2467 
2468     return success;
2469   } else {
2470     log_error("Failed to create directory %s", path);
2471     return FALSE;
2472   }
2473 
2474   return TRUE;
2475 }
2476