Freeciv-3.1
Loading...
Searching...
No Matches
savecompat.c
Go to the documentation of this file.
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 "capability.h"
20#include "log.h"
21
22/* common */
23#include "map.h"
24#include "specialist.h"
25
26/* server */
27#include "aiiface.h"
28#include "unittools.h"
29
30#include "savecompat.h"
31
33
34static char *special_names[] =
35 {
36 "Irrigation", "Mine", "Pollution", "Hut", "Farmland",
37 "Fallout", NULL
38 };
39
40/*
41 For each savefile format after 2.3.0, compatibility functions are defined
42 which translate secfile structures from previous version to that version;
43 all necessary compat functions are called in order to
44 translate between the file and current version. See sg_load_compat().
45
46 The integer version ID should be increased every time the format is changed.
47 If the change is not backwards compatible, please state the changes in the
48 following list and update the compat functions at the end of this file.
49
50 - what was added / removed
51 - when was it added / removed (date and version)
52 - when can additional capability checks be set to mandatory (version)
53 - which compatibility checks are needed and till when (version)
54
55 freeciv | what | date | id
56 --------+------------------------------------------------+------------+----
57 current | (mapped to current savegame format) | ----/--/-- | 0
58 | first version (svn17538) | 2010/07/05 | -
59 2.3.0 | 2.3.0 release | 2010/11/?? | 3
60 2.4.0 | 2.4.0 release | 201./../.. | 10
61 | * player ai type | |
62 | * delegation | |
63 | * citizens | |
64 | * save player color | |
65 | * "known" info format change | |
66 2.5.0 | 2.5.0 release | 201./../.. | 20
67 2.6.0 | 2.6.0 release | 201./../.. | 30
68 3.0.0 | 3.0.0 release | 202./../.. | 40
69 3.1.0 | 3.1.0 release | 202./../.. | 50
70 | | |
71*/
72
73static void compat_load_020400(struct loaddata *loading, enum sgf_version format_class);
74static void compat_load_020500(struct loaddata *loading, enum sgf_version format_class);
75static void compat_load_020600(struct loaddata *loading, enum sgf_version format_class);
76static void compat_load_030000(struct loaddata *loading, enum sgf_version format_class);
77static void compat_load_030100(struct loaddata *loading, enum sgf_version format_class);
78static void compat_post_load_030100(struct loaddata *loading,
79 enum sgf_version format_class);
80
81typedef void (*load_version_func_t) (struct loaddata *loading, enum sgf_version format_class);
82
88
89/* The struct below contains the information about the savegame versions. It
90 * is identified by the version number (first element), which should be
91 * steadily increasing. It is saved as 'savefile.version'. The support
92 * string (first element of 'name') is not saved in the savegame; it is
93 * saved in settings files (so, once assigned, cannot be changed). The
94 * 'pretty' string (second element of 'name') can be changed if necessary
95 * For changes in the development version, edit the definitions above and
96 * add the needed code to load the old version below. Thus, old
97 * savegames can still be loaded while the main definition
98 * represents the current state of the art. */
99/* While developing freeciv 3.1.0, add the compatibility functions to
100 * - compat_load_030100 to load old savegame. */
101static struct compatibility compat[] = {
102 /* dummy; equal to the current version (last element) */
103 { 0, NULL, NULL },
104 /* version 1 and 2 is not used */
105 /* version 3: first savegame2 format, so no compat functions for translation
106 * from previous format */
107 { 3, NULL, NULL },
108 /* version 4 to 9 are reserved for possible changes in 2.3.x */
109 { 10, compat_load_020400, NULL },
110 /* version 11 to 19 are reserved for possible changes in 2.4.x */
111 { 20, compat_load_020500, NULL },
112 /* version 21 to 29 are reserved for possible changes in 2.5.x */
113 { 30, compat_load_020600, NULL },
114 /* version 31 to 39 are reserved for possible changes in 2.6.x */
115 { 40, compat_load_030000, NULL },
116 /* version 41 to 49 are reserved for possible changes in 3.0.x */
118 /* Current savefile version is listed above this line; it corresponds to
119 the definitions in this file. */
120};
121
122static const int compat_num = ARRAY_SIZE(compat);
123#define compat_current (compat_num - 1)
124
125/************************************************************************/
132void sg_load_compat(struct loaddata *loading, enum sgf_version format_class)
133{
134 int i;
135
136 /* Check status and return if not OK (sg_success FALSE). */
137 sg_check_ret();
138
139 loading->version = secfile_lookup_int_default(loading->file, -1,
140 "savefile.version");
141#ifdef FREECIV_DEBUG
142 sg_failure_ret(0 < loading->version, "Invalid savefile format version (%d).",
143 loading->version);
144 if (loading->version > compat[compat_current].version) {
145 /* Debug build can (TRY TO!) load newer versions but ... */
146 log_error("Savegame version newer than this build found (%d > %d). "
147 "Trying to load the game nevertheless ...", loading->version,
149 }
150#else /* FREECIV_DEBUG */
151 sg_failure_ret(0 < loading->version
152 && loading->version <= compat[compat_current].version,
153 "Unknown savefile format version (%d).", loading->version);
154#endif /* FREECIV_DEBUG */
155
156
157 for (i = 0; i < compat_num; i++) {
158 if (loading->version < compat[i].version && compat[i].load != NULL) {
159 log_normal(_("Run compatibility function for version: <%d "
160 "(save file: %d; server: %d)."), compat[i].version,
162 compat[i].load(loading, format_class);
163 }
164 }
165}
166
167/************************************************************************/
178 enum sgf_version format_class)
179{
180 int i;
181
182 /* Check status and return if not OK (sg_success FALSE). */
183 sg_check_ret();
184
185 for (i = 0; i < compat_num; i++) {
186 if (loading->version < compat[i].version
187 && compat[i].post_load != NULL) {
188 log_normal(_("Run post load compatibility function for version: <%d "
189 "(save file: %d; server: %d)."), compat[i].version,
191 compat[i].post_load(loading, format_class);
192 }
193 }
194}
195
196/************************************************************************/
200{
202}
203
204/************************************************************************/
209char bin2ascii_hex(int value, int halfbyte_wanted)
210{
211 return hex_chars[((value) >> ((halfbyte_wanted) * 4)) & 0xf];
212}
213
214/************************************************************************/
221int ascii_hex2bin(char ch, int halfbyte)
222{
223 const char *pch;
224
225 if (ch == ' ') {
226 /* Sane value. It is unknown if there are savegames out there which
227 * need this fix. Savegame.c doesn't write such savegames
228 * (anymore) since the inclusion into CVS (2000-08-25). */
229 return 0;
230 }
231
232 pch = strchr(hex_chars, ch);
233
234 sg_failure_ret_val(NULL != pch && '\0' != ch, 0,
235 "Unknown hex value: '%c' %d", ch, ch);
236 return (pch - hex_chars) << (halfbyte * 4);
237}
238
239static const char num_chars[] =
240 "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_-+";
241
242/************************************************************************/
245int char2num(char ch)
246{
247 const char *pch;
248
249 pch = strchr(num_chars, ch);
250
251 sg_failure_ret_val(NULL != pch, 0,
252 "Unknown ascii value for num: '%c' %d", ch, ch);
253
254 return pch - num_chars;
255}
256
257/************************************************************************/
261{
262 int i;
263
264 for (i = 0; special_names[i] != NULL; i++) {
265 if (!strcmp(name, special_names[i])) {
266 return i;
267 }
268 }
269
270 return S_LAST;
271}
272
273/************************************************************************/
277{
278 fc_assert(type >= 0 && type < S_LAST);
279
280 return special_names[type];
281}
282
283/************************************************************************/
287{
288 struct extra_type_list *elist = extra_type_list_by_cause(EC_SPECIAL);
289
290 if (spe < extra_type_list_size(elist)) {
291 return extra_type_list_get(elist, spe);
292 }
293
294 return NULL;
295}
296
297/************************************************************************/
300struct extra_type *resource_by_identifier(const char identifier)
301{
302 extra_type_by_cause_iterate(EC_RESOURCE, presource) {
303 if (presource->data.resource->id_old_save == identifier) {
304 return presource;
305 }
307
308 return NULL;
309}
310
311/* =======================================================================
312 * Compatibility functions for loading a game.
313 * ======================================================================= */
314
315/************************************************************************/
318static void compat_load_020400(struct loaddata *loading,
319 enum sgf_version format_class)
320{
321 /* Check status and return if not OK (sg_success FALSE). */
322 sg_check_ret();
323
324 log_debug("Upgrading data from savegame to version 2.4.0");
325
326 /* Add the default player AI. */
327 player_slots_iterate(pslot) {
328 int ncities, i, plrno = player_slot_index(pslot);
329
330 if (NULL == secfile_section_lookup(loading->file, "player%d", plrno)) {
331 continue;
332 }
333
335 "player%d.ai_type", player_slot_index(pslot));
336
337 /* Create dummy citizens information. We do not know if citizens are
338 * activated due to the fact that this information
339 * (game.info.citizen_nationality) is not available, but adding the
340 * information does no harm. */
341 ncities = secfile_lookup_int_default(loading->file, 0,
342 "player%d.ncities", plrno);
343 if (ncities > 0) {
344 for (i = 0; i < ncities; i++) {
345 int size = secfile_lookup_int_default(loading->file, 0,
346 "player%d.c%d.size", plrno, i);
347 if (size > 0) {
348 secfile_insert_int(loading->file, size,
349 "player%d.c%d.citizen%d", plrno, i, plrno);
350 }
351 }
352 }
353
355
356 /* Player colors are assigned at the end of player loading, as this
357 * needs information not available here. */
358
359 /* Deal with buggy known tiles information from 2.3.0/2.3.1 (and the
360 * workaround in later 2.3.x); see gna bug #19029.
361 * (The structure of this code is odd as it avoids relying on knowledge of
362 * xsize/ysize, which haven't been extracted from the savefile yet.) */
363 {
364 if (has_capability("knownv2",
365 secfile_lookup_str(loading->file, "savefile.options"))) {
366 /* This savefile contains known information in a sane format.
367 * Just move any entries to where 2.4.x+ expect to find them. */
368 struct section *map = secfile_section_by_name(loading->file, "map");
369 if (map) {
370 entry_list_iterate(section_entries(map), pentry) {
371 const char *name = entry_name(pentry);
372 if (strncmp(name, "kvb", 3) == 0) {
373 /* Rename the "kvb..." entry to "k..." */
374 char *name2 = fc_strdup(name), *newname = name2 + 2;
375 *newname = 'k';
376 /* Savefile probably contains existing "k" entries, which are bogus
377 * so we trash them */
378 secfile_entry_delete(loading->file, "map.%s", newname);
379 entry_set_name(pentry, newname);
380 FC_FREE(name2);
381 }
383 }
384 /* Could remove "knownv2" from savefile.options, but it's doing
385 * no harm there. */
386 } else {
387 /* This savefile only contains known information in the broken
388 * format. Try to recover it to a sane format. */
389 /* MAX_NUM_PLAYER_SLOTS in 2.3.x was 128 */
390 /* MAP_MAX_LINEAR_SIZE in 2.3.x was 512 */
391 const int maxslots = 128, maxmapsize = 512;
392 const int lines = maxslots/32;
393 int xsize = 0, y, l, j, x;
394 unsigned int known_row_old[lines * maxmapsize],
395 known_row[lines * maxmapsize];
396 /* Process a map row at a time */
397 for (y = 0; y < maxmapsize; y++) {
398 /* Look for broken info to convert */
399 bool found = FALSE;
400 memset(known_row_old, 0, sizeof(known_row_old));
401 for (l = 0; l < lines; l++) {
402 for (j = 0; j < 8; j++) {
403 const char *s =
404 secfile_lookup_str_default(loading->file, NULL,
405 "map.k%02d_%04d", l * 8 + j, y);
406 if (s) {
407 found = TRUE;
408 if (xsize == 0) {
409 xsize = strlen(s);
410 }
411 sg_failure_ret(xsize == strlen(s),
412 "Inconsistent xsize in map.k%02d_%04d",
413 l * 8 + j, y);
414 for (x = 0; x < xsize; x++) {
415 known_row_old[l * xsize + x] |= ascii_hex2bin(s[x], j);
416 }
417 }
418 }
419 }
420 if (found) {
421 /* At least one entry found for this row. Let's hope they were
422 * all there. */
423 /* Attempt to munge into sane format */
424 int p;
425 memset(known_row, 0, sizeof(known_row));
426 /* Iterate over possible player slots */
427 for (p = 0; p < maxslots; p++) {
428 l = p / 32;
429 for (x = 0; x < xsize; x++) {
430 /* This test causes bit-shifts of >=32 (undefined behaviour), but
431 * on common platforms, information happens not to be lost, just
432 * oddly arranged. */
433 if (known_row_old[l * xsize + x] & (1u << (p - l * 8))) {
434 known_row[l * xsize + x] |= (1u << (p - l * 32));
435 }
436 }
437 }
438 /* Save sane format back to memory representation of secfile for
439 * real loading code to pick up */
440 for (l = 0; l < lines; l++) {
441 for (j = 0; j < 8; j++) {
442 /* Save info for all slots (not just used ones). It's only
443 * memory, after all. */
444 char row[xsize+1];
445 for (x = 0; x < xsize; x++) {
446 row[x] = bin2ascii_hex(known_row[l * xsize + x], j);
447 }
448 row[xsize] = '\0';
449 secfile_replace_str(loading->file, row,
450 "map.k%02d_%04d", l * 8 + j, y);
451 }
452 }
453 }
454 }
455 }
456 }
457
458 /* Server setting migration. */
459 {
460 int set_count;
461 if (secfile_lookup_int(loading->file, &set_count, "settings.set_count")) {
462 int i, new_opt = set_count;
463 bool gamestart_valid
465 "settings.gamestart_valid");
466 for (i = 0; i < set_count; i++) {
467 const char *name
468 = secfile_lookup_str(loading->file, "settings.set%d.name", i);
469 if (!name) {
470 continue;
471 }
472
473 /* In 2.3.x and prior, saveturns=0 meant no turn-based saves.
474 * This is now controlled by the "autosaves" setting. */
475 if (!fc_strcasecmp("saveturns", name)) {
476 /* XXX: hardcodes details from GAME_AUTOSAVES_DEFAULT
477 * and settings.c:autosaves_name() (but these defaults reflect
478 * 2.3's behaviour). */
479 const char *const nosave = "GAMEOVER|QUITIDLE|INTERRUPT";
480 const char *const save = "TURN|GAMEOVER|QUITIDLE|INTERRUPT";
481 int nturns;
482
483 if (secfile_lookup_int(loading->file, &nturns,
484 "settings.set%d.value", i)) {
485 if (nturns == 0) {
486 /* Invent a new "autosaves" setting */
487 secfile_insert_str(loading->file, nosave,
488 "settings.set%d.value", new_opt);
489 /* Pick something valid for saveturns */
491 "settings.set%d.value", i);
492 } else {
493 secfile_insert_str(loading->file, save,
494 "settings.set%d.value", new_opt);
495 }
496 } else {
497 log_sg("Setting '%s': %s", name, secfile_error());
498 }
499 if (gamestart_valid) {
500 if (secfile_lookup_int(loading->file, &nturns,
501 "settings.set%d.gamestart", i)) {
502 if (nturns == 0) {
503 /* Invent a new "autosaves" setting */
504 secfile_insert_str(loading->file, nosave,
505 "settings.set%d.gamestart", new_opt);
506 /* Pick something valid for saveturns */
508 "settings.set%d.gamestart", i);
509 } else {
510 secfile_insert_str(loading->file, save,
511 "settings.set%d.gamestart", new_opt);
512 }
513 } else {
514 log_sg("Setting '%s': %s", name, secfile_error());
515 }
516 }
517 } else if (!fc_strcasecmp("autosaves", name)) {
518 /* Sanity check. This won't trigger on an option we've just
519 * invented, as the loop won't include it. */
520 log_sg("Unexpected \"autosaves\" setting found in pre-2.4 "
521 "savefile. It may have been overridden.");
522 }
523 }
524 }
525 }
526}
527
528/************************************************************************/
531static const char *killcitizen_enum_str(secfile_data_t data, int bit)
532{
533 switch (bit) {
534 case UMT_LAND:
535 return "LAND";
536 case UMT_SEA:
537 return "SEA";
538 case UMT_BOTH:
539 return "BOTH";
540 }
541
542 return NULL;
543}
544
545/************************************************************************/
548static void compat_load_020500(struct loaddata *loading,
549 enum sgf_version format_class)
550{
551 const char *modname[] = { "Road", "Railroad" };
552 const char *old_activities_names[] = {
553 "Idle",
554 "Pollution",
555 "Unused Road",
556 "Mine",
557 "Irrigate",
558 "Mine",
559 "Irrigate",
560 "Fortified",
561 "Fortress",
562 "Sentry",
563 "Unused Railroad",
564 "Pillage",
565 "Goto",
566 "Explore",
567 "Transform",
568 "Unused",
569 "Unused Airbase",
570 "Fortifying",
571 "Fallout",
572 "Unused Patrol",
573 "Base"
574 };
575
576 /* Check status and return if not OK (sg_success FALSE). */
577 sg_check_ret();
578
579 log_debug("Upgrading data from savegame to version 2.5.0");
580
581 secfile_insert_int(loading->file, 2, "savefile.roads_size");
582 secfile_insert_int(loading->file, 0, "savefile.trait_size");
583
584 secfile_insert_str_vec(loading->file, modname, 2,
585 "savefile.roads_vector");
586
587 secfile_insert_int(loading->file, 19, "savefile.activities_size");
588 secfile_insert_str_vec(loading->file, old_activities_names, 19,
589 "savefile.activities_vector");
590
591 /* Server setting migration. */
592 {
593 int set_count;
594
595 if (secfile_lookup_int(loading->file, &set_count, "settings.set_count")) {
596 int i;
597 bool gamestart_valid
599 "settings.gamestart_valid");
600 for (i = 0; i < set_count; i++) {
601 const char *name
602 = secfile_lookup_str(loading->file, "settings.set%d.name", i);
603 if (!name) {
604 continue;
605 }
606 /* In 2.4.x and prior, "killcitizen" listed move types that
607 * killed citizens after successful attack. Now killcitizen
608 * is just boolean and classes affected are defined in ruleset. */
609 if (!fc_strcasecmp("killcitizen", name)) {
610 int value;
611
612 if (secfile_lookup_enum_data(loading->file, &value, TRUE,
614 "settings.set%d.value", i)) {
615 /* Lowest bit of old killcitizen value indicates if
616 * land units should kill citizens. We take that as
617 * new boolean killcitizen value. */
618 if (value & 0x1) {
620 "settings.set%d.value", i);
621 } else {
623 "settings.set%d.value", i);
624 }
625 } else {
626 log_sg("Setting '%s': %s", name, secfile_error());
627 }
628 if (gamestart_valid) {
629 if (secfile_lookup_enum_data(loading->file, &value, TRUE,
631 "settings.set%d.gamestart", i)) {
632 /* Lowest bit of old killcitizen value indicates if
633 * land units should kill citizens. We take that as
634 * new boolean killcitizen value. */
635 if (value & 0x1) {
637 "settings.set%d.gamestart", i);
638 } else {
640 "settings.set%d.gamestart", i);
641 }
642 } else {
643 log_sg("Setting '%s': %s", name, secfile_error());
644 }
645 }
646 }
647 }
648 }
649 }
650}
651
652/************************************************************************/
656{
657 switch (type) {
658 case REVOLEN_FIXED:
659 return "FIXED";
660 case REVOLEN_RANDOM:
661 return "RANDOM";
663 return "QUICKENING";
665 return "RANDQUICK";
666 }
667
668 return "";
669}
670
671/************************************************************************/
674static void compat_load_020600(struct loaddata *loading,
675 enum sgf_version format_class)
676{
677 bool team_pooled_research = GAME_DEFAULT_TEAM_POOLED_RESEARCH;
678 int tsize;
679 int ti;
680 int turn;
681
682 /* Check status and return if not OK (sg_success FALSE). */
683 sg_check_ret();
684
685 log_debug("Upgrading data from savegame to version 2.6.0");
686
687 /* Terrain mapping table - use current ruleset as we have no way to know
688 * any other old values. */
689 ti = 0;
690 terrain_type_iterate(pterr) {
691 char buf[2];
692
693 secfile_insert_str(loading->file, terrain_rule_name(pterr), "savefile.terrident%d.name", ti);
694 buf[0] = terrain_identifier(pterr);
695 buf[1] = '\0';
696 secfile_insert_str(loading->file, buf, "savefile.terrident%d.identifier", ti++);
698
699 /* Server setting migration. */
700 {
701 int set_count;
702
703 if (secfile_lookup_int(loading->file, &set_count, "settings.set_count")) {
704 char value_buffer[1024] = "";
705 char gamestart_buffer[1024] = "";
706 int i;
707 int dcost = -1;
708 int dstartcost = -1;
711 bool gamestart_valid
713 "settings.gamestart_valid");
714 int new_set_count;
715
716 for (i = 0; i < set_count; i++) {
717 const char *name
718 = secfile_lookup_str(loading->file, "settings.set%d.name", i);
719 if (!name) {
720 continue;
721 }
722
723 /* In 2.5.x and prior, "spacerace" boolean controlled if
724 * spacerace victory condition was active. */
725 if (!fc_strcasecmp("spacerace", name)) {
726 bool value;
727
728 if (secfile_lookup_bool(loading->file, &value,
729 "settings.set%d.value", i)) {
730 if (value) {
731 if (value_buffer[0] != '\0') {
732 sz_strlcat(value_buffer, "|");
733 }
734 sz_strlcat(value_buffer, "SPACERACE");
735 }
736 } else {
737 log_sg("Setting '%s': %s", name, secfile_error());
738 }
739 if (secfile_lookup_bool(loading->file, &value,
740 "settings.set%d.gamestart", i)) {
741 if (value) {
742 if (gamestart_buffer[0] != '\0') {
743 sz_strlcat(gamestart_buffer, "|");
744 }
745 sz_strlcat(gamestart_buffer, "SPACERACE");
746 }
747 } else {
748 log_sg("Setting '%s': %s", name, secfile_error());
749 }
750
751 /* We cannot delete old values from the secfile, or rather cannot
752 * change index of the later settings. Renumbering them is not easy as
753 * we don't know type of each setting we would encounter.
754 * So we keep old setting values and only add new "victories" setting. */
755 } else if (!fc_strcasecmp("alliedvictory", name)) {
756 bool value;
757
758 if (secfile_lookup_bool(loading->file, &value,
759 "settings.set%d.value", i)) {
760 if (value) {
761 if (value_buffer[0] != '\0') {
762 sz_strlcat(value_buffer, "|");
763 }
764 sz_strlcat(value_buffer, "ALLIED");
765 }
766 } else {
767 log_sg("Setting '%s': %s", name, secfile_error());
768 }
769 if (secfile_lookup_bool(loading->file, &value,
770 "settings.set%d.gamestart", i)) {
771 if (value) {
772 if (gamestart_buffer[0] != '\0') {
773 sz_strlcat(gamestart_buffer, "|");
774 }
775 sz_strlcat(gamestart_buffer, "ALLIED");
776 }
777 } else {
778 log_sg("Setting '%s': %s", name, secfile_error());
779 }
780 } else if (!fc_strcasecmp("revolen", name)) {
781 int value;
782
783 if (secfile_lookup_int(loading->file, &value,
784 "settings.set%d.value", i)) {
785 /* 0 meant RANDOM 1-5 */
786 if (value == 0) {
787 rlt = REVOLEN_RANDOM;
788 secfile_replace_int(loading->file, 5,
789 "settings.set%d.value", i);
790 } else {
791 rlt = REVOLEN_FIXED;
792 }
793 } else {
794 log_sg("Setting '%s': %s", name, secfile_error());
795 }
796 if (secfile_lookup_int(loading->file, &value,
797 "settings.set%d.gamestart", i)) {
798 /* 0 meant RANDOM 1-5 */
799 if (value == 0) {
800 gsrlt = REVOLEN_RANDOM;
801 secfile_replace_int(loading->file, 5,
802 "settings.set%d.gamestart", i);
803 } else {
804 gsrlt = REVOLEN_FIXED;
805 }
806 } else {
807 log_sg("Setting '%s': %s", name, secfile_error());
808 }
809 } else if (!fc_strcasecmp("happyborders", name)) {
810 bool value;
811
812 if (secfile_lookup_bool(loading->file, &value,
813 "settings.set%d.value", i)) {
814 secfile_entry_delete(loading->file, "settings.set%d.value", i);
815 if (value) {
816 secfile_insert_str(loading->file, "NATIONAL",
817 "settings.set%d.value", i);
818 } else {
819 secfile_insert_str(loading->file, "DISABLED",
820 "settings.set%d.value", i);
821 }
822 } else {
823 log_sg("Setting '%s': %s", name, secfile_error());
824 }
825 if (secfile_lookup_bool(loading->file, &value,
826 "settings.set%d.gamestart", i)) {
827 secfile_entry_delete(loading->file, "settings.set%d.gamestart", i);
828 if (value) {
829 secfile_insert_str(loading->file, "NATIONAL",
830 "settings.set%d.gamestart", i);
831 } else {
832 secfile_insert_str(loading->file, "DISABLED",
833 "settings.set%d.gamestart", i);
834 }
835 } else {
836 log_sg("Setting '%s': %s", name, secfile_error());
837 }
838 } else if (!fc_strcasecmp("team_pooled_research", name)) {
840 &team_pooled_research,
841 "settings.set%d.value", i),
842 "%s", secfile_error());
843 } else if (!fc_strcasecmp("diplcost", name)) {
844 /* Old 'diplcost' split to 'diplbulbcost' and 'diplgoldcost' */
845 if (secfile_lookup_int(loading->file, &dcost,
846 "settings.set%d.value", i)) {
847 } else {
848 log_sg("Setting '%s': %s", name, secfile_error());
849 }
850
851 if (secfile_lookup_int(loading->file, &dstartcost,
852 "settings.set%d.gamestart", i)) {
853 } else {
854 log_sg("Setting '%s': %s", name, secfile_error());
855 }
856 } else if (!fc_strcasecmp("huts", name)) {
857 /* Scale of 'huts' changed. */
858 int hcount;
859
860 if (secfile_lookup_int(loading->file, &hcount,
861 "settings.set%d.value", i)) {
862 } else {
863 log_sg("Setting '%s': %s", name, secfile_error());
864 }
865
866 /* Store old-style absolute value. */
867 wld.map.server.huts_absolute = hcount;
868 }
869 }
870
871 new_set_count = set_count + 2; /* 'victories' and 'revolentype' */
872
873 if (dcost >= 0) {
874 new_set_count += 2;
875 }
876
877 secfile_replace_int(loading->file, new_set_count, "settings.set_count");
878
879 secfile_insert_str(loading->file, "victories", "settings.set%d.name",
880 set_count);
881 secfile_insert_str(loading->file, value_buffer, "settings.set%d.value",
882 set_count);
883 secfile_insert_str(loading->file, "revolentype", "settings.set%d.name",
884 set_count + 1);
885 secfile_insert_str(loading->file, revolentype_str(rlt), "settings.set%d.value",
886 set_count + 1);
887
888 if (dcost >= 0) {
889 secfile_insert_str(loading->file, "diplbulbcost", "settings.set%d.name", set_count + 2);
890 secfile_insert_int(loading->file, dcost, "settings.set%d.value", set_count + 2);
891 secfile_insert_str(loading->file, "diplgoldcost", "settings.set%d.name", set_count + 3);
892 secfile_insert_int(loading->file, dcost, "settings.set%d.value", set_count + 3);
893 }
894
895 if (gamestart_valid) {
896 secfile_insert_str(loading->file, gamestart_buffer, "settings.set%d.gamestart",
897 set_count);
898 secfile_insert_str(loading->file, revolentype_str(gsrlt), "settings.set%d.gamestart",
899 set_count + 1);
900
901 if (dcost >= 0) {
902 secfile_insert_int(loading->file, dstartcost, "settings.set%d.gamestart", set_count + 2);
903 secfile_insert_int(loading->file, dstartcost, "settings.set%d.gamestart", set_count + 3);
904 }
905 }
906 }
907 }
908
909 sg_failure_ret(secfile_lookup_int(loading->file, &tsize, "savefile.trait_size"),
910 "Trait size: %s", secfile_error());
911
912 turn = secfile_lookup_int_default(loading->file, 0, "game.turn");
913
914 player_slots_iterate(pslot) {
915 int plrno = player_slot_index(pslot);
916 bool got_first_city;
917 int old_barb_type;
918 enum barbarian_type new_barb_type;
919 int i;
920 const char *name;
921 int score;
922 int units_num;
923
924 if (NULL == secfile_section_lookup(loading->file, "player%d", plrno)) {
925 continue;
926 }
927
928 /* Renamed 'capital' to 'got_first_city'. */
929 if (secfile_lookup_bool(loading->file, &got_first_city,
930 "player%d.capital", plrno)) {
931 secfile_insert_bool(loading->file, got_first_city,
932 "player%d.got_first_city", plrno);
933 }
934
935 /* Add 'anonymous' qualifiers for user names */
936 name = secfile_lookup_str_default(loading->file, "", "player%d.username", plrno);
937 secfile_insert_bool(loading->file, (!strcmp(name, ANON_USER_NAME)),
938 "player%d.unassigned_user", plrno);
939
940 name = secfile_lookup_str_default(loading->file, "", "player%d.ranked_username", plrno);
941 secfile_insert_bool(loading->file, (!strcmp(name, ANON_USER_NAME)),
942 "player%d.unassigned_ranked", plrno);
943
944 /* Convert numeric barbarian type to textual */
945 old_barb_type = secfile_lookup_int_default(loading->file, 0,
946 "player%d.ai.is_barbarian", plrno);
947 new_barb_type = barb_type_convert(old_barb_type);
948 secfile_insert_str(loading->file, barbarian_type_name(new_barb_type),
949 "player%d.ai.barb_type", plrno);
950
951 /* Pre-2.6 didn't record when a player was created or died, so we have
952 * to assume they lived from the start of the game until last turn */
953 secfile_insert_int(loading->file, turn, "player%d.turns_alive", plrno);
954
955 /* As if there never has been a war. */
956 secfile_insert_int(loading->file, -1, "player%d.last_war", plrno);
957
958 /* Assume people were playing until current reload */
959 secfile_insert_int(loading->file, 0, "player%d.idle_turns", plrno);
960
961 for (i = 0; i < tsize; i++) {
962 int val;
963
964 val = secfile_lookup_int_default(loading->file, -1, "player%d.trait.val%d",
965 plrno, i);
966 if (val != -1) {
967 secfile_insert_int(loading->file, val, "player%d.trait%d.val", plrno, i);
968 }
969
971 "player%d.trait.mod%d", plrno, i),
972 "Trait mod: %s", secfile_error());
973 secfile_insert_int(loading->file, val, "player%d.trait%d.mod", plrno, i);
974 }
975
976 score = secfile_lookup_int_default(loading->file, -1,
977 "player%d.units_built", plrno);
978 if (score >= 0) {
979 secfile_insert_int(loading->file, score, "score%d.units_built", plrno);
980 }
981
982 score = secfile_lookup_int_default(loading->file, -1,
983 "player%d.units_killed", plrno);
984 if (score >= 0) {
985 secfile_insert_int(loading->file, score, "score%d.units_killed", plrno);
986 }
987
988 score = secfile_lookup_int_default(loading->file, -1,
989 "player%d.units_lost", plrno);
990 if (score >= 0) {
991 secfile_insert_int(loading->file, score, "score%d.units_lost", plrno);
992 }
993
994 /* Units orders. */
995 units_num = secfile_lookup_int_default(loading->file, 0,
996 "player%d.nunits",
997 plrno);
998
999 for (i = 0; i < units_num; i++) {
1000 int len;
1001
1003 "player%d.u%d.orders_last_move_safe",
1004 plrno, i)) {
1005 continue;
1006 }
1007
1008 len = secfile_lookup_int_default(loading->file, 0,
1009 "player%d.u%d.orders_length",
1010 plrno, i);
1011 if (len > 0) {
1012 char orders_str[len + 1];
1013 char *p;
1014
1015 sz_strlcpy(orders_str,
1016 secfile_lookup_str_default(loading->file, "",
1017 "player%d.u%d.orders_list",
1018 plrno, i));
1019 if ((p = strrchr(orders_str, 'm'))
1020 || (p = strrchr(orders_str, 'M'))) {
1021 *p = 'x'; /* ORDER_MOVE -> ORDER_ACTION_MOVE */
1022 secfile_replace_str(loading->file, orders_str,
1023 "player%d.u%d.orders_list", plrno, i);
1024 }
1025 }
1026 }
1028
1029 /* Add specialist order - loading time order is ok here, as we will use
1030 * that when we in later part of compatibility conversion use the specialist
1031 * values */
1033 "savefile.specialists_size");
1034 {
1035 const char **modname;
1036 int i = 0;
1037
1038 modname = fc_calloc(specialist_count(), sizeof(*modname));
1039
1041 modname[i++] = specialist_rule_name(specialist_by_number(sp));
1043
1044 secfile_insert_str_vec(loading->file, modname, specialist_count(),
1045 "savefile.specialists_vector");
1046
1047 free(modname);
1048 }
1049
1050 /* Replace all city specialist count fields with correct names */
1051 player_slots_iterate(pslot) {
1052 int plrno = player_slot_index(pslot);
1053 int ncities;
1054 int i;
1055
1056 if (NULL == secfile_section_lookup(loading->file, "player%d", plrno)) {
1057 continue;
1058 }
1059
1060 ncities = secfile_lookup_int_default(loading->file, 0, "player%d.ncities", plrno);
1061
1062 for (i = 0; i < ncities; i++) {
1063 int k = 0;
1064
1066 struct specialist *psp = specialist_by_number(sp);
1067 int count;
1068
1069 sg_failure_ret(secfile_lookup_int(loading->file, &count,
1070 "player%d.c%d.n%s",
1071 plrno, i, specialist_rule_name(psp)),
1072 "specialist error: %s", secfile_error());
1073 secfile_entry_delete(loading->file, "player%d.c%d.n%s",
1074 plrno, i, specialist_rule_name(psp));
1075 secfile_insert_int(loading->file, count, "player%d.c%d.nspe%d",
1076 plrno, i, k++);
1078 }
1080
1081 /* Build [research]. */
1082 {
1083 const struct {
1084 const char *name;
1085 enum entry_type type;
1086 } entries[] = {
1087 { "goal_name", ENTRY_STR },
1088 { "techs", ENTRY_INT },
1089 { "futuretech", ENTRY_INT },
1090 { "bulbs_before", ENTRY_INT },
1091 { "saved_name", ENTRY_STR },
1092 { "bulbs", ENTRY_INT },
1093 { "now_name", ENTRY_STR },
1094 { "got_tech", ENTRY_BOOL },
1095 { "done", ENTRY_STR }
1096 };
1097
1098 int researches[MAX(player_slot_count(), team_slot_count())];
1099 int count = 0;
1100 int i;
1101
1102 for (i = 0; i < ARRAY_SIZE(researches); i++) {
1103 researches[i] = -1;
1104 }
1105
1106 player_slots_iterate(pslot) {
1107 int plrno = player_slot_index(pslot);
1108 int ival;
1109 bool bval;
1110 const char *sval;
1111 int j;
1112
1113 if (secfile_section_lookup(loading->file, "player%d", plrno) == NULL) {
1114 continue;
1115 }
1116
1117 /* Get the research number. */
1118 if (team_pooled_research) {
1119 i = secfile_lookup_int_default(loading->file, plrno,
1120 "player%d.team_no", plrno);
1121 } else {
1122 i = plrno;
1123 }
1124
1125 sg_failure_ret(i >= 0 && i < ARRAY_SIZE(researches),
1126 "Research out of bounds (%d)!", i);
1127
1128 /* Find the index in [research] section. */
1129 if (researches[i] == -1) {
1130 /* This is the first player for this research. */
1131 secfile_insert_int(loading->file, i, "research.r%d.number", count);
1132 researches[i] = count;
1133 count++;
1134 }
1135 i = researches[i];
1136
1137 /* Move entries. */
1138 for (j = 0; j < ARRAY_SIZE(entries); j++) {
1139 switch (entries[j].type) {
1140 case ENTRY_BOOL:
1141 if (secfile_lookup_bool(loading->file, &bval,
1142 "player%d.research.%s",
1143 plrno, entries[j].name)) {
1144 secfile_insert_bool(loading->file, bval, "research.r%d.%s",
1145 i, entries[j].name);
1146 }
1147 break;
1148 case ENTRY_INT:
1149 if (secfile_lookup_int(loading->file, &ival,
1150 "player%d.research.%s",
1151 plrno, entries[j].name)) {
1152 secfile_insert_int(loading->file, ival, "research.r%d.%s",
1153 i, entries[j].name);
1154 }
1155 break;
1156 case ENTRY_STR:
1157 if ((sval = secfile_lookup_str(loading->file,
1158 "player%d.research.%s",
1159 plrno, entries[j].name))) {
1160 secfile_insert_str(loading->file, sval, "research.r%d.%s",
1161 i, entries[j].name);
1162 }
1163 break;
1164 case ENTRY_FLOAT:
1166 "Research related entry marked as float.");
1167 break;
1170 break;
1171 case ENTRY_ILLEGAL:
1173 break;
1174 }
1175 }
1177 secfile_insert_int(loading->file, count, "research.count");
1178 }
1179
1180 /* Add diplstate type order. */
1181 secfile_insert_int(loading->file, DS_LAST,
1182 "savefile.diplstate_type_size");
1183 if (DS_LAST > 0) {
1184 const char **modname;
1185 int i;
1186 int j;
1187
1188 i = 0;
1189 modname = fc_calloc(DS_LAST, sizeof(*modname));
1190
1191 for (j = 0; j < DS_LAST; j++) {
1192 modname[i++] = diplstate_type_name(j);
1193 }
1194
1195 secfile_insert_str_vec(loading->file, modname,
1196 DS_LAST,
1197 "savefile.diplstate_type_vector");
1198 free(modname);
1199 }
1200
1201 /* Fix save games from Freeciv versions with a bug that made it view
1202 * "Never met" as closer than "Peace" or "Alliance". */
1203 player_slots_iterate(pslot) {
1204 int plrno = player_slot_index(pslot);
1205
1206 if (NULL == secfile_section_lookup(loading->file, "player%d", plrno)) {
1207 continue;
1208 }
1209
1210 player_slots_iterate(pslot2) {
1211 int i = player_slot_index(pslot2);
1212 char buf[32];
1213 int current;
1214 int closest;
1215
1216 if (NULL == secfile_section_lookup(loading->file, "player%d", i)) {
1217 continue;
1218 }
1219
1220 fc_snprintf(buf, sizeof(buf), "player%d.diplstate%d", plrno, i);
1221
1222 /* Read the current diplomatic state. */
1223 current = secfile_lookup_int_default(loading->file, DS_NO_CONTACT,
1224 "%s.type",
1225 buf);
1226
1227 /* Read the closest diplomatic state. */
1228 closest = secfile_lookup_int_default(loading->file, DS_NO_CONTACT,
1229 "%s.max_state",
1230 buf);
1231
1232 if (closest == DS_NO_CONTACT
1233 && (current == DS_PEACE
1234 || current == DS_ALLIANCE)) {
1235 const char *name1 = secfile_lookup_str_default(loading->file, "",
1236 "player%d.name", plrno);
1237 const char *name2 = secfile_lookup_str_default(loading->file, "",
1238 "player%d.name", i);
1239 /* The current relationship is closer than what the save game
1240 * claims is the closes relationship ever. */
1241
1242 log_sg(_("The save game is wrong about what the closest"
1243 " relationship %s (player %d) and %s (player %d) have had is."
1244 " Fixing it..."),
1245 name1, plrno, name2, i);
1246
1247 secfile_replace_int(loading->file, current, "%s.max_state", buf);
1248 }
1251}
1252
1253/************************************************************************/
1256static int increase_secfile_turn_int(struct loaddata *loading, const char *key,
1257 int old_def, bool keep_default)
1258{
1259 int value;
1260
1261 value = secfile_lookup_int_default(loading->file, old_def, "%s", key);
1262
1263 if (value != old_def || !keep_default) {
1264 value++;
1265 secfile_replace_int(loading->file, value, "%s", key);
1266 }
1267
1268 return value;
1269}
1270
1271/************************************************************************/
1277static void compat_load_030000(struct loaddata *loading,
1278 enum sgf_version format_class)
1279{
1280 bool randsaved;
1281 int num_settings;
1282 bool started;
1283 int old_turn;
1284 int event_count;
1285 int i;
1286
1287 /* Check status and return if not OK (sg_success FALSE). */
1288 sg_check_ret();
1289
1290 log_debug("Upgrading data from savegame to version 3.0.0");
1291
1292 /* Rename "random.save" as "random.saved"
1293 * Note that it's not an error if a scenario does not have [random] at all. */
1294 if (secfile_lookup_bool(loading->file, &randsaved, "random.save")) {
1295 secfile_insert_bool(loading->file, randsaved, "random.saved");
1296 }
1297
1298 /* Already started games should have their turn counts increased by 1 */
1299 if (secfile_lookup_bool_default(loading->file, TRUE, "game.save_players")) {
1300 started = TRUE;
1301
1302 old_turn = increase_secfile_turn_int(loading, "game.turn", 0, FALSE) - 1;
1303 increase_secfile_turn_int(loading, "game.scoreturn", old_turn + GAME_DEFAULT_SCORETURN, FALSE);
1304 increase_secfile_turn_int(loading, "history.turn", -2, TRUE);
1305 } else {
1306 started = FALSE;
1307 }
1308
1309 player_slots_iterate(pslot) {
1310 int plrno = player_slot_index(pslot);
1311 const char *flag_names[1];
1312
1313 if (secfile_section_lookup(loading->file, "player%d", plrno) == NULL) {
1314 continue;
1315 }
1316
1317 if (secfile_lookup_bool_default(loading->file, FALSE, "player%d.ai.control",
1318 plrno)) {
1319 flag_names[0] = plr_flag_id_name(PLRF_AI);
1320
1321 secfile_insert_str_vec(loading->file, flag_names, 1,
1322 "player%d.flags", plrno);
1323 }
1324
1325 if (started) {
1326 int num = secfile_lookup_int_default(loading->file, 0,
1327 "player%d.nunits",
1328 plrno);
1329
1330 for (i = 0; i < num; i++) {
1331 char buf[64];
1332
1333 fc_snprintf(buf, sizeof(buf), "player%d.u%d.born", plrno, i);
1334
1335 increase_secfile_turn_int(loading, buf, old_turn, FALSE);
1336 }
1337
1338 num = secfile_lookup_int_default(loading->file, 0,
1339 "player%d.ncities", plrno);
1340
1341 for (i = 0; i < num; i++) {
1342 char buf[64];
1343
1344 fc_snprintf(buf, sizeof(buf), "player%d.c%d.turn_founded", plrno, i);
1345
1346 increase_secfile_turn_int(loading, buf, -2, TRUE);
1347 }
1348 }
1350
1351 /* Settings */
1352 num_settings = secfile_lookup_int_default(loading->file, 0,
1353 "settings.set_count");
1354
1355 /* User meta server message is now a setting. */
1357 "game.meta_usermessage")) {
1358 const char *metamessage;
1359
1360 metamessage = secfile_lookup_str_default(loading->file, "",
1361 "game.meta_message");
1362
1363 /* Insert the meta message as a setting */
1364 secfile_insert_str(loading->file, "metamessage",
1365 "settings.set%d.name", num_settings);
1366 secfile_insert_str(loading->file, metamessage,
1367 "settings.set%d.value", num_settings);
1368 secfile_insert_str(loading->file, "",
1369 "settings.set%d.gamestart", num_settings);
1370 num_settings++;
1371 }
1372
1373 secfile_replace_int(loading->file, num_settings, "settings.set_count");
1374
1375 event_count = secfile_lookup_int_default(loading->file, 0, "event_cache.count");
1376
1377 for (i = 0; i < event_count; i++) {
1378 const char *etype;
1379
1380 etype = secfile_lookup_str(loading->file, "event_cache.events%d.event", i);
1381
1382 if (etype != NULL && !fc_strcasecmp("E_UNIT_WIN", etype)) {
1383 secfile_replace_str(loading->file, "E_UNIT_WIN_DEF",
1384 "event_cache.events%d.event", i);
1385 }
1386 }
1387}
1388
1389/************************************************************************/
1392static void insert_server_side_agent(struct loaddata *loading,
1393 enum sgf_version format_class)
1394{
1395 int ssa_size;
1396
1397 if (format_class == SAVEGAME_2) {
1398 /* Handled in savegame2 */
1399 return;
1400 }
1401
1402 ssa_size = secfile_lookup_int_default(loading->file, 0,
1403 "savefile.server_side_agent_size");
1404
1405 if (ssa_size != 0) {
1406 /* Already inserted. */
1407 return;
1408 }
1409
1410 /* Add server side agent order. */
1411 secfile_insert_int(loading->file, SSA_COUNT,
1412 "savefile.server_side_agent_size");
1413 if (SSA_COUNT > 0) {
1414 const char **modname;
1415 int i;
1416 int j;
1417
1418 i = 0;
1419 modname = fc_calloc(SSA_COUNT, sizeof(*modname));
1420
1421 for (j = 0; j < SSA_COUNT; j++) {
1422 modname[i++] = server_side_agent_name(j);
1423 }
1424
1425 secfile_insert_str_vec(loading->file, modname,
1426 SSA_COUNT,
1427 "savefile.server_side_agent_list");
1428 free(modname);
1429 }
1430
1431 /* Insert server_side_agent unit field. */
1432 player_slots_iterate(pslot) {
1433 int unit;
1434 int units_num;
1435 int plrno = player_slot_index(pslot);
1436
1437 if (secfile_section_lookup(loading->file, "player%d", plrno)
1438 == NULL) {
1439 continue;
1440 }
1441
1442 /* Number of units the player has. */
1443 units_num = secfile_lookup_int_default(loading->file, 0,
1444 "player%d.nunits",
1445 plrno);
1446
1447 for (unit = 0; unit < units_num; unit++) {
1448 bool ai;
1449
1450 if (secfile_section_lookup(loading->file,
1451 "player%d.u%d.server_side_agent",
1452 plrno, unit)
1453 != NULL) {
1454 /* Already updated? */
1455 continue;
1456 }
1457
1458 ai = secfile_lookup_bool_default(loading->file, FALSE,
1459 "player%d.u%d.ai",
1460 plrno, unit);
1461
1462 if (ai) {
1463 /* Autosettler and Autoexplore are separated by
1464 * compat_post_load_030100() when set to SSA_AUTOSETTLER */
1465 secfile_insert_int(loading->file, SSA_AUTOSETTLER,
1466 "player%d.u%d.server_side_agent",
1467 plrno, unit);
1468 } else {
1469 secfile_insert_int(loading->file, SSA_NONE,
1470 "player%d.u%d.server_side_agent",
1471 plrno, unit);
1472 }
1473 }
1475}
1476
1477/************************************************************************/
1483static void compat_load_030100(struct loaddata *loading,
1484 enum sgf_version format_class)
1485{
1486 /* Check status and return if not OK (sg_success FALSE). */
1487 sg_check_ret();
1488
1489 log_debug("Upgrading data from savegame to version 3.1.0");
1490
1491 /* Actions are now stored by number. */
1492 player_slots_iterate(pslot) {
1493 int unit;
1494 int units_num;
1495 int plrno = player_slot_index(pslot);
1496
1497 if (secfile_section_lookup(loading->file, "player%d", plrno)
1498 == NULL) {
1499 continue;
1500 }
1501
1502 /* Number of units the player has. */
1503 units_num = secfile_lookup_int_default(loading->file, 0,
1504 "player%d.nunits",
1505 plrno);
1506
1507 for (unit = 0; unit < units_num; unit++) {
1508 const char *action_unitstr;
1509 int order_len;
1510
1511 order_len = secfile_lookup_int_default(loading->file, 0,
1512 "player%d.u%d.orders_length",
1513 plrno, unit);
1514
1515 if ((action_unitstr = secfile_lookup_str_default(loading->file, "",
1516 "player%d.u%d.action_list",
1517 plrno, unit))) {
1518 int order_num;
1519
1520 if (order_len > strlen(action_unitstr)) {
1521 order_len = strlen(action_unitstr);
1522 }
1523
1524 for (order_num = 0; order_num < order_len; order_num++) {
1525 int unconverted_action_id;
1526
1527 if (action_unitstr[order_num] == '?') {
1528 unconverted_action_id = -1;
1529 } else {
1530 unconverted_action_id = char2num(action_unitstr[order_num]);
1531 }
1532
1533 if (order_num == 0) {
1534 /* The start of a vector has no number. */
1535 secfile_insert_int(loading->file, unconverted_action_id,
1536 "player%d.u%d.action_vec",
1537 plrno, unit);
1538 } else {
1539 secfile_insert_int(loading->file, unconverted_action_id,
1540 "player%d.u%d.action_vec,%d",
1541 plrno, unit, order_num);
1542 }
1543 }
1544 }
1545 }
1547
1548 {
1549 int action_count;
1550
1551 action_count = secfile_lookup_int_default(loading->file, 0,
1552 "savefile.action_size");
1553
1554 if (action_count > 0) {
1555 const char **modname;
1556 const char **savemod;
1557 int j;
1558 const char *dur_name = "Disband Unit Recover";
1559
1560 modname = secfile_lookup_str_vec(loading->file, &loading->action.size,
1561 "savefile.action_vector");
1562
1563 savemod = fc_calloc(action_count, sizeof(*savemod));
1564
1565 for (j = 0; j < action_count; j++) {
1566 if (!fc_strcasecmp("Recycle Unit", modname[j])) {
1567 savemod[j] = dur_name;
1568 } else {
1569 savemod[j] = modname[j];
1570 }
1571 }
1572
1573 secfile_replace_str_vec(loading->file, savemod, action_count,
1574 "savefile.action_vector");
1575
1576 free(savemod);
1577 }
1578 }
1579
1580 /* Server setting migration. */
1581 {
1582 int set_count;
1583
1584 if (secfile_lookup_int(loading->file, &set_count, "settings.set_count")) {
1585 int i;
1586 bool gamestart_valid
1588 "settings.gamestart_valid");
1589
1590 /* Only add gamesetdef if gamestart is valid at all */
1591 if (gamestart_valid) {
1592 for (i = 0; i < set_count; i++) {
1593 const char *name
1594 = secfile_lookup_str(loading->file, "settings.set%d.name", i);
1595
1596 if (!name) {
1597 continue;
1598 }
1599
1600 secfile_insert_str(loading->file, setting_default_level_name(SETDEF_CHANGED),
1601 "settings.set%d.gamesetdef", i);
1602 }
1603 }
1604 }
1605 }
1606
1607 /* Explicit server side agent was new in 3.1 */
1608 insert_server_side_agent(loading, format_class);
1609}
1610
1611/************************************************************************/
1614static void unit_order_activity_to_action(struct unit *act_unit)
1615{
1616 int i;
1617
1618 for (i = 0; i < act_unit->orders.length; i++) {
1619 struct unit_order *order = &act_unit->orders.list[i];
1620
1621 if (order->order != ORDER_ACTIVITY) {
1622 continue;
1623 }
1624
1625 switch (order->activity) {
1626 case ACTIVITY_FALLOUT:
1627 case ACTIVITY_POLLUTION:
1628 case ACTIVITY_MINE:
1629 case ACTIVITY_IRRIGATE:
1630 case ACTIVITY_PLANT:
1631 case ACTIVITY_CULTIVATE:
1632 case ACTIVITY_TRANSFORM:
1633 case ACTIVITY_CONVERT:
1634 case ACTIVITY_FORTIFYING:
1635 case ACTIVITY_BASE:
1636 case ACTIVITY_GEN_ROAD:
1637 case ACTIVITY_PILLAGE:
1638 action_iterate(act_id) {
1639 struct action *paction = action_by_number(act_id);
1640
1641 if (actres_get_activity(paction->result) == order->activity) {
1642 order->order = ORDER_PERFORM_ACTION;
1643 order->action = action_number(paction);
1644 order->activity = ACTIVITY_LAST;
1645 break;
1646 }
1648 break;
1649 case ACTIVITY_SENTRY:
1650 /* Not an action */
1651 break;
1652 case ACTIVITY_EXPLORE:
1653 case ACTIVITY_IDLE:
1654 case ACTIVITY_GOTO:
1655 case ACTIVITY_FORTIFIED:
1656 case ACTIVITY_OLD_ROAD:
1657 case ACTIVITY_OLD_RAILROAD:
1658 case ACTIVITY_FORTRESS:
1659 case ACTIVITY_AIRBASE:
1660 case ACTIVITY_PATROL_UNUSED:
1661 case ACTIVITY_LAST:
1662 case ACTIVITY_UNKNOWN:
1663 log_error("Activity %d is not supposed to appear in unit orders",
1664 order->activity);
1665 break;
1666 }
1667 }
1668}
1669
1670/************************************************************************/
1673static enum direction8 dir_opposite(enum direction8 dir)
1674{
1675 switch (dir) {
1676 case DIR8_NORTH:
1677 return DIR8_SOUTH;
1678 case DIR8_NORTHEAST:
1679 return DIR8_SOUTHWEST;
1680 case DIR8_EAST:
1681 return DIR8_WEST;
1682 case DIR8_SOUTHEAST:
1683 return DIR8_NORTHWEST;
1684 case DIR8_SOUTH:
1685 return DIR8_NORTH;
1686 case DIR8_SOUTHWEST:
1687 return DIR8_NORTHEAST;
1688 case DIR8_WEST:
1689 return DIR8_EAST;
1690 case DIR8_NORTHWEST:
1691 return DIR8_SOUTHEAST;
1692 }
1693
1694 return DIR8_ORIGIN;
1695}
1696
1697/************************************************************************/
1700static void upgrade_unit_order_targets(struct unit *act_unit)
1701{
1702 int i;
1703 struct tile *current_tile;
1704 struct tile *tgt_tile;
1705
1706 if (!unit_has_orders(act_unit)) {
1707 return;
1708 }
1709
1710 /* The order index is for the unit at its current tile. */
1711 current_tile = unit_tile(act_unit);
1712
1713 /* Rewind to the beginning of the orders */
1714 for (i = act_unit->orders.index; i > 0 && current_tile != NULL; i--) {
1715 struct unit_order *prev_order = &act_unit->orders.list[i - 1];
1716
1717 if (!(prev_order->order == ORDER_PERFORM_ACTION
1719 unit_type_get(act_unit)))
1720 && direction8_is_valid(prev_order->dir)) {
1721 current_tile = mapstep(&(wld.map), current_tile,
1722 dir_opposite(prev_order->dir));
1723 }
1724 }
1725
1726 /* Upgrade to explicit target tile */
1727 for (i = 0; i < act_unit->orders.length && current_tile != NULL; i++) {
1728 struct unit_order *order = &act_unit->orders.list[i];
1729
1730 if (order->order == ORDER_PERFORM_ACTION
1731 && order->target != NO_TARGET) {
1732 /* The target is already specified in the new format. */
1733
1734 /* The index_to_tile() call has no side-effects that we
1735 * would want also in NDEBUG builds. */
1736 fc_assert(index_to_tile(&(wld.map), order->target) != NULL);
1737 return;
1738 }
1739
1740 if (!direction8_is_valid(order->dir)) {
1741 /* The target of the action is on the actor's tile. */
1742 tgt_tile = current_tile;
1743 } else {
1744 /* The target of the action is on a tile next to the actor. */
1745 tgt_tile = mapstep(&(wld.map), current_tile, order->dir);
1746 }
1747
1748 if (order->order == ORDER_PERFORM_ACTION) {
1749 if (tgt_tile != NULL) {
1750 struct action *paction = action_by_number(order->action);
1751
1752 order->target = tgt_tile->index;
1753 /* Leave no traces. */
1754 order->dir = DIR8_ORIGIN;
1755
1756 if (!utype_is_unmoved_by_action(paction, unit_type_get(act_unit))) {
1757 /* The action moves the unit to the target tile (unless this is the
1758 * final order) */
1760 unit_type_get(act_unit))
1761 || i == act_unit->orders.length - 1);
1762 current_tile = tgt_tile;
1763 }
1764 } else {
1765 current_tile = NULL;
1766 }
1767 } else {
1768 current_tile = tgt_tile;
1769 }
1770 }
1771
1772 if (current_tile == NULL) {
1773 log_sg("Illegal orders for %s. Cancelling.", unit_rule_name(act_unit));
1774 free_unit_orders(act_unit);
1775 }
1776}
1777
1778/************************************************************************/
1781static void upgrade_server_side_agent(struct loaddata *loading)
1782{
1783 players_iterate_alive(pplayer) {
1784 unit_list_iterate(pplayer->units, punit) {
1785 if (punit->activity == ACTIVITY_EXPLORE) {
1786 punit->ssa_controller = SSA_AUTOEXPLORE;
1787 }
1790}
1791
1792/************************************************************************/
1795static void compat_post_load_030100(struct loaddata *loading,
1796 enum sgf_version format_class)
1797{
1798 /* Check status and return if not OK (sg_success FALSE). */
1799 sg_check_ret();
1800
1801 /* Action orders were new in 3.0 */
1802 if (format_class == SAVEGAME_3) {
1803 /* Only 3.0 savegames may have "Attack" action orders. */
1804 players_iterate_alive(pplayer) {
1805 unit_list_iterate(pplayer->units, punit) {
1806 int i;
1807
1808 if (!punit->has_orders) {
1809 continue;
1810 }
1811
1813 || punit->orders.list != NULL, continue);
1814
1815 for (i = 0; i < punit->orders.length; i++) {
1816 /* "Attack" was split in "Suicide Attack" and "Attack" in 3.1. */
1818 && punit->orders.list[i].action == ACTION_ATTACK
1819 && !unit_can_do_action(punit, ACTION_ATTACK)
1820 && unit_can_do_action(punit, ACTION_SUICIDE_ATTACK)) {
1821 punit->orders.list[i].action = ACTION_SUICIDE_ATTACK;
1822 }
1823
1824 /* Production targeted actions were split from building targeted
1825 * actions in 3.1. The building sub target encoding changed. */
1827 && ((punit->orders.list[i].action
1828 == ACTION_SPY_TARGETED_SABOTAGE_CITY)
1829 || (punit->orders.list[i].action
1830 == ACTION_SPY_TARGETED_SABOTAGE_CITY_ESC))) {
1831 punit->orders.list[i].sub_target -= 1;
1832 }
1834 && (punit->orders.list[i].action
1835 == ACTION_SPY_TARGETED_SABOTAGE_CITY)
1836 && punit->orders.list[i].sub_target == -1) {
1838 = ACTION_SPY_SABOTAGE_CITY_PRODUCTION;
1839 }
1841 && (punit->orders.list[i].action
1842 == ACTION_SPY_TARGETED_SABOTAGE_CITY_ESC)
1843 && punit->orders.list[i].sub_target == -1) {
1845 = ACTION_SPY_SABOTAGE_CITY_PRODUCTION_ESC;
1846 }
1847 }
1850 }
1851
1852 /* Explicit server side agent was new in 3.1 */
1854
1855 /* Some activities should only be ordered in action orders. */
1856 players_iterate_alive(pplayer) {
1857 unit_list_iterate(pplayer->units, punit) {
1861
1862 /* Unit order action target isn't dir anymore */
1863 players_iterate_alive(pplayer) {
1864 unit_list_iterate(pplayer->units, punit) {
1868
1869 /* Backward compatibility: if we had any open-ended orders (pillage)
1870 * in the savegame, assign specific targets now */
1871 players_iterate_alive(pplayer) {
1872 unit_list_iterate(pplayer->units, punit) {
1874 &punit->activity,
1878}
1879
1880/************************************************************************/
1883enum ai_level ai_level_convert(int old_level)
1884{
1885 switch (old_level) {
1886 case 1:
1887 return AI_LEVEL_AWAY;
1888 case 2:
1889 return AI_LEVEL_NOVICE;
1890 case 3:
1891 return AI_LEVEL_EASY;
1892 case 5:
1893 return AI_LEVEL_NORMAL;
1894 case 7:
1895 return AI_LEVEL_HARD;
1896 case 8:
1897 return AI_LEVEL_CHEATING;
1898 case 10:
1899#ifdef FREECIV_DEBUG
1900 return AI_LEVEL_EXPERIMENTAL;
1901#else /* FREECIV_DEBUG */
1902 return AI_LEVEL_HARD;
1903#endif /* FREECIV_DEBUG */
1904 }
1905
1906 return ai_level_invalid();
1907}
1908
1909/************************************************************************/
1912enum barbarian_type barb_type_convert(int old_type)
1913{
1914 switch (old_type) {
1915 case 0:
1916 return NOT_A_BARBARIAN;
1917 case 1:
1918 return LAND_BARBARIAN;
1919 case 2:
1920 return SEA_BARBARIAN;
1921 }
1922
1923 return barbarian_type_invalid();
1924}
1925
1926/************************************************************************/
1935
1936/************************************************************************/
int action_number(const struct action *action)
Definition actions.c:1868
enum unit_activity actres_get_activity(enum action_result result)
Definition actions.c:2136
static struct action * action_by_number(action_id act_id)
Definition actions.h:638
#define action_iterate_end
Definition actions.h:472
#define action_iterate(_act_)
Definition actions.h:467
struct @124::my_agent entries[MAX_AGENTS]
const char * default_ai_type_name(void)
Definition aiiface.c:262
struct extra_type * base_extra_get(const struct base_type *pbase)
Definition base.c:101
struct base_type * base_by_number(const Base_type_id id)
Definition base.c:76
bool has_capability(const char *cap, const char *capstr)
Definition capability.c:77
static void road(QVariant data1, QVariant data2)
Definition dialogs.cpp:2819
static void base(QVariant data1, QVariant data2)
Definition dialogs.cpp:2840
struct unit struct city struct unit struct tile struct extra_type const struct act_prob *act_probs int actor_unit_id struct unit struct unit * punit
Definition dialogs_g.h:73
struct extra_type_list * extra_type_list_by_cause(enum extra_cause cause)
Definition extras.c:241
#define extra_type_by_cause_iterate_end
Definition extras.h:315
#define extra_type_by_cause_iterate(_cause, _extra)
Definition extras.h:309
#define NO_TARGET
Definition fc_types.h:324
int Road_type_id
Definition fc_types.h:354
#define EC_SPECIAL
Definition fc_types.h:968
revolen_type
Definition fc_types.h:1134
@ REVOLEN_RANDOM
Definition fc_types.h:1136
@ REVOLEN_RANDQUICK
Definition fc_types.h:1138
@ REVOLEN_FIXED
Definition fc_types.h:1135
@ REVOLEN_QUICKENING
Definition fc_types.h:1137
#define DIR8_ORIGIN
Definition fc_types.h:427
int Base_type_id
Definition fc_types.h:353
#define _(String)
Definition fcintl.h:67
struct world wld
Definition game.c:58
#define GAME_DEFAULT_REVOLENTYPE
Definition game.h:710
#define GAME_DEFAULT_SCORETURN
Definition game.h:560
#define GAME_DEFAULT_SAVETURNS
Definition game.h:658
#define GAME_DEFAULT_TEAM_POOLED_RESEARCH
Definition game.h:544
GType type
Definition repodlgs.c:1312
const char * name
Definition inputfile.c:127
#define fc_assert(condition)
Definition log.h:176
#define fc_assert_action(condition, action)
Definition log.h:187
#define log_debug(message,...)
Definition log.h:115
#define log_normal(message,...)
Definition log.h:107
#define log_error(message,...)
Definition log.h:103
struct tile * index_to_tile(const struct civ_map *imap, int mindex)
Definition map.c:454
struct tile * mapstep(const struct civ_map *nmap, const struct tile *ptile, enum direction8 dir)
Definition map.c:369
#define fc_calloc(n, esz)
Definition mem.h:38
#define FC_FREE(ptr)
Definition mem.h:41
#define fc_strdup(str)
Definition mem.h:43
char * lines
Definition packhand.c:129
int len
Definition packhand.c:125
int player_slot_count(void)
Definition player.c:411
int player_slot_index(const struct player_slot *pslot)
Definition player.c:419
#define ANON_USER_NAME
Definition player.h:48
#define player_slots_iterate(_pslot)
Definition player.h:521
#define players_iterate_alive_end
Definition player.h:545
#define player_slots_iterate_end
Definition player.h:525
#define players_iterate_alive(_pplayer)
Definition player.h:540
const char * secfile_error(void)
bool secfile_lookup_int(const struct section_file *secfile, int *ival, const char *path,...)
bool secfile_entry_delete(struct section_file *secfile, const char *path,...)
bool entry_set_name(struct entry *pentry, const char *name)
bool secfile_lookup_enum_data(const struct section_file *secfile, int *pvalue, bool bitwise, secfile_enum_name_data_fn_t name_fn, secfile_data_t data, const char *path,...)
const struct entry_list * section_entries(const struct section *psection)
const char ** secfile_lookup_str_vec(const struct section_file *secfile, size_t *dim, const char *path,...)
const char * entry_name(const struct entry *pentry)
const char * secfile_lookup_str(const struct section_file *secfile, const char *path,...)
bool secfile_lookup_bool_default(const struct section_file *secfile, bool def, const char *path,...)
int secfile_lookup_int_default(const struct section_file *secfile, int def, const char *path,...)
struct section * secfile_section_by_name(const struct section_file *secfile, const char *name)
struct section * secfile_section_lookup(const struct section_file *secfile, const char *path,...)
const char * secfile_lookup_str_default(const struct section_file *secfile, const char *def, const char *path,...)
bool secfile_lookup_bool(const struct section_file *secfile, bool *bval, const char *path,...)
#define secfile_replace_str_vec(secfile, strings, dim, path,...)
#define secfile_insert_int(secfile, value, path,...)
entry_type
@ ENTRY_FILEREFERENCE
@ ENTRY_INT
@ ENTRY_FLOAT
@ ENTRY_STR
@ ENTRY_ILLEGAL
@ ENTRY_BOOL
#define secfile_insert_str_vec(secfile, strings, dim, path,...)
#define secfile_replace_int(secfile, value, path,...)
#define entry_list_iterate_end
#define secfile_insert_str(secfile, string, path,...)
#define secfile_insert_bool(secfile, value, path,...)
#define secfile_replace_str(secfile, string, path,...)
#define entry_list_iterate(entlist, pentry)
#define secfile_replace_bool(secfile, value, path,...)
const void * secfile_data_t
struct extra_type * road_extra_get(const struct road_type *proad)
Definition road.c:42
struct road_type * road_by_number(Road_type_id id)
Definition road.c:58
static void compat_post_load_030100(struct loaddata *loading, enum sgf_version format_class)
struct extra_type * resource_by_identifier(const char identifier)
Definition savecompat.c:300
int current_compat_ver(void)
Definition savecompat.c:199
static void compat_load_030000(struct loaddata *loading, enum sgf_version format_class)
static const char num_chars[]
Definition savecompat.c:239
bool sg_success
Definition savecompat.c:32
static const char * killcitizen_enum_str(secfile_data_t data, int bit)
Definition savecompat.c:531
enum barbarian_type barb_type_convert(int old_type)
static char * special_names[]
Definition savecompat.c:34
static struct compatibility compat[]
Definition savecompat.c:101
void set_unit_activity_road(struct unit *punit, Road_type_id road)
static void compat_load_020400(struct loaddata *loading, enum sgf_version format_class)
Definition savecompat.c:318
static void upgrade_unit_order_targets(struct unit *act_unit)
char bin2ascii_hex(int value, int halfbyte_wanted)
Definition savecompat.c:209
static const int compat_num
Definition savecompat.c:122
static void compat_load_020600(struct loaddata *loading, enum sgf_version format_class)
Definition savecompat.c:674
static enum direction8 dir_opposite(enum direction8 dir)
void(* load_version_func_t)(struct loaddata *loading, enum sgf_version format_class)
Definition savecompat.c:81
int char2num(char ch)
Definition savecompat.c:245
static void upgrade_server_side_agent(struct loaddata *loading)
void sg_load_compat(struct loaddata *loading, enum sgf_version format_class)
Definition savecompat.c:132
static void unit_order_activity_to_action(struct unit *act_unit)
static char * revolentype_str(enum revolen_type type)
Definition savecompat.c:655
static int increase_secfile_turn_int(struct loaddata *loading, const char *key, int old_def, bool keep_default)
enum ai_level ai_level_convert(int old_level)
void set_unit_activity_base(struct unit *punit, Base_type_id base)
int ascii_hex2bin(char ch, int halfbyte)
Definition savecompat.c:221
struct extra_type * special_extra_get(int spe)
Definition savecompat.c:286
static void compat_load_020500(struct loaddata *loading, enum sgf_version format_class)
Definition savecompat.c:548
enum tile_special_type special_by_rule_name(const char *name)
Definition savecompat.c:260
void sg_load_post_load_compat(struct loaddata *loading, enum sgf_version format_class)
Definition savecompat.c:177
const char * special_rule_name(enum tile_special_type type)
Definition savecompat.c:276
static void compat_load_030100(struct loaddata *loading, enum sgf_version format_class)
static void insert_server_side_agent(struct loaddata *loading, enum sgf_version format_class)
#define compat_current
Definition savecompat.c:123
#define sg_check_ret(...)
Definition savecompat.h:141
#define sg_warn(condition, message,...)
Definition savecompat.h:151
tile_special_type
Definition savecompat.h:29
@ S_LAST
Definition savecompat.h:38
#define hex_chars
Definition savecompat.h:196
#define sg_failure_ret_val(condition, _val, message,...)
Definition savecompat.h:175
#define sg_failure_ret(condition, message,...)
Definition savecompat.h:168
sgf_version
Definition savecompat.h:27
@ SAVEGAME_2
Definition savecompat.h:27
@ SAVEGAME_3
Definition savecompat.h:27
#define log_sg
Definition savecompat.h:137
#define ARRAY_SIZE(x)
Definition shared.h:85
#define MAX(x, y)
Definition shared.h:54
struct specialist * specialist_by_number(const Specialist_type_id id)
Definition specialist.c:100
const char * specialist_rule_name(const struct specialist *sp)
Definition specialist.c:146
Specialist_type_id specialist_count(void)
Definition specialist.c:71
#define specialist_type_iterate_end
Definition specialist.h:79
#define specialist_type_iterate(sp)
Definition specialist.h:73
size_t size
Definition specvec.h:72
enum action_result result
Definition actions.h:382
struct civ_map::@41::@43 server
int huts_absolute
Definition map_types.h:92
const load_version_func_t post_load
Definition savecompat.c:86
const load_version_func_t load
Definition savecompat.c:85
int version
Definition savecompat.h:50
size_t size
Definition savecompat.h:56
struct loaddata::@110 action
struct section_file * file
Definition savecompat.h:48
struct name_translation name
Definition specialist.h:31
Definition tile.h:49
int index
Definition tile.h:50
enum unit_activity activity
Definition unit.h:94
enum unit_orders order
Definition unit.h:93
int action
Definition unit.h:100
enum direction8 dir
Definition unit.h:102
int target
Definition unit.h:97
int sub_target
Definition unit.h:98
Definition unit.h:138
int length
Definition unit.h:195
bool has_orders
Definition unit.h:193
enum unit_activity activity
Definition unit.h:157
int index
Definition unit.h:195
struct unit::@79 orders
struct extra_type * activity_target
Definition unit.h:164
struct unit_order * list
Definition unit.h:198
enum server_side_agent ssa_controller
Definition unit.h:172
struct civ_map map
int fc_snprintf(char *str, size_t n, const char *format,...)
Definition support.c:969
int fc_strcasecmp(const char *str0, const char *str1)
Definition support.c:189
#define sz_strlcpy(dest, src)
Definition support.h:167
#define TRUE
Definition support.h:46
#define FALSE
Definition support.h:47
#define sz_strlcat(dest, src)
Definition support.h:168
int team_slot_count(void)
Definition team.c:112
char terrain_identifier(const struct terrain *pterrain)
Definition terrain.c:114
const char * terrain_rule_name(const struct terrain *pterrain)
Definition terrain.c:235
#define terrain_type_iterate(_p)
Definition terrain.h:358
#define terrain_type_iterate_end
Definition terrain.h:364
void free_unit_orders(struct unit *punit)
Definition unit.c:1753
bool unit_can_do_action(const struct unit *punit, const action_id act_id)
Definition unit.c:328
void set_unit_activity_targeted(struct unit *punit, enum unit_activity new_activity, struct extra_type *new_target)
Definition unit.c:1124
bool unit_has_orders(const struct unit *punit)
Definition unit.c:204
#define unit_tile(_pu)
Definition unit.h:395
@ ORDER_ACTIVITY
Definition unit.h:41
@ ORDER_PERFORM_ACTION
Definition unit.h:47
#define unit_list_iterate(unitlist, punit)
Definition unitlist.h:31
#define unit_list_iterate_end
Definition unitlist.h:33
void unit_assign_specific_activity_target(struct unit *punit, enum unit_activity *activity, struct extra_type **target)
Definition unittools.c:1098
const struct unit_type * unit_type_get(const struct unit *punit)
Definition unittype.c:123
bool utype_is_moved_to_tgt_by_action(const struct action *paction, const struct unit_type *utype)
Definition unittype.c:1301
const char * unit_rule_name(const struct unit *punit)
Definition unittype.c:1639
bool utype_is_unmoved_by_action(const struct action *paction, const struct unit_type *utype)
Definition unittype.c:1340