OpenTTD
settings.cpp
Go to the documentation of this file.
1 /* $Id: settings.cpp 27151 2015-02-14 21:55:30Z frosch $ */
2 
3 /*
4  * This file is part of OpenTTD.
5  * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
6  * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
7  * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
8  */
9 
26 #include "stdafx.h"
27 #include "currency.h"
28 #include "screenshot.h"
29 #include "network/network.h"
30 #include "network/network_func.h"
31 #include "settings_internal.h"
32 #include "command_func.h"
33 #include "console_func.h"
35 #include "genworld.h"
36 #include "train.h"
37 #include "news_func.h"
38 #include "window_func.h"
39 #include "sound_func.h"
40 #include "company_func.h"
41 #include "rev.h"
42 #ifdef WITH_FREETYPE
43 #include "fontcache.h"
44 #endif
45 #include "textbuf_gui.h"
46 #include "rail_gui.h"
47 #include "elrail_func.h"
48 #include "error.h"
49 #include "town.h"
50 #include "video/video_driver.hpp"
51 #include "sound/sound_driver.hpp"
52 #include "music/music_driver.hpp"
53 #include "blitter/factory.hpp"
54 #include "base_media_base.h"
55 #include "gamelog.h"
56 #include "settings_func.h"
57 #include "ini_type.h"
58 #include "ai/ai_config.hpp"
59 #include "ai/ai.hpp"
60 #include "game/game_config.hpp"
61 #include "game/game.hpp"
62 #include "ship.h"
63 #include "smallmap_gui.h"
64 #include "roadveh.h"
65 #include "fios.h"
66 #include "strings_func.h"
67 
68 #include "void_map.h"
69 #include "station_base.h"
70 
71 #include "table/strings.h"
72 #include "table/settings.h"
73 
74 #include "safeguards.h"
75 
80 char *_config_file;
81 
82 typedef std::list<ErrorMessageData> ErrorList;
84 
85 
86 typedef void SettingDescProc(IniFile *ini, const SettingDesc *desc, const char *grpname, void *object);
87 typedef void SettingDescProcList(IniFile *ini, const char *grpname, StringList *list);
88 
89 static bool IsSignedVarMemType(VarType vt);
90 
94 static const char * const _list_group_names[] = {
95  "bans",
96  "newgrf",
97  "servers",
98  "server_bind_addresses",
99  NULL
100 };
101 
109 static size_t LookupOneOfMany(const char *many, const char *one, size_t onelen = 0)
110 {
111  const char *s;
112  size_t idx;
113 
114  if (onelen == 0) onelen = strlen(one);
115 
116  /* check if it's an integer */
117  if (*one >= '0' && *one <= '9') return strtoul(one, NULL, 0);
118 
119  idx = 0;
120  for (;;) {
121  /* find end of item */
122  s = many;
123  while (*s != '|' && *s != 0) s++;
124  if ((size_t)(s - many) == onelen && !memcmp(one, many, onelen)) return idx;
125  if (*s == 0) return (size_t)-1;
126  many = s + 1;
127  idx++;
128  }
129 }
130 
138 static size_t LookupManyOfMany(const char *many, const char *str)
139 {
140  const char *s;
141  size_t r;
142  size_t res = 0;
143 
144  for (;;) {
145  /* skip "whitespace" */
146  while (*str == ' ' || *str == '\t' || *str == '|') str++;
147  if (*str == 0) break;
148 
149  s = str;
150  while (*s != 0 && *s != ' ' && *s != '\t' && *s != '|') s++;
151 
152  r = LookupOneOfMany(many, str, s - str);
153  if (r == (size_t)-1) return r;
154 
155  SetBit(res, (uint8)r); // value found, set it
156  if (*s == 0) break;
157  str = s + 1;
158  }
159  return res;
160 }
161 
170 static int ParseIntList(const char *p, int *items, int maxitems)
171 {
172  int n = 0; // number of items read so far
173  bool comma = false; // do we accept comma?
174 
175  while (*p != '\0') {
176  switch (*p) {
177  case ',':
178  /* Do not accept multiple commas between numbers */
179  if (!comma) return -1;
180  comma = false;
181  /* FALL THROUGH */
182  case ' ':
183  p++;
184  break;
185 
186  default: {
187  if (n == maxitems) return -1; // we don't accept that many numbers
188  char *end;
189  long v = strtol(p, &end, 0);
190  if (p == end) return -1; // invalid character (not a number)
191  if (sizeof(int) < sizeof(long)) v = ClampToI32(v);
192  items[n++] = v;
193  p = end; // first non-number
194  comma = true; // we accept comma now
195  break;
196  }
197  }
198  }
199 
200  /* If we have read comma but no number after it, fail.
201  * We have read comma when (n != 0) and comma is not allowed */
202  if (n != 0 && !comma) return -1;
203 
204  return n;
205 }
206 
215 static bool LoadIntList(const char *str, void *array, int nelems, VarType type)
216 {
217  int items[64];
218  int i, nitems;
219 
220  if (str == NULL) {
221  memset(items, 0, sizeof(items));
222  nitems = nelems;
223  } else {
224  nitems = ParseIntList(str, items, lengthof(items));
225  if (nitems != nelems) return false;
226  }
227 
228  switch (type) {
229  case SLE_VAR_BL:
230  case SLE_VAR_I8:
231  case SLE_VAR_U8:
232  for (i = 0; i != nitems; i++) ((byte*)array)[i] = items[i];
233  break;
234 
235  case SLE_VAR_I16:
236  case SLE_VAR_U16:
237  for (i = 0; i != nitems; i++) ((uint16*)array)[i] = items[i];
238  break;
239 
240  case SLE_VAR_I32:
241  case SLE_VAR_U32:
242  for (i = 0; i != nitems; i++) ((uint32*)array)[i] = items[i];
243  break;
244 
245  default: NOT_REACHED();
246  }
247 
248  return true;
249 }
250 
260 static void MakeIntList(char *buf, const char *last, const void *array, int nelems, VarType type)
261 {
262  int i, v = 0;
263  const byte *p = (const byte *)array;
264 
265  for (i = 0; i != nelems; i++) {
266  switch (type) {
267  case SLE_VAR_BL:
268  case SLE_VAR_I8: v = *(const int8 *)p; p += 1; break;
269  case SLE_VAR_U8: v = *(const uint8 *)p; p += 1; break;
270  case SLE_VAR_I16: v = *(const int16 *)p; p += 2; break;
271  case SLE_VAR_U16: v = *(const uint16 *)p; p += 2; break;
272  case SLE_VAR_I32: v = *(const int32 *)p; p += 4; break;
273  case SLE_VAR_U32: v = *(const uint32 *)p; p += 4; break;
274  default: NOT_REACHED();
275  }
276  buf += seprintf(buf, last, (i == 0) ? "%d" : ",%d", v);
277  }
278 }
279 
287 static void MakeOneOfMany(char *buf, const char *last, const char *many, int id)
288 {
289  int orig_id = id;
290 
291  /* Look for the id'th element */
292  while (--id >= 0) {
293  for (; *many != '|'; many++) {
294  if (*many == '\0') { // not found
295  seprintf(buf, last, "%d", orig_id);
296  return;
297  }
298  }
299  many++; // pass the |-character
300  }
301 
302  /* copy string until next item (|) or the end of the list if this is the last one */
303  while (*many != '\0' && *many != '|' && buf < last) *buf++ = *many++;
304  *buf = '\0';
305 }
306 
315 static void MakeManyOfMany(char *buf, const char *last, const char *many, uint32 x)
316 {
317  const char *start;
318  int i = 0;
319  bool init = true;
320 
321  for (; x != 0; x >>= 1, i++) {
322  start = many;
323  while (*many != 0 && *many != '|') many++; // advance to the next element
324 
325  if (HasBit(x, 0)) { // item found, copy it
326  if (!init) buf += seprintf(buf, last, "|");
327  init = false;
328  if (start == many) {
329  buf += seprintf(buf, last, "%d", i);
330  } else {
331  memcpy(buf, start, many - start);
332  buf += many - start;
333  }
334  }
335 
336  if (*many == '|') many++;
337  }
338 
339  *buf = '\0';
340 }
341 
348 static const void *StringToVal(const SettingDescBase *desc, const char *orig_str)
349 {
350  const char *str = orig_str == NULL ? "" : orig_str;
351 
352  switch (desc->cmd) {
353  case SDT_NUMX: {
354  char *end;
355  size_t val = strtoul(str, &end, 0);
356  if (end == str) {
357  ErrorMessageData msg(STR_CONFIG_ERROR, STR_CONFIG_ERROR_INVALID_VALUE);
358  msg.SetDParamStr(0, str);
359  msg.SetDParamStr(1, desc->name);
360  _settings_error_list.push_back(msg);
361  return desc->def;
362  }
363  if (*end != '\0') {
364  ErrorMessageData msg(STR_CONFIG_ERROR, STR_CONFIG_ERROR_TRAILING_CHARACTERS);
365  msg.SetDParamStr(0, desc->name);
366  _settings_error_list.push_back(msg);
367  }
368  return (void*)val;
369  }
370 
371  case SDT_ONEOFMANY: {
372  size_t r = LookupOneOfMany(desc->many, str);
373  /* if the first attempt of conversion from string to the appropriate value fails,
374  * look if we have defined a converter from old value to new value. */
375  if (r == (size_t)-1 && desc->proc_cnvt != NULL) r = desc->proc_cnvt(str);
376  if (r != (size_t)-1) return (void*)r; // and here goes converted value
377 
378  ErrorMessageData msg(STR_CONFIG_ERROR, STR_CONFIG_ERROR_INVALID_VALUE);
379  msg.SetDParamStr(0, str);
380  msg.SetDParamStr(1, desc->name);
381  _settings_error_list.push_back(msg);
382  return desc->def;
383  }
384 
385  case SDT_MANYOFMANY: {
386  size_t r = LookupManyOfMany(desc->many, str);
387  if (r != (size_t)-1) return (void*)r;
388  ErrorMessageData msg(STR_CONFIG_ERROR, STR_CONFIG_ERROR_INVALID_VALUE);
389  msg.SetDParamStr(0, str);
390  msg.SetDParamStr(1, desc->name);
391  _settings_error_list.push_back(msg);
392  return desc->def;
393  }
394 
395  case SDT_BOOLX: {
396  if (strcmp(str, "true") == 0 || strcmp(str, "on") == 0 || strcmp(str, "1") == 0) return (void*)true;
397  if (strcmp(str, "false") == 0 || strcmp(str, "off") == 0 || strcmp(str, "0") == 0) return (void*)false;
398 
399  ErrorMessageData msg(STR_CONFIG_ERROR, STR_CONFIG_ERROR_INVALID_VALUE);
400  msg.SetDParamStr(0, str);
401  msg.SetDParamStr(1, desc->name);
402  _settings_error_list.push_back(msg);
403  return desc->def;
404  }
405 
406  case SDT_STRING: return orig_str;
407  case SDT_INTLIST: return str;
408  default: break;
409  }
410 
411  return NULL;
412 }
413 
423 static void Write_ValidateSetting(void *ptr, const SettingDesc *sd, int32 val)
424 {
425  const SettingDescBase *sdb = &sd->desc;
426 
427  if (sdb->cmd != SDT_BOOLX &&
428  sdb->cmd != SDT_NUMX &&
429  sdb->cmd != SDT_ONEOFMANY &&
430  sdb->cmd != SDT_MANYOFMANY) {
431  return;
432  }
433 
434  /* We cannot know the maximum value of a bitset variable, so just have faith */
435  if (sdb->cmd != SDT_MANYOFMANY) {
436  /* We need to take special care of the uint32 type as we receive from the function
437  * a signed integer. While here also bail out on 64-bit settings as those are not
438  * supported. Unsigned 8 and 16-bit variables are safe since they fit into a signed
439  * 32-bit variable
440  * TODO: Support 64-bit settings/variables */
441  switch (GetVarMemType(sd->save.conv)) {
442  case SLE_VAR_NULL: return;
443  case SLE_VAR_BL:
444  case SLE_VAR_I8:
445  case SLE_VAR_U8:
446  case SLE_VAR_I16:
447  case SLE_VAR_U16:
448  case SLE_VAR_I32: {
449  /* Override the minimum value. No value below sdb->min, except special value 0 */
450  if (!(sdb->flags & SGF_0ISDISABLED) || val != 0) val = Clamp(val, sdb->min, sdb->max);
451  break;
452  }
453  case SLE_VAR_U32: {
454  /* Override the minimum value. No value below sdb->min, except special value 0 */
455  uint min = ((sdb->flags & SGF_0ISDISABLED) && (uint)val <= (uint)sdb->min) ? 0 : sdb->min;
456  WriteValue(ptr, SLE_VAR_U32, (int64)ClampU(val, min, sdb->max));
457  return;
458  }
459  case SLE_VAR_I64:
460  case SLE_VAR_U64:
461  default: NOT_REACHED();
462  }
463  }
464 
465  WriteValue(ptr, sd->save.conv, (int64)val);
466 }
467 
476 static void IniLoadSettings(IniFile *ini, const SettingDesc *sd, const char *grpname, void *object)
477 {
478  IniGroup *group;
479  IniGroup *group_def = ini->GetGroup(grpname);
480  IniItem *item;
481  const void *p;
482  void *ptr;
483  const char *s;
484 
485  for (; sd->save.cmd != SL_END; sd++) {
486  const SettingDescBase *sdb = &sd->desc;
487  const SaveLoad *sld = &sd->save;
488 
489  if (!SlIsObjectCurrentlyValid(sld->version_from, sld->version_to)) continue;
490 
491  /* For settings.xx.yy load the settings from [xx] yy = ? */
492  s = strchr(sdb->name, '.');
493  if (s != NULL) {
494  group = ini->GetGroup(sdb->name, s - sdb->name);
495  s++;
496  } else {
497  s = sdb->name;
498  group = group_def;
499  }
500 
501  item = group->GetItem(s, false);
502  if (item == NULL && group != group_def) {
503  /* For settings.xx.yy load the settings from [settingss] yy = ? in case the previous
504  * did not exist (e.g. loading old config files with a [settings] section */
505  item = group_def->GetItem(s, false);
506  }
507  if (item == NULL) {
508  /* For settings.xx.zz.yy load the settings from [zz] yy = ? in case the previous
509  * did not exist (e.g. loading old config files with a [yapf] section */
510  const char *sc = strchr(s, '.');
511  if (sc != NULL) item = ini->GetGroup(s, sc - s)->GetItem(sc + 1, false);
512  }
513 
514  p = (item == NULL) ? sdb->def : StringToVal(sdb, item->value);
515  ptr = GetVariableAddress(object, sld);
516 
517  switch (sdb->cmd) {
518  case SDT_BOOLX: // All four are various types of (integer) numbers
519  case SDT_NUMX:
520  case SDT_ONEOFMANY:
521  case SDT_MANYOFMANY:
522  Write_ValidateSetting(ptr, sd, (int32)(size_t)p);
523  break;
524 
525  case SDT_STRING:
526  switch (GetVarMemType(sld->conv)) {
527  case SLE_VAR_STRB:
528  case SLE_VAR_STRBQ:
529  if (p != NULL) strecpy((char*)ptr, (const char*)p, (char*)ptr + sld->length - 1);
530  break;
531 
532  case SLE_VAR_STR:
533  case SLE_VAR_STRQ:
534  free(*(char**)ptr);
535  *(char**)ptr = p == NULL ? NULL : stredup((const char*)p);
536  break;
537 
538  case SLE_VAR_CHAR: if (p != NULL) *(char *)ptr = *(const char *)p; break;
539 
540  default: NOT_REACHED();
541  }
542  break;
543 
544  case SDT_INTLIST: {
545  if (!LoadIntList((const char*)p, ptr, sld->length, GetVarMemType(sld->conv))) {
546  ErrorMessageData msg(STR_CONFIG_ERROR, STR_CONFIG_ERROR_ARRAY);
547  msg.SetDParamStr(0, sdb->name);
548  _settings_error_list.push_back(msg);
549 
550  /* Use default */
551  LoadIntList((const char*)sdb->def, ptr, sld->length, GetVarMemType(sld->conv));
552  } else if (sd->desc.proc_cnvt != NULL) {
553  sd->desc.proc_cnvt((const char*)p);
554  }
555  break;
556  }
557  default: NOT_REACHED();
558  }
559  }
560 }
561 
574 static void IniSaveSettings(IniFile *ini, const SettingDesc *sd, const char *grpname, void *object)
575 {
576  IniGroup *group_def = NULL, *group;
577  IniItem *item;
578  char buf[512];
579  const char *s;
580  void *ptr;
581 
582  for (; sd->save.cmd != SL_END; sd++) {
583  const SettingDescBase *sdb = &sd->desc;
584  const SaveLoad *sld = &sd->save;
585 
586  /* If the setting is not saved to the configuration
587  * file, just continue with the next setting */
588  if (!SlIsObjectCurrentlyValid(sld->version_from, sld->version_to)) continue;
589  if (sld->conv & SLF_NOT_IN_CONFIG) continue;
590 
591  /* XXX - wtf is this?? (group override?) */
592  s = strchr(sdb->name, '.');
593  if (s != NULL) {
594  group = ini->GetGroup(sdb->name, s - sdb->name);
595  s++;
596  } else {
597  if (group_def == NULL) group_def = ini->GetGroup(grpname);
598  s = sdb->name;
599  group = group_def;
600  }
601 
602  item = group->GetItem(s, true);
603  ptr = GetVariableAddress(object, sld);
604 
605  if (item->value != NULL) {
606  /* check if the value is the same as the old value */
607  const void *p = StringToVal(sdb, item->value);
608 
609  /* The main type of a variable/setting is in bytes 8-15
610  * The subtype (what kind of numbers do we have there) is in 0-7 */
611  switch (sdb->cmd) {
612  case SDT_BOOLX:
613  case SDT_NUMX:
614  case SDT_ONEOFMANY:
615  case SDT_MANYOFMANY:
616  switch (GetVarMemType(sld->conv)) {
617  case SLE_VAR_BL:
618  if (*(bool*)ptr == (p != NULL)) continue;
619  break;
620 
621  case SLE_VAR_I8:
622  case SLE_VAR_U8:
623  if (*(byte*)ptr == (byte)(size_t)p) continue;
624  break;
625 
626  case SLE_VAR_I16:
627  case SLE_VAR_U16:
628  if (*(uint16*)ptr == (uint16)(size_t)p) continue;
629  break;
630 
631  case SLE_VAR_I32:
632  case SLE_VAR_U32:
633  if (*(uint32*)ptr == (uint32)(size_t)p) continue;
634  break;
635 
636  default: NOT_REACHED();
637  }
638  break;
639 
640  default: break; // Assume the other types are always changed
641  }
642  }
643 
644  /* Value has changed, get the new value and put it into a buffer */
645  switch (sdb->cmd) {
646  case SDT_BOOLX:
647  case SDT_NUMX:
648  case SDT_ONEOFMANY:
649  case SDT_MANYOFMANY: {
650  uint32 i = (uint32)ReadValue(ptr, sld->conv);
651 
652  switch (sdb->cmd) {
653  case SDT_BOOLX: strecpy(buf, (i != 0) ? "true" : "false", lastof(buf)); break;
654  case SDT_NUMX: seprintf(buf, lastof(buf), IsSignedVarMemType(sld->conv) ? "%d" : "%u", i); break;
655  case SDT_ONEOFMANY: MakeOneOfMany(buf, lastof(buf), sdb->many, i); break;
656  case SDT_MANYOFMANY: MakeManyOfMany(buf, lastof(buf), sdb->many, i); break;
657  default: NOT_REACHED();
658  }
659  break;
660  }
661 
662  case SDT_STRING:
663  switch (GetVarMemType(sld->conv)) {
664  case SLE_VAR_STRB: strecpy(buf, (char*)ptr, lastof(buf)); break;
665  case SLE_VAR_STRBQ:seprintf(buf, lastof(buf), "\"%s\"", (char*)ptr); break;
666  case SLE_VAR_STR: strecpy(buf, *(char**)ptr, lastof(buf)); break;
667 
668  case SLE_VAR_STRQ:
669  if (*(char**)ptr == NULL) {
670  buf[0] = '\0';
671  } else {
672  seprintf(buf, lastof(buf), "\"%s\"", *(char**)ptr);
673  }
674  break;
675 
676  case SLE_VAR_CHAR: buf[0] = *(char*)ptr; buf[1] = '\0'; break;
677  default: NOT_REACHED();
678  }
679  break;
680 
681  case SDT_INTLIST:
682  MakeIntList(buf, lastof(buf), ptr, sld->length, GetVarMemType(sld->conv));
683  break;
684 
685  default: NOT_REACHED();
686  }
687 
688  /* The value is different, that means we have to write it to the ini */
689  free(item->value);
690  item->value = stredup(buf);
691  }
692 }
693 
703 static void IniLoadSettingList(IniFile *ini, const char *grpname, StringList *list)
704 {
705  IniGroup *group = ini->GetGroup(grpname);
706 
707  if (group == NULL || list == NULL) return;
708 
709  list->Clear();
710 
711  for (const IniItem *item = group->item; item != NULL; item = item->next) {
712  if (item->name != NULL) *list->Append() = stredup(item->name);
713  }
714 }
715 
725 static void IniSaveSettingList(IniFile *ini, const char *grpname, StringList *list)
726 {
727  IniGroup *group = ini->GetGroup(grpname);
728 
729  if (group == NULL || list == NULL) return;
730  group->Clear();
731 
732  for (char **iter = list->Begin(); iter != list->End(); iter++) {
733  group->GetItem(*iter, true)->SetValue("");
734  }
735 }
736 
743 void IniLoadWindowSettings(IniFile *ini, const char *grpname, void *desc)
744 {
745  IniLoadSettings(ini, _window_settings, grpname, desc);
746 }
747 
754 void IniSaveWindowSettings(IniFile *ini, const char *grpname, void *desc)
755 {
756  IniSaveSettings(ini, _window_settings, grpname, desc);
757 }
758 
764 bool SettingDesc::IsEditable(bool do_command) const
765 {
766  if (!do_command && !(this->save.conv & SLF_NO_NETWORK_SYNC) && _networking && !_network_server && !(this->desc.flags & SGF_PER_COMPANY)) return false;
767  if ((this->desc.flags & SGF_NETWORK_ONLY) && !_networking && _game_mode != GM_MENU) return false;
768  if ((this->desc.flags & SGF_NO_NETWORK) && _networking) return false;
769  if ((this->desc.flags & SGF_NEWGAME_ONLY) &&
770  (_game_mode == GM_NORMAL ||
771  (_game_mode == GM_EDITOR && !(this->desc.flags & SGF_SCENEDIT_TOO)))) return false;
772  return true;
773 }
774 
780 {
781  if (this->desc.flags & SGF_PER_COMPANY) return ST_COMPANY;
782  return (this->save.conv & SLF_NOT_IN_SAVE) ? ST_CLIENT : ST_GAME;
783 }
784 
785 /* Begin - Callback Functions for the various settings. */
786 
788 static bool v_PositionMainToolbar(int32 p1)
789 {
790  if (_game_mode != GM_MENU) PositionMainToolbar(NULL);
791  return true;
792 }
793 
795 static bool v_PositionStatusbar(int32 p1)
796 {
797  if (_game_mode != GM_MENU) {
798  PositionStatusbar(NULL);
799  PositionNewsMessage(NULL);
801  }
802  return true;
803 }
804 
805 static bool PopulationInLabelActive(int32 p1)
806 {
808  return true;
809 }
810 
811 static bool RedrawScreen(int32 p1)
812 {
814  return true;
815 }
816 
822 static bool RedrawSmallmap(int32 p1)
823 {
824  BuildLandLegend();
827  return true;
828 }
829 
830 static bool InvalidateDetailsWindow(int32 p1)
831 {
833  return true;
834 }
835 
836 static bool StationSpreadChanged(int32 p1)
837 {
840  return true;
841 }
842 
843 static bool InvalidateBuildIndustryWindow(int32 p1)
844 {
846  return true;
847 }
848 
849 static bool CloseSignalGUI(int32 p1)
850 {
851  if (p1 == 0) {
853  }
854  return true;
855 }
856 
857 static bool InvalidateTownViewWindow(int32 p1)
858 {
860  return true;
861 }
862 
863 static bool DeleteSelectStationWindow(int32 p1)
864 {
866  return true;
867 }
868 
869 static bool UpdateConsists(int32 p1)
870 {
871  Train *t;
872  FOR_ALL_TRAINS(t) {
873  /* Update the consist of all trains so the maximum speed is set correctly. */
874  if (t->IsFrontEngine() || t->IsFreeWagon()) t->ConsistChanged(CCF_TRACK);
875  }
877  return true;
878 }
879 
880 /* Check service intervals of vehicles, p1 is value of % or day based servicing */
881 static bool CheckInterval(int32 p1)
882 {
883  bool update_vehicles;
885  if (_game_mode == GM_MENU || !Company::IsValidID(_current_company)) {
886  vds = &_settings_client.company.vehicle;
887  update_vehicles = false;
888  } else {
889  vds = &Company::Get(_current_company)->settings.vehicle;
890  update_vehicles = true;
891  }
892 
893  if (p1 != 0) {
894  vds->servint_trains = 50;
895  vds->servint_roadveh = 50;
896  vds->servint_aircraft = 50;
897  vds->servint_ships = 50;
898  } else {
899  vds->servint_trains = 150;
900  vds->servint_roadveh = 150;
901  vds->servint_aircraft = 100;
902  vds->servint_ships = 360;
903  }
904 
905  if (update_vehicles) {
907  Vehicle *v;
908  FOR_ALL_VEHICLES(v) {
909  if (v->owner == _current_company && v->IsPrimaryVehicle() && !v->ServiceIntervalIsCustom()) {
910  v->SetServiceInterval(CompanyServiceInterval(c, v->type));
911  v->SetServiceIntervalIsPercent(p1 != 0);
912  }
913  }
914  }
915 
916  InvalidateDetailsWindow(0);
917 
918  return true;
919 }
920 
921 static bool UpdateInterval(VehicleType type, int32 p1)
922 {
923  bool update_vehicles;
925  if (_game_mode == GM_MENU || !Company::IsValidID(_current_company)) {
926  vds = &_settings_client.company.vehicle;
927  update_vehicles = false;
928  } else {
929  vds = &Company::Get(_current_company)->settings.vehicle;
930  update_vehicles = true;
931  }
932 
933  /* Test if the interval is valid */
934  uint16 interval = GetServiceIntervalClamped(p1, vds->servint_ispercent);
935  if (interval != p1) return false;
936 
937  if (update_vehicles) {
938  Vehicle *v;
939  FOR_ALL_VEHICLES(v) {
940  if (v->owner == _current_company && v->type == type && v->IsPrimaryVehicle() && !v->ServiceIntervalIsCustom()) {
941  v->SetServiceInterval(p1);
942  }
943  }
944  }
945 
946  InvalidateDetailsWindow(0);
947 
948  return true;
949 }
950 
951 static bool UpdateIntervalTrains(int32 p1)
952 {
953  return UpdateInterval(VEH_TRAIN, p1);
954 }
955 
956 static bool UpdateIntervalRoadVeh(int32 p1)
957 {
958  return UpdateInterval(VEH_ROAD, p1);
959 }
960 
961 static bool UpdateIntervalShips(int32 p1)
962 {
963  return UpdateInterval(VEH_SHIP, p1);
964 }
965 
966 static bool UpdateIntervalAircraft(int32 p1)
967 {
968  return UpdateInterval(VEH_AIRCRAFT, p1);
969 }
970 
971 static bool TrainAccelerationModelChanged(int32 p1)
972 {
973  Train *t;
974  FOR_ALL_TRAINS(t) {
975  if (t->IsFrontEngine()) {
977  t->UpdateAcceleration();
978  }
979  }
980 
981  /* These windows show acceleration values only when realistic acceleration is on. They must be redrawn after a setting change. */
985 
986  return true;
987 }
988 
994 static bool TrainSlopeSteepnessChanged(int32 p1)
995 {
996  Train *t;
997  FOR_ALL_TRAINS(t) {
998  if (t->IsFrontEngine()) t->CargoChanged();
999  }
1000 
1001  return true;
1002 }
1003 
1009 static bool RoadVehAccelerationModelChanged(int32 p1)
1010 {
1011  if (_settings_game.vehicle.roadveh_acceleration_model != AM_ORIGINAL) {
1012  RoadVehicle *rv;
1013  FOR_ALL_ROADVEHICLES(rv) {
1014  if (rv->IsFrontEngine()) {
1015  rv->CargoChanged();
1016  }
1017  }
1018  }
1019 
1020  /* These windows show acceleration values only when realistic acceleration is on. They must be redrawn after a setting change. */
1024 
1025  return true;
1026 }
1027 
1033 static bool RoadVehSlopeSteepnessChanged(int32 p1)
1034 {
1035  RoadVehicle *rv;
1036  FOR_ALL_ROADVEHICLES(rv) {
1037  if (rv->IsFrontEngine()) rv->CargoChanged();
1038  }
1039 
1040  return true;
1041 }
1042 
1043 static bool DragSignalsDensityChanged(int32)
1044 {
1046 
1047  return true;
1048 }
1049 
1050 static bool TownFoundingChanged(int32 p1)
1051 {
1052  if (_game_mode != GM_EDITOR && _settings_game.economy.found_town == TF_FORBIDDEN) {
1054  return true;
1055  }
1057  return true;
1058 }
1059 
1060 static bool InvalidateVehTimetableWindow(int32 p1)
1061 {
1063  return true;
1064 }
1065 
1066 static bool ZoomMinMaxChanged(int32 p1)
1067 {
1068  extern void ConstrainAllViewportsZoom();
1069  ConstrainAllViewportsZoom();
1071  if (_settings_client.gui.zoom_min > _gui_zoom) {
1072  /* Restrict GUI zoom if it is no longer available. */
1073  _gui_zoom = _settings_client.gui.zoom_min;
1074  UpdateCursorSize();
1076  }
1077  return true;
1078 }
1079 
1087 static bool InvalidateNewGRFChangeWindows(int32 p1)
1088 {
1091  ReInitAllWindows();
1092  return true;
1093 }
1094 
1095 static bool InvalidateCompanyLiveryWindow(int32 p1)
1096 {
1098  return RedrawScreen(p1);
1099 }
1100 
1101 static bool InvalidateIndustryViewWindow(int32 p1)
1102 {
1104  return true;
1105 }
1106 
1107 static bool InvalidateAISettingsWindow(int32 p1)
1108 {
1110  return true;
1111 }
1112 
1118 static bool RedrawTownAuthority(int32 p1)
1119 {
1121  return true;
1122 }
1123 
1130 {
1132  return true;
1133 }
1134 
1140 static bool InvalidateCompanyWindow(int32 p1)
1141 {
1143  return true;
1144 }
1145 
1147 static void ValidateSettings()
1148 {
1149  /* Do not allow a custom sea level with the original land generator. */
1150  if (_settings_newgame.game_creation.land_generator == 0 &&
1153  }
1154 }
1155 
1156 static bool DifficultyNoiseChange(int32 i)
1157 {
1158  if (_game_mode == GM_NORMAL) {
1160  if (_settings_game.economy.station_noise_level) {
1162  }
1163  }
1164 
1165  return true;
1166 }
1167 
1168 static bool MaxNoAIsChange(int32 i)
1169 {
1170  if (GetGameSettings().difficulty.max_no_competitors != 0 &&
1171  AI::GetInfoList()->size() == 0 &&
1172  (!_networking || _network_server)) {
1173  ShowErrorMessage(STR_WARNING_NO_SUITABLE_AI, INVALID_STRING_ID, WL_CRITICAL);
1174  }
1175 
1176  return true;
1177 }
1178 
1184 static bool CheckRoadSide(int p1)
1185 {
1186  extern bool RoadVehiclesAreBuilt();
1187  return _game_mode == GM_MENU || !RoadVehiclesAreBuilt();
1188 }
1189 
1197 static size_t ConvertLandscape(const char *value)
1198 {
1199  /* try with the old values */
1200  return LookupOneOfMany("normal|hilly|desert|candy", value);
1201 }
1202 
1203 static bool CheckFreeformEdges(int32 p1)
1204 {
1205  if (_game_mode == GM_MENU) return true;
1206  if (p1 != 0) {
1207  Ship *s;
1208  FOR_ALL_SHIPS(s) {
1209  /* Check if there is a ship on the northern border. */
1210  if (TileX(s->tile) == 0 || TileY(s->tile) == 0) {
1211  ShowErrorMessage(STR_CONFIG_SETTING_EDGES_NOT_EMPTY, INVALID_STRING_ID, WL_ERROR);
1212  return false;
1213  }
1214  }
1215  BaseStation *st;
1216  FOR_ALL_BASE_STATIONS(st) {
1217  /* Check if there is a non-deleted buoy on the northern border. */
1218  if (st->IsInUse() && (TileX(st->xy) == 0 || TileY(st->xy) == 0)) {
1219  ShowErrorMessage(STR_CONFIG_SETTING_EDGES_NOT_EMPTY, INVALID_STRING_ID, WL_ERROR);
1220  return false;
1221  }
1222  }
1223  for (uint i = 0; i < MapSizeX(); i++) MakeVoid(TileXY(i, 0));
1224  for (uint i = 0; i < MapSizeY(); i++) MakeVoid(TileXY(0, i));
1225  } else {
1226  for (uint i = 0; i < MapMaxX(); i++) {
1227  if (TileHeight(TileXY(i, 1)) != 0) {
1228  ShowErrorMessage(STR_CONFIG_SETTING_EDGES_NOT_WATER, INVALID_STRING_ID, WL_ERROR);
1229  return false;
1230  }
1231  }
1232  for (uint i = 1; i < MapMaxX(); i++) {
1233  if (!IsTileType(TileXY(i, MapMaxY() - 1), MP_WATER) || TileHeight(TileXY(1, MapMaxY())) != 0) {
1234  ShowErrorMessage(STR_CONFIG_SETTING_EDGES_NOT_WATER, INVALID_STRING_ID, WL_ERROR);
1235  return false;
1236  }
1237  }
1238  for (uint i = 0; i < MapMaxY(); i++) {
1239  if (TileHeight(TileXY(1, i)) != 0) {
1240  ShowErrorMessage(STR_CONFIG_SETTING_EDGES_NOT_WATER, INVALID_STRING_ID, WL_ERROR);
1241  return false;
1242  }
1243  }
1244  for (uint i = 1; i < MapMaxY(); i++) {
1245  if (!IsTileType(TileXY(MapMaxX() - 1, i), MP_WATER) || TileHeight(TileXY(MapMaxX(), i)) != 0) {
1246  ShowErrorMessage(STR_CONFIG_SETTING_EDGES_NOT_WATER, INVALID_STRING_ID, WL_ERROR);
1247  return false;
1248  }
1249  }
1250  /* Make tiles at the border water again. */
1251  for (uint i = 0; i < MapMaxX(); i++) {
1252  SetTileHeight(TileXY(i, 0), 0);
1253  SetTileType(TileXY(i, 0), MP_WATER);
1254  }
1255  for (uint i = 0; i < MapMaxY(); i++) {
1256  SetTileHeight(TileXY(0, i), 0);
1257  SetTileType(TileXY(0, i), MP_WATER);
1258  }
1259  }
1261  return true;
1262 }
1263 
1268 static bool ChangeDynamicEngines(int32 p1)
1269 {
1270  if (_game_mode == GM_MENU) return true;
1271 
1273  ShowErrorMessage(STR_CONFIG_SETTING_DYNAMIC_ENGINES_EXISTING_VEHICLES, INVALID_STRING_ID, WL_ERROR);
1274  return false;
1275  }
1276 
1277  return true;
1278 }
1279 
1280 static bool ChangeMaxHeightLevel(int32 p1)
1281 {
1282  if (_game_mode == GM_NORMAL) return false;
1283  if (_game_mode != GM_EDITOR) return true;
1284 
1285  /* Check if at least one mountain on the map is higher than the new value.
1286  * If yes, disallow the change. */
1287  for (TileIndex t = 0; t < MapSize(); t++) {
1288  if ((int32)TileHeight(t) > p1) {
1289  ShowErrorMessage(STR_CONFIG_SETTING_TOO_HIGH_MOUNTAIN, INVALID_STRING_ID, WL_ERROR);
1290  /* Return old, unchanged value */
1291  return false;
1292  }
1293  }
1294 
1295  /* The smallmap uses an index from heightlevels to colours. Trigger rebuilding it. */
1297 
1298  return true;
1299 }
1300 
1301 static bool StationCatchmentChanged(int32 p1)
1302 {
1304  return true;
1305 }
1306 
1307 
1308 #ifdef ENABLE_NETWORK
1309 
1310 static bool UpdateClientName(int32 p1)
1311 {
1313  return true;
1314 }
1315 
1316 static bool UpdateServerPassword(int32 p1)
1317 {
1318  if (strcmp(_settings_client.network.server_password, "*") == 0) {
1319  _settings_client.network.server_password[0] = '\0';
1320  }
1321 
1322  return true;
1323 }
1324 
1325 static bool UpdateRconPassword(int32 p1)
1326 {
1327  if (strcmp(_settings_client.network.rcon_password, "*") == 0) {
1328  _settings_client.network.rcon_password[0] = '\0';
1329  }
1330 
1331  return true;
1332 }
1333 
1334 static bool UpdateClientConfigValues(int32 p1)
1335 {
1337 
1338  return true;
1339 }
1340 
1341 #endif /* ENABLE_NETWORK */
1342 
1343 
1344 /* End - Callback Functions */
1345 
1350 {
1351  memset(_old_diff_custom, 0, sizeof(_old_diff_custom));
1352 }
1353 
1360 static void HandleOldDiffCustom(bool savegame)
1361 {
1362  uint options_to_load = GAME_DIFFICULTY_NUM - ((savegame && IsSavegameVersionBefore(4)) ? 1 : 0);
1363 
1364  if (!savegame) {
1365  /* If we did read to old_diff_custom, then at least one value must be non 0. */
1366  bool old_diff_custom_used = false;
1367  for (uint i = 0; i < options_to_load && !old_diff_custom_used; i++) {
1368  old_diff_custom_used = (_old_diff_custom[i] != 0);
1369  }
1370 
1371  if (!old_diff_custom_used) return;
1372  }
1373 
1374  for (uint i = 0; i < options_to_load; i++) {
1375  const SettingDesc *sd = &_settings[i];
1376  /* Skip deprecated options */
1377  if (!SlIsObjectCurrentlyValid(sd->save.version_from, sd->save.version_to)) continue;
1378  void *var = GetVariableAddress(savegame ? &_settings_game : &_settings_newgame, &sd->save);
1379  Write_ValidateSetting(var, sd, (int32)((i == 4 ? 1000 : 1) * _old_diff_custom[i]));
1380  }
1381 }
1382 
1383 static void AILoadConfig(IniFile *ini, const char *grpname)
1384 {
1385  IniGroup *group = ini->GetGroup(grpname);
1386  IniItem *item;
1387 
1388  /* Clean any configured AI */
1389  for (CompanyID c = COMPANY_FIRST; c < MAX_COMPANIES; c++) {
1391  }
1392 
1393  /* If no group exists, return */
1394  if (group == NULL) return;
1395 
1397  for (item = group->item; c < MAX_COMPANIES && item != NULL; c++, item = item->next) {
1399 
1400  config->Change(item->name);
1401  if (!config->HasScript()) {
1402  if (strcmp(item->name, "none") != 0) {
1403  DEBUG(script, 0, "The AI by the name '%s' was no longer found, and removed from the list.", item->name);
1404  continue;
1405  }
1406  }
1407  if (item->value != NULL) config->StringToSettings(item->value);
1408  }
1409 }
1410 
1411 static void GameLoadConfig(IniFile *ini, const char *grpname)
1412 {
1413  IniGroup *group = ini->GetGroup(grpname);
1414  IniItem *item;
1415 
1416  /* Clean any configured GameScript */
1418 
1419  /* If no group exists, return */
1420  if (group == NULL) return;
1421 
1422  item = group->item;
1423  if (item == NULL) return;
1424 
1426 
1427  config->Change(item->name);
1428  if (!config->HasScript()) {
1429  if (strcmp(item->name, "none") != 0) {
1430  DEBUG(script, 0, "The GameScript by the name '%s' was no longer found, and removed from the list.", item->name);
1431  return;
1432  }
1433  }
1434  if (item->value != NULL) config->StringToSettings(item->value);
1435 }
1436 
1442 static int DecodeHexNibble(char c)
1443 {
1444  if (c >= '0' && c <= '9') return c - '0';
1445  if (c >= 'A' && c <= 'F') return c + 10 - 'A';
1446  if (c >= 'a' && c <= 'f') return c + 10 - 'a';
1447  return -1;
1448 }
1449 
1458 static bool DecodeHexText(char *pos, uint8 *dest, size_t dest_size)
1459 {
1460  while (dest_size > 0) {
1461  int hi = DecodeHexNibble(pos[0]);
1462  int lo = (hi >= 0) ? DecodeHexNibble(pos[1]) : -1;
1463  if (lo < 0) return false;
1464  *dest++ = (hi << 4) | lo;
1465  pos += 2;
1466  dest_size--;
1467  }
1468  return *pos == '|';
1469 }
1470 
1477 static GRFConfig *GRFLoadConfig(IniFile *ini, const char *grpname, bool is_static)
1478 {
1479  IniGroup *group = ini->GetGroup(grpname);
1480  IniItem *item;
1481  GRFConfig *first = NULL;
1482  GRFConfig **curr = &first;
1483 
1484  if (group == NULL) return NULL;
1485 
1486  for (item = group->item; item != NULL; item = item->next) {
1487  GRFConfig *c = NULL;
1488 
1489  uint8 grfid_buf[4], md5sum[16];
1490  char *filename = item->name;
1491  bool has_grfid = false;
1492  bool has_md5sum = false;
1493 
1494  /* Try reading "<grfid>|" and on success, "<md5sum>|". */
1495  has_grfid = DecodeHexText(filename, grfid_buf, lengthof(grfid_buf));
1496  if (has_grfid) {
1497  filename += 1 + 2 * lengthof(grfid_buf);
1498  has_md5sum = DecodeHexText(filename, md5sum, lengthof(md5sum));
1499  if (has_md5sum) filename += 1 + 2 * lengthof(md5sum);
1500 
1501  uint32 grfid = grfid_buf[0] | (grfid_buf[1] << 8) | (grfid_buf[2] << 16) | (grfid_buf[3] << 24);
1502  if (has_md5sum) {
1503  const GRFConfig *s = FindGRFConfig(grfid, FGCM_EXACT, md5sum);
1504  if (s != NULL) c = new GRFConfig(*s);
1505  }
1506  if (c == NULL && !FioCheckFileExists(filename, NEWGRF_DIR)) {
1507  const GRFConfig *s = FindGRFConfig(grfid, FGCM_NEWEST_VALID);
1508  if (s != NULL) c = new GRFConfig(*s);
1509  }
1510  }
1511  if (c == NULL) c = new GRFConfig(filename);
1512 
1513  /* Parse parameters */
1514  if (!StrEmpty(item->value)) {
1515  int count = ParseIntList(item->value, (int*)c->param, lengthof(c->param));
1516  if (count < 0) {
1517  SetDParamStr(0, filename);
1518  ShowErrorMessage(STR_CONFIG_ERROR, STR_CONFIG_ERROR_ARRAY, WL_CRITICAL);
1519  count = 0;
1520  }
1521  c->num_params = count;
1522  }
1523 
1524  /* Check if item is valid */
1525  if (!FillGRFDetails(c, is_static) || HasBit(c->flags, GCF_INVALID)) {
1526  if (c->status == GCS_NOT_FOUND) {
1527  SetDParam(1, STR_CONFIG_ERROR_INVALID_GRF_NOT_FOUND);
1528  } else if (HasBit(c->flags, GCF_UNSAFE)) {
1529  SetDParam(1, STR_CONFIG_ERROR_INVALID_GRF_UNSAFE);
1530  } else if (HasBit(c->flags, GCF_SYSTEM)) {
1531  SetDParam(1, STR_CONFIG_ERROR_INVALID_GRF_SYSTEM);
1532  } else if (HasBit(c->flags, GCF_INVALID)) {
1533  SetDParam(1, STR_CONFIG_ERROR_INVALID_GRF_INCOMPATIBLE);
1534  } else {
1535  SetDParam(1, STR_CONFIG_ERROR_INVALID_GRF_UNKNOWN);
1536  }
1537 
1538  SetDParamStr(0, StrEmpty(filename) ? item->name : filename);
1539  ShowErrorMessage(STR_CONFIG_ERROR, STR_CONFIG_ERROR_INVALID_GRF, WL_CRITICAL);
1540  delete c;
1541  continue;
1542  }
1543 
1544  /* Check for duplicate GRFID (will also check for duplicate filenames) */
1545  bool duplicate = false;
1546  for (const GRFConfig *gc = first; gc != NULL; gc = gc->next) {
1547  if (gc->ident.grfid == c->ident.grfid) {
1548  SetDParamStr(0, c->filename);
1549  SetDParamStr(1, gc->filename);
1550  ShowErrorMessage(STR_CONFIG_ERROR, STR_CONFIG_ERROR_DUPLICATE_GRFID, WL_CRITICAL);
1551  duplicate = true;
1552  break;
1553  }
1554  }
1555  if (duplicate) {
1556  delete c;
1557  continue;
1558  }
1559 
1560  /* Mark file as static to avoid saving in savegame. */
1561  if (is_static) SetBit(c->flags, GCF_STATIC);
1562 
1563  /* Add item to list */
1564  *curr = c;
1565  curr = &c->next;
1566  }
1567 
1568  return first;
1569 }
1570 
1571 static void AISaveConfig(IniFile *ini, const char *grpname)
1572 {
1573  IniGroup *group = ini->GetGroup(grpname);
1574 
1575  if (group == NULL) return;
1576  group->Clear();
1577 
1578  for (CompanyID c = COMPANY_FIRST; c < MAX_COMPANIES; c++) {
1580  const char *name;
1581  char value[1024];
1582  config->SettingsToString(value, lastof(value));
1583 
1584  if (config->HasScript()) {
1585  name = config->GetName();
1586  } else {
1587  name = "none";
1588  }
1589 
1590  IniItem *item = new IniItem(group, name);
1591  item->SetValue(value);
1592  }
1593 }
1594 
1595 static void GameSaveConfig(IniFile *ini, const char *grpname)
1596 {
1597  IniGroup *group = ini->GetGroup(grpname);
1598 
1599  if (group == NULL) return;
1600  group->Clear();
1601 
1603  const char *name;
1604  char value[1024];
1605  config->SettingsToString(value, lastof(value));
1606 
1607  if (config->HasScript()) {
1608  name = config->GetName();
1609  } else {
1610  name = "none";
1611  }
1612 
1613  IniItem *item = new IniItem(group, name);
1614  item->SetValue(value);
1615 }
1616 
1621 static void SaveVersionInConfig(IniFile *ini)
1622 {
1623  IniGroup *group = ini->GetGroup("version");
1624 
1625  char version[9];
1626  seprintf(version, lastof(version), "%08X", _openttd_newgrf_version);
1627 
1628  const char * const versions[][2] = {
1629  { "version_string", _openttd_revision },
1630  { "version_number", version }
1631  };
1632 
1633  for (uint i = 0; i < lengthof(versions); i++) {
1634  group->GetItem(versions[i][0], true)->SetValue(versions[i][1]);
1635  }
1636 }
1637 
1638 /* Save a GRF configuration to the given group name */
1639 static void GRFSaveConfig(IniFile *ini, const char *grpname, const GRFConfig *list)
1640 {
1641  ini->RemoveGroup(grpname);
1642  IniGroup *group = ini->GetGroup(grpname);
1643  const GRFConfig *c;
1644 
1645  for (c = list; c != NULL; c = c->next) {
1646  /* Hex grfid (4 bytes in nibbles), "|", hex md5sum (16 bytes in nibbles), "|", file system path. */
1647  char key[4 * 2 + 1 + 16 * 2 + 1 + MAX_PATH];
1648  char params[512];
1649  GRFBuildParamList(params, c, lastof(params));
1650 
1651  char *pos = key + seprintf(key, lastof(key), "%08X|", BSWAP32(c->ident.grfid));
1652  pos = md5sumToString(pos, lastof(key), c->ident.md5sum);
1653  seprintf(pos, lastof(key), "|%s", c->filename);
1654  group->GetItem(key, true)->SetValue(params);
1655  }
1656 }
1657 
1658 /* Common handler for saving/loading variables to the configuration file */
1659 static void HandleSettingDescs(IniFile *ini, SettingDescProc *proc, SettingDescProcList *proc_list, bool basic_settings = true, bool other_settings = true)
1660 {
1661  if (basic_settings) {
1662  proc(ini, (const SettingDesc*)_misc_settings, "misc", NULL);
1663 #if defined(WIN32) && !defined(DEDICATED)
1664  proc(ini, (const SettingDesc*)_win32_settings, "win32", NULL);
1665 #endif /* WIN32 */
1666  }
1667 
1668  if (other_settings) {
1669  proc(ini, _settings, "patches", &_settings_newgame);
1670  proc(ini, _currency_settings,"currency", &_custom_currency);
1671  proc(ini, _company_settings, "company", &_settings_client.company);
1672 
1673 #ifdef ENABLE_NETWORK
1674  proc_list(ini, "server_bind_addresses", &_network_bind_list);
1675  proc_list(ini, "servers", &_network_host_list);
1676  proc_list(ini, "bans", &_network_ban_list);
1677 #endif /* ENABLE_NETWORK */
1678  }
1679 }
1680 
1681 static IniFile *IniLoadConfig()
1682 {
1683  IniFile *ini = new IniFile(_list_group_names);
1685  return ini;
1686 }
1687 
1692 void LoadFromConfig(bool minimal)
1693 {
1694  IniFile *ini = IniLoadConfig();
1695  if (!minimal) ResetCurrencies(false); // Initialize the array of currencies, without preserving the custom one
1696 
1697  /* Load basic settings only during bootstrap, load other settings not during bootstrap */
1698  HandleSettingDescs(ini, IniLoadSettings, IniLoadSettingList, minimal, !minimal);
1699 
1700  if (!minimal) {
1701  _grfconfig_newgame = GRFLoadConfig(ini, "newgrf", false);
1702  _grfconfig_static = GRFLoadConfig(ini, "newgrf-static", true);
1703  AILoadConfig(ini, "ai_players");
1704  GameLoadConfig(ini, "game_scripts");
1705 
1707  IniLoadSettings(ini, _gameopt_settings, "gameopt", &_settings_newgame);
1708  HandleOldDiffCustom(false);
1709 
1710  ValidateSettings();
1711 
1712  /* Display sheduled errors */
1713  extern void ScheduleErrorMessage(ErrorList &datas);
1715  if (FindWindowById(WC_ERRMSG, 0) == NULL) ShowFirstError();
1716  }
1717 
1718  delete ini;
1719 }
1720 
1723 {
1724  IniFile *ini = IniLoadConfig();
1725 
1726  /* Remove some obsolete groups. These have all been loaded into other groups. */
1727  ini->RemoveGroup("patches");
1728  ini->RemoveGroup("yapf");
1729  ini->RemoveGroup("gameopt");
1730 
1731  HandleSettingDescs(ini, IniSaveSettings, IniSaveSettingList);
1732  GRFSaveConfig(ini, "newgrf", _grfconfig_newgame);
1733  GRFSaveConfig(ini, "newgrf-static", _grfconfig_static);
1734  AISaveConfig(ini, "ai_players");
1735  GameSaveConfig(ini, "game_scripts");
1736  SaveVersionInConfig(ini);
1737  ini->SaveToDisk(_config_file);
1738  delete ini;
1739 }
1740 
1746 {
1747  list->Clear();
1748 
1749  IniFile *ini = IniLoadConfig();
1750  IniGroup *group;
1751  for (group = ini->group; group != NULL; group = group->next) {
1752  if (strncmp(group->name, "preset-", 7) == 0) {
1753  *list->Append() = stredup(group->name + 7);
1754  }
1755  }
1756 
1757  delete ini;
1758 }
1759 
1766 GRFConfig *LoadGRFPresetFromConfig(const char *config_name)
1767 {
1768  size_t len = strlen(config_name) + 8;
1769  char *section = (char*)alloca(len);
1770  seprintf(section, section + len - 1, "preset-%s", config_name);
1771 
1772  IniFile *ini = IniLoadConfig();
1773  GRFConfig *config = GRFLoadConfig(ini, section, false);
1774  delete ini;
1775 
1776  return config;
1777 }
1778 
1785 void SaveGRFPresetToConfig(const char *config_name, GRFConfig *config)
1786 {
1787  size_t len = strlen(config_name) + 8;
1788  char *section = (char*)alloca(len);
1789  seprintf(section, section + len - 1, "preset-%s", config_name);
1790 
1791  IniFile *ini = IniLoadConfig();
1792  GRFSaveConfig(ini, section, config);
1793  ini->SaveToDisk(_config_file);
1794  delete ini;
1795 }
1796 
1801 void DeleteGRFPresetFromConfig(const char *config_name)
1802 {
1803  size_t len = strlen(config_name) + 8;
1804  char *section = (char*)alloca(len);
1805  seprintf(section, section + len - 1, "preset-%s", config_name);
1806 
1807  IniFile *ini = IniLoadConfig();
1808  ini->RemoveGroup(section);
1809  ini->SaveToDisk(_config_file);
1810  delete ini;
1811 }
1812 
1813 const SettingDesc *GetSettingDescription(uint index)
1814 {
1815  if (index >= lengthof(_settings)) return NULL;
1816  return &_settings[index];
1817 }
1818 
1830 CommandCost CmdChangeSetting(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
1831 {
1832  const SettingDesc *sd = GetSettingDescription(p1);
1833 
1834  if (sd == NULL) return CMD_ERROR;
1836 
1837  if (!sd->IsEditable(true)) return CMD_ERROR;
1838 
1839  if (flags & DC_EXEC) {
1840  void *var = GetVariableAddress(&GetGameSettings(), &sd->save);
1841 
1842  int32 oldval = (int32)ReadValue(var, sd->save.conv);
1843  int32 newval = (int32)p2;
1844 
1845  Write_ValidateSetting(var, sd, newval);
1846  newval = (int32)ReadValue(var, sd->save.conv);
1847 
1848  if (oldval == newval) return CommandCost();
1849 
1850  if (sd->desc.proc != NULL && !sd->desc.proc(newval)) {
1851  WriteValue(var, sd->save.conv, (int64)oldval);
1852  return CommandCost();
1853  }
1854 
1855  if (sd->desc.flags & SGF_NO_NETWORK) {
1857  GamelogSetting(sd->desc.name, oldval, newval);
1859  }
1860 
1862  }
1863 
1864  return CommandCost();
1865 }
1866 
1877 CommandCost CmdChangeCompanySetting(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
1878 {
1879  if (p1 >= lengthof(_company_settings)) return CMD_ERROR;
1880  const SettingDesc *sd = &_company_settings[p1];
1881 
1882  if (flags & DC_EXEC) {
1883  void *var = GetVariableAddress(&Company::Get(_current_company)->settings, &sd->save);
1884 
1885  int32 oldval = (int32)ReadValue(var, sd->save.conv);
1886  int32 newval = (int32)p2;
1887 
1888  Write_ValidateSetting(var, sd, newval);
1889  newval = (int32)ReadValue(var, sd->save.conv);
1890 
1891  if (oldval == newval) return CommandCost();
1892 
1893  if (sd->desc.proc != NULL && !sd->desc.proc(newval)) {
1894  WriteValue(var, sd->save.conv, (int64)oldval);
1895  return CommandCost();
1896  }
1897 
1899  }
1900 
1901  return CommandCost();
1902 }
1903 
1911 bool SetSettingValue(uint index, int32 value, bool force_newgame)
1912 {
1913  const SettingDesc *sd = &_settings[index];
1914  /* If an item is company-based, we do not send it over the network
1915  * (if any) to change. Also *hack*hack* we update the _newgame version
1916  * of settings because changing a company-based setting in a game also
1917  * changes its defaults. At least that is the convention we have chosen */
1918  if (sd->save.conv & SLF_NO_NETWORK_SYNC) {
1919  void *var = GetVariableAddress(&GetGameSettings(), &sd->save);
1920  Write_ValidateSetting(var, sd, value);
1921 
1922  if (_game_mode != GM_MENU) {
1923  void *var2 = GetVariableAddress(&_settings_newgame, &sd->save);
1924  Write_ValidateSetting(var2, sd, value);
1925  }
1926  if (sd->desc.proc != NULL) sd->desc.proc((int32)ReadValue(var, sd->save.conv));
1927 
1929 
1930  return true;
1931  }
1932 
1933  if (force_newgame) {
1934  void *var2 = GetVariableAddress(&_settings_newgame, &sd->save);
1935  Write_ValidateSetting(var2, sd, value);
1936  return true;
1937  }
1938 
1939  /* send non-company-based settings over the network */
1940  if (!_networking || (_networking && _network_server)) {
1941  return DoCommandP(0, index, value, CMD_CHANGE_SETTING);
1942  }
1943  return false;
1944 }
1945 
1952 void SetCompanySetting(uint index, int32 value)
1953 {
1954  const SettingDesc *sd = &_company_settings[index];
1955  if (Company::IsValidID(_local_company) && _game_mode != GM_MENU) {
1956  DoCommandP(0, index, value, CMD_CHANGE_COMPANY_SETTING);
1957  } else {
1958  void *var = GetVariableAddress(&_settings_client.company, &sd->save);
1959  Write_ValidateSetting(var, sd, value);
1960  if (sd->desc.proc != NULL) sd->desc.proc((int32)ReadValue(var, sd->save.conv));
1961  }
1962 }
1963 
1968 {
1969  Company *c = Company::Get(cid);
1970  const SettingDesc *sd;
1971  for (sd = _company_settings; sd->save.cmd != SL_END; sd++) {
1972  void *var = GetVariableAddress(&c->settings, &sd->save);
1973  Write_ValidateSetting(var, sd, (int32)(size_t)sd->desc.def);
1974  }
1975 }
1976 
1977 #if defined(ENABLE_NETWORK)
1978 
1982 {
1983  const SettingDesc *sd;
1984  uint i = 0;
1985  for (sd = _company_settings; sd->save.cmd != SL_END; sd++, i++) {
1986  const void *old_var = GetVariableAddress(&Company::Get(_current_company)->settings, &sd->save);
1987  const void *new_var = GetVariableAddress(&_settings_client.company, &sd->save);
1988  uint32 old_value = (uint32)ReadValue(old_var, sd->save.conv);
1989  uint32 new_value = (uint32)ReadValue(new_var, sd->save.conv);
1990  if (old_value != new_value) NetworkSendCommand(0, i, new_value, CMD_CHANGE_COMPANY_SETTING, NULL, NULL, _local_company);
1991  }
1992 }
1993 #endif /* ENABLE_NETWORK */
1994 
2000 uint GetCompanySettingIndex(const char *name)
2001 {
2002  uint i;
2003  const SettingDesc *sd = GetSettingFromName(name, &i);
2004  assert(sd != NULL && (sd->desc.flags & SGF_PER_COMPANY) != 0);
2005  return i;
2006 }
2007 
2015 bool SetSettingValue(uint index, const char *value, bool force_newgame)
2016 {
2017  const SettingDesc *sd = &_settings[index];
2018  assert(sd->save.conv & SLF_NO_NETWORK_SYNC);
2019 
2020  if (GetVarMemType(sd->save.conv) == SLE_VAR_STRQ) {
2021  char **var = (char**)GetVariableAddress((_game_mode == GM_MENU || force_newgame) ? &_settings_newgame : &_settings_game, &sd->save);
2022  free(*var);
2023  *var = strcmp(value, "(null)") == 0 ? NULL : stredup(value);
2024  } else {
2025  char *var = (char*)GetVariableAddress(NULL, &sd->save);
2026  strecpy(var, value, &var[sd->save.length - 1]);
2027  }
2028  if (sd->desc.proc != NULL) sd->desc.proc(0);
2029 
2030  return true;
2031 }
2032 
2040 const SettingDesc *GetSettingFromName(const char *name, uint *i)
2041 {
2042  const SettingDesc *sd;
2043 
2044  /* First check all full names */
2045  for (*i = 0, sd = _settings; sd->save.cmd != SL_END; sd++, (*i)++) {
2046  if (!SlIsObjectCurrentlyValid(sd->save.version_from, sd->save.version_to)) continue;
2047  if (strcmp(sd->desc.name, name) == 0) return sd;
2048  }
2049 
2050  /* Then check the shortcut variant of the name. */
2051  for (*i = 0, sd = _settings; sd->save.cmd != SL_END; sd++, (*i)++) {
2052  if (!SlIsObjectCurrentlyValid(sd->save.version_from, sd->save.version_to)) continue;
2053  const char *short_name = strchr(sd->desc.name, '.');
2054  if (short_name != NULL) {
2055  short_name++;
2056  if (strcmp(short_name, name) == 0) return sd;
2057  }
2058  }
2059 
2060  if (strncmp(name, "company.", 8) == 0) name += 8;
2061  /* And finally the company-based settings */
2062  for (*i = 0, sd = _company_settings; sd->save.cmd != SL_END; sd++, (*i)++) {
2063  if (!SlIsObjectCurrentlyValid(sd->save.version_from, sd->save.version_to)) continue;
2064  if (strcmp(sd->desc.name, name) == 0) return sd;
2065  }
2066 
2067  return NULL;
2068 }
2069 
2070 /* Those 2 functions need to be here, else we have to make some stuff non-static
2071  * and besides, it is also better to keep stuff like this at the same place */
2072 void IConsoleSetSetting(const char *name, const char *value, bool force_newgame)
2073 {
2074  uint index;
2075  const SettingDesc *sd = GetSettingFromName(name, &index);
2076 
2077  if (sd == NULL) {
2078  IConsolePrintF(CC_WARNING, "'%s' is an unknown setting.", name);
2079  return;
2080  }
2081 
2082  bool success;
2083  if (sd->desc.cmd == SDT_STRING) {
2084  success = SetSettingValue(index, value, force_newgame);
2085  } else {
2086  uint32 val;
2087  extern bool GetArgumentInteger(uint32 *value, const char *arg);
2088  success = GetArgumentInteger(&val, value);
2089  if (!success) {
2090  IConsolePrintF(CC_ERROR, "'%s' is not an integer.", value);
2091  return;
2092  }
2093 
2094  success = SetSettingValue(index, val, force_newgame);
2095  }
2096 
2097  if (!success) {
2098  if (_network_server) {
2099  IConsoleError("This command/variable is not available during network games.");
2100  } else {
2101  IConsoleError("This command/variable is only available to a network server.");
2102  }
2103  }
2104 }
2105 
2106 void IConsoleSetSetting(const char *name, int value)
2107 {
2108  uint index;
2109  const SettingDesc *sd = GetSettingFromName(name, &index);
2110  assert(sd != NULL);
2111  SetSettingValue(index, value);
2112 }
2113 
2119 void IConsoleGetSetting(const char *name, bool force_newgame)
2120 {
2121  char value[20];
2122  uint index;
2123  const SettingDesc *sd = GetSettingFromName(name, &index);
2124  const void *ptr;
2125 
2126  if (sd == NULL) {
2127  IConsolePrintF(CC_WARNING, "'%s' is an unknown setting.", name);
2128  return;
2129  }
2130 
2131  ptr = GetVariableAddress((_game_mode == GM_MENU || force_newgame) ? &_settings_newgame : &_settings_game, &sd->save);
2132 
2133  if (sd->desc.cmd == SDT_STRING) {
2134  IConsolePrintF(CC_WARNING, "Current value for '%s' is: '%s'", name, (GetVarMemType(sd->save.conv) == SLE_VAR_STRQ) ? *(const char * const *)ptr : (const char *)ptr);
2135  } else {
2136  if (sd->desc.cmd == SDT_BOOLX) {
2137  seprintf(value, lastof(value), (*(const bool*)ptr != 0) ? "on" : "off");
2138  } else {
2139  seprintf(value, lastof(value), sd->desc.min < 0 ? "%d" : "%u", (int32)ReadValue(ptr, sd->save.conv));
2140  }
2141 
2142  IConsolePrintF(CC_WARNING, "Current value for '%s' is: '%s' (min: %s%d, max: %u)",
2143  name, value, (sd->desc.flags & SGF_0ISDISABLED) ? "(0) " : "", sd->desc.min, sd->desc.max);
2144  }
2145 }
2146 
2152 void IConsoleListSettings(const char *prefilter)
2153 {
2154  IConsolePrintF(CC_WARNING, "All settings with their current value:");
2155 
2156  for (const SettingDesc *sd = _settings; sd->save.cmd != SL_END; sd++) {
2157  if (!SlIsObjectCurrentlyValid(sd->save.version_from, sd->save.version_to)) continue;
2158  if (prefilter != NULL && strstr(sd->desc.name, prefilter) == NULL) continue;
2159  char value[80];
2160  const void *ptr = GetVariableAddress(&GetGameSettings(), &sd->save);
2161 
2162  if (sd->desc.cmd == SDT_BOOLX) {
2163  seprintf(value, lastof(value), (*(const bool *)ptr != 0) ? "on" : "off");
2164  } else if (sd->desc.cmd == SDT_STRING) {
2165  seprintf(value, lastof(value), "%s", (GetVarMemType(sd->save.conv) == SLE_VAR_STRQ) ? *(const char * const *)ptr : (const char *)ptr);
2166  } else {
2167  seprintf(value, lastof(value), sd->desc.min < 0 ? "%d" : "%u", (int32)ReadValue(ptr, sd->save.conv));
2168  }
2169  IConsolePrintF(CC_DEFAULT, "%s = %s", sd->desc.name, value);
2170  }
2171 
2172  IConsolePrintF(CC_WARNING, "Use 'setting' command to change a value");
2173 }
2174 
2181 static void LoadSettings(const SettingDesc *osd, void *object)
2182 {
2183  for (; osd->save.cmd != SL_END; osd++) {
2184  const SaveLoad *sld = &osd->save;
2185  void *ptr = GetVariableAddress(object, sld);
2186 
2187  if (!SlObjectMember(ptr, sld)) continue;
2188  if (IsNumericType(sld->conv)) Write_ValidateSetting(ptr, osd, ReadValue(ptr, sld->conv));
2189  }
2190 }
2191 
2198 static void SaveSettings(const SettingDesc *sd, void *object)
2199 {
2200  /* We need to write the CH_RIFF header, but unfortunately can't call
2201  * SlCalcLength() because we have a different format. So do this manually */
2202  const SettingDesc *i;
2203  size_t length = 0;
2204  for (i = sd; i->save.cmd != SL_END; i++) {
2205  length += SlCalcObjMemberLength(object, &i->save);
2206  }
2207  SlSetLength(length);
2208 
2209  for (i = sd; i->save.cmd != SL_END; i++) {
2210  void *ptr = GetVariableAddress(object, &i->save);
2211  SlObjectMember(ptr, &i->save);
2212  }
2213 }
2214 
2215 static void Load_OPTS()
2216 {
2217  /* Copy over default setting since some might not get loaded in
2218  * a networking environment. This ensures for example that the local
2219  * autosave-frequency stays when joining a network-server */
2221  LoadSettings(_gameopt_settings, &_settings_game);
2222  HandleOldDiffCustom(true);
2223 }
2224 
2225 static void Load_PATS()
2226 {
2227  /* Copy over default setting since some might not get loaded in
2228  * a networking environment. This ensures for example that the local
2229  * currency setting stays when joining a network-server */
2230  LoadSettings(_settings, &_settings_game);
2231 }
2232 
2233 static void Check_PATS()
2234 {
2235  LoadSettings(_settings, &_load_check_data.settings);
2236 }
2237 
2238 static void Save_PATS()
2239 {
2240  SaveSettings(_settings, &_settings_game);
2241 }
2242 
2243 void CheckConfig()
2244 {
2245  /*
2246  * Increase old default values for pf_maxdepth and pf_maxlength
2247  * to support big networks.
2248  */
2249  if (_settings_newgame.pf.opf.pf_maxdepth == 16 && _settings_newgame.pf.opf.pf_maxlength == 512) {
2250  _settings_newgame.pf.opf.pf_maxdepth = 48;
2251  _settings_newgame.pf.opf.pf_maxlength = 4096;
2252  }
2253 }
2254 
2255 extern const ChunkHandler _setting_chunk_handlers[] = {
2256  { 'OPTS', NULL, Load_OPTS, NULL, NULL, CH_RIFF},
2257  { 'PATS', Save_PATS, Load_PATS, NULL, Check_PATS, CH_RIFF | CH_LAST},
2258 };
2259 
2260 static bool IsSignedVarMemType(VarType vt)
2261 {
2262  switch (GetVarMemType(vt)) {
2263  case SLE_VAR_I8:
2264  case SLE_VAR_I16:
2265  case SLE_VAR_I32:
2266  case SLE_VAR_I64:
2267  return true;
2268  }
2269  return false;
2270 }