2023-01-05 13:25:55 +01:00
|
|
|
/**************************************************************************/
|
|
|
|
/* extension_api_dump.cpp */
|
|
|
|
/**************************************************************************/
|
|
|
|
/* This file is part of: */
|
|
|
|
/* GODOT ENGINE */
|
|
|
|
/* https://godotengine.org */
|
|
|
|
/**************************************************************************/
|
|
|
|
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
|
|
|
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
|
|
|
/* */
|
|
|
|
/* Permission is hereby granted, free of charge, to any person obtaining */
|
|
|
|
/* a copy of this software and associated documentation files (the */
|
|
|
|
/* "Software"), to deal in the Software without restriction, including */
|
|
|
|
/* without limitation the rights to use, copy, modify, merge, publish, */
|
|
|
|
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
|
|
|
/* permit persons to whom the Software is furnished to do so, subject to */
|
|
|
|
/* the following conditions: */
|
|
|
|
/* */
|
|
|
|
/* The above copyright notice and this permission notice shall be */
|
|
|
|
/* included in all copies or substantial portions of the Software. */
|
|
|
|
/* */
|
|
|
|
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
|
|
|
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
|
|
|
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
|
|
|
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
|
|
|
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
|
|
|
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
|
|
|
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
|
|
|
/**************************************************************************/
|
2021-06-19 17:58:49 +02:00
|
|
|
|
|
|
|
#include "extension_api_dump.h"
|
2022-11-07 14:31:10 +01:00
|
|
|
|
2021-06-19 17:58:49 +02:00
|
|
|
#include "core/config/engine.h"
|
|
|
|
#include "core/core_constants.h"
|
|
|
|
#include "core/io/file_access.h"
|
|
|
|
#include "core/io/json.h"
|
|
|
|
#include "core/templates/pair.h"
|
|
|
|
#include "core/version.h"
|
|
|
|
|
|
|
|
#ifdef TOOLS_ENABLED
|
|
|
|
|
2022-10-02 18:57:48 +02:00
|
|
|
static String get_builtin_or_variant_type_name(const Variant::Type p_type) {
|
|
|
|
if (p_type == Variant::NIL) {
|
|
|
|
return "Variant";
|
|
|
|
} else {
|
|
|
|
return Variant::get_type_name(p_type);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static String get_property_info_type_name(const PropertyInfo &p_info) {
|
2021-08-23 19:53:27 +02:00
|
|
|
if (p_info.type == Variant::INT && (p_info.hint == PROPERTY_HINT_INT_IS_POINTER)) {
|
2021-12-09 10:42:46 +01:00
|
|
|
if (p_info.hint_string.is_empty()) {
|
2021-08-23 19:53:27 +02:00
|
|
|
return "void*";
|
|
|
|
} else {
|
|
|
|
return p_info.hint_string + "*";
|
|
|
|
}
|
|
|
|
}
|
2022-08-25 11:35:30 +02:00
|
|
|
if (p_info.type == Variant::ARRAY && (p_info.hint == PROPERTY_HINT_ARRAY_TYPE)) {
|
|
|
|
return String("typedarray::") + p_info.hint_string;
|
|
|
|
}
|
2022-07-11 11:40:31 +02:00
|
|
|
if (p_info.type == Variant::INT && (p_info.usage & (PROPERTY_USAGE_CLASS_IS_ENUM))) {
|
2021-07-29 15:17:07 +02:00
|
|
|
return String("enum::") + String(p_info.class_name);
|
|
|
|
}
|
2022-07-11 11:40:31 +02:00
|
|
|
if (p_info.type == Variant::INT && (p_info.usage & (PROPERTY_USAGE_CLASS_IS_BITFIELD))) {
|
|
|
|
return String("bitfield::") + String(p_info.class_name);
|
|
|
|
}
|
2022-08-15 11:37:58 +02:00
|
|
|
if (p_info.type == Variant::INT && (p_info.usage & PROPERTY_USAGE_ARRAY)) {
|
|
|
|
return "int";
|
|
|
|
}
|
2021-07-29 15:17:07 +02:00
|
|
|
if (p_info.class_name != StringName()) {
|
|
|
|
return p_info.class_name;
|
|
|
|
}
|
|
|
|
if (p_info.hint == PROPERTY_HINT_RESOURCE_TYPE) {
|
|
|
|
return p_info.hint_string;
|
|
|
|
}
|
|
|
|
if (p_info.type == Variant::NIL && (p_info.usage & PROPERTY_USAGE_NIL_IS_VARIANT)) {
|
|
|
|
return "Variant";
|
|
|
|
}
|
|
|
|
if (p_info.type == Variant::NIL) {
|
|
|
|
return "void";
|
|
|
|
}
|
2022-10-02 18:57:48 +02:00
|
|
|
return get_builtin_or_variant_type_name(p_info.type);
|
2021-07-29 15:17:07 +02:00
|
|
|
}
|
|
|
|
|
2022-12-07 12:11:28 +01:00
|
|
|
Dictionary GDExtensionAPIDump::generate_extension_api() {
|
2021-06-19 17:58:49 +02:00
|
|
|
Dictionary api_dump;
|
|
|
|
|
|
|
|
{
|
|
|
|
//header
|
|
|
|
Dictionary header;
|
|
|
|
header["version_major"] = VERSION_MAJOR;
|
|
|
|
header["version_minor"] = VERSION_MINOR;
|
|
|
|
#if VERSION_PATCH
|
|
|
|
header["version_patch"] = VERSION_PATCH;
|
|
|
|
#else
|
|
|
|
header["version_patch"] = 0;
|
|
|
|
#endif
|
|
|
|
header["version_status"] = VERSION_STATUS;
|
|
|
|
header["version_build"] = VERSION_BUILD;
|
|
|
|
header["version_full_name"] = VERSION_FULL_NAME;
|
|
|
|
|
|
|
|
api_dump["header"] = header;
|
|
|
|
}
|
|
|
|
|
|
|
|
const uint32_t vec3_elems = 3;
|
2022-08-21 12:49:28 +02:00
|
|
|
const uint32_t vec4_elems = 4;
|
2021-06-19 17:58:49 +02:00
|
|
|
const uint32_t ptrsize_32 = 4;
|
2021-07-29 15:17:07 +02:00
|
|
|
const uint32_t ptrsize_64 = 8;
|
2021-06-19 17:58:49 +02:00
|
|
|
static const char *build_config_name[4] = { "float_32", "float_64", "double_32", "double_64" };
|
|
|
|
|
|
|
|
{
|
|
|
|
//type sizes
|
2021-07-29 15:17:07 +02:00
|
|
|
constexpr struct {
|
2021-06-19 17:58:49 +02:00
|
|
|
Variant::Type type;
|
|
|
|
uint32_t size_32_bits_real_float;
|
|
|
|
uint32_t size_64_bits_real_float;
|
|
|
|
uint32_t size_32_bits_real_double;
|
|
|
|
uint32_t size_64_bits_real_double;
|
2021-07-29 15:17:07 +02:00
|
|
|
|
|
|
|
// For compile-time size check.
|
|
|
|
constexpr uint32_t operator[](int index) const {
|
|
|
|
switch (index) {
|
|
|
|
#ifndef REAL_T_IS_DOUBLE
|
|
|
|
case sizeof(uint32_t):
|
|
|
|
return size_32_bits_real_float;
|
|
|
|
case sizeof(uint64_t):
|
|
|
|
return size_64_bits_real_float;
|
|
|
|
#else // REAL_T_IS_DOUBLE
|
|
|
|
case sizeof(uint32_t):
|
|
|
|
return size_32_bits_real_double;
|
|
|
|
case sizeof(uint64_t):
|
|
|
|
return size_64_bits_real_double;
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
return -1;
|
|
|
|
}
|
2021-06-19 17:58:49 +02:00
|
|
|
} type_size_array[Variant::VARIANT_MAX + 1] = {
|
|
|
|
{ Variant::NIL, 0, 0, 0, 0 },
|
2021-07-29 15:17:07 +02:00
|
|
|
{ Variant::BOOL, sizeof(uint8_t), sizeof(uint8_t), sizeof(uint8_t), sizeof(uint8_t) },
|
2021-06-19 17:58:49 +02:00
|
|
|
{ Variant::INT, sizeof(int64_t), sizeof(int64_t), sizeof(int64_t), sizeof(int64_t) },
|
|
|
|
{ Variant::FLOAT, sizeof(double), sizeof(double), sizeof(double), sizeof(double) },
|
|
|
|
{ Variant::STRING, ptrsize_32, ptrsize_64, ptrsize_32, ptrsize_64 },
|
|
|
|
{ Variant::VECTOR2, 2 * sizeof(float), 2 * sizeof(float), 2 * sizeof(double), 2 * sizeof(double) },
|
|
|
|
{ Variant::VECTOR2I, 2 * sizeof(int32_t), 2 * sizeof(int32_t), 2 * sizeof(int32_t), 2 * sizeof(int32_t) },
|
|
|
|
{ Variant::RECT2, 4 * sizeof(float), 4 * sizeof(float), 4 * sizeof(double), 4 * sizeof(double) },
|
|
|
|
{ Variant::RECT2I, 4 * sizeof(int32_t), 4 * sizeof(int32_t), 4 * sizeof(int32_t), 4 * sizeof(int32_t) },
|
|
|
|
{ Variant::VECTOR3, vec3_elems * sizeof(float), vec3_elems * sizeof(float), vec3_elems * sizeof(double), vec3_elems * sizeof(double) },
|
|
|
|
{ Variant::VECTOR3I, 3 * sizeof(int32_t), 3 * sizeof(int32_t), 3 * sizeof(int32_t), 3 * sizeof(int32_t) },
|
|
|
|
{ Variant::TRANSFORM2D, 6 * sizeof(float), 6 * sizeof(float), 6 * sizeof(double), 6 * sizeof(double) },
|
Implement Vector4, Vector4i, Projection
Implement built-in classes Vector4, Vector4i and Projection.
* Two versions of Vector4 (float and integer).
* A Projection class, which is a 4x4 matrix specialized in projection types.
These types have been requested for a long time, but given they were very corner case they were not added before.
Because in Godot 4, reimplementing parts of the rendering engine is now possible, access to these types (heavily used by the rendering code) becomes a necessity.
**Q**: Why Projection and not Matrix4?
**A**: Godot does not use Matrix2, Matrix3, Matrix4x3, etc. naming convention because, within the engine, these types always have a *purpose*. As such, Godot names them: Transform2D, Transform3D or Basis. In this case, this 4x4 matrix is _always_ used as a _Projection_, hence the naming.
2022-07-20 01:11:13 +02:00
|
|
|
{ Variant::VECTOR4, 4 * sizeof(float), 4 * sizeof(float), 4 * sizeof(double), 4 * sizeof(double) },
|
|
|
|
{ Variant::VECTOR4I, 4 * sizeof(int32_t), 4 * sizeof(int32_t), 4 * sizeof(int32_t), 4 * sizeof(int32_t) },
|
2021-06-19 17:58:49 +02:00
|
|
|
{ Variant::PLANE, (vec3_elems + 1) * sizeof(float), (vec3_elems + 1) * sizeof(float), (vec3_elems + 1) * sizeof(double), (vec3_elems + 1) * sizeof(double) },
|
|
|
|
{ Variant::QUATERNION, 4 * sizeof(float), 4 * sizeof(float), 4 * sizeof(double), 4 * sizeof(double) },
|
|
|
|
{ Variant::AABB, (vec3_elems * 2) * sizeof(float), (vec3_elems * 2) * sizeof(float), (vec3_elems * 2) * sizeof(double), (vec3_elems * 2) * sizeof(double) },
|
|
|
|
{ Variant::BASIS, (vec3_elems * 3) * sizeof(float), (vec3_elems * 3) * sizeof(float), (vec3_elems * 3) * sizeof(double), (vec3_elems * 3) * sizeof(double) },
|
|
|
|
{ Variant::TRANSFORM3D, (vec3_elems * 4) * sizeof(float), (vec3_elems * 4) * sizeof(float), (vec3_elems * 4) * sizeof(double), (vec3_elems * 4) * sizeof(double) },
|
2022-08-21 12:49:28 +02:00
|
|
|
{ Variant::PROJECTION, (vec4_elems * 4) * sizeof(float), (vec4_elems * 4) * sizeof(float), (vec4_elems * 4) * sizeof(double), (vec4_elems * 4) * sizeof(double) },
|
2021-06-19 17:58:49 +02:00
|
|
|
{ Variant::COLOR, 4 * sizeof(float), 4 * sizeof(float), 4 * sizeof(float), 4 * sizeof(float) },
|
|
|
|
{ Variant::STRING_NAME, ptrsize_32, ptrsize_64, ptrsize_32, ptrsize_64 },
|
|
|
|
{ Variant::NODE_PATH, ptrsize_32, ptrsize_64, ptrsize_32, ptrsize_64 },
|
|
|
|
{ Variant::RID, sizeof(uint64_t), sizeof(uint64_t), sizeof(uint64_t), sizeof(uint64_t) },
|
|
|
|
{ Variant::OBJECT, ptrsize_32, ptrsize_64, ptrsize_32, ptrsize_64 },
|
Fix various typos with codespell
Found via `codespell -q 3 -S ./thirdparty,*.po,./DONORS.md -L ackward,ang,ans,ba,beng,cas,childs,childrens,dof,doubleclick,fave,findn,hist,inout,leapyear,lod,nd,numer,ois,ony,paket,seeked,sinc,switchs,te,uint`
2021-07-07 17:17:32 +02:00
|
|
|
{ Variant::CALLABLE, sizeof(Callable), sizeof(Callable), sizeof(Callable), sizeof(Callable) }, // Hardcoded align.
|
|
|
|
{ Variant::SIGNAL, sizeof(Signal), sizeof(Signal), sizeof(Signal), sizeof(Signal) }, // Hardcoded align.
|
2021-06-19 17:58:49 +02:00
|
|
|
{ Variant::DICTIONARY, ptrsize_32, ptrsize_64, ptrsize_32, ptrsize_64 },
|
|
|
|
{ Variant::ARRAY, ptrsize_32, ptrsize_64, ptrsize_32, ptrsize_64 },
|
2021-07-29 15:17:07 +02:00
|
|
|
{ Variant::PACKED_BYTE_ARRAY, ptrsize_32 * 2, ptrsize_64 * 2, ptrsize_32 * 2, ptrsize_64 * 2 },
|
|
|
|
{ Variant::PACKED_INT32_ARRAY, ptrsize_32 * 2, ptrsize_64 * 2, ptrsize_32 * 2, ptrsize_64 * 2 },
|
|
|
|
{ Variant::PACKED_INT64_ARRAY, ptrsize_32 * 2, ptrsize_64 * 2, ptrsize_32 * 2, ptrsize_64 * 2 },
|
|
|
|
{ Variant::PACKED_FLOAT32_ARRAY, ptrsize_32 * 2, ptrsize_64 * 2, ptrsize_32 * 2, ptrsize_64 * 2 },
|
|
|
|
{ Variant::PACKED_FLOAT64_ARRAY, ptrsize_32 * 2, ptrsize_64 * 2, ptrsize_32 * 2, ptrsize_64 * 2 },
|
|
|
|
{ Variant::PACKED_STRING_ARRAY, ptrsize_32 * 2, ptrsize_64 * 2, ptrsize_32 * 2, ptrsize_64 * 2 },
|
|
|
|
{ Variant::PACKED_VECTOR2_ARRAY, ptrsize_32 * 2, ptrsize_64 * 2, ptrsize_32 * 2, ptrsize_64 * 2 },
|
|
|
|
{ Variant::PACKED_VECTOR3_ARRAY, ptrsize_32 * 2, ptrsize_64 * 2, ptrsize_32 * 2, ptrsize_64 * 2 },
|
|
|
|
{ Variant::PACKED_COLOR_ARRAY, ptrsize_32 * 2, ptrsize_64 * 2, ptrsize_32 * 2, ptrsize_64 * 2 },
|
2021-06-19 17:58:49 +02:00
|
|
|
{ Variant::VARIANT_MAX, sizeof(uint64_t) + sizeof(float) * 4, sizeof(uint64_t) + sizeof(float) * 4, sizeof(uint64_t) + sizeof(double) * 4, sizeof(uint64_t) + sizeof(double) * 4 },
|
|
|
|
};
|
|
|
|
|
2021-07-29 15:17:07 +02:00
|
|
|
// Validate sizes at compile time for the current build configuration.
|
2022-12-07 12:11:28 +01:00
|
|
|
static_assert(type_size_array[Variant::BOOL][sizeof(void *)] == sizeof(GDExtensionBool), "Size of bool mismatch");
|
|
|
|
static_assert(type_size_array[Variant::INT][sizeof(void *)] == sizeof(GDExtensionInt), "Size of int mismatch");
|
2021-07-29 15:17:07 +02:00
|
|
|
static_assert(type_size_array[Variant::FLOAT][sizeof(void *)] == sizeof(double), "Size of float mismatch");
|
|
|
|
static_assert(type_size_array[Variant::STRING][sizeof(void *)] == sizeof(String), "Size of String mismatch");
|
|
|
|
static_assert(type_size_array[Variant::VECTOR2][sizeof(void *)] == sizeof(Vector2), "Size of Vector2 mismatch");
|
|
|
|
static_assert(type_size_array[Variant::VECTOR2I][sizeof(void *)] == sizeof(Vector2i), "Size of Vector2i mismatch");
|
|
|
|
static_assert(type_size_array[Variant::RECT2][sizeof(void *)] == sizeof(Rect2), "Size of Rect2 mismatch");
|
|
|
|
static_assert(type_size_array[Variant::RECT2I][sizeof(void *)] == sizeof(Rect2i), "Size of Rect2i mismatch");
|
|
|
|
static_assert(type_size_array[Variant::VECTOR3][sizeof(void *)] == sizeof(Vector3), "Size of Vector3 mismatch");
|
|
|
|
static_assert(type_size_array[Variant::VECTOR3I][sizeof(void *)] == sizeof(Vector3i), "Size of Vector3i mismatch");
|
|
|
|
static_assert(type_size_array[Variant::TRANSFORM2D][sizeof(void *)] == sizeof(Transform2D), "Size of Transform2D mismatch");
|
Implement Vector4, Vector4i, Projection
Implement built-in classes Vector4, Vector4i and Projection.
* Two versions of Vector4 (float and integer).
* A Projection class, which is a 4x4 matrix specialized in projection types.
These types have been requested for a long time, but given they were very corner case they were not added before.
Because in Godot 4, reimplementing parts of the rendering engine is now possible, access to these types (heavily used by the rendering code) becomes a necessity.
**Q**: Why Projection and not Matrix4?
**A**: Godot does not use Matrix2, Matrix3, Matrix4x3, etc. naming convention because, within the engine, these types always have a *purpose*. As such, Godot names them: Transform2D, Transform3D or Basis. In this case, this 4x4 matrix is _always_ used as a _Projection_, hence the naming.
2022-07-20 01:11:13 +02:00
|
|
|
static_assert(type_size_array[Variant::VECTOR4][sizeof(void *)] == sizeof(Vector4), "Size of Vector4 mismatch");
|
|
|
|
static_assert(type_size_array[Variant::VECTOR4I][sizeof(void *)] == sizeof(Vector4i), "Size of Vector4i mismatch");
|
2021-07-29 15:17:07 +02:00
|
|
|
static_assert(type_size_array[Variant::PLANE][sizeof(void *)] == sizeof(Plane), "Size of Plane mismatch");
|
|
|
|
static_assert(type_size_array[Variant::QUATERNION][sizeof(void *)] == sizeof(Quaternion), "Size of Quaternion mismatch");
|
|
|
|
static_assert(type_size_array[Variant::AABB][sizeof(void *)] == sizeof(AABB), "Size of AABB mismatch");
|
|
|
|
static_assert(type_size_array[Variant::BASIS][sizeof(void *)] == sizeof(Basis), "Size of Basis mismatch");
|
|
|
|
static_assert(type_size_array[Variant::TRANSFORM3D][sizeof(void *)] == sizeof(Transform3D), "Size of Transform3D mismatch");
|
Implement Vector4, Vector4i, Projection
Implement built-in classes Vector4, Vector4i and Projection.
* Two versions of Vector4 (float and integer).
* A Projection class, which is a 4x4 matrix specialized in projection types.
These types have been requested for a long time, but given they were very corner case they were not added before.
Because in Godot 4, reimplementing parts of the rendering engine is now possible, access to these types (heavily used by the rendering code) becomes a necessity.
**Q**: Why Projection and not Matrix4?
**A**: Godot does not use Matrix2, Matrix3, Matrix4x3, etc. naming convention because, within the engine, these types always have a *purpose*. As such, Godot names them: Transform2D, Transform3D or Basis. In this case, this 4x4 matrix is _always_ used as a _Projection_, hence the naming.
2022-07-20 01:11:13 +02:00
|
|
|
static_assert(type_size_array[Variant::PROJECTION][sizeof(void *)] == sizeof(Projection), "Size of Projection mismatch");
|
2021-07-29 15:17:07 +02:00
|
|
|
static_assert(type_size_array[Variant::COLOR][sizeof(void *)] == sizeof(Color), "Size of Color mismatch");
|
|
|
|
static_assert(type_size_array[Variant::STRING_NAME][sizeof(void *)] == sizeof(StringName), "Size of StringName mismatch");
|
|
|
|
static_assert(type_size_array[Variant::NODE_PATH][sizeof(void *)] == sizeof(NodePath), "Size of NodePath mismatch");
|
|
|
|
static_assert(type_size_array[Variant::RID][sizeof(void *)] == sizeof(RID), "Size of RID mismatch");
|
|
|
|
static_assert(type_size_array[Variant::OBJECT][sizeof(void *)] == sizeof(Object *), "Size of Object mismatch");
|
|
|
|
static_assert(type_size_array[Variant::CALLABLE][sizeof(void *)] == sizeof(Callable), "Size of Callable mismatch");
|
|
|
|
static_assert(type_size_array[Variant::SIGNAL][sizeof(void *)] == sizeof(Signal), "Size of Signal mismatch");
|
|
|
|
static_assert(type_size_array[Variant::DICTIONARY][sizeof(void *)] == sizeof(Dictionary), "Size of Dictionary mismatch");
|
|
|
|
static_assert(type_size_array[Variant::ARRAY][sizeof(void *)] == sizeof(Array), "Size of Array mismatch");
|
|
|
|
static_assert(type_size_array[Variant::PACKED_BYTE_ARRAY][sizeof(void *)] == sizeof(PackedByteArray), "Size of PackedByteArray mismatch");
|
|
|
|
static_assert(type_size_array[Variant::PACKED_INT32_ARRAY][sizeof(void *)] == sizeof(PackedInt32Array), "Size of PackedInt32Array mismatch");
|
|
|
|
static_assert(type_size_array[Variant::PACKED_INT64_ARRAY][sizeof(void *)] == sizeof(PackedInt64Array), "Size of PackedInt64Array mismatch");
|
|
|
|
static_assert(type_size_array[Variant::PACKED_FLOAT32_ARRAY][sizeof(void *)] == sizeof(PackedFloat32Array), "Size of PackedFloat32Array mismatch");
|
|
|
|
static_assert(type_size_array[Variant::PACKED_FLOAT64_ARRAY][sizeof(void *)] == sizeof(PackedFloat64Array), "Size of PackedFloat64Array mismatch");
|
|
|
|
static_assert(type_size_array[Variant::PACKED_STRING_ARRAY][sizeof(void *)] == sizeof(PackedStringArray), "Size of PackedStringArray mismatch");
|
|
|
|
static_assert(type_size_array[Variant::PACKED_VECTOR2_ARRAY][sizeof(void *)] == sizeof(PackedVector2Array), "Size of PackedVector2Array mismatch");
|
|
|
|
static_assert(type_size_array[Variant::PACKED_VECTOR3_ARRAY][sizeof(void *)] == sizeof(PackedVector3Array), "Size of PackedVector3Array mismatch");
|
|
|
|
static_assert(type_size_array[Variant::PACKED_COLOR_ARRAY][sizeof(void *)] == sizeof(PackedColorArray), "Size of PackedColorArray mismatch");
|
|
|
|
static_assert(type_size_array[Variant::VARIANT_MAX][sizeof(void *)] == sizeof(Variant), "Size of Variant mismatch");
|
|
|
|
|
2021-06-19 17:58:49 +02:00
|
|
|
Array core_type_sizes;
|
|
|
|
|
|
|
|
for (int i = 0; i < 4; i++) {
|
|
|
|
Dictionary d;
|
|
|
|
d["build_configuration"] = build_config_name[i];
|
|
|
|
Array sizes;
|
2021-07-29 15:17:07 +02:00
|
|
|
for (int j = 0; j <= Variant::VARIANT_MAX; j++) {
|
2021-06-19 17:58:49 +02:00
|
|
|
Variant::Type t = type_size_array[j].type;
|
|
|
|
String name = t == Variant::VARIANT_MAX ? String("Variant") : Variant::get_type_name(t);
|
|
|
|
Dictionary d2;
|
|
|
|
d2["name"] = name;
|
2022-09-22 09:25:47 +02:00
|
|
|
uint32_t size = 0;
|
2021-06-19 17:58:49 +02:00
|
|
|
switch (i) {
|
|
|
|
case 0:
|
|
|
|
size = type_size_array[j].size_32_bits_real_float;
|
|
|
|
break;
|
|
|
|
case 1:
|
|
|
|
size = type_size_array[j].size_64_bits_real_float;
|
|
|
|
break;
|
|
|
|
case 2:
|
|
|
|
size = type_size_array[j].size_32_bits_real_double;
|
|
|
|
break;
|
|
|
|
case 3:
|
|
|
|
size = type_size_array[j].size_64_bits_real_double;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
d2["size"] = size;
|
|
|
|
sizes.push_back(d2);
|
|
|
|
}
|
|
|
|
d["sizes"] = sizes;
|
|
|
|
core_type_sizes.push_back(d);
|
|
|
|
}
|
|
|
|
api_dump["builtin_class_sizes"] = core_type_sizes;
|
|
|
|
}
|
|
|
|
|
|
|
|
{
|
2022-08-13 16:02:00 +02:00
|
|
|
// Member offsets, meta types and sizes.
|
|
|
|
|
|
|
|
#define REAL_MEMBER_OFFSET(type, member) \
|
|
|
|
{ \
|
|
|
|
type, \
|
|
|
|
member, \
|
|
|
|
"float", \
|
|
|
|
sizeof(float), \
|
|
|
|
"float", \
|
|
|
|
sizeof(float), \
|
|
|
|
"double", \
|
|
|
|
sizeof(double), \
|
|
|
|
"double", \
|
|
|
|
sizeof(double), \
|
|
|
|
}
|
|
|
|
|
|
|
|
#define INT32_MEMBER_OFFSET(type, member) \
|
|
|
|
{ \
|
|
|
|
type, \
|
|
|
|
member, \
|
|
|
|
"int32", \
|
|
|
|
sizeof(int32_t), \
|
|
|
|
"int32", \
|
|
|
|
sizeof(int32_t), \
|
|
|
|
"int32", \
|
|
|
|
sizeof(int32_t), \
|
|
|
|
"int32", \
|
|
|
|
sizeof(int32_t), \
|
|
|
|
}
|
|
|
|
|
|
|
|
#define INT32_BASED_BUILTIN_MEMBER_OFFSET(type, member, member_type, member_elems) \
|
|
|
|
{ \
|
|
|
|
type, \
|
|
|
|
member, \
|
|
|
|
member_type, \
|
|
|
|
sizeof(int32_t) * member_elems, \
|
|
|
|
member_type, \
|
|
|
|
sizeof(int32_t) * member_elems, \
|
|
|
|
member_type, \
|
|
|
|
sizeof(int32_t) * member_elems, \
|
|
|
|
member_type, \
|
|
|
|
sizeof(int32_t) * member_elems, \
|
|
|
|
}
|
|
|
|
|
|
|
|
#define REAL_BASED_BUILTIN_MEMBER_OFFSET(type, member, member_type, member_elems) \
|
|
|
|
{ \
|
|
|
|
type, \
|
|
|
|
member, \
|
|
|
|
member_type, \
|
|
|
|
sizeof(float) * member_elems, \
|
|
|
|
member_type, \
|
|
|
|
sizeof(float) * member_elems, \
|
|
|
|
member_type, \
|
|
|
|
sizeof(double) * member_elems, \
|
|
|
|
member_type, \
|
|
|
|
sizeof(double) * member_elems, \
|
|
|
|
}
|
|
|
|
|
2021-06-19 17:58:49 +02:00
|
|
|
struct {
|
|
|
|
Variant::Type type;
|
|
|
|
const char *member;
|
2022-08-13 16:02:00 +02:00
|
|
|
const char *member_meta_32_bits_real_float;
|
|
|
|
const uint32_t member_size_32_bits_real_float;
|
|
|
|
const char *member_meta_64_bits_real_float;
|
|
|
|
const uint32_t member_size_64_bits_real_float;
|
|
|
|
const char *member_meta_32_bits_real_double;
|
|
|
|
const uint32_t member_size_32_bits_real_double;
|
|
|
|
const char *member_meta_64_bits_real_double;
|
|
|
|
const uint32_t member_size_64_bits_real_double;
|
2021-06-19 17:58:49 +02:00
|
|
|
} member_offset_array[] = {
|
2022-08-13 16:02:00 +02:00
|
|
|
// Vector2
|
|
|
|
REAL_MEMBER_OFFSET(Variant::VECTOR2, "x"),
|
|
|
|
REAL_MEMBER_OFFSET(Variant::VECTOR2, "y"),
|
|
|
|
// Vector2i
|
|
|
|
INT32_MEMBER_OFFSET(Variant::VECTOR2I, "x"),
|
|
|
|
INT32_MEMBER_OFFSET(Variant::VECTOR2I, "y"),
|
|
|
|
// Rect2
|
|
|
|
REAL_BASED_BUILTIN_MEMBER_OFFSET(Variant::RECT2, "position", "Vector2", 2),
|
|
|
|
REAL_BASED_BUILTIN_MEMBER_OFFSET(Variant::RECT2, "size", "Vector2", 2),
|
|
|
|
// Rect2i
|
|
|
|
INT32_BASED_BUILTIN_MEMBER_OFFSET(Variant::RECT2I, "position", "Vector2i", 2),
|
|
|
|
INT32_BASED_BUILTIN_MEMBER_OFFSET(Variant::RECT2I, "size", "Vector2i", 2),
|
|
|
|
// Vector3
|
|
|
|
REAL_MEMBER_OFFSET(Variant::VECTOR3, "x"),
|
|
|
|
REAL_MEMBER_OFFSET(Variant::VECTOR3, "y"),
|
|
|
|
REAL_MEMBER_OFFSET(Variant::VECTOR3, "z"),
|
|
|
|
// Vector3i
|
|
|
|
INT32_MEMBER_OFFSET(Variant::VECTOR3I, "x"),
|
|
|
|
INT32_MEMBER_OFFSET(Variant::VECTOR3I, "y"),
|
|
|
|
INT32_MEMBER_OFFSET(Variant::VECTOR3I, "z"),
|
|
|
|
// Transform2D
|
|
|
|
REAL_BASED_BUILTIN_MEMBER_OFFSET(Variant::TRANSFORM2D, "x", "Vector2", 2),
|
|
|
|
REAL_BASED_BUILTIN_MEMBER_OFFSET(Variant::TRANSFORM2D, "y", "Vector2", 2),
|
|
|
|
REAL_BASED_BUILTIN_MEMBER_OFFSET(Variant::TRANSFORM2D, "origin", "Vector2", 2),
|
|
|
|
// Vector4
|
|
|
|
REAL_MEMBER_OFFSET(Variant::VECTOR4, "x"),
|
|
|
|
REAL_MEMBER_OFFSET(Variant::VECTOR4, "y"),
|
|
|
|
REAL_MEMBER_OFFSET(Variant::VECTOR4, "z"),
|
|
|
|
REAL_MEMBER_OFFSET(Variant::VECTOR4, "w"),
|
|
|
|
// Vector4i
|
|
|
|
INT32_MEMBER_OFFSET(Variant::VECTOR4I, "x"),
|
|
|
|
INT32_MEMBER_OFFSET(Variant::VECTOR4I, "y"),
|
|
|
|
INT32_MEMBER_OFFSET(Variant::VECTOR4I, "z"),
|
|
|
|
INT32_MEMBER_OFFSET(Variant::VECTOR4I, "w"),
|
|
|
|
// Plane
|
|
|
|
REAL_BASED_BUILTIN_MEMBER_OFFSET(Variant::PLANE, "normal", "Vector3", vec3_elems),
|
|
|
|
REAL_MEMBER_OFFSET(Variant::PLANE, "d"),
|
|
|
|
// Quaternion
|
|
|
|
REAL_MEMBER_OFFSET(Variant::QUATERNION, "x"),
|
|
|
|
REAL_MEMBER_OFFSET(Variant::QUATERNION, "y"),
|
|
|
|
REAL_MEMBER_OFFSET(Variant::QUATERNION, "z"),
|
|
|
|
REAL_MEMBER_OFFSET(Variant::QUATERNION, "w"),
|
|
|
|
// AABB
|
|
|
|
REAL_BASED_BUILTIN_MEMBER_OFFSET(Variant::AABB, "position", "Vector3", vec3_elems),
|
|
|
|
REAL_BASED_BUILTIN_MEMBER_OFFSET(Variant::AABB, "size", "Vector3", vec3_elems),
|
|
|
|
// Basis (remember that basis vectors are flipped!)
|
|
|
|
REAL_BASED_BUILTIN_MEMBER_OFFSET(Variant::BASIS, "x", "Vector3", vec3_elems),
|
|
|
|
REAL_BASED_BUILTIN_MEMBER_OFFSET(Variant::BASIS, "y", "Vector3", vec3_elems),
|
|
|
|
REAL_BASED_BUILTIN_MEMBER_OFFSET(Variant::BASIS, "z", "Vector3", vec3_elems),
|
|
|
|
// Transform3D
|
|
|
|
REAL_BASED_BUILTIN_MEMBER_OFFSET(Variant::TRANSFORM3D, "basis", "Basis", vec3_elems * 3),
|
|
|
|
REAL_BASED_BUILTIN_MEMBER_OFFSET(Variant::TRANSFORM3D, "origin", "Vector3", vec3_elems),
|
|
|
|
// Projection
|
|
|
|
REAL_BASED_BUILTIN_MEMBER_OFFSET(Variant::PROJECTION, "x", "Vector4", vec4_elems),
|
|
|
|
REAL_BASED_BUILTIN_MEMBER_OFFSET(Variant::PROJECTION, "y", "Vector4", vec4_elems),
|
|
|
|
REAL_BASED_BUILTIN_MEMBER_OFFSET(Variant::PROJECTION, "z", "Vector4", vec4_elems),
|
|
|
|
REAL_BASED_BUILTIN_MEMBER_OFFSET(Variant::PROJECTION, "w", "Vector4", vec4_elems),
|
|
|
|
// Color (always composed of 4bytes floats)
|
|
|
|
{ Variant::COLOR, "r", "float", sizeof(float), "float", sizeof(float), "float", sizeof(float), "float", sizeof(float) },
|
|
|
|
{ Variant::COLOR, "g", "float", sizeof(float), "float", sizeof(float), "float", sizeof(float), "float", sizeof(float) },
|
|
|
|
{ Variant::COLOR, "b", "float", sizeof(float), "float", sizeof(float), "float", sizeof(float), "float", sizeof(float) },
|
|
|
|
{ Variant::COLOR, "a", "float", sizeof(float), "float", sizeof(float), "float", sizeof(float), "float", sizeof(float) },
|
|
|
|
// End marker, must stay last
|
|
|
|
{ Variant::NIL, nullptr, nullptr, 0, nullptr, 0, nullptr, 0, nullptr, 0 },
|
2021-06-19 17:58:49 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
Array core_type_member_offsets;
|
|
|
|
|
|
|
|
for (int i = 0; i < 4; i++) {
|
|
|
|
Dictionary d;
|
|
|
|
d["build_configuration"] = build_config_name[i];
|
|
|
|
Array type_offsets;
|
|
|
|
uint32_t idx = 0;
|
|
|
|
|
2022-08-13 16:02:00 +02:00
|
|
|
Variant::Type previous_type = Variant::NIL;
|
2021-06-19 17:58:49 +02:00
|
|
|
|
|
|
|
Dictionary d2;
|
|
|
|
Array members;
|
2022-08-13 16:02:00 +02:00
|
|
|
uint32_t offset = 0;
|
2021-06-19 17:58:49 +02:00
|
|
|
|
|
|
|
while (true) {
|
|
|
|
Variant::Type t = member_offset_array[idx].type;
|
2022-08-13 16:02:00 +02:00
|
|
|
if (t != previous_type) {
|
|
|
|
if (previous_type != Variant::NIL) {
|
2021-06-19 17:58:49 +02:00
|
|
|
d2["members"] = members;
|
|
|
|
type_offsets.push_back(d2);
|
|
|
|
}
|
|
|
|
if (t == Variant::NIL) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
String name = t == Variant::VARIANT_MAX ? String("Variant") : Variant::get_type_name(t);
|
|
|
|
d2 = Dictionary();
|
|
|
|
members = Array();
|
2022-08-13 16:02:00 +02:00
|
|
|
offset = 0;
|
2021-06-19 17:58:49 +02:00
|
|
|
d2["name"] = name;
|
2022-08-13 16:02:00 +02:00
|
|
|
previous_type = t;
|
2021-06-19 17:58:49 +02:00
|
|
|
}
|
|
|
|
Dictionary d3;
|
2022-08-13 16:02:00 +02:00
|
|
|
const char *member_meta = nullptr;
|
|
|
|
uint32_t member_size = 0;
|
2021-06-19 17:58:49 +02:00
|
|
|
switch (i) {
|
|
|
|
case 0:
|
2022-08-13 16:02:00 +02:00
|
|
|
member_meta = member_offset_array[idx].member_meta_32_bits_real_float;
|
|
|
|
member_size = member_offset_array[idx].member_size_32_bits_real_float;
|
2021-06-19 17:58:49 +02:00
|
|
|
break;
|
|
|
|
case 1:
|
2022-08-13 16:02:00 +02:00
|
|
|
member_meta = member_offset_array[idx].member_meta_64_bits_real_float;
|
|
|
|
member_size = member_offset_array[idx].member_size_64_bits_real_float;
|
2021-06-19 17:58:49 +02:00
|
|
|
break;
|
|
|
|
case 2:
|
2022-08-13 16:02:00 +02:00
|
|
|
member_meta = member_offset_array[idx].member_meta_32_bits_real_double;
|
|
|
|
member_size = member_offset_array[idx].member_size_32_bits_real_double;
|
2021-06-19 17:58:49 +02:00
|
|
|
break;
|
|
|
|
case 3:
|
2022-08-13 16:02:00 +02:00
|
|
|
member_meta = member_offset_array[idx].member_meta_64_bits_real_double;
|
|
|
|
member_size = member_offset_array[idx].member_size_64_bits_real_double;
|
2021-06-19 17:58:49 +02:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
d3["member"] = member_offset_array[idx].member;
|
|
|
|
d3["offset"] = offset;
|
2022-08-13 16:02:00 +02:00
|
|
|
d3["meta"] = member_meta;
|
|
|
|
offset += member_size;
|
2021-06-19 17:58:49 +02:00
|
|
|
members.push_back(d3);
|
|
|
|
idx++;
|
|
|
|
}
|
|
|
|
d["classes"] = type_offsets;
|
|
|
|
core_type_member_offsets.push_back(d);
|
|
|
|
}
|
|
|
|
api_dump["builtin_class_member_offsets"] = core_type_member_offsets;
|
|
|
|
}
|
|
|
|
|
|
|
|
{
|
Fix various typos with codespell
Found via `codespell -q 3 -S ./thirdparty,*.po,./DONORS.md -L ackward,ang,ans,ba,beng,cas,childs,childrens,dof,doubleclick,fave,findn,hist,inout,leapyear,lod,nd,numer,ois,ony,paket,seeked,sinc,switchs,te,uint`
2021-07-07 17:17:32 +02:00
|
|
|
// Global enums and constants.
|
2021-06-19 17:58:49 +02:00
|
|
|
Array constants;
|
2022-05-09 11:47:10 +02:00
|
|
|
HashMap<String, List<Pair<String, int64_t>>> enum_list;
|
2021-06-19 17:58:49 +02:00
|
|
|
|
|
|
|
for (int i = 0; i < CoreConstants::get_global_constant_count(); i++) {
|
2022-05-09 11:47:10 +02:00
|
|
|
int64_t value = CoreConstants::get_global_constant_value(i);
|
2021-06-19 17:58:49 +02:00
|
|
|
String enum_name = CoreConstants::get_global_constant_enum(i);
|
|
|
|
String name = CoreConstants::get_global_constant_name(i);
|
2023-01-08 00:55:54 +01:00
|
|
|
bool bitfield = CoreConstants::is_global_constant_bitfield(i);
|
2021-12-09 10:42:46 +01:00
|
|
|
if (!enum_name.is_empty()) {
|
2022-05-09 11:47:10 +02:00
|
|
|
enum_list[enum_name].push_back(Pair<String, int64_t>(name, value));
|
2021-06-19 17:58:49 +02:00
|
|
|
} else {
|
|
|
|
Dictionary d;
|
|
|
|
d["name"] = name;
|
|
|
|
d["value"] = value;
|
2023-01-08 00:55:54 +01:00
|
|
|
d["is_bitfield"] = bitfield;
|
2021-06-19 17:58:49 +02:00
|
|
|
constants.push_back(d);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
api_dump["global_constants"] = constants;
|
|
|
|
|
|
|
|
Array enums;
|
2022-05-09 11:47:10 +02:00
|
|
|
for (const KeyValue<String, List<Pair<String, int64_t>>> &E : enum_list) {
|
2021-06-19 17:58:49 +02:00
|
|
|
Dictionary d1;
|
2021-08-09 22:13:42 +02:00
|
|
|
d1["name"] = E.key;
|
2021-06-19 17:58:49 +02:00
|
|
|
Array values;
|
2022-05-09 11:47:10 +02:00
|
|
|
for (const Pair<String, int64_t> &F : E.value) {
|
2021-06-19 17:58:49 +02:00
|
|
|
Dictionary d2;
|
2021-07-16 05:45:57 +02:00
|
|
|
d2["name"] = F.first;
|
|
|
|
d2["value"] = F.second;
|
2021-06-19 17:58:49 +02:00
|
|
|
values.push_back(d2);
|
|
|
|
}
|
|
|
|
d1["values"] = values;
|
|
|
|
enums.push_back(d1);
|
|
|
|
}
|
|
|
|
|
|
|
|
api_dump["global_enums"] = enums;
|
|
|
|
}
|
|
|
|
{
|
|
|
|
Array utility_funcs;
|
|
|
|
|
|
|
|
List<StringName> utility_func_names;
|
|
|
|
Variant::get_utility_function_list(&utility_func_names);
|
|
|
|
|
2021-07-24 15:46:25 +02:00
|
|
|
for (const StringName &name : utility_func_names) {
|
2021-06-19 17:58:49 +02:00
|
|
|
Dictionary func;
|
|
|
|
func["name"] = String(name);
|
|
|
|
if (Variant::has_utility_function_return_value(name)) {
|
|
|
|
Variant::Type rt = Variant::get_utility_function_return_type(name);
|
|
|
|
func["return_type"] = rt == Variant::NIL ? String("Variant") : Variant::get_type_name(rt);
|
|
|
|
}
|
|
|
|
switch (Variant::get_utility_function_type(name)) {
|
|
|
|
case Variant::UTILITY_FUNC_TYPE_MATH:
|
|
|
|
func["category"] = "math";
|
|
|
|
break;
|
|
|
|
case Variant::UTILITY_FUNC_TYPE_RANDOM:
|
|
|
|
func["category"] = "random";
|
|
|
|
break;
|
|
|
|
case Variant::UTILITY_FUNC_TYPE_GENERAL:
|
|
|
|
func["category"] = "general";
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
bool vararg = Variant::is_utility_function_vararg(name);
|
|
|
|
func["is_vararg"] = Variant::is_utility_function_vararg(name);
|
|
|
|
func["hash"] = Variant::get_utility_function_hash(name);
|
|
|
|
Array arguments;
|
|
|
|
int argcount = Variant::get_utility_function_argument_count(name);
|
|
|
|
for (int i = 0; i < argcount; i++) {
|
|
|
|
Dictionary arg;
|
|
|
|
String argname = vararg ? "arg" + itos(i + 1) : Variant::get_utility_function_argument_name(name, i);
|
|
|
|
arg["name"] = argname;
|
2022-10-02 18:57:48 +02:00
|
|
|
arg["type"] = get_builtin_or_variant_type_name(Variant::get_utility_function_argument_type(name, i));
|
2021-06-19 17:58:49 +02:00
|
|
|
//no default value support in utility functions
|
|
|
|
arguments.push_back(arg);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (arguments.size()) {
|
|
|
|
func["arguments"] = arguments;
|
|
|
|
}
|
|
|
|
|
|
|
|
utility_funcs.push_back(func);
|
|
|
|
}
|
|
|
|
|
|
|
|
api_dump["utility_functions"] = utility_funcs;
|
|
|
|
}
|
|
|
|
|
|
|
|
{
|
|
|
|
// builtin types
|
|
|
|
|
|
|
|
Array builtins;
|
|
|
|
|
|
|
|
for (int i = 0; i < Variant::VARIANT_MAX; i++) {
|
|
|
|
if (i == Variant::OBJECT) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
Variant::Type type = Variant::Type(i);
|
|
|
|
|
|
|
|
Dictionary d;
|
|
|
|
d["name"] = Variant::get_type_name(type);
|
|
|
|
if (Variant::has_indexing(type)) {
|
2022-10-02 18:57:48 +02:00
|
|
|
d["indexing_return_type"] = get_builtin_or_variant_type_name(Variant::get_indexed_element_type(type));
|
2021-06-19 17:58:49 +02:00
|
|
|
}
|
|
|
|
|
2022-09-21 00:14:53 +02:00
|
|
|
d["is_keyed"] = Variant::is_keyed(type);
|
2021-07-29 15:17:07 +02:00
|
|
|
|
2021-06-19 17:58:49 +02:00
|
|
|
{
|
|
|
|
//members
|
|
|
|
Array members;
|
|
|
|
|
|
|
|
List<StringName> member_names;
|
|
|
|
Variant::get_member_list(type, &member_names);
|
2021-07-24 15:46:25 +02:00
|
|
|
for (const StringName &member_name : member_names) {
|
2021-06-19 17:58:49 +02:00
|
|
|
Dictionary d2;
|
|
|
|
d2["name"] = String(member_name);
|
2022-10-02 18:57:48 +02:00
|
|
|
d2["type"] = get_builtin_or_variant_type_name(Variant::get_member_type(type, member_name));
|
2021-06-19 17:58:49 +02:00
|
|
|
members.push_back(d2);
|
|
|
|
}
|
|
|
|
if (members.size()) {
|
|
|
|
d["members"] = members;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
{
|
|
|
|
//constants
|
|
|
|
Array constants;
|
|
|
|
|
|
|
|
List<StringName> constant_names;
|
|
|
|
Variant::get_constants_for_type(type, &constant_names);
|
2021-07-24 15:46:25 +02:00
|
|
|
for (const StringName &constant_name : constant_names) {
|
2021-06-19 17:58:49 +02:00
|
|
|
Dictionary d2;
|
|
|
|
d2["name"] = String(constant_name);
|
|
|
|
Variant constant = Variant::get_constant_value(type, constant_name);
|
2022-10-02 18:57:48 +02:00
|
|
|
d2["type"] = get_builtin_or_variant_type_name(constant.get_type());
|
2021-06-19 17:58:49 +02:00
|
|
|
d2["value"] = constant.get_construct_string();
|
|
|
|
constants.push_back(d2);
|
|
|
|
}
|
|
|
|
if (constants.size()) {
|
|
|
|
d["constants"] = constants;
|
|
|
|
}
|
|
|
|
}
|
2022-04-18 20:17:03 +02:00
|
|
|
{
|
|
|
|
//enums
|
|
|
|
Array enums;
|
|
|
|
|
|
|
|
List<StringName> enum_names;
|
|
|
|
Variant::get_enums_for_type(type, &enum_names);
|
|
|
|
for (const StringName &enum_name : enum_names) {
|
|
|
|
Dictionary enum_dict;
|
|
|
|
enum_dict["name"] = String(enum_name);
|
|
|
|
|
|
|
|
List<StringName> enumeration_names;
|
|
|
|
Variant::get_enumerations_for_enum(type, enum_name, &enumeration_names);
|
|
|
|
|
|
|
|
Array values;
|
|
|
|
|
|
|
|
for (const StringName &enumeration : enumeration_names) {
|
|
|
|
Dictionary values_dict;
|
|
|
|
values_dict["name"] = String(enumeration);
|
|
|
|
values_dict["value"] = Variant::get_enum_value(type, enum_name, enumeration);
|
|
|
|
values.push_back(values_dict);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (values.size()) {
|
|
|
|
enum_dict["values"] = values;
|
|
|
|
}
|
|
|
|
enums.push_back(enum_dict);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (enums.size()) {
|
|
|
|
d["enums"] = enums;
|
|
|
|
}
|
|
|
|
}
|
2021-06-19 17:58:49 +02:00
|
|
|
{
|
|
|
|
//operators
|
|
|
|
Array operators;
|
|
|
|
|
|
|
|
for (int j = 0; j < Variant::VARIANT_MAX; j++) {
|
|
|
|
for (int k = 0; k < Variant::OP_MAX; k++) {
|
|
|
|
Variant::Type rt = Variant::get_operator_return_type(Variant::Operator(k), type, Variant::Type(j));
|
|
|
|
if (rt != Variant::NIL) {
|
|
|
|
Dictionary d2;
|
|
|
|
d2["name"] = Variant::get_operator_name(Variant::Operator(k));
|
|
|
|
if (k != Variant::OP_NEGATE && k != Variant::OP_POSITIVE && k != Variant::OP_NOT && k != Variant::OP_BIT_NEGATE) {
|
2022-10-02 18:57:48 +02:00
|
|
|
d2["right_type"] = get_builtin_or_variant_type_name(Variant::Type(j));
|
2021-06-19 17:58:49 +02:00
|
|
|
}
|
2022-10-02 18:57:48 +02:00
|
|
|
d2["return_type"] = get_builtin_or_variant_type_name(Variant::get_operator_return_type(Variant::Operator(k), type, Variant::Type(j)));
|
2021-06-19 17:58:49 +02:00
|
|
|
operators.push_back(d2);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (operators.size()) {
|
|
|
|
d["operators"] = operators;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
{
|
|
|
|
//methods
|
|
|
|
Array methods;
|
|
|
|
|
|
|
|
List<StringName> method_names;
|
|
|
|
Variant::get_builtin_method_list(type, &method_names);
|
2021-07-24 15:46:25 +02:00
|
|
|
for (const StringName &method_name : method_names) {
|
2021-06-19 17:58:49 +02:00
|
|
|
Dictionary d2;
|
|
|
|
d2["name"] = String(method_name);
|
|
|
|
if (Variant::has_builtin_method_return_value(type, method_name)) {
|
|
|
|
Variant::Type ret_type = Variant::get_builtin_method_return_type(type, method_name);
|
|
|
|
d2["return_type"] = ret_type == Variant::NIL ? String("Variant") : Variant::get_type_name(ret_type);
|
|
|
|
}
|
|
|
|
d2["is_vararg"] = Variant::is_builtin_method_vararg(type, method_name);
|
|
|
|
d2["is_const"] = Variant::is_builtin_method_const(type, method_name);
|
|
|
|
d2["is_static"] = Variant::is_builtin_method_static(type, method_name);
|
|
|
|
d2["hash"] = Variant::get_builtin_method_hash(type, method_name);
|
|
|
|
|
|
|
|
Vector<Variant> default_args = Variant::get_builtin_method_default_arguments(type, method_name);
|
|
|
|
|
|
|
|
Array arguments;
|
|
|
|
int argcount = Variant::get_builtin_method_argument_count(type, method_name);
|
|
|
|
for (int j = 0; j < argcount; j++) {
|
|
|
|
Dictionary d3;
|
|
|
|
d3["name"] = Variant::get_builtin_method_argument_name(type, method_name, j);
|
2022-10-02 18:57:48 +02:00
|
|
|
d3["type"] = get_builtin_or_variant_type_name(Variant::get_builtin_method_argument_type(type, method_name, j));
|
2021-06-19 17:58:49 +02:00
|
|
|
|
|
|
|
if (j >= (argcount - default_args.size())) {
|
|
|
|
int dargidx = j - (argcount - default_args.size());
|
|
|
|
d3["default_value"] = default_args[dargidx].get_construct_string();
|
|
|
|
}
|
|
|
|
arguments.push_back(d3);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (arguments.size()) {
|
|
|
|
d2["arguments"] = arguments;
|
|
|
|
}
|
|
|
|
|
|
|
|
methods.push_back(d2);
|
|
|
|
}
|
|
|
|
if (methods.size()) {
|
|
|
|
d["methods"] = methods;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
{
|
|
|
|
//constructors
|
|
|
|
Array constructors;
|
|
|
|
|
|
|
|
for (int j = 0; j < Variant::get_constructor_count(type); j++) {
|
|
|
|
Dictionary d2;
|
|
|
|
d2["index"] = j;
|
|
|
|
|
|
|
|
Array arguments;
|
|
|
|
int argcount = Variant::get_constructor_argument_count(type, j);
|
|
|
|
for (int k = 0; k < argcount; k++) {
|
|
|
|
Dictionary d3;
|
|
|
|
d3["name"] = Variant::get_constructor_argument_name(type, j, k);
|
2022-10-02 18:57:48 +02:00
|
|
|
d3["type"] = get_builtin_or_variant_type_name(Variant::get_constructor_argument_type(type, j, k));
|
2021-06-19 17:58:49 +02:00
|
|
|
arguments.push_back(d3);
|
|
|
|
}
|
|
|
|
if (arguments.size()) {
|
|
|
|
d2["arguments"] = arguments;
|
|
|
|
}
|
|
|
|
constructors.push_back(d2);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (constructors.size()) {
|
|
|
|
d["constructors"] = constructors;
|
|
|
|
}
|
|
|
|
}
|
2021-07-29 15:53:05 +02:00
|
|
|
{
|
|
|
|
//destructor
|
|
|
|
d["has_destructor"] = Variant::has_destructor(type);
|
|
|
|
}
|
2021-06-19 17:58:49 +02:00
|
|
|
|
|
|
|
builtins.push_back(d);
|
|
|
|
}
|
|
|
|
|
|
|
|
api_dump["builtin_classes"] = builtins;
|
|
|
|
}
|
|
|
|
|
|
|
|
{
|
|
|
|
// classes
|
|
|
|
Array classes;
|
|
|
|
|
|
|
|
List<StringName> class_list;
|
|
|
|
|
|
|
|
ClassDB::get_class_list(&class_list);
|
|
|
|
|
|
|
|
class_list.sort_custom<StringName::AlphCompare>();
|
|
|
|
|
2021-07-24 15:46:25 +02:00
|
|
|
for (const StringName &class_name : class_list) {
|
2021-06-19 17:58:49 +02:00
|
|
|
Dictionary d;
|
|
|
|
d["name"] = String(class_name);
|
|
|
|
d["is_refcounted"] = ClassDB::is_parent_class(class_name, "RefCounted");
|
|
|
|
d["is_instantiable"] = ClassDB::can_instantiate(class_name);
|
|
|
|
StringName parent_class = ClassDB::get_parent_class(class_name);
|
|
|
|
if (parent_class != StringName()) {
|
|
|
|
d["inherits"] = String(parent_class);
|
|
|
|
}
|
|
|
|
|
|
|
|
{
|
|
|
|
ClassDB::APIType api = ClassDB::get_api_type(class_name);
|
|
|
|
static const char *api_type[5] = { "core", "editor", "extension", "editor_extension" };
|
|
|
|
d["api_type"] = api_type[api];
|
|
|
|
}
|
|
|
|
|
|
|
|
{
|
|
|
|
//constants
|
|
|
|
Array constants;
|
|
|
|
List<String> constant_list;
|
|
|
|
ClassDB::get_integer_constant_list(class_name, &constant_list, true);
|
2021-07-24 15:46:25 +02:00
|
|
|
for (const String &F : constant_list) {
|
2021-07-16 05:45:57 +02:00
|
|
|
StringName enum_name = ClassDB::get_integer_constant_enum(class_name, F);
|
2021-06-19 17:58:49 +02:00
|
|
|
if (enum_name != StringName()) {
|
|
|
|
continue; //enums will be handled on their own
|
|
|
|
}
|
|
|
|
|
|
|
|
Dictionary d2;
|
2021-07-16 05:45:57 +02:00
|
|
|
d2["name"] = String(F);
|
|
|
|
d2["value"] = ClassDB::get_integer_constant(class_name, F);
|
2021-06-19 17:58:49 +02:00
|
|
|
|
|
|
|
constants.push_back(d2);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (constants.size()) {
|
|
|
|
d["constants"] = constants;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
{
|
|
|
|
//enum
|
|
|
|
Array enums;
|
|
|
|
List<StringName> enum_list;
|
|
|
|
ClassDB::get_enum_list(class_name, &enum_list, true);
|
2021-07-24 15:46:25 +02:00
|
|
|
for (const StringName &F : enum_list) {
|
2021-06-19 17:58:49 +02:00
|
|
|
Dictionary d2;
|
2021-07-16 05:45:57 +02:00
|
|
|
d2["name"] = String(F);
|
2022-06-24 11:16:37 +02:00
|
|
|
d2["is_bitfield"] = ClassDB::is_enum_bitfield(class_name, F);
|
2021-06-19 17:58:49 +02:00
|
|
|
|
|
|
|
Array values;
|
|
|
|
List<StringName> enum_constant_list;
|
2021-07-16 05:45:57 +02:00
|
|
|
ClassDB::get_enum_constants(class_name, F, &enum_constant_list, true);
|
2021-06-19 17:58:49 +02:00
|
|
|
for (List<StringName>::Element *G = enum_constant_list.front(); G; G = G->next()) {
|
|
|
|
Dictionary d3;
|
|
|
|
d3["name"] = String(G->get());
|
|
|
|
d3["value"] = ClassDB::get_integer_constant(class_name, G->get());
|
|
|
|
values.push_back(d3);
|
|
|
|
}
|
|
|
|
|
|
|
|
d2["values"] = values;
|
|
|
|
|
|
|
|
enums.push_back(d2);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (enums.size()) {
|
|
|
|
d["enums"] = enums;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
{
|
|
|
|
//methods
|
|
|
|
Array methods;
|
|
|
|
List<MethodInfo> method_list;
|
|
|
|
ClassDB::get_method_list(class_name, &method_list, true);
|
2021-07-24 15:46:25 +02:00
|
|
|
for (const MethodInfo &F : method_list) {
|
2021-07-16 05:45:57 +02:00
|
|
|
StringName method_name = F.name;
|
2021-08-22 03:52:44 +02:00
|
|
|
if ((F.flags & METHOD_FLAG_VIRTUAL) && !(F.flags & METHOD_FLAG_OBJECT_CORE)) {
|
2021-06-19 17:58:49 +02:00
|
|
|
//virtual method
|
2021-07-16 05:45:57 +02:00
|
|
|
const MethodInfo &mi = F;
|
2021-06-19 17:58:49 +02:00
|
|
|
Dictionary d2;
|
|
|
|
d2["name"] = String(method_name);
|
2021-07-16 05:45:57 +02:00
|
|
|
d2["is_const"] = (F.flags & METHOD_FLAG_CONST) ? true : false;
|
2022-03-19 13:18:52 +01:00
|
|
|
d2["is_static"] = (F.flags & METHOD_FLAG_STATIC) ? true : false;
|
2021-06-19 17:58:49 +02:00
|
|
|
d2["is_vararg"] = false;
|
|
|
|
d2["is_virtual"] = true;
|
|
|
|
// virtual functions have no hash since no MethodBind is involved
|
|
|
|
bool has_return = mi.return_val.type != Variant::NIL || (mi.return_val.usage & PROPERTY_USAGE_NIL_IS_VARIANT);
|
|
|
|
Array arguments;
|
|
|
|
for (int i = (has_return ? -1 : 0); i < mi.arguments.size(); i++) {
|
|
|
|
PropertyInfo pinfo = i == -1 ? mi.return_val : mi.arguments[i];
|
|
|
|
Dictionary d3;
|
|
|
|
|
|
|
|
if (i >= 0) {
|
|
|
|
d3["name"] = pinfo.name;
|
|
|
|
}
|
2021-07-29 15:17:07 +02:00
|
|
|
|
2022-10-02 18:57:48 +02:00
|
|
|
d3["type"] = get_property_info_type_name(pinfo);
|
2021-06-19 17:58:49 +02:00
|
|
|
|
|
|
|
if (i == -1) {
|
|
|
|
d2["return_value"] = d3;
|
|
|
|
} else {
|
|
|
|
arguments.push_back(d3);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (arguments.size()) {
|
|
|
|
d2["arguments"] = arguments;
|
|
|
|
}
|
|
|
|
|
|
|
|
methods.push_back(d2);
|
|
|
|
|
2021-07-16 05:45:57 +02:00
|
|
|
} else if (F.name.begins_with("_")) {
|
2021-06-19 17:58:49 +02:00
|
|
|
//hidden method, ignore
|
|
|
|
|
|
|
|
} else {
|
|
|
|
Dictionary d2;
|
|
|
|
d2["name"] = String(method_name);
|
|
|
|
|
|
|
|
MethodBind *method = ClassDB::get_method(class_name, method_name);
|
|
|
|
if (!method) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
d2["is_const"] = method->is_const();
|
|
|
|
d2["is_vararg"] = method->is_vararg();
|
2022-03-19 13:18:52 +01:00
|
|
|
d2["is_static"] = method->is_static();
|
2021-06-19 17:58:49 +02:00
|
|
|
d2["is_virtual"] = false;
|
|
|
|
d2["hash"] = method->get_hash();
|
|
|
|
|
|
|
|
Vector<Variant> default_args = method->get_default_arguments();
|
|
|
|
|
|
|
|
Array arguments;
|
|
|
|
for (int i = (method->has_return() ? -1 : 0); i < method->get_argument_count(); i++) {
|
|
|
|
PropertyInfo pinfo = i == -1 ? method->get_return_info() : method->get_argument_info(i);
|
|
|
|
Dictionary d3;
|
|
|
|
|
|
|
|
if (i >= 0) {
|
|
|
|
d3["name"] = pinfo.name;
|
|
|
|
}
|
2022-10-02 18:57:48 +02:00
|
|
|
d3["type"] = get_property_info_type_name(pinfo);
|
2021-06-19 17:58:49 +02:00
|
|
|
|
|
|
|
if (method->get_argument_meta(i) > 0) {
|
|
|
|
static const char *argmeta[11] = { "none", "int8", "int16", "int32", "int64", "uint8", "uint16", "uint32", "uint64", "float", "double" };
|
|
|
|
d3["meta"] = argmeta[method->get_argument_meta(i)];
|
|
|
|
}
|
|
|
|
|
|
|
|
if (i >= 0 && i >= (method->get_argument_count() - default_args.size())) {
|
|
|
|
int dargidx = i - (method->get_argument_count() - default_args.size());
|
|
|
|
d3["default_value"] = default_args[dargidx].get_construct_string();
|
|
|
|
}
|
|
|
|
|
|
|
|
if (i == -1) {
|
|
|
|
d2["return_value"] = d3;
|
|
|
|
} else {
|
|
|
|
arguments.push_back(d3);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (arguments.size()) {
|
|
|
|
d2["arguments"] = arguments;
|
|
|
|
}
|
|
|
|
|
|
|
|
methods.push_back(d2);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (methods.size()) {
|
|
|
|
d["methods"] = methods;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
{
|
|
|
|
//signals
|
|
|
|
Array signals;
|
|
|
|
List<MethodInfo> signal_list;
|
|
|
|
ClassDB::get_signal_list(class_name, &signal_list, true);
|
2021-07-24 15:46:25 +02:00
|
|
|
for (const MethodInfo &F : signal_list) {
|
2021-07-16 05:45:57 +02:00
|
|
|
StringName signal_name = F.name;
|
2021-06-19 17:58:49 +02:00
|
|
|
Dictionary d2;
|
|
|
|
d2["name"] = String(signal_name);
|
|
|
|
|
|
|
|
Array arguments;
|
|
|
|
|
2021-07-16 05:45:57 +02:00
|
|
|
for (int i = 0; i < F.arguments.size(); i++) {
|
2021-06-19 17:58:49 +02:00
|
|
|
Dictionary d3;
|
2021-07-16 05:45:57 +02:00
|
|
|
d3["name"] = F.arguments[i].name;
|
2022-10-02 18:57:48 +02:00
|
|
|
d3["type"] = get_property_info_type_name(F.arguments[i]);
|
2021-06-19 17:58:49 +02:00
|
|
|
arguments.push_back(d3);
|
|
|
|
}
|
|
|
|
if (arguments.size()) {
|
|
|
|
d2["arguments"] = arguments;
|
|
|
|
}
|
|
|
|
|
|
|
|
signals.push_back(d2);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (signals.size()) {
|
|
|
|
d["signals"] = signals;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
{
|
|
|
|
//properties
|
|
|
|
Array properties;
|
|
|
|
List<PropertyInfo> property_list;
|
|
|
|
ClassDB::get_property_list(class_name, &property_list, true);
|
2021-07-24 15:46:25 +02:00
|
|
|
for (const PropertyInfo &F : property_list) {
|
2022-08-15 11:37:58 +02:00
|
|
|
if (F.usage & PROPERTY_USAGE_CATEGORY || F.usage & PROPERTY_USAGE_GROUP || F.usage & PROPERTY_USAGE_SUBGROUP || (F.type == Variant::NIL && F.usage & PROPERTY_USAGE_ARRAY)) {
|
2021-06-19 17:58:49 +02:00
|
|
|
continue; //not real properties
|
|
|
|
}
|
2021-07-16 05:45:57 +02:00
|
|
|
if (F.name.begins_with("_")) {
|
2021-06-19 17:58:49 +02:00
|
|
|
continue; //hidden property
|
|
|
|
}
|
2022-08-15 11:37:58 +02:00
|
|
|
if (F.name.find("/") >= 0) {
|
|
|
|
// Ignore properties with '/' (slash) in the name. These are only meant for use in the inspector.
|
|
|
|
continue;
|
|
|
|
}
|
2021-07-16 05:45:57 +02:00
|
|
|
StringName property_name = F.name;
|
2021-06-19 17:58:49 +02:00
|
|
|
Dictionary d2;
|
2022-10-02 18:57:48 +02:00
|
|
|
d2["type"] = get_property_info_type_name(F);
|
2021-06-19 17:58:49 +02:00
|
|
|
d2["name"] = String(property_name);
|
2022-08-15 11:09:14 +02:00
|
|
|
StringName setter = ClassDB::get_property_setter(class_name, F.name);
|
|
|
|
if (!(setter == "")) {
|
|
|
|
d2["setter"] = setter;
|
|
|
|
}
|
|
|
|
StringName getter = ClassDB::get_property_getter(class_name, F.name);
|
|
|
|
if (!(getter == "")) {
|
|
|
|
d2["getter"] = getter;
|
|
|
|
}
|
|
|
|
int index = ClassDB::get_property_index(class_name, F.name);
|
|
|
|
if (index != -1) {
|
|
|
|
d2["index"] = index;
|
|
|
|
}
|
2021-06-19 17:58:49 +02:00
|
|
|
properties.push_back(d2);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (properties.size()) {
|
|
|
|
d["properties"] = properties;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
classes.push_back(d);
|
|
|
|
}
|
|
|
|
|
|
|
|
api_dump["classes"] = classes;
|
|
|
|
}
|
|
|
|
|
|
|
|
{
|
|
|
|
// singletons
|
|
|
|
|
|
|
|
Array singletons;
|
|
|
|
List<Engine::Singleton> singleton_list;
|
|
|
|
Engine::get_singleton()->get_singletons(&singleton_list);
|
|
|
|
|
2021-07-24 15:46:25 +02:00
|
|
|
for (const Engine::Singleton &s : singleton_list) {
|
2021-06-19 17:58:49 +02:00
|
|
|
Dictionary d;
|
|
|
|
d["name"] = s.name;
|
|
|
|
if (s.class_name != StringName()) {
|
|
|
|
d["type"] = String(s.class_name);
|
|
|
|
} else {
|
|
|
|
d["type"] = String(s.ptr->get_class());
|
|
|
|
}
|
|
|
|
singletons.push_back(d);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (singletons.size()) {
|
|
|
|
api_dump["singletons"] = singletons;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-08-23 19:53:27 +02:00
|
|
|
{
|
|
|
|
Array native_structures;
|
|
|
|
|
2022-03-14 15:52:03 +01:00
|
|
|
List<StringName> native_structs;
|
|
|
|
ClassDB::get_native_struct_list(&native_structs);
|
|
|
|
native_structs.sort_custom<StringName::AlphCompare>();
|
2021-08-23 19:53:27 +02:00
|
|
|
|
2022-03-14 15:52:03 +01:00
|
|
|
for (const StringName &E : native_structs) {
|
|
|
|
String code = ClassDB::get_native_struct_code(E);
|
2021-08-23 19:53:27 +02:00
|
|
|
|
2021-08-27 23:19:51 +02:00
|
|
|
Dictionary d;
|
2022-03-14 15:52:03 +01:00
|
|
|
d["name"] = String(E);
|
|
|
|
d["format"] = code;
|
2021-08-27 23:19:51 +02:00
|
|
|
|
|
|
|
native_structures.push_back(d);
|
|
|
|
}
|
|
|
|
|
2021-08-23 19:53:27 +02:00
|
|
|
api_dump["native_structures"] = native_structures;
|
|
|
|
}
|
|
|
|
|
2021-06-19 17:58:49 +02:00
|
|
|
return api_dump;
|
|
|
|
}
|
|
|
|
|
2022-12-07 12:11:28 +01:00
|
|
|
void GDExtensionAPIDump::generate_extension_json_file(const String &p_path) {
|
2021-06-19 17:58:49 +02:00
|
|
|
Dictionary api = generate_extension_api();
|
|
|
|
Ref<JSON> json;
|
|
|
|
json.instantiate();
|
|
|
|
|
2022-11-07 14:31:10 +01:00
|
|
|
String text = json->stringify(api, "\t", false) + "\n";
|
2022-03-23 10:08:58 +01:00
|
|
|
Ref<FileAccess> fa = FileAccess::open(p_path, FileAccess::WRITE);
|
2022-11-07 14:31:10 +01:00
|
|
|
fa->store_string(text);
|
2021-06-19 17:58:49 +02:00
|
|
|
}
|
2022-11-07 14:31:10 +01:00
|
|
|
|
|
|
|
#endif // TOOLS_ENABLED
|