OpenTTD
script_info.cpp
Go to the documentation of this file.
1 /* $Id: script_info.cpp 26771 2014-09-06 17:30:33Z rubidium $ */
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 
12 #include "../stdafx.h"
13 #include "../settings_type.h"
14 
15 #include "squirrel_helper.hpp"
16 
17 #include "script_info.hpp"
18 #include "script_scanner.hpp"
19 
20 #include "../safeguards.h"
21 
22 ScriptInfo::~ScriptInfo()
23 {
24  /* Free all allocated strings */
25  for (ScriptConfigItemList::iterator it = this->config_list.begin(); it != this->config_list.end(); it++) {
26  free((*it).name);
27  free((*it).description);
28  if (it->labels != NULL) {
29  for (LabelMapping::iterator it2 = (*it).labels->Begin(); it2 != (*it).labels->End(); it2++) {
30  free(it2->second);
31  }
32  delete it->labels;
33  }
34  }
35  this->config_list.clear();
36 
37  free(this->author);
38  free(this->name);
39  free(this->short_name);
40  free(this->description);
41  free(this->date);
42  free(this->instance_name);
43  free(this->url);
44  free(this->main_script);
45  free(this->tar_file);
46  free(this->SQ_instance);
47 }
48 
49 bool ScriptInfo::CheckMethod(const char *name) const
50 {
51  if (!this->engine->MethodExists(*this->SQ_instance, name)) {
52  char error[1024];
53  seprintf(error, lastof(error), "your info.nut/library.nut doesn't have the method '%s'", name);
54  this->engine->ThrowError(error);
55  return false;
56  }
57  return true;
58 }
59 
60 /* static */ SQInteger ScriptInfo::Constructor(HSQUIRRELVM vm, ScriptInfo *info)
61 {
62  /* Set some basic info from the parent */
63  info->SQ_instance = MallocT<SQObject>(1);
64  Squirrel::GetInstance(vm, info->SQ_instance, 2);
65  /* Make sure the instance stays alive over time */
66  sq_addref(vm, info->SQ_instance);
67 
69  info->engine = info->scanner->GetEngine();
70 
71  /* Ensure the mandatory functions exist */
72  static const char * const required_functions[] = {
73  "GetAuthor",
74  "GetName",
75  "GetShortName",
76  "GetDescription",
77  "GetVersion",
78  "GetDate",
79  "CreateInstance",
80  };
81  for (size_t i = 0; i < lengthof(required_functions); i++) {
82  if (!info->CheckMethod(required_functions[i])) return SQ_ERROR;
83  }
84 
85  /* Get location information of the scanner */
86  info->main_script = stredup(info->scanner->GetMainScript());
87  const char *tar_name = info->scanner->GetTarFile();
88  if (tar_name != NULL) info->tar_file = stredup(tar_name);
89 
90  /* Cache the data the info file gives us. */
91  if (!info->engine->CallStringMethodStrdup(*info->SQ_instance, "GetAuthor", &info->author, MAX_GET_OPS)) return SQ_ERROR;
92  if (!info->engine->CallStringMethodStrdup(*info->SQ_instance, "GetName", &info->name, MAX_GET_OPS)) return SQ_ERROR;
93  if (!info->engine->CallStringMethodStrdup(*info->SQ_instance, "GetShortName", &info->short_name, MAX_GET_OPS)) return SQ_ERROR;
94  if (!info->engine->CallStringMethodStrdup(*info->SQ_instance, "GetDescription", &info->description, MAX_GET_OPS)) return SQ_ERROR;
95  if (!info->engine->CallStringMethodStrdup(*info->SQ_instance, "GetDate", &info->date, MAX_GET_OPS)) return SQ_ERROR;
96  if (!info->engine->CallIntegerMethod(*info->SQ_instance, "GetVersion", &info->version, MAX_GET_OPS)) return SQ_ERROR;
97  if (!info->engine->CallStringMethodStrdup(*info->SQ_instance, "CreateInstance", &info->instance_name, MAX_CREATEINSTANCE_OPS)) return SQ_ERROR;
98 
99  /* The GetURL function is optional. */
100  if (info->engine->MethodExists(*info->SQ_instance, "GetURL")) {
101  if (!info->engine->CallStringMethodStrdup(*info->SQ_instance, "GetURL", &info->url, MAX_GET_OPS)) return SQ_ERROR;
102  }
103 
104  /* Check if we have settings */
105  if (info->engine->MethodExists(*info->SQ_instance, "GetSettings")) {
106  if (!info->GetSettings()) return SQ_ERROR;
107  }
108 
109  return 0;
110 }
111 
113 {
114  return this->engine->CallMethod(*this->SQ_instance, "GetSettings", NULL, MAX_GET_SETTING_OPS);
115 }
116 
117 SQInteger ScriptInfo::AddSetting(HSQUIRRELVM vm)
118 {
119  ScriptConfigItem config;
120  memset(&config, 0, sizeof(config));
121  config.max_value = 1;
122  config.step_size = 1;
123  uint items = 0;
124 
125  /* Read the table, and find all properties we care about */
126  sq_pushnull(vm);
127  while (SQ_SUCCEEDED(sq_next(vm, -2))) {
128  const SQChar *key;
129  if (SQ_FAILED(sq_getstring(vm, -2, &key))) return SQ_ERROR;
130  ValidateString(key);
131 
132  if (strcmp(key, "name") == 0) {
133  const SQChar *sqvalue;
134  if (SQ_FAILED(sq_getstring(vm, -1, &sqvalue))) return SQ_ERROR;
135  char *name = stredup(sqvalue);
136  char *s;
137  ValidateString(name);
138 
139  /* Don't allow '=' and ',' in configure setting names, as we need those
140  * 2 chars to nicely store the settings as a string. */
141  while ((s = strchr(name, '=')) != NULL) *s = '_';
142  while ((s = strchr(name, ',')) != NULL) *s = '_';
143  config.name = name;
144  items |= 0x001;
145  } else if (strcmp(key, "description") == 0) {
146  const SQChar *sqdescription;
147  if (SQ_FAILED(sq_getstring(vm, -1, &sqdescription))) return SQ_ERROR;
148  config.description = stredup(sqdescription);
149  ValidateString(config.description);
150  items |= 0x002;
151  } else if (strcmp(key, "min_value") == 0) {
152  SQInteger res;
153  if (SQ_FAILED(sq_getinteger(vm, -1, &res))) return SQ_ERROR;
154  config.min_value = res;
155  items |= 0x004;
156  } else if (strcmp(key, "max_value") == 0) {
157  SQInteger res;
158  if (SQ_FAILED(sq_getinteger(vm, -1, &res))) return SQ_ERROR;
159  config.max_value = res;
160  items |= 0x008;
161  } else if (strcmp(key, "easy_value") == 0) {
162  SQInteger res;
163  if (SQ_FAILED(sq_getinteger(vm, -1, &res))) return SQ_ERROR;
164  config.easy_value = res;
165  items |= 0x010;
166  } else if (strcmp(key, "medium_value") == 0) {
167  SQInteger res;
168  if (SQ_FAILED(sq_getinteger(vm, -1, &res))) return SQ_ERROR;
169  config.medium_value = res;
170  items |= 0x020;
171  } else if (strcmp(key, "hard_value") == 0) {
172  SQInteger res;
173  if (SQ_FAILED(sq_getinteger(vm, -1, &res))) return SQ_ERROR;
174  config.hard_value = res;
175  items |= 0x040;
176  } else if (strcmp(key, "random_deviation") == 0) {
177  SQInteger res;
178  if (SQ_FAILED(sq_getinteger(vm, -1, &res))) return SQ_ERROR;
179  config.random_deviation = res;
180  items |= 0x200;
181  } else if (strcmp(key, "custom_value") == 0) {
182  SQInteger res;
183  if (SQ_FAILED(sq_getinteger(vm, -1, &res))) return SQ_ERROR;
184  config.custom_value = res;
185  items |= 0x080;
186  } else if (strcmp(key, "step_size") == 0) {
187  SQInteger res;
188  if (SQ_FAILED(sq_getinteger(vm, -1, &res))) return SQ_ERROR;
189  config.step_size = res;
190  } else if (strcmp(key, "flags") == 0) {
191  SQInteger res;
192  if (SQ_FAILED(sq_getinteger(vm, -1, &res))) return SQ_ERROR;
193  config.flags = (ScriptConfigFlags)res;
194  items |= 0x100;
195  } else {
196  char error[1024];
197  seprintf(error, lastof(error), "unknown setting property '%s'", key);
198  this->engine->ThrowError(error);
199  return SQ_ERROR;
200  }
201 
202  sq_pop(vm, 2);
203  }
204  sq_pop(vm, 1);
205 
206  /* Don't allow both random_deviation and SCRIPTCONFIG_RANDOM to
207  * be set for the same config item. */
208  if ((items & 0x200) != 0 && (config.flags & SCRIPTCONFIG_RANDOM) != 0) {
209  char error[1024];
210  seprintf(error, lastof(error), "Setting both random_deviation and SCRIPTCONFIG_RANDOM is not allowed");
211  this->engine->ThrowError(error);
212  return SQ_ERROR;
213  }
214  /* Reset the bit for random_deviation as it's optional. */
215  items &= ~0x200;
216 
217  /* Make sure all properties are defined */
218  uint mask = (config.flags & SCRIPTCONFIG_BOOLEAN) ? 0x1F3 : 0x1FF;
219  if (items != mask) {
220  char error[1024];
221  seprintf(error, lastof(error), "please define all properties of a setting (min/max not allowed for booleans)");
222  this->engine->ThrowError(error);
223  return SQ_ERROR;
224  }
225 
226  this->config_list.push_back(config);
227  return 0;
228 }
229 
230 SQInteger ScriptInfo::AddLabels(HSQUIRRELVM vm)
231 {
232  const SQChar *setting_name;
233  if (SQ_FAILED(sq_getstring(vm, -2, &setting_name))) return SQ_ERROR;
234  ValidateString(setting_name);
235 
236  ScriptConfigItem *config = NULL;
237  for (ScriptConfigItemList::iterator it = this->config_list.begin(); it != this->config_list.end(); it++) {
238  if (strcmp((*it).name, setting_name) == 0) config = &(*it);
239  }
240 
241  if (config == NULL) {
242  char error[1024];
243  seprintf(error, lastof(error), "Trying to add labels for non-defined setting '%s'", setting_name);
244  this->engine->ThrowError(error);
245  return SQ_ERROR;
246  }
247  if (config->labels != NULL) return SQ_ERROR;
248 
249  config->labels = new LabelMapping;
250 
251  /* Read the table and find all labels */
252  sq_pushnull(vm);
253  while (SQ_SUCCEEDED(sq_next(vm, -2))) {
254  const SQChar *key_string;
255  const SQChar *label;
256  if (SQ_FAILED(sq_getstring(vm, -2, &key_string))) return SQ_ERROR;
257  if (SQ_FAILED(sq_getstring(vm, -1, &label))) return SQ_ERROR;
258  /* Because squirrel doesn't support identifiers starting with a digit,
259  * we skip the first character. */
260  int key = atoi(key_string + 1);
261  ValidateString(label);
262 
263  /* !Contains() prevents stredup from leaking. */
264  if (!config->labels->Contains(key)) config->labels->Insert(key, stredup(label));
265 
266  sq_pop(vm, 2);
267  }
268  sq_pop(vm, 1);
269 
270  /* Check labels for completeness */
271  config->complete_labels = true;
272  for (int value = config->min_value; value <= config->max_value; value++) {
273  if (!config->labels->Contains(value)) {
274  config->complete_labels = false;
275  break;
276  }
277  }
278 
279  return 0;
280 }
281 
283 {
284  return &this->config_list;
285 }
286 
288 {
289  for (ScriptConfigItemList::const_iterator it = this->config_list.begin(); it != this->config_list.end(); it++) {
290  if (strcmp((*it).name, name) == 0) return &(*it);
291  }
292  return NULL;
293 }
294 
296 {
297  for (ScriptConfigItemList::const_iterator it = this->config_list.begin(); it != this->config_list.end(); it++) {
298  if (strcmp((*it).name, name) != 0) continue;
299  /* The default value depends on the difficulty level */
301  case SP_EASY: return (*it).easy_value;
302  case SP_MEDIUM: return (*it).medium_value;
303  case SP_HARD: return (*it).hard_value;
304  case SP_CUSTOM: return (*it).custom_value;
305  default: NOT_REACHED();
306  }
307  }
308 
309  /* There is no such setting */
310  return -1;
311 }
const char * GetMainScript()
Get the current main script the ScanDir is currently tracking.
const char * GetTarFile()
Get the current tar file the ScanDir is currently tracking.
bool Contains(const T &key) const
Tests whether a key is assigned in this map.
int version
Version of the script.
class ScriptScanner * scanner
ScriptScanner object that was used to scan this script info.
HSQOBJECT * SQ_instance
The Squirrel instance created for this info.
int random_deviation
The maximum random deviation from the default value.
void ThrowError(const char *error)
Throw a Squirrel error that will be nicely displayed to the user.
Definition: squirrel.hpp:233
bool CallMethod(HSQOBJECT instance, const char *method_name, HSQOBJECT *ret, int suspend)
Call a method of an instance, in various flavors.
Definition: squirrel.cpp:217
bool CheckMethod(const char *name) const
Check if a given method exists.
Definition: script_info.cpp:49
int CDECL seprintf(char *str, const char *last, const char *format,...)
Safer implementation of snprintf; same as snprintf except:
Definition: string.cpp:398
const ScriptConfigItemList * GetConfigList() const
Get the config list for this Script.
int min_value
The minimal value this configuration setting can have.
const char * date
The date the script was written at.
std::list< ScriptConfigItem > ScriptConfigItemList
List of ScriptConfig items.
static bool GetInstance(HSQUIRRELVM vm, HSQOBJECT *ptr, int pos=1)
Get the Squirrel-instance pointer.
Definition: squirrel.hpp:197
const char * url
URL of the script.
Medium difficulty.
Definition: settings_type.h:30
ScriptConfigItemList config_list
List of settings from this Script.
No profile, special "custom" highscore.
Definition: settings_type.h:35
LabelMapping * labels
Text labels for the integer values.
bool Insert(const T &key, const U &data)
Adds new item to this map.
const char * instance_name
Name of the main class in the script.
int max_value
The maximal value this configuration setting can have.
int GetSettingDefaultValue(const char *name) const
Get the default value for a setting.
#define lastof(x)
Get the last element of an fixed size array.
Definition: depend.cpp:50
static void * GetGlobalPointer(HSQUIRRELVM vm)
Get the pointer as set by SetGlobalPointer.
Definition: squirrel.hpp:223
declarations and parts of the implementation of the class for convert code
int medium_value
The default value on medium difficulty setting.
Scanner to help finding scripts.
ScriptConfigFlags
Bitmask of flags for Script settings.
const char * short_name
Short name (4 chars) which uniquely identifies the script.
SQInteger AddLabels(HSQUIRRELVM vm)
Add labels for a setting.
bool complete_labels
True if all values have a label.
Simple pair of data.
class Squirrel * GetEngine()
Get the engine of the main squirrel handler (it indexes all available scripts).
All static information from an Script like name, version, etc.
Definition: script_info.hpp:32
const char * author
Author of the script.
int easy_value
The default value on easy difficulty setting.
char * stredup(const char *s, const char *last)
Create a duplicate of the given string.
Definition: string.cpp:126
bool GetSettings()
Get the settings of the Script.
When randomizing the Script, pick any value between min_value and max_value when on custom difficulty...
#define lengthof(x)
Return the length of an fixed size array.
Definition: depend.cpp:42
int custom_value
The default value on custom difficulty setting.
const ScriptConfigItem * GetConfigItem(const char *name) const
Get the description of a certain Script config option.
SmallMap< int, char * > LabelMapping
Map-type used to map the setting numbers to labels.
This value is a boolean (either 0 (false) or 1 (true) ).
const char * description
The description of the configuration setting.
ScriptSettings script
settings for scripts
const char * name
Full name of the script.
ScriptConfigFlags flags
Flags for the configuration setting.
SQInteger AddSetting(HSQUIRRELVM vm)
Set a setting.
Hard difficulty.
Definition: settings_type.h:31
Info about a single Script setting.
static GameSettings & GetGameSettings()
Get the settings-object applicable for the current situation: the newgame settings when we&#39;re in the ...
static const int MAX_GET_OPS
Number of operations to get the author and similar information.
Definition: script_info.hpp:27
bool MethodExists(HSQOBJECT instance, const char *method_name)
Check if a method exists in an instance.
Definition: squirrel.cpp:171
void CDECL error(const char *s,...)
Error handling for fatal non-user errors.
Definition: openttd.cpp:110
char * main_script
The full path of the script.
static const int MAX_CREATEINSTANCE_OPS
Number of operations to create an instance of a script.
Definition: script_info.hpp:25
Declarations of the class for the script scanner.
Easy difficulty.
Definition: settings_type.h:29
class Squirrel * engine
Engine used to register for Squirrel.
static SQInteger Constructor(HSQUIRRELVM vm, ScriptInfo *info)
Process the creation of a FileInfo object.
Definition: script_info.cpp:60
static void free(const void *ptr)
Version of the standard free that accepts const pointers.
Definition: depend.cpp:114
uint8 settings_profile
difficulty profile to set initial settings of scripts, esp. random AIs
const char * description
Small description of the script.
int hard_value
The default value on hard difficulty setting.
char * tar_file
If, which tar file the script was in.
static const int MAX_GET_SETTING_OPS
Maximum number of operations allowed for getting a particular setting.
Definition: script_info.hpp:29
void ValidateString(const char *str)
Scans the string for valid characters and if it finds invalid ones, replaces them with a question mar...
Definition: string.cpp:233
int step_size
The step size in the gui.
const char * name
The name of the configuration setting.
ScriptInfo keeps track of all information of a script, like Author, Description, ...