#pragma once #include #include #include namespace extra2d { namespace sq { // ============================================================================ // Type tag helpers — unique address per type // ============================================================================ template inline SQUserPointer typeTag() { static int tag; return &tag; } // ============================================================================ // SqClassName trait — maps C++ types to Squirrel class names // ============================================================================ template struct SqClassName { static const char *name(); }; // ============================================================================ // push / get primitives // ============================================================================ inline void push(HSQUIRRELVM vm, int v) { sq_pushinteger(vm, static_cast(v)); } inline void push(HSQUIRRELVM vm, float v) { sq_pushfloat(vm, static_cast(v)); } inline void push(HSQUIRRELVM vm, double v) { sq_pushfloat(vm, static_cast(v)); } inline void push(HSQUIRRELVM vm, bool v) { sq_pushbool(vm, v ? SQTrue : SQFalse); } inline void push(HSQUIRRELVM vm, const char *v) { sq_pushstring(vm, v, -1); } inline void push(HSQUIRRELVM vm, const std::string &v) { sq_pushstring(vm, v.c_str(), static_cast(v.size())); } inline void pushNull(HSQUIRRELVM vm) { sq_pushnull(vm); } inline SQInteger getInt(HSQUIRRELVM vm, SQInteger idx) { SQInteger val = 0; sq_getinteger(vm, idx, &val); return val; } inline SQFloat getFloat(HSQUIRRELVM vm, SQInteger idx) { SQFloat val = 0; if (SQ_FAILED(sq_getfloat(vm, idx, &val))) { SQInteger ival = 0; if (SQ_SUCCEEDED(sq_getinteger(vm, idx, &ival))) val = static_cast(ival); } return val; } inline bool getBool(HSQUIRRELVM vm, SQInteger idx) { SQBool val = SQFalse; sq_getbool(vm, idx, &val); return val != SQFalse; } inline std::string getString(HSQUIRRELVM vm, SQInteger idx) { const SQChar *str = nullptr; sq_getstring(vm, idx, &str); return str ? std::string(str) : std::string(); } // ============================================================================ // Value type userdata helpers // ============================================================================ template T *pushValueInstance(HSQUIRRELVM vm, const T &val) { sq_pushroottable(vm); sq_pushstring(vm, SqClassName::name(), -1); if (SQ_FAILED(sq_get(vm, -2))) { sq_pop(vm, 1); return nullptr; } if (SQ_FAILED(sq_createinstance(vm, -1))) { sq_pop(vm, 2); return nullptr; } T *ud = nullptr; sq_getinstanceup(vm, -1, reinterpret_cast(&ud), nullptr, SQFalse); if (ud) *ud = val; sq_remove(vm, -2); // class sq_remove(vm, -2); // roottable return ud; } template T *getValueInstance(HSQUIRRELVM vm, SQInteger idx) { T *ud = nullptr; sq_getinstanceup(vm, idx, reinterpret_cast(&ud), typeTag(), SQFalse); return ud; } // ============================================================================ // Shared pointer bridge for reference types // ============================================================================ template void pushPtr(HSQUIRRELVM vm, Ptr ptr) { if (!ptr) { sq_pushnull(vm); return; } sq_pushroottable(vm); sq_pushstring(vm, SqClassName::name(), -1); if (SQ_FAILED(sq_get(vm, -2))) { sq_pop(vm, 1); sq_pushnull(vm); return; } if (SQ_FAILED(sq_createinstance(vm, -1))) { sq_pop(vm, 2); sq_pushnull(vm); return; } auto *storage = new Ptr(std::move(ptr)); sq_setinstanceup(vm, -1, storage); sq_setreleasehook(vm, -1, [](SQUserPointer p, SQInteger) -> SQInteger { delete static_cast *>(p); return 0; }); sq_remove(vm, -2); // class sq_remove(vm, -2); // roottable } template Ptr getPtr(HSQUIRRELVM vm, SQInteger idx) { SQUserPointer up = nullptr; sq_getinstanceup(vm, idx, &up, typeTag(), SQFalse); if (!up) return nullptr; return *static_cast *>(up); } template T *getRawPtr(HSQUIRRELVM vm, SQInteger idx) { auto p = getPtr(vm, idx); return p ? p.get() : nullptr; } // ============================================================================ // Singleton pointer (no release hook) // ============================================================================ template void pushSingleton(HSQUIRRELVM vm, T *ptr, const char *className) { sq_pushroottable(vm); sq_pushstring(vm, className, -1); if (SQ_FAILED(sq_get(vm, -2))) { sq_pop(vm, 1); sq_pushnull(vm); return; } if (SQ_FAILED(sq_createinstance(vm, -1))) { sq_pop(vm, 2); sq_pushnull(vm); return; } sq_setinstanceup(vm, -1, ptr); sq_remove(vm, -2); // class sq_remove(vm, -2); // roottable } template T *getSingleton(HSQUIRRELVM vm, SQInteger idx) { SQUserPointer up = nullptr; sq_getinstanceup(vm, idx, &up, nullptr, SQFalse); return static_cast(up); } // ============================================================================ // User pointer helpers (for non-class user pointers) // ============================================================================ inline void pushUserPointer(HSQUIRRELVM vm, void *ptr) { sq_pushuserpointer(vm, ptr); } inline void *getUserPointer(HSQUIRRELVM vm, SQInteger idx) { SQUserPointer up = nullptr; sq_getuserpointer(vm, idx, &up); return up; } // ============================================================================ // ClassDef — fluent API for registering a class // ============================================================================ struct ClassDef { HSQUIRRELVM vm; ClassDef(HSQUIRRELVM v, const char *name, const char *base = nullptr) : vm(v) { sq_pushroottable(vm); sq_pushstring(vm, name, -1); if (base) { sq_pushstring(vm, base, -1); if (SQ_FAILED(sq_get(vm, -3))) { sq_newclass(vm, SQFalse); } else { sq_newclass(vm, SQTrue); } } else { sq_newclass(vm, SQFalse); } } ClassDef &setTypeTag(SQUserPointer tag) { sq_settypetag(vm, -1, tag); return *this; } ClassDef &method(const char *name, SQFUNCTION fn, SQInteger nparams = 0, const char *typemask = nullptr) { sq_pushstring(vm, name, -1); sq_newclosure(vm, fn, 0); if (nparams > 0 && typemask) sq_setparamscheck(vm, nparams, typemask); sq_newslot(vm, -3, SQFalse); return *this; } ClassDef &staticMethod(const char *name, SQFUNCTION fn, SQInteger nparams = 0, const char *typemask = nullptr) { sq_pushstring(vm, name, -1); sq_newclosure(vm, fn, 0); if (nparams > 0 && typemask) sq_setparamscheck(vm, nparams, typemask); sq_newslot(vm, -3, SQTrue); return *this; } template ClassDef &setValueType(SQFUNCTION constructor) { sq_settypetag(vm, -1, typeTag()); sq_setclassudsize(vm, -1, sizeof(T)); sq_pushstring(vm, "constructor", -1); sq_newclosure(vm, constructor, 0); sq_newslot(vm, -3, SQFalse); return *this; } void commit() { sq_newslot(vm, -3, SQFalse); sq_pop(vm, 1); } }; // ============================================================================ // Register a table of integer constants // ============================================================================ inline void registerConstTable(HSQUIRRELVM vm, const char *tableName, const char *const *names, const SQInteger *values, int count) { sq_pushroottable(vm); sq_pushstring(vm, tableName, -1); sq_newtable(vm); for (int i = 0; i < count; ++i) { sq_pushstring(vm, names[i], -1); sq_pushinteger(vm, values[i]); sq_newslot(vm, -3, SQFalse); } sq_newslot(vm, -3, SQFalse); sq_pop(vm, 1); } } // namespace sq } // namespace extra2d