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