OpenTTD Source 20241224-master-gf74b0cf984
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
21bool 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 */
34 /* Make sure the instance stays alive over time */
35 sq_addref(vm, &info->SQ_instance);
36
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 (const auto &required_function : required_functions) {
51 if (!info->CheckMethod(required_function)) 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
81{
82 return this->engine->CallMethod(this->SQ_instance, "GetSettings", nullptr, MAX_GET_SETTING_OPS);
83}
84
85SQInteger 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
190SQInteger 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
243{
244 return &this->config_list;
245}
246
247const 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
255int 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}
All static information from an Script like name, version, etc.
std::string short_name
Short name (4 chars) which uniquely identifies the script.
int version
Version of the script.
class ScriptScanner * scanner
ScriptScanner object that was used to scan this script info.
static SQInteger Constructor(HSQUIRRELVM vm, ScriptInfo *info)
Process the creation of a FileInfo object.
std::string description
Small description of the script.
ScriptConfigItemList config_list
List of settings from this Script.
const ScriptConfigItemList * GetConfigList() const
Get the config list for this Script.
bool CheckMethod(const char *name) const
Check if a given method exists.
class Squirrel * engine
Engine used to register for Squirrel.
std::string name
Full name of the script.
SQInteger AddSetting(HSQUIRRELVM vm)
Set a setting.
std::string main_script
The full path of the script.
SQInteger AddLabels(HSQUIRRELVM vm)
Add labels for a setting.
std::string instance_name
Name of the main class in the script.
bool GetSettings()
Get the settings of the Script.
HSQOBJECT SQ_instance
The Squirrel instance created for this info.
std::string author
Author of the script.
int GetSettingDefaultValue(const std::string &name) const
Get the default value for a setting.
std::string tar_file
If, which tar file the script was in.
const ScriptConfigItem * GetConfigItem(const std::string_view name) const
Get the description of a certain Script config option.
std::string url
URL of the script.
std::string date
The date the script was written at.
Scanner to help finding scripts.
std::string GetTarFile()
Get the current tar file the ScanDir is currently tracking.
class Squirrel * GetEngine()
Get the engine of the main squirrel handler (it indexes all available scripts).
std::string GetMainScript()
Get the current main script the ScanDir is currently tracking.
static void * GetGlobalPointer(HSQUIRRELVM vm)
Get the pointer as set by SetGlobalPointer.
Definition squirrel.hpp:226
void ThrowError(const std::string_view error)
Throw a Squirrel error that will be nicely displayed to the user.
Definition squirrel.hpp:236
static bool GetInstance(HSQUIRRELVM vm, HSQOBJECT *ptr, int pos=1)
Get the Squirrel-instance pointer.
Definition squirrel.hpp:200
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
bool MethodExists(HSQOBJECT instance, const char *method_name)
Check if a method exists in an instance.
Definition squirrel.cpp:345
ScriptConfigFlags
Bitmask of flags for Script settings.
@ SCRIPTCONFIG_BOOLEAN
This value is a boolean (either 0 (false) or 1 (true) ).
std::vector< ScriptConfigItem > ScriptConfigItemList
List of ScriptConfig items.
ScriptInfo keeps track of all information of a script, like Author, Description, ....
static const int MAX_CREATEINSTANCE_OPS
Number of operations to create an instance of a script.
static const int MAX_GET_SETTING_OPS
Maximum number of operations allowed for getting a particular setting.
static const int MAX_GET_OPS
Number of operations to get the author and similar information.
Declarations of the class for the script scanner.
declarations and parts of the implementation of the class for convert code
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:107
Info about a single Script setting.
ScriptConfigFlags flags
Flags for the configuration setting.
LabelMapping labels
Text labels for the integer values.
std::string name
The name of the configuration setting.
int default_value
The default value of this configuration setting.
int min_value
The minimal value this configuration setting can have.
int max_value
The maximal value this configuration setting can have.
int step_size
The step size in the gui.
std::string description
The description of the configuration setting.
bool complete_labels
True if all values have a label.