OpenTTD Source: src/script/script_info.cpp Source File

OpenTTD Source 14.0-RC3

script_info.cpp
Go to the documentation of this file.
1 /*
2 * This file is part of OpenTTD.
3 * 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.
4 * 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.
5 * 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/>.
6 */
7
10 #include "../stdafx.h"
11 #include "../settings_type.h"
12
13 #include "squirrel_helper.hpp"
14
15 #include "script_info.hpp"
16 #include "script_scanner.hpp"
17 #include "../3rdparty/fmt/format.h"
18
19 #include "../safeguards.h"
20
21 bool ScriptInfo::CheckMethod(const char *name) const
22 {
23 if (!this->engine->MethodExists(this->SQ_instance, name)) {
24 this->engine->ThrowError(fmt::format("your info.nut/library.nut doesn't have the method '{}'", name));
25 return false;
26 }
27 return true;
28 }
29
30 /* static */ SQInteger ScriptInfo::Constructor(HSQUIRRELVM vm, ScriptInfo *info)
31 {
32 /* Set some basic info from the parent */
33 Squirrel::GetInstance(vm, &info->SQ_instance, 2);
34 /* Make sure the instance stays alive over time */
35 sq_addref(vm, &info->SQ_instance);
36
37 info->scanner = (ScriptScanner *)Squirrel::GetGlobalPointer(vm);
38 info->engine = info->scanner->GetEngine();
39
40 /* Ensure the mandatory functions exist */
41 static const char * const required_functions[] = {
42 "GetAuthor",
43 "GetName",
44 "GetShortName",
45 "GetDescription",
46 "GetVersion",
47 "GetDate",
48 "CreateInstance",
49 };
50 for (size_t i = 0; i < lengthof(required_functions); i++) {
51 if (!info->CheckMethod(required_functions[i])) return SQ_ERROR;
52 }
53
54 /* Get location information of the scanner */
55 info->main_script = info->scanner->GetMainScript();
56 info->tar_file = info->scanner->GetTarFile();
57
58 /* Cache the data the info file gives us. */
59 if (!info->engine->CallStringMethod(info->SQ_instance, "GetAuthor", &info->author, MAX_GET_OPS)) return SQ_ERROR;
60 if (!info->engine->CallStringMethod(info->SQ_instance, "GetName", &info->name, MAX_GET_OPS)) return SQ_ERROR;
61 if (!info->engine->CallStringMethod(info->SQ_instance, "GetShortName", &info->short_name, MAX_GET_OPS)) return SQ_ERROR;
62 if (!info->engine->CallStringMethod(info->SQ_instance, "GetDescription", &info->description, MAX_GET_OPS)) return SQ_ERROR;
63 if (!info->engine->CallStringMethod(info->SQ_instance, "GetDate", &info->date, MAX_GET_OPS)) return SQ_ERROR;
64 if (!info->engine->CallIntegerMethod(info->SQ_instance, "GetVersion", &info->version, MAX_GET_OPS)) return SQ_ERROR;
65 if (!info->engine->CallStringMethod(info->SQ_instance, "CreateInstance", &info->instance_name, MAX_CREATEINSTANCE_OPS)) return SQ_ERROR;
66
67 /* The GetURL function is optional. */
68 if (info->engine->MethodExists(info->SQ_instance, "GetURL")) {
69 if (!info->engine->CallStringMethod(info->SQ_instance, "GetURL", &info->url, MAX_GET_OPS)) return SQ_ERROR;
70 }
71
72 /* Check if we have settings */
73 if (info->engine->MethodExists(info->SQ_instance, "GetSettings")) {
74 if (!info->GetSettings()) return SQ_ERROR;
75 }
76
77 return 0;
78 }
79
80 bool ScriptInfo::GetSettings()
81 {
82 return this->engine->CallMethod(this->SQ_instance, "GetSettings", nullptr, MAX_GET_SETTING_OPS);
83 }
84
85 SQInteger ScriptInfo::AddSetting(HSQUIRRELVM vm)
86 {
87 ScriptConfigItem config;
88 uint items = 0;
89
90 int medium_value = INT32_MIN;
91
92 /* Read the table, and find all properties we care about */
93 sq_pushnull(vm);
94 while (SQ_SUCCEEDED(sq_next(vm, -2))) {
95 const SQChar *key_string;
96 if (SQ_FAILED(sq_getstring(vm, -2, &key_string))) return SQ_ERROR;
97 std::string key = StrMakeValid(key_string);
98
99 if (key == "name") {
100 const SQChar *sqvalue;
101 if (SQ_FAILED(sq_getstring(vm, -1, &sqvalue))) return SQ_ERROR;
102
103 /* Don't allow '=' and ',' in configure setting names, as we need those
104 * 2 chars to nicely store the settings as a string. */
105 auto replace_with_underscore = [](auto c) { return c == '=' || c == ','; };
106 config.name = StrMakeValid(sqvalue);
107 std::replace_if(config.name.begin(), config.name.end(), replace_with_underscore, '_');
108 items |= 0x001;
109 } else if (key == "description") {
110 const SQChar *sqdescription;
111 if (SQ_FAILED(sq_getstring(vm, -1, &sqdescription))) return SQ_ERROR;
112 config.description = StrMakeValid(sqdescription);
113 items |= 0x002;
114 } else if (key == "min_value") {
115 SQInteger res;
116 if (SQ_FAILED(sq_getinteger(vm, -1, &res))) return SQ_ERROR;
117 config.min_value = ClampTo<int32_t>(res);
118 items |= 0x004;
119 } else if (key == "max_value") {
120 SQInteger res;
121 if (SQ_FAILED(sq_getinteger(vm, -1, &res))) return SQ_ERROR;
122 config.max_value = ClampTo<int32_t>(res);
123 items |= 0x008;
124 } else if (key == "easy_value") {
125 // No longer parsed.
126 items |= 0x010;
127 } else if (key == "medium_value") {
128 SQInteger res;
129 if (SQ_FAILED(sq_getinteger(vm, -1, &res))) return SQ_ERROR;
130 medium_value = ClampTo<int32_t>(res);
131 items |= 0x020;
132 } else if (key == "hard_value") {
133 // No longer parsed.
134 items |= 0x040;
135 } else if (key == "custom_value") {
136 // No longer parsed.
137 } else if (key == "default_value") {
138 SQInteger res;
139 if (SQ_FAILED(sq_getinteger(vm, -1, &res))) return SQ_ERROR;
140 config.default_value = ClampTo<int32_t>(res);
141 items |= 0x080;
142 } else if (key == "random_deviation") {
143 // No longer parsed.
144 } else if (key == "step_size") {
145 SQInteger res;
146 if (SQ_FAILED(sq_getinteger(vm, -1, &res))) return SQ_ERROR;
147 config.step_size = ClampTo<int32_t>(res);
148 } else if (key == "flags") {
149 SQInteger res;
150 if (SQ_FAILED(sq_getinteger(vm, -1, &res))) return SQ_ERROR;
151 config.flags = (ScriptConfigFlags)res;
152 items |= 0x100;
153 } else {
154 this->engine->ThrowError(fmt::format("unknown setting property '{}'", key));
155 return SQ_ERROR;
156 }
157
158 sq_pop(vm, 2);
159 }
160 sq_pop(vm, 1);
161
162 /* Check if default_value is set. Although required, this was changed with
163 * 14.0, and as such, older AIs don't use it yet. So we convert the older
164 * values into a default_value. */
165 if ((items & 0x080) == 0) {
166 /* Easy/medium/hard should all three be defined. */
167 if ((items & 0x010) == 0 || (items & 0x020) == 0 || (items & 0x040) == 0) {
168 this->engine->ThrowError("please define all properties of a setting (min/max not allowed for booleans)");
169 return SQ_ERROR;
170 }
171
172 config.default_value = medium_value;
173 items |= 0x080;
174 } else {
175 /* For compatibility, also act like the default sets the easy/medium/hard. */
176 items |= 0x010 | 0x020 | 0x040;
177 }
178
179 /* Make sure all properties are defined */
180 uint mask = (config.flags & SCRIPTCONFIG_BOOLEAN) ? 0x1F3 : 0x1FF;
181 if (items != mask) {
182 this->engine->ThrowError("please define all properties of a setting (min/max not allowed for booleans)");
183 return SQ_ERROR;
184 }
185
186 this->config_list.emplace_back(config);
187 return 0;
188 }
189
190 SQInteger ScriptInfo::AddLabels(HSQUIRRELVM vm)
191 {
192 const SQChar *setting_name_str;
193 if (SQ_FAILED(sq_getstring(vm, -2, &setting_name_str))) return SQ_ERROR;
194 std::string setting_name = StrMakeValid(setting_name_str);
195
196 ScriptConfigItem *config = nullptr;
197 for (auto &item : this->config_list) {
198 if (item.name == setting_name) config = &item;
199 }
200
201 if (config == nullptr) {
202 this->engine->ThrowError(fmt::format("Trying to add labels for non-defined setting '{}'", setting_name));
203 return SQ_ERROR;
204 }
205 if (!config->labels.empty()) return SQ_ERROR;
206
207 /* Read the table and find all labels */
208 sq_pushnull(vm);
209 while (SQ_SUCCEEDED(sq_next(vm, -2))) {
210 const SQChar *key_string;
211 const SQChar *label;
212 if (SQ_FAILED(sq_getstring(vm, -2, &key_string))) return SQ_ERROR;
213 if (SQ_FAILED(sq_getstring(vm, -1, &label))) return SQ_ERROR;
214 /* Because squirrel doesn't support identifiers starting with a digit,
215 * we skip the first character. */
216 key_string++;
217 int sign = 1;
218 if (*key_string == '_') {
219 /* When the second character is '_', it indicates the value is negative. */
220 sign = -1;
221 key_string++;
222 }
223 int key = atoi(key_string) * sign;
224 config->labels[key] = StrMakeValid(label);
225
226 sq_pop(vm, 2);
227 }
228 sq_pop(vm, 1);
229
230 /* Check labels for completeness */
231 config->complete_labels = true;
232 for (int value = config->min_value; value <= config->max_value; value++) {
233 if (config->labels.find(value) == config->labels.end()) {
234 config->complete_labels = false;
235 break;
236 }
237 }
238
239 return 0;
240 }
241
242 const ScriptConfigItemList *ScriptInfo::GetConfigList() const
243 {
244 return &this->config_list;
245 }
246
247 const ScriptConfigItem *ScriptInfo::GetConfigItem(const std::string_view name) const
248 {
249 for (const auto &item : this->config_list) {
250 if (item.name == name) return &item;
251 }
252 return nullptr;
253 }
254
255 int ScriptInfo::GetSettingDefaultValue(const std::string &name) const
256 {
257 for (const auto &item : this->config_list) {
258 if (item.name != name) continue;
259 return item.default_value;
260 }
261
262 /* There is no such setting */
263 return -1;
264 }
ScriptInfo::short_name
std::string short_name
Short name (4 chars) which uniquely identifies the script.
Definition: script_info.hpp:148
ScriptInfo::AddSetting
SQInteger AddSetting(HSQUIRRELVM vm)
Set a setting.
Definition: script_info.cpp:85
ScriptInfo::main_script
std::string main_script
The full path of the script.
Definition: script_info.hpp:144
ScriptInfo::date
std::string date
The date the script was written at.
Definition: script_info.hpp:150
ScriptInfo::engine
class Squirrel * engine
Engine used to register for Squirrel.
Definition: script_info.hpp:139
ScriptConfigItem::step_size
int step_size
The step size in the gui.
Definition: script_config.hpp:38
ScriptInfo::Constructor
static SQInteger Constructor(HSQUIRRELVM vm, ScriptInfo *info)
Process the creation of a FileInfo object.
Definition: script_info.cpp:30
ScriptScanner::GetMainScript
std::string GetMainScript()
Get the current main script the ScanDir is currently tracking.
Definition: script_scanner.hpp:34
ScriptConfigFlags
ScriptConfigFlags
Bitmask of flags for Script settings.
Definition: script_config.hpp:21
ScriptConfigItem::name
std::string name
The name of the configuration setting.
Definition: script_config.hpp:33
ScriptConfigItemList
std::vector< ScriptConfigItem > ScriptConfigItemList
List of ScriptConfig items.
Definition: script_config.hpp:44
MAX_CREATEINSTANCE_OPS
static const int MAX_CREATEINSTANCE_OPS
Number of operations to create an instance of a script.
Definition: script_info.hpp:23
StrMakeValid
static void StrMakeValid(T &dst, const char *str, const char *last, StringValidationSettings settings)
Copies the valid (UTF-8) characters from str up to last to the dst.
Definition: string.cpp:114
Squirrel::GetInstance
static bool GetInstance(HSQUIRRELVM vm, HSQOBJECT *ptr, int pos=1)
Get the Squirrel-instance pointer.
Definition: squirrel.hpp:200
squirrel_helper.hpp
SCRIPTCONFIG_BOOLEAN
@ SCRIPTCONFIG_BOOLEAN
This value is a boolean (either 0 (false) or 1 (true) ).
Definition: script_config.hpp:24
ScriptConfigItem::min_value
int min_value
The minimal value this configuration setting can have.
Definition: script_config.hpp:35
ScriptInfo::version
int version
Version of the script.
Definition: script_info.hpp:152
ScriptConfigItem::complete_labels
bool complete_labels
True if all values have a label.
Definition: script_config.hpp:41
ScriptInfo::GetSettingDefaultValue
int GetSettingDefaultValue(const std::string &name) const
Get the default value for a setting.
Definition: script_info.cpp:255
ScriptConfigItem::max_value
int max_value
The maximal value this configuration setting can have.
Definition: script_config.hpp:36
ScriptInfo::GetConfigList
const ScriptConfigItemList * GetConfigList() const
Get the config list for this Script.
Definition: script_info.cpp:242
ScriptInfo::description
std::string description
Small description of the script.
Definition: script_info.hpp:149
ScriptInfo::scanner
class ScriptScanner * scanner
ScriptScanner object that was used to scan this script info.
Definition: script_info.hpp:155
MAX_GET_OPS
static const int MAX_GET_OPS
Number of operations to get the author and similar information.
Definition: script_info.hpp:25
ScriptScanner
Scanner to help finding scripts.
Definition: script_scanner.hpp:19
Squirrel::GetGlobalPointer
static void * GetGlobalPointer(HSQUIRRELVM vm)
Get the pointer as set by SetGlobalPointer.
Definition: squirrel.hpp:226
ScriptInfo::CheckMethod
bool CheckMethod(const char *name) const
Check if a given method exists.
Definition: script_info.cpp:21
Squirrel::ThrowError
void ThrowError(const std::string_view error)
Throw a Squirrel error that will be nicely displayed to the user.
Definition: squirrel.hpp:236
ScriptScanner::GetEngine
class Squirrel * GetEngine()
Get the engine of the main squirrel handler (it indexes all available scripts).
Definition: script_scanner.hpp:29
Squirrel::CallMethod
bool CallMethod(HSQOBJECT instance, const char *method_name, HSQOBJECT *ret, int suspend)
Call a method of an instance, in various flavors.
Definition: squirrel.cpp:398
ScriptInfo::config_list
ScriptConfigItemList config_list
List of settings from this Script.
Definition: script_info.hpp:141
ScriptInfo::author
std::string author
Author of the script.
Definition: script_info.hpp:146
Squirrel::MethodExists
bool MethodExists(HSQOBJECT instance, const char *method_name)
Check if a method exists in an instance.
Definition: squirrel.cpp:345
ScriptInfo::AddLabels
SQInteger AddLabels(HSQUIRRELVM vm)
Add labels for a setting.
Definition: script_info.cpp:190
MAX_GET_SETTING_OPS
static const int MAX_GET_SETTING_OPS
Maximum number of operations allowed for getting a particular setting.
Definition: script_info.hpp:27
script_info.hpp
script_scanner.hpp
ScriptInfo::name
std::string name
Full name of the script.
Definition: script_info.hpp:147
ScriptConfigItem::default_value
int default_value
The default value of this configuration setting.
Definition: script_config.hpp:37
lengthof
#define lengthof(x)
Return the length of an fixed size array.
Definition: stdafx.h:300
ScriptInfo::tar_file
std::string tar_file
If, which tar file the script was in.
Definition: script_info.hpp:145
ScriptConfigItem::description
std::string description
The description of the configuration setting.
Definition: script_config.hpp:34
ScriptConfigItem::flags
ScriptConfigFlags flags
Flags for the configuration setting.
Definition: script_config.hpp:39
ScriptInfo::instance_name
std::string instance_name
Name of the main class in the script.
Definition: script_info.hpp:151
ScriptConfigItem::labels
LabelMapping labels
Text labels for the integer values.
Definition: script_config.hpp:40
ScriptInfo::SQ_instance
HSQOBJECT SQ_instance
The Squirrel instance created for this info.
Definition: script_info.hpp:140
ScriptInfo::url
std::string url
URL of the script.
Definition: script_info.hpp:153
ScriptScanner::GetTarFile
std::string GetTarFile()
Get the current tar file the ScanDir is currently tracking.
Definition: script_scanner.hpp:39
ScriptInfo
All static information from an Script like name, version, etc.
Definition: script_info.hpp:30
ScriptInfo::GetSettings
bool GetSettings()
Get the settings of the Script.
Definition: script_info.cpp:80
ScriptConfigItem
Info about a single Script setting.
Definition: script_config.hpp:32
ScriptInfo::GetConfigItem
const ScriptConfigItem * GetConfigItem(const std::string_view name) const
Get the description of a certain Script config option.
Definition: script_info.cpp:247