Update: Translations from eints
[openttd-github.git] / src / script / squirrel_helper.hpp
blob3adf6375004a19e6ad686dae817340fc7855edeb
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 */
8 /** @file squirrel_helper.hpp declarations and parts of the implementation of the class for convert code */
10 #ifndef SQUIRREL_HELPER_HPP
11 #define SQUIRREL_HELPER_HPP
13 #include "squirrel.hpp"
14 #include "../core/alloc_func.hpp"
15 #include "../economy_type.h"
16 #include "../string_func.h"
17 #include "../tile_type.h"
18 #include "squirrel_helper_type.hpp"
20 template <class CL, ScriptType ST> const char *GetClassName();
22 /**
23 * The Squirrel convert routines
25 namespace SQConvert {
26 /**
27 * To return a value to squirrel, we use this helper class. It converts to the right format.
28 * We use a class instead of a plain function to allow us to use partial template specializations.
30 template <typename T> struct Return;
32 template <> struct Return<uint8_t> { static inline int Set(HSQUIRRELVM vm, uint8_t res) { sq_pushinteger(vm, (int32_t)res); return 1; } };
33 template <> struct Return<uint16_t> { static inline int Set(HSQUIRRELVM vm, uint16_t res) { sq_pushinteger(vm, (int32_t)res); return 1; } };
34 template <> struct Return<uint32_t> { static inline int Set(HSQUIRRELVM vm, uint32_t res) { sq_pushinteger(vm, (int32_t)res); return 1; } };
35 template <> struct Return<int8_t> { static inline int Set(HSQUIRRELVM vm, int8_t res) { sq_pushinteger(vm, res); return 1; } };
36 template <> struct Return<int16_t> { static inline int Set(HSQUIRRELVM vm, int16_t res) { sq_pushinteger(vm, res); return 1; } };
37 template <> struct Return<int32_t> { static inline int Set(HSQUIRRELVM vm, int32_t res) { sq_pushinteger(vm, res); return 1; } };
38 template <> struct Return<int64_t> { static inline int Set(HSQUIRRELVM vm, int64_t res) { sq_pushinteger(vm, res); return 1; } };
39 template <> struct Return<Money> { static inline int Set(HSQUIRRELVM vm, Money res) { sq_pushinteger(vm, res); return 1; } };
40 template <> struct Return<TileIndex> { static inline int Set(HSQUIRRELVM vm, TileIndex res) { sq_pushinteger(vm, (int32_t)res.base()); return 1; } };
41 template <> struct Return<bool> { static inline int Set(HSQUIRRELVM vm, bool res) { sq_pushbool (vm, res); return 1; } };
42 template <> struct Return<char *> { /* Do not use char *, use std::optional<std::string> instead. */ };
43 template <> struct Return<const char *> { /* Do not use const char *, use std::optional<std::string> instead. */ };
44 template <> struct Return<void *> { static inline int Set(HSQUIRRELVM vm, void *res) { sq_pushuserpointer(vm, res); return 1; } };
45 template <> struct Return<HSQOBJECT> { static inline int Set(HSQUIRRELVM vm, HSQOBJECT res) { sq_pushobject(vm, res); return 1; } };
47 template <> struct Return<std::optional<std::string>> {
48 static inline int Set(HSQUIRRELVM vm, std::optional<std::string> res)
50 if (res.has_value()) {
51 sq_pushstring(vm, res.value(), -1);
52 } else {
53 sq_pushnull(vm);
55 return 1;
59 /**
60 * To get a param from squirrel, we use this helper class. It converts to the right format.
61 * We use a class instead of a plain function to allow us to use partial template specializations.
63 template <typename T> struct Param;
65 template <> struct Param<uint8_t> { static inline uint8_t Get(HSQUIRRELVM vm, int index) { SQInteger tmp; sq_getinteger (vm, index, &tmp); return tmp; } };
66 template <> struct Param<uint16_t> { static inline uint16_t Get(HSQUIRRELVM vm, int index) { SQInteger tmp; sq_getinteger (vm, index, &tmp); return tmp; } };
67 template <> struct Param<uint32_t> { static inline uint32_t Get(HSQUIRRELVM vm, int index) { SQInteger tmp; sq_getinteger (vm, index, &tmp); return tmp; } };
68 template <> struct Param<int8_t> { static inline int8_t Get(HSQUIRRELVM vm, int index) { SQInteger tmp; sq_getinteger (vm, index, &tmp); return tmp; } };
69 template <> struct Param<int16_t> { static inline int16_t Get(HSQUIRRELVM vm, int index) { SQInteger tmp; sq_getinteger (vm, index, &tmp); return tmp; } };
70 template <> struct Param<int32_t> { static inline int32_t Get(HSQUIRRELVM vm, int index) { SQInteger tmp; sq_getinteger (vm, index, &tmp); return tmp; } };
71 template <> struct Param<int64_t> { static inline int64_t Get(HSQUIRRELVM vm, int index) { SQInteger tmp; sq_getinteger (vm, index, &tmp); return tmp; } };
72 template <> struct Param<TileIndex> { static inline TileIndex Get(HSQUIRRELVM vm, int index) { SQInteger tmp; sq_getinteger (vm, index, &tmp); return TileIndex((uint32_t)(int32_t)tmp); } };
73 template <> struct Param<Money> { static inline Money Get(HSQUIRRELVM vm, int index) { SQInteger tmp; sq_getinteger (vm, index, &tmp); return tmp; } };
74 template <> struct Param<bool> { static inline bool Get(HSQUIRRELVM vm, int index) { SQBool tmp; sq_getbool (vm, index, &tmp); return tmp != 0; } };
75 template <> struct Param<const char *> { /* Do not use const char *, use std::string& instead. */ };
76 template <> struct Param<void *> { static inline void *Get(HSQUIRRELVM vm, int index) { SQUserPointer tmp; sq_getuserpointer(vm, index, &tmp); return tmp; } };
78 template <> struct Param<const std::string &> {
79 static inline const std::string Get(HSQUIRRELVM vm, int index)
81 /* Convert what-ever there is as parameter to a string */
82 sq_tostring(vm, index);
84 const SQChar *tmp;
85 sq_getstring(vm, -1, &tmp);
86 std::string result = StrMakeValid(tmp);
87 sq_poptop(vm);
88 return result;
92 template <typename Titem>
93 struct Param<Array<Titem> &&> {
94 static inline Array<Titem> Get(HSQUIRRELVM vm, int index)
96 /* Sanity check of the size. */
97 if (sq_getsize(vm, index) > UINT16_MAX) throw sq_throwerror(vm, "an array used as parameter to a function is too large");
99 SQObject obj;
100 sq_getstackobj(vm, index, &obj);
101 sq_pushobject(vm, obj);
102 sq_pushnull(vm);
104 Array<Titem> data;
106 while (SQ_SUCCEEDED(sq_next(vm, -2))) {
107 data.emplace_back(Param<Titem>::Get(vm, -1));
108 sq_pop(vm, 2);
110 sq_pop(vm, 2);
112 return data;
117 * Helper class to recognize the function type (retval type, args) and use the proper specialization
118 * for SQ callback. The partial specializations for the second arg (Tis_void_retval) are not possible
119 * on the function. Therefore the class is used instead.
121 template <typename Tfunc> struct HelperT;
124 * The real C++ caller for functions.
126 template <typename Tretval, typename... Targs>
127 struct HelperT<Tretval (*)(Targs...)> {
128 static int SQCall(void *instance, Tretval(*func)(Targs...), HSQUIRRELVM vm)
130 return SQCall(instance, func, vm, std::index_sequence_for<Targs...>{});
133 private:
134 template <size_t... i>
135 static int SQCall(void *, Tretval(*func)(Targs...), [[maybe_unused]] HSQUIRRELVM vm, std::index_sequence<i...>)
137 if constexpr (std::is_void_v<Tretval>) {
138 (*func)(
139 Param<Targs>::Get(vm, 2 + i)...
141 return 0;
142 } else {
143 Tretval ret = (*func)(
144 Param<Targs>::Get(vm, 2 + i)...
146 return Return<Tretval>::Set(vm, ret);
152 * The real C++ caller for methods.
154 template <class Tcls, typename Tretval, typename... Targs>
155 struct HelperT<Tretval(Tcls:: *)(Targs...)> {
156 static int SQCall(Tcls *instance, Tretval(Tcls:: *func)(Targs...), HSQUIRRELVM vm)
158 return SQCall(instance, func, vm, std::index_sequence_for<Targs...>{});
161 static Tcls *SQConstruct(Tcls *instance, Tretval(Tcls:: *func)(Targs...), HSQUIRRELVM vm)
163 return SQConstruct(instance, func, vm, std::index_sequence_for<Targs...>{});
166 private:
167 template <size_t... i>
168 static int SQCall(Tcls *instance, Tretval(Tcls:: *func)(Targs...), [[maybe_unused]] HSQUIRRELVM vm, std::index_sequence<i...>)
170 if constexpr (std::is_void_v<Tretval>) {
171 (instance->*func)(
172 Param<Targs>::Get(vm, 2 + i)...
174 return 0;
175 } else {
176 Tretval ret = (instance->*func)(
177 Param<Targs>::Get(vm, 2 + i)...
179 return Return<Tretval>::Set(vm, ret);
183 template <size_t... i>
184 static Tcls *SQConstruct(Tcls *, Tretval(Tcls:: *)(Targs...), [[maybe_unused]] HSQUIRRELVM vm, std::index_sequence<i...>)
186 Tcls *inst = new Tcls(
187 Param<Targs>::Get(vm, 2 + i)...
190 return inst;
196 * A general template for all non-static method callbacks from Squirrel.
197 * In here the function_proc is recovered, and the SQCall is called that
198 * can handle this exact amount of params.
200 template <typename Tcls, typename Tmethod, ScriptType Ttype>
201 inline SQInteger DefSQNonStaticCallback(HSQUIRRELVM vm)
203 /* Find the amount of params we got */
204 int nparam = sq_gettop(vm);
205 SQUserPointer ptr = nullptr;
206 SQUserPointer real_instance = nullptr;
207 HSQOBJECT instance;
209 /* Get the 'SQ' instance of this class */
210 Squirrel::GetInstance(vm, &instance);
212 /* Protect against calls to a non-static method in a static way */
213 sq_pushroottable(vm);
214 const char *className = GetClassName<Tcls, Ttype>();
215 sq_pushstring(vm, className, -1);
216 sq_get(vm, -2);
217 sq_pushobject(vm, instance);
218 if (sq_instanceof(vm) != SQTrue) return sq_throwerror(vm, "class method is non-static");
219 sq_pop(vm, 3);
221 /* Get the 'real' instance of this class */
222 sq_getinstanceup(vm, 1, &real_instance, nullptr);
223 /* Get the real function pointer */
224 sq_getuserdata(vm, nparam, &ptr, nullptr);
225 if (real_instance == nullptr) return sq_throwerror(vm, "couldn't detect real instance of class for non-static call");
226 /* Remove the userdata from the stack */
227 sq_pop(vm, 1);
229 try {
230 /* Delegate it to a template that can handle this specific function */
231 return HelperT<Tmethod>::SQCall((Tcls *)real_instance, *(Tmethod *)ptr, vm);
232 } catch (SQInteger &e) {
233 return e;
238 * A general template for all non-static advanced method callbacks from Squirrel.
239 * In here the function_proc is recovered, and the SQCall is called that
240 * can handle this exact amount of params.
242 template <typename Tcls, typename Tmethod, ScriptType Ttype>
243 inline SQInteger DefSQAdvancedNonStaticCallback(HSQUIRRELVM vm)
245 /* Find the amount of params we got */
246 int nparam = sq_gettop(vm);
247 SQUserPointer ptr = nullptr;
248 SQUserPointer real_instance = nullptr;
249 HSQOBJECT instance;
251 /* Get the 'SQ' instance of this class */
252 Squirrel::GetInstance(vm, &instance);
254 /* Protect against calls to a non-static method in a static way */
255 sq_pushroottable(vm);
256 const char *className = GetClassName<Tcls, Ttype>();
257 sq_pushstring(vm, className, -1);
258 sq_get(vm, -2);
259 sq_pushobject(vm, instance);
260 if (sq_instanceof(vm) != SQTrue) return sq_throwerror(vm, "class method is non-static");
261 sq_pop(vm, 3);
263 /* Get the 'real' instance of this class */
264 sq_getinstanceup(vm, 1, &real_instance, nullptr);
265 /* Get the real function pointer */
266 sq_getuserdata(vm, nparam, &ptr, nullptr);
267 if (real_instance == nullptr) return sq_throwerror(vm, "couldn't detect real instance of class for non-static call");
268 /* Remove the userdata from the stack */
269 sq_pop(vm, 1);
271 /* Call the function, which its only param is always the VM */
272 return (SQInteger)(((Tcls *)real_instance)->*(*(Tmethod *)ptr))(vm);
276 * A general template for all function/static method callbacks from Squirrel.
277 * In here the function_proc is recovered, and the SQCall is called that
278 * can handle this exact amount of params.
280 template <typename Tcls, typename Tmethod>
281 inline SQInteger DefSQStaticCallback(HSQUIRRELVM vm)
283 /* Find the amount of params we got */
284 int nparam = sq_gettop(vm);
285 SQUserPointer ptr = nullptr;
287 /* Get the real function pointer */
288 sq_getuserdata(vm, nparam, &ptr, nullptr);
290 try {
291 /* Delegate it to a template that can handle this specific function */
292 return HelperT<Tmethod>::SQCall((Tcls *)nullptr, *(Tmethod *)ptr, vm);
293 } catch (SQInteger &e) {
294 return e;
300 * A general template for all static advanced method callbacks from Squirrel.
301 * In here the function_proc is recovered, and the SQCall is called that
302 * can handle this exact amount of params.
304 template <typename Tcls, typename Tmethod>
305 inline SQInteger DefSQAdvancedStaticCallback(HSQUIRRELVM vm)
307 /* Find the amount of params we got */
308 int nparam = sq_gettop(vm);
309 SQUserPointer ptr = nullptr;
311 /* Get the real function pointer */
312 sq_getuserdata(vm, nparam, &ptr, nullptr);
313 /* Remove the userdata from the stack */
314 sq_pop(vm, 1);
316 /* Call the function, which its only param is always the VM */
317 return (SQInteger)(*(*(Tmethod *)ptr))(vm);
321 * A general template for the destructor of SQ instances. This is needed
322 * here as it has to be in the same scope as DefSQConstructorCallback.
324 template <typename Tcls>
325 static SQInteger DefSQDestructorCallback(SQUserPointer p, SQInteger)
327 /* Remove the real instance too */
328 if (p != nullptr) ((Tcls *)p)->Release();
329 return 0;
333 * A general template to handle creating of instance with any amount of
334 * params. It creates the instance in C++, and it sets all the needed
335 * settings in SQ to register the instance.
337 template <typename Tcls, typename Tmethod, int Tnparam>
338 inline SQInteger DefSQConstructorCallback(HSQUIRRELVM vm)
340 try {
341 /* Create the real instance */
342 Tcls *instance = HelperT<Tmethod>::SQConstruct((Tcls *)nullptr, (Tmethod)nullptr, vm);
343 sq_setinstanceup(vm, -Tnparam, instance);
344 sq_setreleasehook(vm, -Tnparam, DefSQDestructorCallback<Tcls>);
345 instance->AddRef();
346 return 0;
347 } catch (SQInteger &e) {
348 return e;
353 * A general template to handle creating of an instance with a complex
354 * constructor.
356 template <typename Tcls>
357 inline SQInteger DefSQAdvancedConstructorCallback(HSQUIRRELVM vm)
359 try {
360 /* Find the amount of params we got */
361 int nparam = sq_gettop(vm);
363 /* Create the real instance */
364 Tcls *instance = new Tcls(vm);
365 sq_setinstanceup(vm, -nparam, instance);
366 sq_setreleasehook(vm, -nparam, DefSQDestructorCallback<Tcls>);
367 instance->AddRef();
368 return 0;
369 } catch (SQInteger &e) {
370 return e;
374 } // namespace SQConvert
376 #endif /* SQUIRREL_HELPER_HPP */