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/>.
10 /** @file script_info.cpp Implementation of ScriptInfo. */
12 #include "../stdafx.h"
13 #include "../settings_type.h"
15 #include "squirrel_helper.hpp"
17 #include "script_info.hpp"
18 #include "script_scanner.hpp"
20 #include "../safeguards.h"
22 ScriptInfo::~ScriptInfo()
24 /* Free all allocated strings */
25 for (ScriptConfigItemList::iterator it
= this->config_list
.begin(); it
!= this->config_list
.end(); it
++) {
27 free((*it
).description
);
28 if (it
->labels
!= NULL
) {
29 for (LabelMapping::iterator it2
= (*it
).labels
->Begin(); it2
!= (*it
).labels
->End(); it2
++) {
35 this->config_list
.clear();
39 free(this->short_name
);
40 free(this->description
);
42 free(this->instance_name
);
44 free(this->main_script
);
46 free(this->SQ_instance
);
49 bool ScriptInfo::CheckMethod(const char *name
) const
51 if (!this->engine
->MethodExists(*this->SQ_instance
, name
)) {
53 seprintf(error
, lastof(error
), "your info.nut/library.nut doesn't have the method '%s'", name
);
54 this->engine
->ThrowError(error
);
60 /* static */ SQInteger
ScriptInfo::Constructor(HSQUIRRELVM vm
, ScriptInfo
*info
)
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
);
68 info
->scanner
= (ScriptScanner
*)Squirrel::GetGlobalPointer(vm
);
69 info
->engine
= info
->scanner
->GetEngine();
71 /* Ensure the mandatory functions exist */
72 static const char * const required_functions
[] = {
81 for (size_t i
= 0; i
< lengthof(required_functions
); i
++) {
82 if (!info
->CheckMethod(required_functions
[i
])) return SQ_ERROR
;
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
);
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
;
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
;
104 /* Check if we have settings */
105 if (info
->engine
->MethodExists(*info
->SQ_instance
, "GetSettings")) {
106 if (!info
->GetSettings()) return SQ_ERROR
;
112 bool ScriptInfo::GetSettings()
114 return this->engine
->CallMethod(*this->SQ_instance
, "GetSettings", NULL
, MAX_GET_SETTING_OPS
);
117 SQInteger
ScriptInfo::AddSetting(HSQUIRRELVM vm
)
119 ScriptConfigItem config
;
120 memset(&config
, 0, sizeof(config
));
121 config
.max_value
= 1;
122 config
.step_size
= 1;
125 /* Read the table, and find all properties we care about */
127 while (SQ_SUCCEEDED(sq_next(vm
, -2))) {
129 if (SQ_FAILED(sq_getstring(vm
, -2, &key
))) return SQ_ERROR
;
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
);
137 ValidateString(name
);
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
= '_';
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
);
151 } else if (strcmp(key
, "min_value") == 0) {
153 if (SQ_FAILED(sq_getinteger(vm
, -1, &res
))) return SQ_ERROR
;
154 config
.min_value
= res
;
156 } else if (strcmp(key
, "max_value") == 0) {
158 if (SQ_FAILED(sq_getinteger(vm
, -1, &res
))) return SQ_ERROR
;
159 config
.max_value
= res
;
161 } else if (strcmp(key
, "easy_value") == 0) {
163 if (SQ_FAILED(sq_getinteger(vm
, -1, &res
))) return SQ_ERROR
;
164 config
.easy_value
= res
;
166 } else if (strcmp(key
, "medium_value") == 0) {
168 if (SQ_FAILED(sq_getinteger(vm
, -1, &res
))) return SQ_ERROR
;
169 config
.medium_value
= res
;
171 } else if (strcmp(key
, "hard_value") == 0) {
173 if (SQ_FAILED(sq_getinteger(vm
, -1, &res
))) return SQ_ERROR
;
174 config
.hard_value
= res
;
176 } else if (strcmp(key
, "random_deviation") == 0) {
178 if (SQ_FAILED(sq_getinteger(vm
, -1, &res
))) return SQ_ERROR
;
179 config
.random_deviation
= res
;
181 } else if (strcmp(key
, "custom_value") == 0) {
183 if (SQ_FAILED(sq_getinteger(vm
, -1, &res
))) return SQ_ERROR
;
184 config
.custom_value
= res
;
186 } else if (strcmp(key
, "step_size") == 0) {
188 if (SQ_FAILED(sq_getinteger(vm
, -1, &res
))) return SQ_ERROR
;
189 config
.step_size
= res
;
190 } else if (strcmp(key
, "flags") == 0) {
192 if (SQ_FAILED(sq_getinteger(vm
, -1, &res
))) return SQ_ERROR
;
193 config
.flags
= (ScriptConfigFlags
)res
;
197 seprintf(error
, lastof(error
), "unknown setting property '%s'", key
);
198 this->engine
->ThrowError(error
);
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) {
210 seprintf(error
, lastof(error
), "Setting both random_deviation and SCRIPTCONFIG_RANDOM is not allowed");
211 this->engine
->ThrowError(error
);
214 /* Reset the bit for random_deviation as it's optional. */
217 /* Make sure all properties are defined */
218 uint mask
= (config
.flags
& SCRIPTCONFIG_BOOLEAN
) ? 0x1F3 : 0x1FF;
221 seprintf(error
, lastof(error
), "please define all properties of a setting (min/max not allowed for booleans)");
222 this->engine
->ThrowError(error
);
226 this->config_list
.push_back(config
);
230 SQInteger
ScriptInfo::AddLabels(HSQUIRRELVM vm
)
232 const SQChar
*setting_name
;
233 if (SQ_FAILED(sq_getstring(vm
, -2, &setting_name
))) return SQ_ERROR
;
234 ValidateString(setting_name
);
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
);
241 if (config
== NULL
) {
243 seprintf(error
, lastof(error
), "Trying to add labels for non-defined setting '%s'", setting_name
);
244 this->engine
->ThrowError(error
);
247 if (config
->labels
!= NULL
) return SQ_ERROR
;
249 config
->labels
= new LabelMapping
;
251 /* Read the table and find all labels */
253 while (SQ_SUCCEEDED(sq_next(vm
, -2))) {
254 const SQChar
*key_string
;
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
);
263 /* !Contains() prevents stredup from leaking. */
264 if (!config
->labels
->Contains(key
)) config
->labels
->Insert(key
, stredup(label
));
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;
282 const ScriptConfigItemList
*ScriptInfo::GetConfigList() const
284 return &this->config_list
;
287 const ScriptConfigItem
*ScriptInfo::GetConfigItem(const char *name
) const
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
);
295 int ScriptInfo::GetSettingDefaultValue(const char *name
) const
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 */
300 switch (GetGameSettings().script
.settings_profile
) {
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();
309 /* There is no such setting */