/*
* This file is part of OpenTTD.
* 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.
* 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.
* 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 .
*/
/** @file squirrel_helper.hpp declarations and parts of the implementation of the class for convert code */
#ifndef SQUIRREL_HELPER_HPP
#define SQUIRREL_HELPER_HPP
#include "squirrel.hpp"
#include "../core/alloc_func.hpp"
#include "../economy_type.h"
#include "../string_func.h"
#include "../tile_type.h"
#include "squirrel_helper_type.hpp"
template const char *GetClassName();
/**
* The Squirrel convert routines
*/
namespace SQConvert {
/**
* To return a value to squirrel, we use this helper class. It converts to the right format.
* We use a class instead of a plain function to allow us to use partial template specializations.
*/
template struct Return;
template <> struct Return { static inline int Set(HSQUIRRELVM vm, uint8_t res) { sq_pushinteger(vm, (int32_t)res); return 1; } };
template <> struct Return { static inline int Set(HSQUIRRELVM vm, uint16_t res) { sq_pushinteger(vm, (int32_t)res); return 1; } };
template <> struct Return { static inline int Set(HSQUIRRELVM vm, uint32_t res) { sq_pushinteger(vm, (int32_t)res); return 1; } };
template <> struct Return { static inline int Set(HSQUIRRELVM vm, int8_t res) { sq_pushinteger(vm, res); return 1; } };
template <> struct Return { static inline int Set(HSQUIRRELVM vm, int16_t res) { sq_pushinteger(vm, res); return 1; } };
template <> struct Return { static inline int Set(HSQUIRRELVM vm, int32_t res) { sq_pushinteger(vm, res); return 1; } };
template <> struct Return { static inline int Set(HSQUIRRELVM vm, int64_t res) { sq_pushinteger(vm, res); return 1; } };
template <> struct Return { static inline int Set(HSQUIRRELVM vm, Money res) { sq_pushinteger(vm, res); return 1; } };
template <> struct Return { static inline int Set(HSQUIRRELVM vm, TileIndex res) { sq_pushinteger(vm, (int32_t)static_cast(res)); return 1; } };
template <> struct Return { static inline int Set(HSQUIRRELVM vm, bool res) { sq_pushbool (vm, res); return 1; } };
template <> struct Return { /* Do not use char *, use std::optional instead. */ };
template <> struct Return { /* Do not use const char *, use std::optional instead. */ };
template <> struct Return { static inline int Set(HSQUIRRELVM vm, void *res) { sq_pushuserpointer(vm, res); return 1; } };
template <> struct Return { static inline int Set(HSQUIRRELVM vm, HSQOBJECT res) { sq_pushobject(vm, res); return 1; } };
template <> struct Return> {
static inline int Set(HSQUIRRELVM vm, std::optional res) {
if (res.has_value()) {
sq_pushstring(vm, res.value(), -1);
} else {
sq_pushnull(vm);
}
return 1;
}
};
/**
* To get a param from squirrel, we use this helper class. It converts to the right format.
* We use a class instead of a plain function to allow us to use partial template specializations.
*/
template struct Param;
template <> struct Param { static inline uint8_t Get(HSQUIRRELVM vm, int index) { SQInteger tmp; sq_getinteger (vm, index, &tmp); return tmp; } };
template <> struct Param { static inline uint16_t Get(HSQUIRRELVM vm, int index) { SQInteger tmp; sq_getinteger (vm, index, &tmp); return tmp; } };
template <> struct Param { static inline uint32_t Get(HSQUIRRELVM vm, int index) { SQInteger tmp; sq_getinteger (vm, index, &tmp); return tmp; } };
template <> struct Param { static inline int8_t Get(HSQUIRRELVM vm, int index) { SQInteger tmp; sq_getinteger (vm, index, &tmp); return tmp; } };
template <> struct Param { static inline int16_t Get(HSQUIRRELVM vm, int index) { SQInteger tmp; sq_getinteger (vm, index, &tmp); return tmp; } };
template <> struct Param { static inline int32_t Get(HSQUIRRELVM vm, int index) { SQInteger tmp; sq_getinteger (vm, index, &tmp); return tmp; } };
template <> struct Param { static inline int64_t Get(HSQUIRRELVM vm, int index) { SQInteger tmp; sq_getinteger (vm, index, &tmp); return tmp; } };
template <> struct Param { static inline TileIndex Get(HSQUIRRELVM vm, int index) { SQInteger tmp; sq_getinteger (vm, index, &tmp); return TileIndex((uint32_t)(int32_t)tmp); } };
template <> struct Param { static inline Money Get(HSQUIRRELVM vm, int index) { SQInteger tmp; sq_getinteger (vm, index, &tmp); return tmp; } };
template <> struct Param { static inline bool Get(HSQUIRRELVM vm, int index) { SQBool tmp; sq_getbool (vm, index, &tmp); return tmp != 0; } };
template <> struct Param { /* Do not use const char *, use std::string& instead. */ };
template <> struct Param { static inline void *Get(HSQUIRRELVM vm, int index) { SQUserPointer tmp; sq_getuserpointer(vm, index, &tmp); return tmp; } };
template <> struct Param {
static inline const std::string Get(HSQUIRRELVM vm, int index)
{
/* Convert what-ever there is as parameter to a string */
sq_tostring(vm, index);
const SQChar *tmp;
sq_getstring(vm, -1, &tmp);
std::string result = StrMakeValid(tmp);
sq_poptop(vm);
return result;
}
};
template
struct Param &&> {
static inline Array Get(HSQUIRRELVM vm, int index)
{
/* Sanity check of the size. */
if (sq_getsize(vm, index) > UINT16_MAX) throw sq_throwerror(vm, "an array used as parameter to a function is too large");
SQObject obj;
sq_getstackobj(vm, index, &obj);
sq_pushobject(vm, obj);
sq_pushnull(vm);
Array data;
while (SQ_SUCCEEDED(sq_next(vm, -2))) {
data.emplace_back(Param::Get(vm, -1));
sq_pop(vm, 2);
}
sq_pop(vm, 2);
return data;
}
};
/**
* Helper class to recognize the function type (retval type, args) and use the proper specialization
* for SQ callback. The partial specializations for the second arg (Tis_void_retval) are not possible
* on the function. Therefore the class is used instead.
*/
template struct HelperT;
/**
* The real C++ caller for functions.
*/
template
struct HelperT {
static int SQCall(void *instance, Tretval(*func)(Targs...), HSQUIRRELVM vm)
{
return SQCall(instance, func, vm, std::index_sequence_for{});
}
private:
template
static int SQCall(void *, Tretval(*func)(Targs...), [[maybe_unused]] HSQUIRRELVM vm, std::index_sequence)
{
if constexpr (std::is_void_v) {
(*func)(
Param::Get(vm, 2 + i)...
);
return 0;
} else {
Tretval ret = (*func)(
Param::Get(vm, 2 + i)...
);
return Return::Set(vm, ret);
}
}
};
/**
* The real C++ caller for methods.
*/
template
struct HelperT {
static int SQCall(Tcls *instance, Tretval(Tcls:: *func)(Targs...), HSQUIRRELVM vm)
{
return SQCall(instance, func, vm, std::index_sequence_for{});
}
static Tcls *SQConstruct(Tcls *instance, Tretval(Tcls:: *func)(Targs...), HSQUIRRELVM vm)
{
return SQConstruct(instance, func, vm, std::index_sequence_for{});
}
private:
template
static int SQCall(Tcls *instance, Tretval(Tcls:: *func)(Targs...), [[maybe_unused]] HSQUIRRELVM vm, std::index_sequence)
{
if constexpr (std::is_void_v) {
(instance->*func)(
Param::Get(vm, 2 + i)...
);
return 0;
} else {
Tretval ret = (instance->*func)(
Param::Get(vm, 2 + i)...
);
return Return::Set(vm, ret);
}
}
template
static Tcls *SQConstruct(Tcls *, Tretval(Tcls:: *)(Targs...), [[maybe_unused]] HSQUIRRELVM vm, std::index_sequence)
{
Tcls *inst = new Tcls(
Param::Get(vm, 2 + i)...
);
return inst;
}
};
/**
* A general template for all non-static method callbacks from Squirrel.
* In here the function_proc is recovered, and the SQCall is called that
* can handle this exact amount of params.
*/
template
inline SQInteger DefSQNonStaticCallback(HSQUIRRELVM vm)
{
/* Find the amount of params we got */
int nparam = sq_gettop(vm);
SQUserPointer ptr = nullptr;
SQUserPointer real_instance = nullptr;
HSQOBJECT instance;
/* Get the 'SQ' instance of this class */
Squirrel::GetInstance(vm, &instance);
/* Protect against calls to a non-static method in a static way */
sq_pushroottable(vm);
const char *className = GetClassName();
sq_pushstring(vm, className, -1);
sq_get(vm, -2);
sq_pushobject(vm, instance);
if (sq_instanceof(vm) != SQTrue) return sq_throwerror(vm, "class method is non-static");
sq_pop(vm, 3);
/* Get the 'real' instance of this class */
sq_getinstanceup(vm, 1, &real_instance, nullptr);
/* Get the real function pointer */
sq_getuserdata(vm, nparam, &ptr, nullptr);
if (real_instance == nullptr) return sq_throwerror(vm, "couldn't detect real instance of class for non-static call");
/* Remove the userdata from the stack */
sq_pop(vm, 1);
try {
/* Delegate it to a template that can handle this specific function */
return HelperT::SQCall((Tcls *)real_instance, *(Tmethod *)ptr, vm);
} catch (SQInteger &e) {
return e;
}
}
/**
* A general template for all non-static advanced method callbacks from Squirrel.
* In here the function_proc is recovered, and the SQCall is called that
* can handle this exact amount of params.
*/
template
inline SQInteger DefSQAdvancedNonStaticCallback(HSQUIRRELVM vm)
{
/* Find the amount of params we got */
int nparam = sq_gettop(vm);
SQUserPointer ptr = nullptr;
SQUserPointer real_instance = nullptr;
HSQOBJECT instance;
/* Get the 'SQ' instance of this class */
Squirrel::GetInstance(vm, &instance);
/* Protect against calls to a non-static method in a static way */
sq_pushroottable(vm);
const char *className = GetClassName();
sq_pushstring(vm, className, -1);
sq_get(vm, -2);
sq_pushobject(vm, instance);
if (sq_instanceof(vm) != SQTrue) return sq_throwerror(vm, "class method is non-static");
sq_pop(vm, 3);
/* Get the 'real' instance of this class */
sq_getinstanceup(vm, 1, &real_instance, nullptr);
/* Get the real function pointer */
sq_getuserdata(vm, nparam, &ptr, nullptr);
if (real_instance == nullptr) return sq_throwerror(vm, "couldn't detect real instance of class for non-static call");
/* Remove the userdata from the stack */
sq_pop(vm, 1);
/* Call the function, which its only param is always the VM */
return (SQInteger)(((Tcls *)real_instance)->*(*(Tmethod *)ptr))(vm);
}
/**
* A general template for all function/static method callbacks from Squirrel.
* In here the function_proc is recovered, and the SQCall is called that
* can handle this exact amount of params.
*/
template
inline SQInteger DefSQStaticCallback(HSQUIRRELVM vm)
{
/* Find the amount of params we got */
int nparam = sq_gettop(vm);
SQUserPointer ptr = nullptr;
/* Get the real function pointer */
sq_getuserdata(vm, nparam, &ptr, nullptr);
try {
/* Delegate it to a template that can handle this specific function */
return HelperT::SQCall((Tcls *)nullptr, *(Tmethod *)ptr, vm);
} catch (SQInteger &e) {
return e;
}
}
/**
* A general template for all static advanced method callbacks from Squirrel.
* In here the function_proc is recovered, and the SQCall is called that
* can handle this exact amount of params.
*/
template
inline SQInteger DefSQAdvancedStaticCallback(HSQUIRRELVM vm)
{
/* Find the amount of params we got */
int nparam = sq_gettop(vm);
SQUserPointer ptr = nullptr;
/* Get the real function pointer */
sq_getuserdata(vm, nparam, &ptr, nullptr);
/* Remove the userdata from the stack */
sq_pop(vm, 1);
/* Call the function, which its only param is always the VM */
return (SQInteger)(*(*(Tmethod *)ptr))(vm);
}
/**
* A general template for the destructor of SQ instances. This is needed
* here as it has to be in the same scope as DefSQConstructorCallback.
*/
template
static SQInteger DefSQDestructorCallback(SQUserPointer p, SQInteger)
{
/* Remove the real instance too */
if (p != nullptr) ((Tcls *)p)->Release();
return 0;
}
/**
* A general template to handle creating of instance with any amount of
* params. It creates the instance in C++, and it sets all the needed
* settings in SQ to register the instance.
*/
template
inline SQInteger DefSQConstructorCallback(HSQUIRRELVM vm)
{
try {
/* Create the real instance */
Tcls *instance = HelperT::SQConstruct((Tcls *)nullptr, (Tmethod)nullptr, vm);
sq_setinstanceup(vm, -Tnparam, instance);
sq_setreleasehook(vm, -Tnparam, DefSQDestructorCallback);
instance->AddRef();
return 0;
} catch (SQInteger &e) {
return e;
}
}
/**
* A general template to handle creating of an instance with a complex
* constructor.
*/
template
inline SQInteger DefSQAdvancedConstructorCallback(HSQUIRRELVM vm)
{
try {
/* Find the amount of params we got */
int nparam = sq_gettop(vm);
/* Create the real instance */
Tcls *instance = new Tcls(vm);
sq_setinstanceup(vm, -nparam, instance);
sq_setreleasehook(vm, -nparam, DefSQDestructorCallback);
instance->AddRef();
return 0;
} catch (SQInteger &e) {
return e;
}
}
} // namespace SQConvert
#endif /* SQUIRREL_HELPER_HPP */