OpenTTD
settings.cpp
Go to the documentation of this file.
1 /* $Id: settings.cpp 27285 2015-05-16 12:01:19Z alberth $ */
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 == LG_ORIGINAL &&
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 static bool MaxVehiclesChanged(int32 p1)
1308 {
1311  return true;
1312 }
1313 
1314 
1315 #ifdef ENABLE_NETWORK
1316 
1317 static bool UpdateClientName(int32 p1)
1318 {
1320  return true;
1321 }
1322 
1323 static bool UpdateServerPassword(int32 p1)
1324 {
1325  if (strcmp(_settings_client.network.server_password, "*") == 0) {
1326  _settings_client.network.server_password[0] = '\0';
1327  }
1328 
1329  return true;
1330 }
1331 
1332 static bool UpdateRconPassword(int32 p1)
1333 {
1334  if (strcmp(_settings_client.network.rcon_password, "*") == 0) {
1335  _settings_client.network.rcon_password[0] = '\0';
1336  }
1337 
1338  return true;
1339 }
1340 
1341 static bool UpdateClientConfigValues(int32 p1)
1342 {
1344 
1345  return true;
1346 }
1347 
1348 #endif /* ENABLE_NETWORK */
1349 
1350 
1351 /* End - Callback Functions */
1352 
1357 {
1358  memset(_old_diff_custom, 0, sizeof(_old_diff_custom));
1359 }
1360 
1367 static void HandleOldDiffCustom(bool savegame)
1368 {
1369  uint options_to_load = GAME_DIFFICULTY_NUM - ((savegame && IsSavegameVersionBefore(4)) ? 1 : 0);
1370 
1371  if (!savegame) {
1372  /* If we did read to old_diff_custom, then at least one value must be non 0. */
1373  bool old_diff_custom_used = false;
1374  for (uint i = 0; i < options_to_load && !old_diff_custom_used; i++) {
1375  old_diff_custom_used = (_old_diff_custom[i] != 0);
1376  }
1377 
1378  if (!old_diff_custom_used) return;
1379  }
1380 
1381  for (uint i = 0; i < options_to_load; i++) {
1382  const SettingDesc *sd = &_settings[i];
1383  /* Skip deprecated options */
1384  if (!SlIsObjectCurrentlyValid(sd->save.version_from, sd->save.version_to)) continue;
1385  void *var = GetVariableAddress(savegame ? &_settings_game : &_settings_newgame, &sd->save);
1386  Write_ValidateSetting(var, sd, (int32)((i == 4 ? 1000 : 1) * _old_diff_custom[i]));
1387  }
1388 }
1389 
1390 static void AILoadConfig(IniFile *ini, const char *grpname)
1391 {
1392  IniGroup *group = ini->GetGroup(grpname);
1393  IniItem *item;
1394 
1395  /* Clean any configured AI */
1396  for (CompanyID c = COMPANY_FIRST; c < MAX_COMPANIES; c++) {
1398  }
1399 
1400  /* If no group exists, return */
1401  if (group == NULL) return;
1402 
1404  for (item = group->item; c < MAX_COMPANIES && item != NULL; c++, item = item->next) {
1406 
1407  config->Change(item->name);
1408  if (!config->HasScript()) {
1409  if (strcmp(item->name, "none") != 0) {
1410  DEBUG(script, 0, "The AI by the name '%s' was no longer found, and removed from the list.", item->name);
1411  continue;
1412  }
1413  }
1414  if (item->value != NULL) config->StringToSettings(item->value);
1415  }
1416 }
1417 
1418 static void GameLoadConfig(IniFile *ini, const char *grpname)
1419 {
1420  IniGroup *group = ini->GetGroup(grpname);
1421  IniItem *item;
1422 
1423  /* Clean any configured GameScript */
1425 
1426  /* If no group exists, return */
1427  if (group == NULL) return;
1428 
1429  item = group->item;
1430  if (item == NULL) return;
1431 
1433 
1434  config->Change(item->name);
1435  if (!config->HasScript()) {
1436  if (strcmp(item->name, "none") != 0) {
1437  DEBUG(script, 0, "The GameScript by the name '%s' was no longer found, and removed from the list.", item->name);
1438  return;
1439  }
1440  }
1441  if (item->value != NULL) config->StringToSettings(item->value);
1442 }
1443 
1449 static int DecodeHexNibble(char c)
1450 {
1451  if (c >= '0' && c <= '9') return c - '0';
1452  if (c >= 'A' && c <= 'F') return c + 10 - 'A';
1453  if (c >= 'a' && c <= 'f') return c + 10 - 'a';
1454  return -1;
1455 }
1456 
1465 static bool DecodeHexText(char *pos, uint8 *dest, size_t dest_size)
1466 {
1467  while (dest_size > 0) {
1468  int hi = DecodeHexNibble(pos[0]);
1469  int lo = (hi >= 0) ? DecodeHexNibble(pos[1]) : -1;
1470  if (lo < 0) return false;
1471  *dest++ = (hi << 4) | lo;
1472  pos += 2;
1473  dest_size--;
1474  }
1475  return *pos == '|';
1476 }
1477 
1484 static GRFConfig *GRFLoadConfig(IniFile *ini, const char *grpname, bool is_static)
1485 {
1486  IniGroup *group = ini->GetGroup(grpname);
1487  IniItem *item;
1488  GRFConfig *first = NULL;
1489  GRFConfig **curr = &first;
1490 
1491  if (group == NULL) return NULL;
1492 
1493  for (item = group->item; item != NULL; item = item->next) {
1494  GRFConfig *c = NULL;
1495 
1496  uint8 grfid_buf[4], md5sum[16];
1497  char *filename = item->name;
1498  bool has_grfid = false;
1499  bool has_md5sum = false;
1500 
1501  /* Try reading "<grfid>|" and on success, "<md5sum>|". */
1502  has_grfid = DecodeHexText(filename, grfid_buf, lengthof(grfid_buf));
1503  if (has_grfid) {
1504  filename += 1 + 2 * lengthof(grfid_buf);
1505  has_md5sum = DecodeHexText(filename, md5sum, lengthof(md5sum));
1506  if (has_md5sum) filename += 1 + 2 * lengthof(md5sum);
1507 
1508  uint32 grfid = grfid_buf[0] | (grfid_buf[1] << 8) | (grfid_buf[2] << 16) | (grfid_buf[3] << 24);
1509  if (has_md5sum) {
1510  const GRFConfig *s = FindGRFConfig(grfid, FGCM_EXACT, md5sum);
1511  if (s != NULL) c = new GRFConfig(*s);
1512  }
1513  if (c == NULL && !FioCheckFileExists(filename, NEWGRF_DIR)) {
1514  const GRFConfig *s = FindGRFConfig(grfid, FGCM_NEWEST_VALID);
1515  if (s != NULL) c = new GRFConfig(*s);
1516  }
1517  }
1518  if (c == NULL) c = new GRFConfig(filename);
1519 
1520  /* Parse parameters */
1521  if (!StrEmpty(item->value)) {
1522  int count = ParseIntList(item->value, (int*)c->param, lengthof(c->param));
1523  if (count < 0) {
1524  SetDParamStr(0, filename);
1525  ShowErrorMessage(STR_CONFIG_ERROR, STR_CONFIG_ERROR_ARRAY, WL_CRITICAL);
1526  count = 0;
1527  }
1528  c->num_params = count;
1529  }
1530 
1531  /* Check if item is valid */
1532  if (!FillGRFDetails(c, is_static) || HasBit(c->flags, GCF_INVALID)) {
1533  if (c->status == GCS_NOT_FOUND) {
1534  SetDParam(1, STR_CONFIG_ERROR_INVALID_GRF_NOT_FOUND);
1535  } else if (HasBit(c->flags, GCF_UNSAFE)) {
1536  SetDParam(1, STR_CONFIG_ERROR_INVALID_GRF_UNSAFE);
1537  } else if (HasBit(c->flags, GCF_SYSTEM)) {
1538  SetDParam(1, STR_CONFIG_ERROR_INVALID_GRF_SYSTEM);
1539  } else if (HasBit(c->flags, GCF_INVALID)) {
1540  SetDParam(1, STR_CONFIG_ERROR_INVALID_GRF_INCOMPATIBLE);
1541  } else {
1542  SetDParam(1, STR_CONFIG_ERROR_INVALID_GRF_UNKNOWN);
1543  }
1544 
1545  SetDParamStr(0, StrEmpty(filename) ? item->name : filename);
1546  ShowErrorMessage(STR_CONFIG_ERROR, STR_CONFIG_ERROR_INVALID_GRF, WL_CRITICAL);
1547  delete c;
1548  continue;
1549  }
1550 
1551  /* Check for duplicate GRFID (will also check for duplicate filenames) */
1552  bool duplicate = false;
1553  for (const GRFConfig *gc = first; gc != NULL; gc = gc->next) {
1554  if (gc->ident.grfid == c->ident.grfid) {
1555  SetDParamStr(0, c->filename);
1556  SetDParamStr(1, gc->filename);
1557  ShowErrorMessage(STR_CONFIG_ERROR, STR_CONFIG_ERROR_DUPLICATE_GRFID, WL_CRITICAL);
1558  duplicate = true;
1559  break;
1560  }
1561  }
1562  if (duplicate) {
1563  delete c;
1564  continue;
1565  }
1566 
1567  /* Mark file as static to avoid saving in savegame. */
1568  if (is_static) SetBit(c->flags, GCF_STATIC);
1569 
1570  /* Add item to list */
1571  *curr = c;
1572  curr = &c->next;
1573  }
1574 
1575  return first;
1576 }
1577 
1578 static void AISaveConfig(IniFile *ini, const char *grpname)
1579 {
1580  IniGroup *group = ini->GetGroup(grpname);
1581 
1582  if (group == NULL) return;
1583  group->Clear();
1584 
1585  for (CompanyID c = COMPANY_FIRST; c < MAX_COMPANIES; c++) {
1587  const char *name;
1588  char value[1024];
1589  config->SettingsToString(value, lastof(value));
1590 
1591  if (config->HasScript()) {
1592  name = config->GetName();
1593  } else {
1594  name = "none";
1595  }
1596 
1597  IniItem *item = new IniItem(group, name);
1598  item->SetValue(value);
1599  }
1600 }
1601 
1602 static void GameSaveConfig(IniFile *ini, const char *grpname)
1603 {
1604  IniGroup *group = ini->GetGroup(grpname);
1605 
1606  if (group == NULL) return;
1607  group->Clear();
1608 
1610  const char *name;
1611  char value[1024];
1612  config->SettingsToString(value, lastof(value));
1613 
1614  if (config->HasScript()) {
1615  name = config->GetName();
1616  } else {
1617  name = "none";
1618  }
1619 
1620  IniItem *item = new IniItem(group, name);
1621  item->SetValue(value);
1622 }
1623 
1628 static void SaveVersionInConfig(IniFile *ini)
1629 {
1630  IniGroup *group = ini->GetGroup("version");
1631 
1632  char version[9];
1633  seprintf(version, lastof(version), "%08X", _openttd_newgrf_version);
1634 
1635  const char * const versions[][2] = {
1636  { "version_string", _openttd_revision },
1637  { "version_number", version }
1638  };
1639 
1640  for (uint i = 0; i < lengthof(versions); i++) {
1641  group->GetItem(versions[i][0], true)->SetValue(versions[i][1]);
1642  }
1643 }
1644 
1645 /* Save a GRF configuration to the given group name */
1646 static void GRFSaveConfig(IniFile *ini, const char *grpname, const GRFConfig *list)
1647 {
1648  ini->RemoveGroup(grpname);
1649  IniGroup *group = ini->GetGroup(grpname);
1650  const GRFConfig *c;
1651 
1652  for (c = list; c != NULL; c = c->next) {
1653  /* Hex grfid (4 bytes in nibbles), "|", hex md5sum (16 bytes in nibbles), "|", file system path. */
1654  char key[4 * 2 + 1 + 16 * 2 + 1 + MAX_PATH];
1655  char params[512];
1656  GRFBuildParamList(params, c, lastof(params));
1657 
1658  char *pos = key + seprintf(key, lastof(key), "%08X|", BSWAP32(c->ident.grfid));
1659  pos = md5sumToString(pos, lastof(key), c->ident.md5sum);
1660  seprintf(pos, lastof(key), "|%s", c->filename);
1661  group->GetItem(key, true)->SetValue(params);
1662  }
1663 }
1664 
1665 /* Common handler for saving/loading variables to the configuration file */
1666 static void HandleSettingDescs(IniFile *ini, SettingDescProc *proc, SettingDescProcList *proc_list, bool basic_settings = true, bool other_settings = true)
1667 {
1668  if (basic_settings) {
1669  proc(ini, (const SettingDesc*)_misc_settings, "misc", NULL);
1670 #if defined(WIN32) && !defined(DEDICATED)
1671  proc(ini, (const SettingDesc*)_win32_settings, "win32", NULL);
1672 #endif /* WIN32 */
1673  }
1674 
1675  if (other_settings) {
1676  proc(ini, _settings, "patches", &_settings_newgame);
1677  proc(ini, _currency_settings,"currency", &_custom_currency);
1678  proc(ini, _company_settings, "company", &_settings_client.company);
1679 
1680 #ifdef ENABLE_NETWORK
1681  proc_list(ini, "server_bind_addresses", &_network_bind_list);
1682  proc_list(ini, "servers", &_network_host_list);
1683  proc_list(ini, "bans", &_network_ban_list);
1684 #endif /* ENABLE_NETWORK */
1685  }
1686 }
1687 
1688 static IniFile *IniLoadConfig()
1689 {
1690  IniFile *ini = new IniFile(_list_group_names);
1692  return ini;
1693 }
1694 
1699 void LoadFromConfig(bool minimal)
1700 {
1701  IniFile *ini = IniLoadConfig();
1702  if (!minimal) ResetCurrencies(false); // Initialize the array of currencies, without preserving the custom one
1703 
1704  /* Load basic settings only during bootstrap, load other settings not during bootstrap */
1705  HandleSettingDescs(ini, IniLoadSettings, IniLoadSettingList, minimal, !minimal);
1706 
1707  if (!minimal) {
1708  _grfconfig_newgame = GRFLoadConfig(ini, "newgrf", false);
1709  _grfconfig_static = GRFLoadConfig(ini, "newgrf-static", true);
1710  AILoadConfig(ini, "ai_players");
1711  GameLoadConfig(ini, "game_scripts");
1712 
1714  IniLoadSettings(ini, _gameopt_settings, "gameopt", &_settings_newgame);
1715  HandleOldDiffCustom(false);
1716 
1717  ValidateSettings();
1718 
1719  /* Display sheduled errors */
1720  extern void ScheduleErrorMessage(ErrorList &datas);
1722  if (FindWindowById(WC_ERRMSG, 0) == NULL) ShowFirstError();
1723  }
1724 
1725  delete ini;
1726 }
1727 
1730 {
1731  IniFile *ini = IniLoadConfig();
1732 
1733  /* Remove some obsolete groups. These have all been loaded into other groups. */
1734  ini->RemoveGroup("patches");
1735  ini->RemoveGroup("yapf");
1736  ini->RemoveGroup("gameopt");
1737 
1738  HandleSettingDescs(ini, IniSaveSettings, IniSaveSettingList);
1739  GRFSaveConfig(ini, "newgrf", _grfconfig_newgame);
1740  GRFSaveConfig(ini, "newgrf-static", _grfconfig_static);
1741  AISaveConfig(ini, "ai_players");
1742  GameSaveConfig(ini, "game_scripts");
1743  SaveVersionInConfig(ini);
1744  ini->SaveToDisk(_config_file);
1745  delete ini;
1746 }
1747 
1753 {
1754  list->Clear();
1755 
1756  IniFile *ini = IniLoadConfig();
1757  IniGroup *group;
1758  for (group = ini->group; group != NULL; group = group->next) {
1759  if (strncmp(group->name, "preset-", 7) == 0) {
1760  *list->Append() = stredup(group->name + 7);
1761  }
1762  }
1763 
1764  delete ini;
1765 }
1766 
1773 GRFConfig *LoadGRFPresetFromConfig(const char *config_name)
1774 {
1775  size_t len = strlen(config_name) + 8;
1776  char *section = (char*)alloca(len);
1777  seprintf(section, section + len - 1, "preset-%s", config_name);
1778 
1779  IniFile *ini = IniLoadConfig();
1780  GRFConfig *config = GRFLoadConfig(ini, section, false);
1781  delete ini;
1782 
1783  return config;
1784 }
1785 
1792 void SaveGRFPresetToConfig(const char *config_name, GRFConfig *config)
1793 {
1794  size_t len = strlen(config_name) + 8;
1795  char *section = (char*)alloca(len);
1796  seprintf(section, section + len - 1, "preset-%s", config_name);
1797 
1798  IniFile *ini = IniLoadConfig();
1799  GRFSaveConfig(ini, section, config);
1800  ini->SaveToDisk(_config_file);
1801  delete ini;
1802 }
1803 
1808 void DeleteGRFPresetFromConfig(const char *config_name)
1809 {
1810  size_t len = strlen(config_name) + 8;
1811  char *section = (char*)alloca(len);
1812  seprintf(section, section + len - 1, "preset-%s", config_name);
1813 
1814  IniFile *ini = IniLoadConfig();
1815  ini->RemoveGroup(section);
1816  ini->SaveToDisk(_config_file);
1817  delete ini;
1818 }
1819 
1820 const SettingDesc *GetSettingDescription(uint index)
1821 {
1822  if (index >= lengthof(_settings)) return NULL;
1823  return &_settings[index];
1824 }
1825 
1837 CommandCost CmdChangeSetting(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
1838 {
1839  const SettingDesc *sd = GetSettingDescription(p1);
1840 
1841  if (sd == NULL) return CMD_ERROR;
1843 
1844  if (!sd->IsEditable(true)) return CMD_ERROR;
1845 
1846  if (flags & DC_EXEC) {
1847  void *var = GetVariableAddress(&GetGameSettings(), &sd->save);
1848 
1849  int32 oldval = (int32)ReadValue(var, sd->save.conv);
1850  int32 newval = (int32)p2;
1851 
1852  Write_ValidateSetting(var, sd, newval);
1853  newval = (int32)ReadValue(var, sd->save.conv);
1854 
1855  if (oldval == newval) return CommandCost();
1856 
1857  if (sd->desc.proc != NULL && !sd->desc.proc(newval)) {
1858  WriteValue(var, sd->save.conv, (int64)oldval);
1859  return CommandCost();
1860  }
1861 
1862  if (sd->desc.flags & SGF_NO_NETWORK) {
1864  GamelogSetting(sd->desc.name, oldval, newval);
1866  }
1867 
1869  }
1870 
1871  return CommandCost();
1872 }
1873 
1884 CommandCost CmdChangeCompanySetting(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
1885 {
1886  if (p1 >= lengthof(_company_settings)) return CMD_ERROR;
1887  const SettingDesc *sd = &_company_settings[p1];
1888 
1889  if (flags & DC_EXEC) {
1890  void *var = GetVariableAddress(&Company::Get(_current_company)->settings, &sd->save);
1891 
1892  int32 oldval = (int32)ReadValue(var, sd->save.conv);
1893  int32 newval = (int32)p2;
1894 
1895  Write_ValidateSetting(var, sd, newval);
1896  newval = (int32)ReadValue(var, sd->save.conv);
1897 
1898  if (oldval == newval) return CommandCost();
1899 
1900  if (sd->desc.proc != NULL && !sd->desc.proc(newval)) {
1901  WriteValue(var, sd->save.conv, (int64)oldval);
1902  return CommandCost();
1903  }
1904 
1906  }
1907 
1908  return CommandCost();
1909 }
1910 
1918 bool SetSettingValue(uint index, int32 value, bool force_newgame)
1919 {
1920  const SettingDesc *sd = &_settings[index];
1921  /* If an item is company-based, we do not send it over the network
1922  * (if any) to change. Also *hack*hack* we update the _newgame version
1923  * of settings because changing a company-based setting in a game also
1924  * changes its defaults. At least that is the convention we have chosen */
1925  if (sd->save.conv & SLF_NO_NETWORK_SYNC) {
1926  void *var = GetVariableAddress(&GetGameSettings(), &sd->save);
1927  Write_ValidateSetting(var, sd, value);
1928 
1929  if (_game_mode != GM_MENU) {
1930  void *var2 = GetVariableAddress(&_settings_newgame, &sd->save);
1931  Write_ValidateSetting(var2, sd, value);
1932  }
1933  if (sd->desc.proc != NULL) sd->desc.proc((int32)ReadValue(var, sd->save.conv));
1934 
1936 
1937  return true;
1938  }
1939 
1940  if (force_newgame) {
1941  void *var2 = GetVariableAddress(&_settings_newgame, &sd->save);
1942  Write_ValidateSetting(var2, sd, value);
1943  return true;
1944  }
1945 
1946  /* send non-company-based settings over the network */
1947  if (!_networking || (_networking && _network_server)) {
1948  return DoCommandP(0, index, value, CMD_CHANGE_SETTING);
1949  }
1950  return false;
1951 }
1952 
1959 void SetCompanySetting(uint index, int32 value)
1960 {
1961  const SettingDesc *sd = &_company_settings[index];
1962  if (Company::IsValidID(_local_company) && _game_mode != GM_MENU) {
1963  DoCommandP(0, index, value, CMD_CHANGE_COMPANY_SETTING);
1964  } else {
1965  void *var = GetVariableAddress(&_settings_client.company, &sd->save);
1966  Write_ValidateSetting(var, sd, value);
1967  if (sd->desc.proc != NULL) sd->desc.proc((int32)ReadValue(var, sd->save.conv));
1968  }
1969 }
1970 
1975 {
1976  Company *c = Company::Get(cid);
1977  const SettingDesc *sd;
1978  for (sd = _company_settings; sd->save.cmd != SL_END; sd++) {
1979  void *var = GetVariableAddress(&c->settings, &sd->save);
1980  Write_ValidateSetting(var, sd, (int32)(size_t)sd->desc.def);
1981  }
1982 }
1983 
1984 #if defined(ENABLE_NETWORK)
1985 
1989 {
1990  const SettingDesc *sd;
1991  uint i = 0;
1992  for (sd = _company_settings; sd->save.cmd != SL_END; sd++, i++) {
1993  const void *old_var = GetVariableAddress(&Company::Get(_current_company)->settings, &sd->save);
1994  const void *new_var = GetVariableAddress(&_settings_client.company, &sd->save);
1995  uint32 old_value = (uint32)ReadValue(old_var, sd->save.conv);
1996  uint32 new_value = (uint32)ReadValue(new_var, sd->save.conv);
1997  if (old_value != new_value) NetworkSendCommand(0, i, new_value, CMD_CHANGE_COMPANY_SETTING, NULL, NULL, _local_company);
1998  }
1999 }
2000 #endif /* ENABLE_NETWORK */
2001 
2007 uint GetCompanySettingIndex(const char *name)
2008 {
2009  uint i;
2010  const SettingDesc *sd = GetSettingFromName(name, &i);
2011  assert(sd != NULL && (sd->desc.flags & SGF_PER_COMPANY) != 0);
2012  return i;
2013 }
2014 
2022 bool SetSettingValue(uint index, const char *value, bool force_newgame)
2023 {
2024  const SettingDesc *sd = &_settings[index];
2025  assert(sd->save.conv & SLF_NO_NETWORK_SYNC);
2026 
2027  if (GetVarMemType(sd->save.conv) == SLE_VAR_STRQ) {
2028  char **var = (char**)GetVariableAddress((_game_mode == GM_MENU || force_newgame) ? &_settings_newgame : &_settings_game, &sd->save);
2029  free(*var);
2030  *var = strcmp(value, "(null)") == 0 ? NULL : stredup(value);
2031  } else {
2032  char *var = (char*)GetVariableAddress(NULL, &sd->save);
2033  strecpy(var, value, &var[sd->save.length - 1]);
2034  }
2035  if (sd->desc.proc != NULL) sd->desc.proc(0);
2036 
2037  return true;
2038 }
2039 
2047 const SettingDesc *GetSettingFromName(const char *name, uint *i)
2048 {
2049  const SettingDesc *sd;
2050 
2051  /* First check all full names */
2052  for (*i = 0, sd = _settings; sd->save.cmd != SL_END; sd++, (*i)++) {
2053  if (!SlIsObjectCurrentlyValid(sd->save.version_from, sd->save.version_to)) continue;
2054  if (strcmp(sd->desc.name, name) == 0) return sd;
2055  }
2056 
2057  /* Then check the shortcut variant of the name. */
2058  for (*i = 0, sd = _settings; sd->save.cmd != SL_END; sd++, (*i)++) {
2059  if (!SlIsObjectCurrentlyValid(sd->save.version_from, sd->save.version_to)) continue;
2060  const char *short_name = strchr(sd->desc.name, '.');
2061  if (short_name != NULL) {
2062  short_name++;
2063  if (strcmp(short_name, name) == 0) return sd;
2064  }
2065  }
2066 
2067  if (strncmp(name, "company.", 8) == 0) name += 8;
2068  /* And finally the company-based settings */
2069  for (*i = 0, sd = _company_settings; sd->save.cmd != SL_END; sd++, (*i)++) {
2070  if (!SlIsObjectCurrentlyValid(sd->save.version_from, sd->save.version_to)) continue;
2071  if (strcmp(sd->desc.name, name) == 0) return sd;
2072  }
2073 
2074  return NULL;
2075 }
2076 
2077 /* Those 2 functions need to be here, else we have to make some stuff non-static
2078  * and besides, it is also better to keep stuff like this at the same place */
2079 void IConsoleSetSetting(const char *name, const char *value, bool force_newgame)
2080 {
2081  uint index;
2082  const SettingDesc *sd = GetSettingFromName(name, &index);
2083 
2084  if (sd == NULL) {
2085  IConsolePrintF(CC_WARNING, "'%s' is an unknown setting.", name);
2086  return;
2087  }
2088 
2089  bool success;
2090  if (sd->desc.cmd == SDT_STRING) {
2091  success = SetSettingValue(index, value, force_newgame);
2092  } else {
2093  uint32 val;
2094  extern bool GetArgumentInteger(uint32 *value, const char *arg);
2095  success = GetArgumentInteger(&val, value);
2096  if (!success) {
2097  IConsolePrintF(CC_ERROR, "'%s' is not an integer.", value);
2098  return;
2099  }
2100 
2101  success = SetSettingValue(index, val, force_newgame);
2102  }
2103 
2104  if (!success) {
2105  if (_network_server) {
2106  IConsoleError("This command/variable is not available during network games.");
2107  } else {
2108  IConsoleError("This command/variable is only available to a network server.");
2109  }
2110  }
2111 }
2112 
2113 void IConsoleSetSetting(const char *name, int value)
2114 {
2115  uint index;
2116  const SettingDesc *sd = GetSettingFromName(name, &index);
2117  assert(sd != NULL);
2118  SetSettingValue(index, value);
2119 }
2120 
2126 void IConsoleGetSetting(const char *name, bool force_newgame)
2127 {
2128  char value[20];
2129  uint index;
2130  const SettingDesc *sd = GetSettingFromName(name, &index);
2131  const void *ptr;
2132 
2133  if (sd == NULL) {
2134  IConsolePrintF(CC_WARNING, "'%s' is an unknown setting.", name);
2135  return;
2136  }
2137 
2138  ptr = GetVariableAddress((_game_mode == GM_MENU || force_newgame) ? &_settings_newgame : &_settings_game, &sd->save);
2139 
2140  if (sd->desc.cmd == SDT_STRING) {
2141  IConsolePrintF(CC_WARNING, "Current value for '%s' is: '%s'", name, (GetVarMemType(sd->save.conv) == SLE_VAR_STRQ) ? *(const char * const *)ptr : (const char *)ptr);
2142  } else {
2143  if (sd->desc.cmd == SDT_BOOLX) {
2144  seprintf(value, lastof(value), (*(const bool*)ptr != 0) ? "on" : "off");
2145  } else {
2146  seprintf(value, lastof(value), sd->desc.min < 0 ? "%d" : "%u", (int32)ReadValue(ptr, sd->save.conv));
2147  }
2148 
2149  IConsolePrintF(CC_WARNING, "Current value for '%s' is: '%s' (min: %s%d, max: %u)",
2150  name, value, (sd->desc.flags & SGF_0ISDISABLED) ? "(0) " : "", sd->desc.min, sd->desc.max);
2151  }
2152 }
2153 
2159 void IConsoleListSettings(const char *prefilter)
2160 {
2161  IConsolePrintF(CC_WARNING, "All settings with their current value:");
2162 
2163  for (const SettingDesc *sd = _settings; sd->save.cmd != SL_END; sd++) {
2164  if (!SlIsObjectCurrentlyValid(sd->save.version_from, sd->save.version_to)) continue;
2165  if (prefilter != NULL && strstr(sd->desc.name, prefilter) == NULL) continue;
2166  char value[80];
2167  const void *ptr = GetVariableAddress(&GetGameSettings(), &sd->save);
2168 
2169  if (sd->desc.cmd == SDT_BOOLX) {
2170  seprintf(value, lastof(value), (*(const bool *)ptr != 0) ? "on" : "off");
2171  } else if (sd->desc.cmd == SDT_STRING) {
2172  seprintf(value, lastof(value), "%s", (GetVarMemType(sd->save.conv) == SLE_VAR_STRQ) ? *(const char * const *)ptr : (const char *)ptr);
2173  } else {
2174  seprintf(value, lastof(value), sd->desc.min < 0 ? "%d" : "%u", (int32)ReadValue(ptr, sd->save.conv));
2175  }
2176  IConsolePrintF(CC_DEFAULT, "%s = %s", sd->desc.name, value);
2177  }
2178 
2179  IConsolePrintF(CC_WARNING, "Use 'setting' command to change a value");
2180 }
2181 
2188 static void LoadSettings(const SettingDesc *osd, void *object)
2189 {
2190  for (; osd->save.cmd != SL_END; osd++) {
2191  const SaveLoad *sld = &osd->save;
2192  void *ptr = GetVariableAddress(object, sld);
2193 
2194  if (!SlObjectMember(ptr, sld)) continue;
2195  if (IsNumericType(sld->conv)) Write_ValidateSetting(ptr, osd, ReadValue(ptr, sld->conv));
2196  }
2197 }
2198 
2205 static void SaveSettings(const SettingDesc *sd, void *object)
2206 {
2207  /* We need to write the CH_RIFF header, but unfortunately can't call
2208  * SlCalcLength() because we have a different format. So do this manually */
2209  const SettingDesc *i;
2210  size_t length = 0;
2211  for (i = sd; i->save.cmd != SL_END; i++) {
2212  length += SlCalcObjMemberLength(object, &i->save);
2213  }
2214  SlSetLength(length);
2215 
2216  for (i = sd; i->save.cmd != SL_END; i++) {
2217  void *ptr = GetVariableAddress(object, &i->save);
2218  SlObjectMember(ptr, &i->save);
2219  }
2220 }
2221 
2222 static void Load_OPTS()
2223 {
2224  /* Copy over default setting since some might not get loaded in
2225  * a networking environment. This ensures for example that the local
2226  * autosave-frequency stays when joining a network-server */
2228  LoadSettings(_gameopt_settings, &_settings_game);
2229  HandleOldDiffCustom(true);
2230 }
2231 
2232 static void Load_PATS()
2233 {
2234  /* Copy over default setting since some might not get loaded in
2235  * a networking environment. This ensures for example that the local
2236  * currency setting stays when joining a network-server */
2237  LoadSettings(_settings, &_settings_game);
2238 }
2239 
2240 static void Check_PATS()
2241 {
2242  LoadSettings(_settings, &_load_check_data.settings);
2243 }
2244 
2245 static void Save_PATS()
2246 {
2247  SaveSettings(_settings, &_settings_game);
2248 }
2249 
2250 void CheckConfig()
2251 {
2252  /*
2253  * Increase old default values for pf_maxdepth and pf_maxlength
2254  * to support big networks.
2255  */
2256  if (_settings_newgame.pf.opf.pf_maxdepth == 16 && _settings_newgame.pf.opf.pf_maxlength == 512) {
2257  _settings_newgame.pf.opf.pf_maxdepth = 48;
2258  _settings_newgame.pf.opf.pf_maxlength = 4096;
2259  }
2260 }
2261 
2262 extern const ChunkHandler _setting_chunk_handlers[] = {
2263  { 'OPTS', NULL, Load_OPTS, NULL, NULL, CH_RIFF},
2264  { 'PATS', Save_PATS, Load_PATS, NULL, Check_PATS, CH_RIFF | CH_LAST},
2265 };
2266 
2267 static bool IsSignedVarMemType(VarType vt)
2268 {
2269  switch (GetVarMemType(vt)) {
2270  case SLE_VAR_I8:
2271  case SLE_VAR_I16:
2272  case SLE_VAR_I32:
2273  case SLE_VAR_I64:
2274  return true;
2275  }
2276  return false;
2277 }