initial commit
This commit is contained in:
581
add_on/autowrapper/aswrappedcall.h
Normal file
581
add_on/autowrapper/aswrappedcall.h
Normal file
@@ -0,0 +1,581 @@
|
||||
#ifndef AS_GEN_WRAPPER_H
|
||||
#define AS_GEN_WRAPPER_H
|
||||
|
||||
#ifndef ANGELSCRIPT_H
|
||||
// Avoid having to inform include path if header is already include before
|
||||
#include <angelscript.h>
|
||||
#endif
|
||||
|
||||
#include <new>
|
||||
|
||||
namespace gw {
|
||||
|
||||
template <typename T> class Proxy {
|
||||
public:
|
||||
T value;
|
||||
Proxy(T value) : value(value) {}
|
||||
static T cast(void * ptr) {
|
||||
return reinterpret_cast<Proxy<T> *>(&ptr)->value;
|
||||
}
|
||||
private:
|
||||
Proxy(const Proxy &);
|
||||
Proxy & operator=(const Proxy &);
|
||||
};
|
||||
|
||||
template <typename T> struct Wrapper {};
|
||||
template <typename T> struct ObjFirst {};
|
||||
template <typename T> struct ObjLast {};
|
||||
template <typename T> struct Constructor {};
|
||||
|
||||
template <typename T>
|
||||
void destroy(AS_NAMESPACE_QUALIFIER asIScriptGeneric * gen) {
|
||||
static_cast<T *>(gen->GetObject())->~T();
|
||||
}
|
||||
template <>
|
||||
struct Wrapper<void (*)(void)> {
|
||||
template <void (*fp)(void)>
|
||||
static void f(AS_NAMESPACE_QUALIFIER asIScriptGeneric * /*gen*/) {
|
||||
((fp)());
|
||||
}
|
||||
};
|
||||
template <typename R>
|
||||
struct Wrapper<R (*)(void)> {
|
||||
template <R (*fp)(void)>
|
||||
static void f(AS_NAMESPACE_QUALIFIER asIScriptGeneric * gen) {
|
||||
new (gen->GetAddressOfReturnLocation()) Proxy<R>((fp)());
|
||||
}
|
||||
};
|
||||
template <typename T>
|
||||
struct Wrapper<void (T::*)(void)> {
|
||||
template <void (T::*fp)(void)>
|
||||
static void f(AS_NAMESPACE_QUALIFIER asIScriptGeneric * gen) {
|
||||
((static_cast<T *>(gen->GetObject())->*fp)());
|
||||
}
|
||||
};
|
||||
template <typename T, typename R>
|
||||
struct Wrapper<R (T::*)(void)> {
|
||||
template <R (T::*fp)(void)>
|
||||
static void f(AS_NAMESPACE_QUALIFIER asIScriptGeneric * gen) {
|
||||
new (gen->GetAddressOfReturnLocation()) Proxy<R>((static_cast<T *>(gen->GetObject())->*fp)());
|
||||
}
|
||||
};
|
||||
template <typename T>
|
||||
struct Wrapper<void (T::*)(void) const> {
|
||||
template <void (T::*fp)(void) const>
|
||||
static void f(AS_NAMESPACE_QUALIFIER asIScriptGeneric * gen) {
|
||||
((static_cast<T *>(gen->GetObject())->*fp)());
|
||||
}
|
||||
};
|
||||
template <typename T, typename R>
|
||||
struct Wrapper<R (T::*)(void) const> {
|
||||
template <R (T::*fp)(void) const>
|
||||
static void f(AS_NAMESPACE_QUALIFIER asIScriptGeneric * gen) {
|
||||
new (gen->GetAddressOfReturnLocation()) Proxy<R>((static_cast<T *>(gen->GetObject())->*fp)());
|
||||
}
|
||||
};
|
||||
template <typename T>
|
||||
struct ObjFirst<void (*)(T)> {
|
||||
template <void (*fp)(T)>
|
||||
static void f(AS_NAMESPACE_QUALIFIER asIScriptGeneric * gen) {
|
||||
((fp)(
|
||||
Proxy<T>::cast(gen->GetObject())));
|
||||
}
|
||||
};
|
||||
template <typename T, typename R>
|
||||
struct ObjFirst<R (*)(T)> {
|
||||
template <R (*fp)(T)>
|
||||
static void f(AS_NAMESPACE_QUALIFIER asIScriptGeneric * gen) {
|
||||
new (gen->GetAddressOfReturnLocation()) Proxy<R>((fp)(
|
||||
Proxy<T>::cast(gen->GetObject())));
|
||||
}
|
||||
};
|
||||
template <typename T>
|
||||
struct ObjLast<void (*)(T)> {
|
||||
template <void (*fp)(T)>
|
||||
static void f(AS_NAMESPACE_QUALIFIER asIScriptGeneric * gen) {
|
||||
((fp)(
|
||||
Proxy<T>::cast(gen->GetObject())));
|
||||
}
|
||||
};
|
||||
template <typename T, typename R>
|
||||
struct ObjLast<R (*)(T)> {
|
||||
template <R (*fp)(T)>
|
||||
static void f(AS_NAMESPACE_QUALIFIER asIScriptGeneric * gen) {
|
||||
new (gen->GetAddressOfReturnLocation()) Proxy<R>((fp)(
|
||||
Proxy<T>::cast(gen->GetObject())));
|
||||
}
|
||||
};
|
||||
template <typename T>
|
||||
struct Constructor <T ()> {
|
||||
static void f(AS_NAMESPACE_QUALIFIER asIScriptGeneric * gen) {
|
||||
new (gen->GetObject()) T();
|
||||
}
|
||||
};
|
||||
template <typename A0>
|
||||
struct Wrapper<void (*)(A0)> {
|
||||
template <void (*fp)(A0)>
|
||||
static void f(AS_NAMESPACE_QUALIFIER asIScriptGeneric * gen) {
|
||||
((fp)(
|
||||
static_cast<Proxy <A0> *>(gen->GetAddressOfArg(0))->value));
|
||||
}
|
||||
};
|
||||
template <typename R, typename A0>
|
||||
struct Wrapper<R (*)(A0)> {
|
||||
template <R (*fp)(A0)>
|
||||
static void f(AS_NAMESPACE_QUALIFIER asIScriptGeneric * gen) {
|
||||
new (gen->GetAddressOfReturnLocation()) Proxy<R>((fp)(
|
||||
static_cast<Proxy <A0> *>(gen->GetAddressOfArg(0))->value));
|
||||
}
|
||||
};
|
||||
template <typename T, typename A0>
|
||||
struct Wrapper<void (T::*)(A0)> {
|
||||
template <void (T::*fp)(A0)>
|
||||
static void f(AS_NAMESPACE_QUALIFIER asIScriptGeneric * gen) {
|
||||
((static_cast<T *>(gen->GetObject())->*fp)(
|
||||
static_cast<Proxy <A0> *>(gen->GetAddressOfArg(0))->value));
|
||||
}
|
||||
};
|
||||
template <typename T, typename R, typename A0>
|
||||
struct Wrapper<R (T::*)(A0)> {
|
||||
template <R (T::*fp)(A0)>
|
||||
static void f(AS_NAMESPACE_QUALIFIER asIScriptGeneric * gen) {
|
||||
new (gen->GetAddressOfReturnLocation()) Proxy<R>((static_cast<T *>(gen->GetObject())->*fp)(
|
||||
static_cast<Proxy <A0> *>(gen->GetAddressOfArg(0))->value));
|
||||
}
|
||||
};
|
||||
template <typename T, typename A0>
|
||||
struct Wrapper<void (T::*)(A0) const> {
|
||||
template <void (T::*fp)(A0) const>
|
||||
static void f(AS_NAMESPACE_QUALIFIER asIScriptGeneric * gen) {
|
||||
((static_cast<T *>(gen->GetObject())->*fp)(
|
||||
static_cast<Proxy <A0> *>(gen->GetAddressOfArg(0))->value));
|
||||
}
|
||||
};
|
||||
template <typename T, typename R, typename A0>
|
||||
struct Wrapper<R (T::*)(A0) const> {
|
||||
template <R (T::*fp)(A0) const>
|
||||
static void f(AS_NAMESPACE_QUALIFIER asIScriptGeneric * gen) {
|
||||
new (gen->GetAddressOfReturnLocation()) Proxy<R>((static_cast<T *>(gen->GetObject())->*fp)(
|
||||
static_cast<Proxy <A0> *>(gen->GetAddressOfArg(0))->value));
|
||||
}
|
||||
};
|
||||
template <typename T, typename A0>
|
||||
struct ObjFirst<void (*)(T, A0)> {
|
||||
template <void (*fp)(T, A0)>
|
||||
static void f(AS_NAMESPACE_QUALIFIER asIScriptGeneric * gen) {
|
||||
((fp)(
|
||||
Proxy<T>::cast(gen->GetObject()),
|
||||
static_cast<Proxy <A0> *>(gen->GetAddressOfArg(0))->value));
|
||||
}
|
||||
};
|
||||
template <typename T, typename R, typename A0>
|
||||
struct ObjFirst<R (*)(T, A0)> {
|
||||
template <R (*fp)(T, A0)>
|
||||
static void f(AS_NAMESPACE_QUALIFIER asIScriptGeneric * gen) {
|
||||
new (gen->GetAddressOfReturnLocation()) Proxy<R>((fp)(
|
||||
Proxy<T>::cast(gen->GetObject()),
|
||||
static_cast<Proxy <A0> *>(gen->GetAddressOfArg(0))->value));
|
||||
}
|
||||
};
|
||||
template <typename T, typename A0>
|
||||
struct ObjLast<void (*)(A0, T)> {
|
||||
template <void (*fp)(A0, T)>
|
||||
static void f(AS_NAMESPACE_QUALIFIER asIScriptGeneric * gen) {
|
||||
((fp)(
|
||||
static_cast<Proxy <A0> *>(gen->GetAddressOfArg(0))->value,
|
||||
Proxy<T>::cast(gen->GetObject())));
|
||||
}
|
||||
};
|
||||
template <typename T, typename R, typename A0>
|
||||
struct ObjLast<R (*)(A0, T)> {
|
||||
template <R (*fp)(A0, T)>
|
||||
static void f(AS_NAMESPACE_QUALIFIER asIScriptGeneric * gen) {
|
||||
new (gen->GetAddressOfReturnLocation()) Proxy<R>((fp)(
|
||||
static_cast<Proxy <A0> *>(gen->GetAddressOfArg(0))->value,
|
||||
Proxy<T>::cast(gen->GetObject())));
|
||||
}
|
||||
};
|
||||
template <typename T, typename A0>
|
||||
struct Constructor <T (A0)> {
|
||||
static void f(AS_NAMESPACE_QUALIFIER asIScriptGeneric * gen) {
|
||||
new (gen->GetObject()) T(
|
||||
static_cast<Proxy <A0> *>(gen->GetAddressOfArg(0))->value);
|
||||
}
|
||||
};
|
||||
template <typename A0, typename A1>
|
||||
struct Wrapper<void (*)(A0, A1)> {
|
||||
template <void (*fp)(A0, A1)>
|
||||
static void f(AS_NAMESPACE_QUALIFIER asIScriptGeneric * gen) {
|
||||
((fp)(
|
||||
static_cast<Proxy <A0> *>(gen->GetAddressOfArg(0))->value,
|
||||
static_cast<Proxy <A1> *>(gen->GetAddressOfArg(1))->value));
|
||||
}
|
||||
};
|
||||
template <typename R, typename A0, typename A1>
|
||||
struct Wrapper<R (*)(A0, A1)> {
|
||||
template <R (*fp)(A0, A1)>
|
||||
static void f(AS_NAMESPACE_QUALIFIER asIScriptGeneric * gen) {
|
||||
new (gen->GetAddressOfReturnLocation()) Proxy<R>((fp)(
|
||||
static_cast<Proxy <A0> *>(gen->GetAddressOfArg(0))->value,
|
||||
static_cast<Proxy <A1> *>(gen->GetAddressOfArg(1))->value));
|
||||
}
|
||||
};
|
||||
template <typename T, typename A0, typename A1>
|
||||
struct Wrapper<void (T::*)(A0, A1)> {
|
||||
template <void (T::*fp)(A0, A1)>
|
||||
static void f(AS_NAMESPACE_QUALIFIER asIScriptGeneric * gen) {
|
||||
((static_cast<T *>(gen->GetObject())->*fp)(
|
||||
static_cast<Proxy <A0> *>(gen->GetAddressOfArg(0))->value,
|
||||
static_cast<Proxy <A1> *>(gen->GetAddressOfArg(1))->value));
|
||||
}
|
||||
};
|
||||
template <typename T, typename R, typename A0, typename A1>
|
||||
struct Wrapper<R (T::*)(A0, A1)> {
|
||||
template <R (T::*fp)(A0, A1)>
|
||||
static void f(AS_NAMESPACE_QUALIFIER asIScriptGeneric * gen) {
|
||||
new (gen->GetAddressOfReturnLocation()) Proxy<R>((static_cast<T *>(gen->GetObject())->*fp)(
|
||||
static_cast<Proxy <A0> *>(gen->GetAddressOfArg(0))->value,
|
||||
static_cast<Proxy <A1> *>(gen->GetAddressOfArg(1))->value));
|
||||
}
|
||||
};
|
||||
template <typename T, typename A0, typename A1>
|
||||
struct Wrapper<void (T::*)(A0, A1) const> {
|
||||
template <void (T::*fp)(A0, A1) const>
|
||||
static void f(AS_NAMESPACE_QUALIFIER asIScriptGeneric * gen) {
|
||||
((static_cast<T *>(gen->GetObject())->*fp)(
|
||||
static_cast<Proxy <A0> *>(gen->GetAddressOfArg(0))->value,
|
||||
static_cast<Proxy <A1> *>(gen->GetAddressOfArg(1))->value));
|
||||
}
|
||||
};
|
||||
template <typename T, typename R, typename A0, typename A1>
|
||||
struct Wrapper<R (T::*)(A0, A1) const> {
|
||||
template <R (T::*fp)(A0, A1) const>
|
||||
static void f(AS_NAMESPACE_QUALIFIER asIScriptGeneric * gen) {
|
||||
new (gen->GetAddressOfReturnLocation()) Proxy<R>((static_cast<T *>(gen->GetObject())->*fp)(
|
||||
static_cast<Proxy <A0> *>(gen->GetAddressOfArg(0))->value,
|
||||
static_cast<Proxy <A1> *>(gen->GetAddressOfArg(1))->value));
|
||||
}
|
||||
};
|
||||
template <typename T, typename A0, typename A1>
|
||||
struct ObjFirst<void (*)(T, A0, A1)> {
|
||||
template <void (*fp)(T, A0, A1)>
|
||||
static void f(AS_NAMESPACE_QUALIFIER asIScriptGeneric * gen) {
|
||||
((fp)(
|
||||
Proxy<T>::cast(gen->GetObject()),
|
||||
static_cast<Proxy <A0> *>(gen->GetAddressOfArg(0))->value,
|
||||
static_cast<Proxy <A1> *>(gen->GetAddressOfArg(1))->value));
|
||||
}
|
||||
};
|
||||
template <typename T, typename R, typename A0, typename A1>
|
||||
struct ObjFirst<R (*)(T, A0, A1)> {
|
||||
template <R (*fp)(T, A0, A1)>
|
||||
static void f(AS_NAMESPACE_QUALIFIER asIScriptGeneric * gen) {
|
||||
new (gen->GetAddressOfReturnLocation()) Proxy<R>((fp)(
|
||||
Proxy<T>::cast(gen->GetObject()),
|
||||
static_cast<Proxy <A0> *>(gen->GetAddressOfArg(0))->value,
|
||||
static_cast<Proxy <A1> *>(gen->GetAddressOfArg(1))->value));
|
||||
}
|
||||
};
|
||||
template <typename T, typename A0, typename A1>
|
||||
struct ObjLast<void (*)(A0, A1, T)> {
|
||||
template <void (*fp)(A0, A1, T)>
|
||||
static void f(AS_NAMESPACE_QUALIFIER asIScriptGeneric * gen) {
|
||||
((fp)(
|
||||
static_cast<Proxy <A0> *>(gen->GetAddressOfArg(0))->value,
|
||||
static_cast<Proxy <A1> *>(gen->GetAddressOfArg(1))->value,
|
||||
Proxy<T>::cast(gen->GetObject())));
|
||||
}
|
||||
};
|
||||
template <typename T, typename R, typename A0, typename A1>
|
||||
struct ObjLast<R (*)(A0, A1, T)> {
|
||||
template <R (*fp)(A0, A1, T)>
|
||||
static void f(AS_NAMESPACE_QUALIFIER asIScriptGeneric * gen) {
|
||||
new (gen->GetAddressOfReturnLocation()) Proxy<R>((fp)(
|
||||
static_cast<Proxy <A0> *>(gen->GetAddressOfArg(0))->value,
|
||||
static_cast<Proxy <A1> *>(gen->GetAddressOfArg(1))->value,
|
||||
Proxy<T>::cast(gen->GetObject())));
|
||||
}
|
||||
};
|
||||
template <typename T, typename A0, typename A1>
|
||||
struct Constructor <T (A0, A1)> {
|
||||
static void f(AS_NAMESPACE_QUALIFIER asIScriptGeneric * gen) {
|
||||
new (gen->GetObject()) T(
|
||||
static_cast<Proxy <A0> *>(gen->GetAddressOfArg(0))->value,
|
||||
static_cast<Proxy <A1> *>(gen->GetAddressOfArg(1))->value);
|
||||
}
|
||||
};
|
||||
template <typename A0, typename A1, typename A2>
|
||||
struct Wrapper<void (*)(A0, A1, A2)> {
|
||||
template <void (*fp)(A0, A1, A2)>
|
||||
static void f(AS_NAMESPACE_QUALIFIER asIScriptGeneric * gen) {
|
||||
((fp)(
|
||||
static_cast<Proxy <A0> *>(gen->GetAddressOfArg(0))->value,
|
||||
static_cast<Proxy <A1> *>(gen->GetAddressOfArg(1))->value,
|
||||
static_cast<Proxy <A2> *>(gen->GetAddressOfArg(2))->value));
|
||||
}
|
||||
};
|
||||
template <typename R, typename A0, typename A1, typename A2>
|
||||
struct Wrapper<R (*)(A0, A1, A2)> {
|
||||
template <R (*fp)(A0, A1, A2)>
|
||||
static void f(AS_NAMESPACE_QUALIFIER asIScriptGeneric * gen) {
|
||||
new (gen->GetAddressOfReturnLocation()) Proxy<R>((fp)(
|
||||
static_cast<Proxy <A0> *>(gen->GetAddressOfArg(0))->value,
|
||||
static_cast<Proxy <A1> *>(gen->GetAddressOfArg(1))->value,
|
||||
static_cast<Proxy <A2> *>(gen->GetAddressOfArg(2))->value));
|
||||
}
|
||||
};
|
||||
template <typename T, typename A0, typename A1, typename A2>
|
||||
struct Wrapper<void (T::*)(A0, A1, A2)> {
|
||||
template <void (T::*fp)(A0, A1, A2)>
|
||||
static void f(AS_NAMESPACE_QUALIFIER asIScriptGeneric * gen) {
|
||||
((static_cast<T *>(gen->GetObject())->*fp)(
|
||||
static_cast<Proxy <A0> *>(gen->GetAddressOfArg(0))->value,
|
||||
static_cast<Proxy <A1> *>(gen->GetAddressOfArg(1))->value,
|
||||
static_cast<Proxy <A2> *>(gen->GetAddressOfArg(2))->value));
|
||||
}
|
||||
};
|
||||
template <typename T, typename R, typename A0, typename A1, typename A2>
|
||||
struct Wrapper<R (T::*)(A0, A1, A2)> {
|
||||
template <R (T::*fp)(A0, A1, A2)>
|
||||
static void f(AS_NAMESPACE_QUALIFIER asIScriptGeneric * gen) {
|
||||
new (gen->GetAddressOfReturnLocation()) Proxy<R>((static_cast<T *>(gen->GetObject())->*fp)(
|
||||
static_cast<Proxy <A0> *>(gen->GetAddressOfArg(0))->value,
|
||||
static_cast<Proxy <A1> *>(gen->GetAddressOfArg(1))->value,
|
||||
static_cast<Proxy <A2> *>(gen->GetAddressOfArg(2))->value));
|
||||
}
|
||||
};
|
||||
template <typename T, typename A0, typename A1, typename A2>
|
||||
struct Wrapper<void (T::*)(A0, A1, A2) const> {
|
||||
template <void (T::*fp)(A0, A1, A2) const>
|
||||
static void f(AS_NAMESPACE_QUALIFIER asIScriptGeneric * gen) {
|
||||
((static_cast<T *>(gen->GetObject())->*fp)(
|
||||
static_cast<Proxy <A0> *>(gen->GetAddressOfArg(0))->value,
|
||||
static_cast<Proxy <A1> *>(gen->GetAddressOfArg(1))->value,
|
||||
static_cast<Proxy <A2> *>(gen->GetAddressOfArg(2))->value));
|
||||
}
|
||||
};
|
||||
template <typename T, typename R, typename A0, typename A1, typename A2>
|
||||
struct Wrapper<R (T::*)(A0, A1, A2) const> {
|
||||
template <R (T::*fp)(A0, A1, A2) const>
|
||||
static void f(AS_NAMESPACE_QUALIFIER asIScriptGeneric * gen) {
|
||||
new (gen->GetAddressOfReturnLocation()) Proxy<R>((static_cast<T *>(gen->GetObject())->*fp)(
|
||||
static_cast<Proxy <A0> *>(gen->GetAddressOfArg(0))->value,
|
||||
static_cast<Proxy <A1> *>(gen->GetAddressOfArg(1))->value,
|
||||
static_cast<Proxy <A2> *>(gen->GetAddressOfArg(2))->value));
|
||||
}
|
||||
};
|
||||
template <typename T, typename A0, typename A1, typename A2>
|
||||
struct ObjFirst<void (*)(T, A0, A1, A2)> {
|
||||
template <void (*fp)(T, A0, A1, A2)>
|
||||
static void f(AS_NAMESPACE_QUALIFIER asIScriptGeneric * gen) {
|
||||
((fp)(
|
||||
Proxy<T>::cast(gen->GetObject()),
|
||||
static_cast<Proxy <A0> *>(gen->GetAddressOfArg(0))->value,
|
||||
static_cast<Proxy <A1> *>(gen->GetAddressOfArg(1))->value,
|
||||
static_cast<Proxy <A2> *>(gen->GetAddressOfArg(2))->value));
|
||||
}
|
||||
};
|
||||
template <typename T, typename R, typename A0, typename A1, typename A2>
|
||||
struct ObjFirst<R (*)(T, A0, A1, A2)> {
|
||||
template <R (*fp)(T, A0, A1, A2)>
|
||||
static void f(AS_NAMESPACE_QUALIFIER asIScriptGeneric * gen) {
|
||||
new (gen->GetAddressOfReturnLocation()) Proxy<R>((fp)(
|
||||
Proxy<T>::cast(gen->GetObject()),
|
||||
static_cast<Proxy <A0> *>(gen->GetAddressOfArg(0))->value,
|
||||
static_cast<Proxy <A1> *>(gen->GetAddressOfArg(1))->value,
|
||||
static_cast<Proxy <A2> *>(gen->GetAddressOfArg(2))->value));
|
||||
}
|
||||
};
|
||||
template <typename T, typename A0, typename A1, typename A2>
|
||||
struct ObjLast<void (*)(A0, A1, A2, T)> {
|
||||
template <void (*fp)(A0, A1, A2, T)>
|
||||
static void f(AS_NAMESPACE_QUALIFIER asIScriptGeneric * gen) {
|
||||
((fp)(
|
||||
static_cast<Proxy <A0> *>(gen->GetAddressOfArg(0))->value,
|
||||
static_cast<Proxy <A1> *>(gen->GetAddressOfArg(1))->value,
|
||||
static_cast<Proxy <A2> *>(gen->GetAddressOfArg(2))->value,
|
||||
Proxy<T>::cast(gen->GetObject())));
|
||||
}
|
||||
};
|
||||
template <typename T, typename R, typename A0, typename A1, typename A2>
|
||||
struct ObjLast<R (*)(A0, A1, A2, T)> {
|
||||
template <R (*fp)(A0, A1, A2, T)>
|
||||
static void f(AS_NAMESPACE_QUALIFIER asIScriptGeneric * gen) {
|
||||
new (gen->GetAddressOfReturnLocation()) Proxy<R>((fp)(
|
||||
static_cast<Proxy <A0> *>(gen->GetAddressOfArg(0))->value,
|
||||
static_cast<Proxy <A1> *>(gen->GetAddressOfArg(1))->value,
|
||||
static_cast<Proxy <A2> *>(gen->GetAddressOfArg(2))->value,
|
||||
Proxy<T>::cast(gen->GetObject())));
|
||||
}
|
||||
};
|
||||
template <typename T, typename A0, typename A1, typename A2>
|
||||
struct Constructor <T (A0, A1, A2)> {
|
||||
static void f(AS_NAMESPACE_QUALIFIER asIScriptGeneric * gen) {
|
||||
new (gen->GetObject()) T(
|
||||
static_cast<Proxy <A0> *>(gen->GetAddressOfArg(0))->value,
|
||||
static_cast<Proxy <A1> *>(gen->GetAddressOfArg(1))->value,
|
||||
static_cast<Proxy <A2> *>(gen->GetAddressOfArg(2))->value);
|
||||
}
|
||||
};
|
||||
template <typename A0, typename A1, typename A2, typename A3>
|
||||
struct Wrapper<void (*)(A0, A1, A2, A3)> {
|
||||
template <void (*fp)(A0, A1, A2, A3)>
|
||||
static void f(AS_NAMESPACE_QUALIFIER asIScriptGeneric * gen) {
|
||||
((fp)(
|
||||
static_cast<Proxy <A0> *>(gen->GetAddressOfArg(0))->value,
|
||||
static_cast<Proxy <A1> *>(gen->GetAddressOfArg(1))->value,
|
||||
static_cast<Proxy <A2> *>(gen->GetAddressOfArg(2))->value,
|
||||
static_cast<Proxy <A3> *>(gen->GetAddressOfArg(3))->value));
|
||||
}
|
||||
};
|
||||
template <typename R, typename A0, typename A1, typename A2, typename A3>
|
||||
struct Wrapper<R (*)(A0, A1, A2, A3)> {
|
||||
template <R (*fp)(A0, A1, A2, A3)>
|
||||
static void f(AS_NAMESPACE_QUALIFIER asIScriptGeneric * gen) {
|
||||
new (gen->GetAddressOfReturnLocation()) Proxy<R>((fp)(
|
||||
static_cast<Proxy <A0> *>(gen->GetAddressOfArg(0))->value,
|
||||
static_cast<Proxy <A1> *>(gen->GetAddressOfArg(1))->value,
|
||||
static_cast<Proxy <A2> *>(gen->GetAddressOfArg(2))->value,
|
||||
static_cast<Proxy <A3> *>(gen->GetAddressOfArg(3))->value));
|
||||
}
|
||||
};
|
||||
template <typename T, typename A0, typename A1, typename A2, typename A3>
|
||||
struct Wrapper<void (T::*)(A0, A1, A2, A3)> {
|
||||
template <void (T::*fp)(A0, A1, A2, A3)>
|
||||
static void f(AS_NAMESPACE_QUALIFIER asIScriptGeneric * gen) {
|
||||
((static_cast<T *>(gen->GetObject())->*fp)(
|
||||
static_cast<Proxy <A0> *>(gen->GetAddressOfArg(0))->value,
|
||||
static_cast<Proxy <A1> *>(gen->GetAddressOfArg(1))->value,
|
||||
static_cast<Proxy <A2> *>(gen->GetAddressOfArg(2))->value,
|
||||
static_cast<Proxy <A3> *>(gen->GetAddressOfArg(3))->value));
|
||||
}
|
||||
};
|
||||
template <typename T, typename R, typename A0, typename A1, typename A2, typename A3>
|
||||
struct Wrapper<R (T::*)(A0, A1, A2, A3)> {
|
||||
template <R (T::*fp)(A0, A1, A2, A3)>
|
||||
static void f(AS_NAMESPACE_QUALIFIER asIScriptGeneric * gen) {
|
||||
new (gen->GetAddressOfReturnLocation()) Proxy<R>((static_cast<T *>(gen->GetObject())->*fp)(
|
||||
static_cast<Proxy <A0> *>(gen->GetAddressOfArg(0))->value,
|
||||
static_cast<Proxy <A1> *>(gen->GetAddressOfArg(1))->value,
|
||||
static_cast<Proxy <A2> *>(gen->GetAddressOfArg(2))->value,
|
||||
static_cast<Proxy <A3> *>(gen->GetAddressOfArg(3))->value));
|
||||
}
|
||||
};
|
||||
template <typename T, typename A0, typename A1, typename A2, typename A3>
|
||||
struct Wrapper<void (T::*)(A0, A1, A2, A3) const> {
|
||||
template <void (T::*fp)(A0, A1, A2, A3) const>
|
||||
static void f(AS_NAMESPACE_QUALIFIER asIScriptGeneric * gen) {
|
||||
((static_cast<T *>(gen->GetObject())->*fp)(
|
||||
static_cast<Proxy <A0> *>(gen->GetAddressOfArg(0))->value,
|
||||
static_cast<Proxy <A1> *>(gen->GetAddressOfArg(1))->value,
|
||||
static_cast<Proxy <A2> *>(gen->GetAddressOfArg(2))->value,
|
||||
static_cast<Proxy <A3> *>(gen->GetAddressOfArg(3))->value));
|
||||
}
|
||||
};
|
||||
template <typename T, typename R, typename A0, typename A1, typename A2, typename A3>
|
||||
struct Wrapper<R (T::*)(A0, A1, A2, A3) const> {
|
||||
template <R (T::*fp)(A0, A1, A2, A3) const>
|
||||
static void f(AS_NAMESPACE_QUALIFIER asIScriptGeneric * gen) {
|
||||
new (gen->GetAddressOfReturnLocation()) Proxy<R>((static_cast<T *>(gen->GetObject())->*fp)(
|
||||
static_cast<Proxy <A0> *>(gen->GetAddressOfArg(0))->value,
|
||||
static_cast<Proxy <A1> *>(gen->GetAddressOfArg(1))->value,
|
||||
static_cast<Proxy <A2> *>(gen->GetAddressOfArg(2))->value,
|
||||
static_cast<Proxy <A3> *>(gen->GetAddressOfArg(3))->value));
|
||||
}
|
||||
};
|
||||
template <typename T, typename A0, typename A1, typename A2, typename A3>
|
||||
struct ObjFirst<void (*)(T, A0, A1, A2, A3)> {
|
||||
template <void (*fp)(T, A0, A1, A2, A3)>
|
||||
static void f(AS_NAMESPACE_QUALIFIER asIScriptGeneric * gen) {
|
||||
((fp)(
|
||||
Proxy<T>::cast(gen->GetObject()),
|
||||
static_cast<Proxy <A0> *>(gen->GetAddressOfArg(0))->value,
|
||||
static_cast<Proxy <A1> *>(gen->GetAddressOfArg(1))->value,
|
||||
static_cast<Proxy <A2> *>(gen->GetAddressOfArg(2))->value,
|
||||
static_cast<Proxy <A3> *>(gen->GetAddressOfArg(3))->value));
|
||||
}
|
||||
};
|
||||
template <typename T, typename R, typename A0, typename A1, typename A2, typename A3>
|
||||
struct ObjFirst<R (*)(T, A0, A1, A2, A3)> {
|
||||
template <R (*fp)(T, A0, A1, A2, A3)>
|
||||
static void f(AS_NAMESPACE_QUALIFIER asIScriptGeneric * gen) {
|
||||
new (gen->GetAddressOfReturnLocation()) Proxy<R>((fp)(
|
||||
Proxy<T>::cast(gen->GetObject()),
|
||||
static_cast<Proxy <A0> *>(gen->GetAddressOfArg(0))->value,
|
||||
static_cast<Proxy <A1> *>(gen->GetAddressOfArg(1))->value,
|
||||
static_cast<Proxy <A2> *>(gen->GetAddressOfArg(2))->value,
|
||||
static_cast<Proxy <A3> *>(gen->GetAddressOfArg(3))->value));
|
||||
}
|
||||
};
|
||||
template <typename T, typename A0, typename A1, typename A2, typename A3>
|
||||
struct ObjLast<void (*)(A0, A1, A2, A3, T)> {
|
||||
template <void (*fp)(A0, A1, A2, A3, T)>
|
||||
static void f(AS_NAMESPACE_QUALIFIER asIScriptGeneric * gen) {
|
||||
((fp)(
|
||||
static_cast<Proxy <A0> *>(gen->GetAddressOfArg(0))->value,
|
||||
static_cast<Proxy <A1> *>(gen->GetAddressOfArg(1))->value,
|
||||
static_cast<Proxy <A2> *>(gen->GetAddressOfArg(2))->value,
|
||||
static_cast<Proxy <A3> *>(gen->GetAddressOfArg(3))->value,
|
||||
Proxy<T>::cast(gen->GetObject())));
|
||||
}
|
||||
};
|
||||
template <typename T, typename R, typename A0, typename A1, typename A2, typename A3>
|
||||
struct ObjLast<R (*)(A0, A1, A2, A3, T)> {
|
||||
template <R (*fp)(A0, A1, A2, A3, T)>
|
||||
static void f(AS_NAMESPACE_QUALIFIER asIScriptGeneric * gen) {
|
||||
new (gen->GetAddressOfReturnLocation()) Proxy<R>((fp)(
|
||||
static_cast<Proxy <A0> *>(gen->GetAddressOfArg(0))->value,
|
||||
static_cast<Proxy <A1> *>(gen->GetAddressOfArg(1))->value,
|
||||
static_cast<Proxy <A2> *>(gen->GetAddressOfArg(2))->value,
|
||||
static_cast<Proxy <A3> *>(gen->GetAddressOfArg(3))->value,
|
||||
Proxy<T>::cast(gen->GetObject())));
|
||||
}
|
||||
};
|
||||
template <typename T, typename A0, typename A1, typename A2, typename A3>
|
||||
struct Constructor <T (A0, A1, A2, A3)> {
|
||||
static void f(AS_NAMESPACE_QUALIFIER asIScriptGeneric * gen) {
|
||||
new (gen->GetObject()) T(
|
||||
static_cast<Proxy <A0> *>(gen->GetAddressOfArg(0))->value,
|
||||
static_cast<Proxy <A1> *>(gen->GetAddressOfArg(1))->value,
|
||||
static_cast<Proxy <A2> *>(gen->GetAddressOfArg(2))->value,
|
||||
static_cast<Proxy <A3> *>(gen->GetAddressOfArg(3))->value);
|
||||
}
|
||||
};
|
||||
template <typename T>
|
||||
struct Id {
|
||||
template <T fn_ptr> AS_NAMESPACE_QUALIFIER asSFuncPtr f(void) { return asFUNCTION(&Wrapper<T>::template f<fn_ptr>); }
|
||||
template <T fn_ptr> AS_NAMESPACE_QUALIFIER asSFuncPtr of(void) { return asFUNCTION(&ObjFirst<T>::template f<fn_ptr>); }
|
||||
template <T fn_ptr> AS_NAMESPACE_QUALIFIER asSFuncPtr ol(void) { return asFUNCTION(&ObjLast<T>::template f<fn_ptr>); }
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
Id<T> id(T /*fn_ptr*/) { return Id<T>(); }
|
||||
|
||||
// On some versions of GNUC it is necessary to use the template keyword as disambiguator,
|
||||
// on others the template keyword gives an error, hence the need for the following define.
|
||||
// MSVC on the other hand seems to accept both with or without the template keyword.
|
||||
#if defined(__GNUC__) && (__GNUC__ < 4 || (__GNUC__ == 4 && __GNUC_MINOR__ < 4))
|
||||
// GNUC 4.4.3 doesn't need the template keyword, and
|
||||
// hopefully upcoming versions won't need it either
|
||||
#define TMPL template
|
||||
#else
|
||||
#define TMPL
|
||||
#endif
|
||||
|
||||
#define WRAP_FN(name) (::gw::id(name).TMPL f< name >())
|
||||
#define WRAP_MFN(ClassType, name) (::gw::id(&ClassType::name).TMPL f< &ClassType::name >())
|
||||
#define WRAP_OBJ_FIRST(name) (::gw::id(name).TMPL of< name >())
|
||||
#define WRAP_OBJ_LAST(name) (::gw::id(name).TMPL ol< name >())
|
||||
|
||||
#define WRAP_FN_PR(name, Parameters, ReturnType) asFUNCTION((::gw::Wrapper<ReturnType (*)Parameters>::TMPL f< name >))
|
||||
#define WRAP_MFN_PR(ClassType, name, Parameters, ReturnType) asFUNCTION((::gw::Wrapper<ReturnType (ClassType::*)Parameters>::TMPL f< &ClassType::name >))
|
||||
#define WRAP_OBJ_FIRST_PR(name, Parameters, ReturnType) asFUNCTION((::gw::ObjFirst<ReturnType (*)Parameters>::TMPL f< name >))
|
||||
#define WRAP_OBJ_LAST_PR(name, Parameters, ReturnType) asFUNCTION((::gw::ObjLast<ReturnType (*)Parameters>::TMPL f< name >))
|
||||
|
||||
#define WRAP_CON(ClassType, Parameters) asFUNCTION((::gw::Constructor<ClassType Parameters>::f))
|
||||
#define WRAP_DES(ClassType) asFUNCTION((::gw::destroy<ClassType>))
|
||||
|
||||
|
||||
} // end namespace gw
|
||||
|
||||
#endif
|
||||
166
add_on/autowrapper/generator/generateheader.cpp
Normal file
166
add_on/autowrapper/generator/generateheader.cpp
Normal file
@@ -0,0 +1,166 @@
|
||||
//
|
||||
// This generator creates a header file that implements automatic
|
||||
// wrapper functions for the generic calling convention.
|
||||
//
|
||||
// Originally implemented by George Yohng from 4Front Technologies in 2009-03-11
|
||||
// Modifications by Pierre Fortin in order to add constructor wrapper generation
|
||||
//
|
||||
// A completely new implementation of automatic wrapper functions was
|
||||
// implemented by SiCrane at GameDev.net in 2011-12-18. The generator was
|
||||
// adapted from Python to C++ by Andreas.
|
||||
//
|
||||
// ref: http://www.gamedev.net/topic/617111-more-angelscript-binding-wrappers/
|
||||
//
|
||||
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string>
|
||||
|
||||
// Generate templates for up to this number of function parameters
|
||||
const int max_args = 4;
|
||||
|
||||
using namespace std;
|
||||
|
||||
void PrintTemplate(const char *base, const char *typeNameList, const char *retType, const char *objType, const char *isConst, const char *newExpr, const char *objExpr, const char *argList1, const char *argList2, const char *wrapName);
|
||||
void PrintConstructor(const char *comma, const char *typeNameList, const char *typeList, const char *argList);
|
||||
|
||||
int main()
|
||||
{
|
||||
printf("#ifndef AS_GEN_WRAPPER_H\n"
|
||||
"#define AS_GEN_WRAPPER_H\n"
|
||||
"\n"
|
||||
"#ifndef ANGELSCRIPT_H\n"
|
||||
"// Avoid having to inform include path if header is already include before\n"
|
||||
"#include <angelscript.h>\n"
|
||||
"#endif\n"
|
||||
"#include <new>\n"
|
||||
"\n"
|
||||
"namespace gw {\n"
|
||||
"\n"
|
||||
"template <typename T> class Proxy {\n"
|
||||
" public:\n"
|
||||
" T value;\n"
|
||||
" Proxy(T value) : value(value) {}\n"
|
||||
" static T cast(void * ptr) {\n"
|
||||
" return reinterpret_cast<Proxy<T> *>(&ptr)->value;\n"
|
||||
" }\n"
|
||||
" private:\n"
|
||||
" Proxy(const Proxy &);\n"
|
||||
" Proxy & operator=(const Proxy &);\n"
|
||||
"};\n"
|
||||
"\n"
|
||||
"template <typename T> struct Wrapper {};\n"
|
||||
"template <typename T> struct ObjFirst {};\n"
|
||||
"template <typename T> struct ObjLast {};\n"
|
||||
"template <typename T> struct Constructor {};\n"
|
||||
"\n"
|
||||
"template <typename T>\n"
|
||||
"void destroy(AS_NAMESPACE_QUALIFIER asIScriptGeneric * gen) {\n"
|
||||
" static_cast<T *>(gen->GetObject())->~T();\n"
|
||||
"}\n");
|
||||
|
||||
string typename_list = "typename A0";
|
||||
string type_list = "A0";
|
||||
string arg_list = "\n static_cast<Proxy <A0> *>(gen->GetAddressOfArg(0))->value";
|
||||
string new_exp = "new (gen->GetAddressOfReturnLocation()) Proxy<R>";
|
||||
string obj_exp = "static_cast<T *>(gen->GetObject())->*";
|
||||
string obj_arg_exp = "\n Proxy<T>::cast(gen->GetObject())";
|
||||
|
||||
PrintTemplate("", "", "void", "", "", "", "", "void", "", "Wrapper");
|
||||
PrintTemplate("typename R", "", "R", "", "", new_exp.c_str(), "", "void", "", "Wrapper");
|
||||
PrintTemplate("typename T", "", "void", "T::", "", "", obj_exp.c_str(), "void", "", "Wrapper");
|
||||
PrintTemplate("typename T, typename R", "", "R", "T::", "", new_exp.c_str(), obj_exp.c_str(), "void", "", "Wrapper");
|
||||
PrintTemplate("typename T", "", "void", "T::", " const", "", obj_exp.c_str(), "void", "", "Wrapper");
|
||||
PrintTemplate("typename T, typename R", "", "R", "T::", " const", new_exp.c_str(), obj_exp.c_str(), "void", "", "Wrapper");
|
||||
|
||||
PrintTemplate("typename T", "", "void", "", "", "", "", "T", obj_arg_exp.c_str(), "ObjFirst");
|
||||
PrintTemplate("typename T, typename R", "", "R", "", "", new_exp.c_str(), "", "T", obj_arg_exp.c_str(), "ObjFirst");
|
||||
PrintTemplate("typename T", "", "void", "", "", "", "", "T", obj_arg_exp.c_str(), "ObjLast");
|
||||
PrintTemplate("typename T, typename R", "", "R", "", "", new_exp.c_str(), "", "T", obj_arg_exp.c_str(), "ObjLast");
|
||||
|
||||
PrintConstructor("", "", "", "");
|
||||
|
||||
for( int i = 0; i < max_args; i++ )
|
||||
{
|
||||
PrintTemplate("", typename_list.c_str(), "void", "", "", "", "", type_list.c_str(), arg_list.c_str(), "Wrapper");
|
||||
PrintTemplate("typename R, ", typename_list.c_str(), "R", "", "", new_exp.c_str(), "", type_list.c_str(), arg_list.c_str(), "Wrapper");
|
||||
PrintTemplate("typename T, ", typename_list.c_str(), "void", "T::", "", "", obj_exp.c_str(), type_list.c_str(), arg_list.c_str(), "Wrapper");
|
||||
PrintTemplate("typename T, typename R, ", typename_list.c_str(), "R", "T::", "", new_exp.c_str(), obj_exp.c_str(), type_list.c_str(), arg_list.c_str(), "Wrapper");
|
||||
PrintTemplate("typename T, ", typename_list.c_str(), "void", "T::", " const", "", obj_exp.c_str(), type_list.c_str(), arg_list.c_str(), "Wrapper");
|
||||
PrintTemplate("typename T, typename R, ", typename_list.c_str(), "R", "T::", " const", new_exp.c_str(), obj_exp.c_str(), type_list.c_str(), arg_list.c_str(), "Wrapper");
|
||||
|
||||
PrintTemplate("typename T, ", typename_list.c_str(), "void", "", "", "", "", ("T, " + type_list).c_str(), (obj_arg_exp + "," + arg_list).c_str(), "ObjFirst");
|
||||
PrintTemplate("typename T, typename R, ", typename_list.c_str(), "R", "", "", new_exp.c_str(), "", ("T, " + type_list).c_str(), (obj_arg_exp + "," + arg_list).c_str(), "ObjFirst");
|
||||
PrintTemplate("typename T, ", typename_list.c_str(), "void", "", "", "", "", (type_list + ", T").c_str(), (arg_list + "," + obj_arg_exp).c_str(), "ObjLast");
|
||||
PrintTemplate("typename T, typename R, ", typename_list.c_str(), "R", "", "", new_exp.c_str(), "", (type_list + ", T").c_str(), (arg_list + "," + obj_arg_exp).c_str(), "ObjLast");
|
||||
|
||||
PrintConstructor(", ", typename_list.c_str(), type_list.c_str(), arg_list.c_str());
|
||||
|
||||
char buf[5];
|
||||
sprintf(buf, "%d", i + 1);
|
||||
typename_list += ", typename A" + string(buf);
|
||||
type_list += ", A" + string(buf);
|
||||
arg_list += ",\n static_cast<Proxy <A" + string(buf) + "> *>(gen->GetAddressOfArg(" + string(buf) + "))->value";
|
||||
}
|
||||
|
||||
printf("template <typename T>\n"
|
||||
"struct Id {\n"
|
||||
" template <T fn_ptr> AS_NAMESPACE_QUALIFIER asSFuncPtr f(void) { return asFUNCTION(&Wrapper<T>::template f<fn_ptr>); }\n"
|
||||
" template <T fn_ptr> AS_NAMESPACE_QUALIFIER asSFuncPtr of(void) { return asFUNCTION(&ObjFirst<T>::template f<fn_ptr>); }\n"
|
||||
" template <T fn_ptr> AS_NAMESPACE_QUALIFIER asSFuncPtr ol(void) { return asFUNCTION(&ObjLast<T>::template f<fn_ptr>); }\n"
|
||||
"};\n"
|
||||
"\n"
|
||||
"template <typename T>\n"
|
||||
"Id<T> id(T fn_ptr) { return Id<T>(); }\n"
|
||||
"\n"
|
||||
"// On some versions of GNUC it is necessary to use the template keyword as disambiguator,\n"
|
||||
"// on others the template keyword gives an error, hence the need for the following define.\n"
|
||||
"// MSVC on the other hand seems to accept both with or without the template keyword.\n"
|
||||
"#if defined(__GNUC__) && (__GNUC__ < 4 || (__GNUC__ == 4 && __GNUC_MINOR__ < 4))\n"
|
||||
" // GNUC 4.4.3 doesn't need the template keyword, and\n"
|
||||
" // hopefully upcoming versions won't need it either\n"
|
||||
" #define TMPL template\n"
|
||||
"#else\n"
|
||||
" #define TMPL\n"
|
||||
"#endif\n"
|
||||
"\n"
|
||||
"#define WRAP_FN(name) (::gw::id(name).TMPL f< name >())\n"
|
||||
"#define WRAP_MFN(ClassType, name) (::gw::id(&ClassType::name).TMPL f< &ClassType::name >())\n"
|
||||
"#define WRAP_OBJ_FIRST(name) (::gw::id(name).TMPL of< name >())\n"
|
||||
"#define WRAP_OBJ_LAST(name) (::gw::id(name).TMPL ol< name >())\n"
|
||||
"\n"
|
||||
"#define WRAP_FN_PR(name, Parameters, ReturnType) asFUNCTION((::gw::Wrapper<ReturnType (*)Parameters>::TMPL f< name >))\n"
|
||||
"#define WRAP_MFN_PR(ClassType, name, Parameters, ReturnType) asFUNCTION((::gw::Wrapper<ReturnType (ClassType::*)Parameters>::TMPL f< &ClassType::name >))\n"
|
||||
"#define WRAP_OBJ_FIRST_PR(name, Parameters, ReturnType) asFUNCTION((::gw::ObjFirst<ReturnType (*)Parameters>::TMPL f< name >))\n"
|
||||
"#define WRAP_OBJ_LAST_PR(name, Parameters, ReturnType) asFUNCTION((::gw::ObjLast<ReturnType (*)Parameters>::TMPL f< name >))\n"
|
||||
"\n"
|
||||
"#define WRAP_CON(ClassType, Parameters) asFUNCTION((::gw::Constructor<ClassType Parameters>::f))\n"
|
||||
"#define WRAP_DES(ClassType) asFUNCTION((::gw::destroy<ClassType>))\n"
|
||||
"\n"
|
||||
"} // end namespace gw\n"
|
||||
"\n"
|
||||
"#endif\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void PrintTemplate(const char *base, const char *typeNameList, const char *retType, const char *objType, const char *isConst, const char *newExpr, const char *objExpr, const char *argList1, const char *argList2, const char *wrapName)
|
||||
{
|
||||
printf("template <%s%s>\n", base, typeNameList);
|
||||
printf("struct %s<%s (%s*)(%s)%s> {\n", wrapName, retType, objType, argList1, isConst);
|
||||
printf(" template <%s (%s*fp)(%s)%s>\n", retType, objType, argList1, isConst);
|
||||
printf(" static void f(AS_NAMESPACE_QUALIFIER asIScriptGeneric * gen) {\n");
|
||||
printf(" %s((%sfp)(%s));\n", newExpr, objExpr, argList2);
|
||||
printf(" }\n");
|
||||
printf("};\n");
|
||||
}
|
||||
|
||||
void PrintConstructor(const char *comma, const char *typeNameList, const char *typeList, const char *argList)
|
||||
{
|
||||
printf("template <typename T%s%s>\n", comma, typeNameList);
|
||||
printf("struct Constructor <T (%s)> {\n", typeList);
|
||||
printf(" static void f(AS_NAMESPACE_QUALIFIER asIScriptGeneric * gen) {\n");
|
||||
printf(" new (gen->GetObject()) T(%s);\n", argList);
|
||||
printf(" }\n");
|
||||
printf("};\n");
|
||||
}
|
||||
20
add_on/autowrapper/generator/generator.sln
Normal file
20
add_on/autowrapper/generator/generator.sln
Normal file
@@ -0,0 +1,20 @@
|
||||
|
||||
Microsoft Visual Studio Solution File, Format Version 10.00
|
||||
# Visual C++ Express 2008
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "generator", "generator.vcproj", "{086A2F1A-01B1-4EB3-A8FA-0926FF10E953}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Win32 = Debug|Win32
|
||||
Release|Win32 = Release|Win32
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{086A2F1A-01B1-4EB3-A8FA-0926FF10E953}.Debug|Win32.ActiveCfg = Debug|Win32
|
||||
{086A2F1A-01B1-4EB3-A8FA-0926FF10E953}.Debug|Win32.Build.0 = Debug|Win32
|
||||
{086A2F1A-01B1-4EB3-A8FA-0926FF10E953}.Release|Win32.ActiveCfg = Release|Win32
|
||||
{086A2F1A-01B1-4EB3-A8FA-0926FF10E953}.Release|Win32.Build.0 = Release|Win32
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
236
add_on/autowrapper/generator/generator.vcproj
Normal file
236
add_on/autowrapper/generator/generator.vcproj
Normal file
@@ -0,0 +1,236 @@
|
||||
<?xml version="1.0" encoding="Windows-1252"?>
|
||||
<VisualStudioProject
|
||||
ProjectType="Visual C++"
|
||||
Version="9,00"
|
||||
Name="generator"
|
||||
ProjectGUID="{086A2F1A-01B1-4EB3-A8FA-0926FF10E953}"
|
||||
TargetFrameworkVersion="0"
|
||||
>
|
||||
<Platforms>
|
||||
<Platform
|
||||
Name="Win32"
|
||||
/>
|
||||
</Platforms>
|
||||
<ToolFiles>
|
||||
</ToolFiles>
|
||||
<Configurations>
|
||||
<Configuration
|
||||
Name="Debug|Win32"
|
||||
OutputDirectory=".\Debug"
|
||||
IntermediateDirectory=".\Debug"
|
||||
ConfigurationType="1"
|
||||
InheritedPropertySheets="$(VCInstallDir)VCProjectDefaults\UpgradeFromVC60.vsprops"
|
||||
UseOfMFC="0"
|
||||
ATLMinimizesCRunTimeLibraryUsage="false"
|
||||
CharacterSet="2"
|
||||
>
|
||||
<Tool
|
||||
Name="VCPreBuildEventTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCCustomBuildTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCXMLDataGeneratorTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCWebServiceProxyGeneratorTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCMIDLTool"
|
||||
TypeLibraryName=".\Debug/generator.tlb"
|
||||
HeaderFileName=""
|
||||
/>
|
||||
<Tool
|
||||
Name="VCCLCompilerTool"
|
||||
Optimization="0"
|
||||
PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE"
|
||||
MinimalRebuild="true"
|
||||
BasicRuntimeChecks="3"
|
||||
RuntimeLibrary="1"
|
||||
PrecompiledHeaderFile=".\Debug/generator.pch"
|
||||
AssemblerListingLocation=".\Debug/"
|
||||
ObjectFile=".\Debug/"
|
||||
ProgramDataBaseFileName=".\Debug/"
|
||||
WarningLevel="3"
|
||||
SuppressStartupBanner="true"
|
||||
DebugInformationFormat="4"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCManagedResourceCompilerTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCResourceCompilerTool"
|
||||
PreprocessorDefinitions="_DEBUG"
|
||||
Culture="1033"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCPreLinkEventTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCLinkerTool"
|
||||
OutputFile=".\Debug/generator.exe"
|
||||
LinkIncremental="2"
|
||||
SuppressStartupBanner="true"
|
||||
GenerateDebugInformation="true"
|
||||
ProgramDatabaseFile=".\Debug/generator.pdb"
|
||||
SubSystem="1"
|
||||
RandomizedBaseAddress="1"
|
||||
DataExecutionPrevention="0"
|
||||
TargetMachine="1"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCALinkTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCManifestTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCXDCMakeTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCBscMakeTool"
|
||||
SuppressStartupBanner="true"
|
||||
OutputFile=".\Debug/generator.bsc"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCFxCopTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCAppVerifierTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCPostBuildEventTool"
|
||||
/>
|
||||
</Configuration>
|
||||
<Configuration
|
||||
Name="Release|Win32"
|
||||
OutputDirectory=".\Release"
|
||||
IntermediateDirectory=".\Release"
|
||||
ConfigurationType="1"
|
||||
InheritedPropertySheets="$(VCInstallDir)VCProjectDefaults\UpgradeFromVC60.vsprops"
|
||||
UseOfMFC="0"
|
||||
ATLMinimizesCRunTimeLibraryUsage="false"
|
||||
CharacterSet="2"
|
||||
>
|
||||
<Tool
|
||||
Name="VCPreBuildEventTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCCustomBuildTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCXMLDataGeneratorTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCWebServiceProxyGeneratorTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCMIDLTool"
|
||||
TypeLibraryName=".\Release/generator.tlb"
|
||||
HeaderFileName=""
|
||||
/>
|
||||
<Tool
|
||||
Name="VCCLCompilerTool"
|
||||
Optimization="2"
|
||||
InlineFunctionExpansion="1"
|
||||
PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE"
|
||||
StringPooling="true"
|
||||
RuntimeLibrary="0"
|
||||
EnableFunctionLevelLinking="true"
|
||||
PrecompiledHeaderFile=".\Release/generator.pch"
|
||||
AssemblerListingLocation=".\Release/"
|
||||
ObjectFile=".\Release/"
|
||||
ProgramDataBaseFileName=".\Release/"
|
||||
WarningLevel="3"
|
||||
SuppressStartupBanner="true"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCManagedResourceCompilerTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCResourceCompilerTool"
|
||||
PreprocessorDefinitions="NDEBUG"
|
||||
Culture="1033"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCPreLinkEventTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCLinkerTool"
|
||||
OutputFile=".\Release/generator.exe"
|
||||
LinkIncremental="1"
|
||||
SuppressStartupBanner="true"
|
||||
ProgramDatabaseFile=".\Release/generator.pdb"
|
||||
SubSystem="1"
|
||||
RandomizedBaseAddress="1"
|
||||
DataExecutionPrevention="0"
|
||||
TargetMachine="1"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCALinkTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCManifestTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCXDCMakeTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCBscMakeTool"
|
||||
SuppressStartupBanner="true"
|
||||
OutputFile=".\Release/generator.bsc"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCFxCopTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCAppVerifierTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCPostBuildEventTool"
|
||||
/>
|
||||
</Configuration>
|
||||
</Configurations>
|
||||
<References>
|
||||
</References>
|
||||
<Files>
|
||||
<Filter
|
||||
Name="Source Files"
|
||||
Filter="cpp;c;cxx;rc;def;r;odl;idl;hpj;bat"
|
||||
>
|
||||
<File
|
||||
RelativePath="generateheader.cpp"
|
||||
>
|
||||
<FileConfiguration
|
||||
Name="Debug|Win32"
|
||||
>
|
||||
<Tool
|
||||
Name="VCCLCompilerTool"
|
||||
PreprocessorDefinitions=""
|
||||
/>
|
||||
</FileConfiguration>
|
||||
<FileConfiguration
|
||||
Name="Release|Win32"
|
||||
>
|
||||
<Tool
|
||||
Name="VCCLCompilerTool"
|
||||
PreprocessorDefinitions=""
|
||||
/>
|
||||
</FileConfiguration>
|
||||
</File>
|
||||
</Filter>
|
||||
<Filter
|
||||
Name="Header Files"
|
||||
Filter="h;hpp;hxx;hm;inl"
|
||||
>
|
||||
</Filter>
|
||||
<Filter
|
||||
Name="Resource Files"
|
||||
Filter="ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe"
|
||||
>
|
||||
</Filter>
|
||||
</Files>
|
||||
<Globals>
|
||||
</Globals>
|
||||
</VisualStudioProject>
|
||||
401
add_on/contextmgr/contextmgr.cpp
Normal file
401
add_on/contextmgr/contextmgr.cpp
Normal file
@@ -0,0 +1,401 @@
|
||||
#include <assert.h>
|
||||
#include <string>
|
||||
|
||||
#include "contextmgr.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
// TODO: Should have a pool of free asIScriptContext so that new contexts
|
||||
// won't be allocated every time. The application must not keep
|
||||
// its own references, instead it must tell the context manager
|
||||
// that it is using the context. Otherwise the context manager may
|
||||
// think it can reuse the context too early.
|
||||
|
||||
// TODO: Need to have a callback for when scripts finishes, so that the
|
||||
// application can receive return values.
|
||||
|
||||
BEGIN_AS_NAMESPACE
|
||||
|
||||
// The id for the context manager user data.
|
||||
// The add-ons have reserved the numbers 1000
|
||||
// through 1999 for this purpose, so we should be fine.
|
||||
const asPWORD CONTEXT_MGR = 1002;
|
||||
|
||||
struct SContextInfo
|
||||
{
|
||||
asUINT sleepUntil;
|
||||
vector<asIScriptContext*> coRoutines;
|
||||
asUINT currentCoRoutine;
|
||||
asIScriptContext * keepCtxAfterExecution;
|
||||
};
|
||||
|
||||
static void ScriptSleep(asUINT milliSeconds)
|
||||
{
|
||||
// Get a pointer to the context that is currently being executed
|
||||
asIScriptContext *ctx = asGetActiveContext();
|
||||
if( ctx )
|
||||
{
|
||||
// Get the context manager from the user data
|
||||
CContextMgr *ctxMgr = reinterpret_cast<CContextMgr*>(ctx->GetUserData(CONTEXT_MGR));
|
||||
if( ctxMgr )
|
||||
{
|
||||
// Suspend its execution. The VM will continue until the current
|
||||
// statement is finished and then return from the Execute() method
|
||||
ctx->Suspend();
|
||||
|
||||
// Tell the context manager when the context is to continue execution
|
||||
ctxMgr->SetSleeping(ctx, milliSeconds);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void ScriptYield()
|
||||
{
|
||||
// Get a pointer to the context that is currently being executed
|
||||
asIScriptContext *ctx = asGetActiveContext();
|
||||
if( ctx )
|
||||
{
|
||||
// Get the context manager from the user data
|
||||
CContextMgr *ctxMgr = reinterpret_cast<CContextMgr*>(ctx->GetUserData(CONTEXT_MGR));
|
||||
if( ctxMgr )
|
||||
{
|
||||
// Let the context manager know that it should run the next co-routine
|
||||
ctxMgr->NextCoRoutine();
|
||||
|
||||
// The current context must be suspended so that VM will return from
|
||||
// the Execute() method where the context manager will continue.
|
||||
ctx->Suspend();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ScriptCreateCoRoutine(asIScriptFunction *func, CScriptDictionary *arg)
|
||||
{
|
||||
if( func == 0 )
|
||||
return;
|
||||
|
||||
asIScriptContext *ctx = asGetActiveContext();
|
||||
if( ctx )
|
||||
{
|
||||
// Get the context manager from the user data
|
||||
CContextMgr *ctxMgr = reinterpret_cast<CContextMgr*>(ctx->GetUserData(CONTEXT_MGR));
|
||||
if( ctxMgr )
|
||||
{
|
||||
// Create a new context for the co-routine
|
||||
asIScriptContext *coctx = ctxMgr->AddContextForCoRoutine(ctx, func);
|
||||
|
||||
// Pass the argument to the context
|
||||
coctx->SetArgObject(0, arg);
|
||||
|
||||
// The context manager will call Execute() on the context when it is time
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef AS_MAX_PORTABILITY
|
||||
void ScriptYield_generic(asIScriptGeneric *)
|
||||
{
|
||||
ScriptYield();
|
||||
}
|
||||
|
||||
void ScriptCreateCoRoutine_generic(asIScriptGeneric *gen)
|
||||
{
|
||||
asIScriptFunction *func = reinterpret_cast<asIScriptFunction*>(gen->GetArgAddress(0));
|
||||
CScriptDictionary *dict = reinterpret_cast<CScriptDictionary*>(gen->GetArgAddress(1));
|
||||
ScriptCreateCoRoutine(func, dict);
|
||||
}
|
||||
#endif
|
||||
|
||||
CContextMgr::CContextMgr()
|
||||
{
|
||||
m_getTimeFunc = 0;
|
||||
m_currentThread = 0;
|
||||
|
||||
m_numExecutions = 0;
|
||||
m_numGCObjectsCreated = 0;
|
||||
m_numGCObjectsDestroyed = 0;
|
||||
}
|
||||
|
||||
CContextMgr::~CContextMgr()
|
||||
{
|
||||
asUINT n;
|
||||
|
||||
// Free the memory
|
||||
for( n = 0; n < m_threads.size(); n++ )
|
||||
{
|
||||
if( m_threads[n] )
|
||||
{
|
||||
for( asUINT c = 0; c < m_threads[n]->coRoutines.size(); c++ )
|
||||
{
|
||||
asIScriptContext *ctx = m_threads[n]->coRoutines[c];
|
||||
if( ctx )
|
||||
{
|
||||
// Return the context to the engine (and possible context pool configured in it)
|
||||
ctx->GetEngine()->ReturnContext(ctx);
|
||||
}
|
||||
}
|
||||
|
||||
delete m_threads[n];
|
||||
}
|
||||
}
|
||||
|
||||
for( n = 0; n < m_freeThreads.size(); n++ )
|
||||
{
|
||||
if( m_freeThreads[n] )
|
||||
{
|
||||
assert( m_freeThreads[n]->coRoutines.size() == 0 );
|
||||
|
||||
delete m_freeThreads[n];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int CContextMgr::ExecuteScripts()
|
||||
{
|
||||
// TODO: Should have an optional time out for this function. If not all scripts executed before the
|
||||
// time out, the next time the function is called the loop should continue
|
||||
// where it left off.
|
||||
|
||||
// TODO: There should be a time out per thread as well. If a thread executes for too
|
||||
// long, it should be aborted. A group of co-routines count as a single thread.
|
||||
|
||||
// Check if the system time is higher than the time set for the contexts
|
||||
asUINT time = m_getTimeFunc ? m_getTimeFunc() : asUINT(-1);
|
||||
for( m_currentThread = 0; m_currentThread < m_threads.size(); m_currentThread++ )
|
||||
{
|
||||
SContextInfo *thread = m_threads[m_currentThread];
|
||||
if( thread->sleepUntil < time )
|
||||
{
|
||||
int currentCoRoutine = thread->currentCoRoutine;
|
||||
|
||||
// Gather some statistics from the GC
|
||||
asIScriptEngine *engine = thread->coRoutines[currentCoRoutine]->GetEngine();
|
||||
asUINT gcSize1, gcSize2, gcSize3;
|
||||
engine->GetGCStatistics(&gcSize1);
|
||||
|
||||
// Execute the script for this thread and co-routine
|
||||
int r = thread->coRoutines[currentCoRoutine]->Execute();
|
||||
|
||||
// Determine how many new objects were created in the GC
|
||||
engine->GetGCStatistics(&gcSize2);
|
||||
m_numGCObjectsCreated += gcSize2 - gcSize1;
|
||||
m_numExecutions++;
|
||||
|
||||
if( r != asEXECUTION_SUSPENDED )
|
||||
{
|
||||
// The context has terminated execution (for one reason or other)
|
||||
// Unless the application has requested to keep the context we'll return it to the pool now
|
||||
if( thread->keepCtxAfterExecution != thread->coRoutines[currentCoRoutine] )
|
||||
engine->ReturnContext(thread->coRoutines[currentCoRoutine]);
|
||||
thread->coRoutines[currentCoRoutine] = 0;
|
||||
|
||||
thread->coRoutines.erase(thread->coRoutines.begin() + thread->currentCoRoutine);
|
||||
if( thread->currentCoRoutine >= thread->coRoutines.size() )
|
||||
thread->currentCoRoutine = 0;
|
||||
|
||||
// If this was the last co-routine terminate the thread
|
||||
if( thread->coRoutines.size() == 0 )
|
||||
{
|
||||
m_freeThreads.push_back(thread);
|
||||
m_threads.erase(m_threads.begin() + m_currentThread);
|
||||
m_currentThread--;
|
||||
}
|
||||
}
|
||||
|
||||
// Destroy all known garbage if any new objects were created
|
||||
if( gcSize2 > gcSize1 )
|
||||
{
|
||||
engine->GarbageCollect(asGC_FULL_CYCLE | asGC_DESTROY_GARBAGE);
|
||||
|
||||
// Determine how many objects were destroyed
|
||||
engine->GetGCStatistics(&gcSize3);
|
||||
m_numGCObjectsDestroyed += gcSize3 - gcSize2;
|
||||
}
|
||||
|
||||
// TODO: If more objects are created per execution than destroyed on average
|
||||
// then it may be necessary to run more iterations of the detection of
|
||||
// cyclic references. At the startup of an application there is usually
|
||||
// a lot of objects created that will live on through out the application
|
||||
// so the average number of objects created per execution will be higher
|
||||
// than the number of destroyed objects in the beginning, but afterwards
|
||||
// it usually levels out to be more or less equal.
|
||||
|
||||
// Just run an incremental step for detecting cyclic references
|
||||
engine->GarbageCollect(asGC_ONE_STEP | asGC_DETECT_GARBAGE);
|
||||
}
|
||||
}
|
||||
|
||||
return int(m_threads.size());
|
||||
}
|
||||
|
||||
void CContextMgr::DoneWithContext(asIScriptContext *ctx)
|
||||
{
|
||||
ctx->GetEngine()->ReturnContext(ctx);
|
||||
}
|
||||
|
||||
void CContextMgr::NextCoRoutine()
|
||||
{
|
||||
m_threads[m_currentThread]->currentCoRoutine++;
|
||||
if( m_threads[m_currentThread]->currentCoRoutine >= m_threads[m_currentThread]->coRoutines.size() )
|
||||
m_threads[m_currentThread]->currentCoRoutine = 0;
|
||||
}
|
||||
|
||||
void CContextMgr::AbortAll()
|
||||
{
|
||||
// Abort all contexts and release them. The script engine will make
|
||||
// sure that all resources held by the scripts are properly released.
|
||||
|
||||
for( asUINT n = 0; n < m_threads.size(); n++ )
|
||||
{
|
||||
for( asUINT c = 0; c < m_threads[n]->coRoutines.size(); c++ )
|
||||
{
|
||||
asIScriptContext *ctx = m_threads[n]->coRoutines[c];
|
||||
if( ctx )
|
||||
{
|
||||
ctx->Abort();
|
||||
ctx->GetEngine()->ReturnContext(ctx);
|
||||
ctx = 0;
|
||||
}
|
||||
}
|
||||
m_threads[n]->coRoutines.resize(0);
|
||||
|
||||
m_freeThreads.push_back(m_threads[n]);
|
||||
}
|
||||
|
||||
m_threads.resize(0);
|
||||
|
||||
m_currentThread = 0;
|
||||
}
|
||||
|
||||
asIScriptContext *CContextMgr::AddContext(asIScriptEngine *engine, asIScriptFunction *func, bool keepCtxAfterExec)
|
||||
{
|
||||
// Use RequestContext instead of CreateContext so we can take
|
||||
// advantage of possible context pooling configured with the engine
|
||||
asIScriptContext *ctx = engine->RequestContext();
|
||||
if( ctx == 0 )
|
||||
return 0;
|
||||
|
||||
// Prepare it to execute the function
|
||||
int r = ctx->Prepare(func);
|
||||
if( r < 0 )
|
||||
{
|
||||
engine->ReturnContext(ctx);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Set the context manager as user data with the context so it
|
||||
// can be retrieved by the functions registered with the engine
|
||||
ctx->SetUserData(this, CONTEXT_MGR);
|
||||
|
||||
// Add the context to the list for execution
|
||||
SContextInfo *info = 0;
|
||||
if( m_freeThreads.size() > 0 )
|
||||
{
|
||||
info = *m_freeThreads.rbegin();
|
||||
m_freeThreads.pop_back();
|
||||
}
|
||||
else
|
||||
{
|
||||
info = new SContextInfo;
|
||||
}
|
||||
|
||||
info->coRoutines.push_back(ctx);
|
||||
info->currentCoRoutine = 0;
|
||||
info->sleepUntil = 0;
|
||||
info->keepCtxAfterExecution = keepCtxAfterExec ? ctx : 0;
|
||||
m_threads.push_back(info);
|
||||
|
||||
return ctx;
|
||||
}
|
||||
|
||||
asIScriptContext *CContextMgr::AddContextForCoRoutine(asIScriptContext *currCtx, asIScriptFunction *func)
|
||||
{
|
||||
asIScriptEngine *engine = currCtx->GetEngine();
|
||||
asIScriptContext *coctx = engine->RequestContext();
|
||||
if( coctx == 0 )
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Prepare the context
|
||||
int r = coctx->Prepare(func);
|
||||
if( r < 0 )
|
||||
{
|
||||
// Couldn't prepare the context
|
||||
engine->ReturnContext(coctx);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Set the context manager as user data with the context so it
|
||||
// can be retrieved by the functions registered with the engine
|
||||
coctx->SetUserData(this, CONTEXT_MGR);
|
||||
|
||||
// Find the current context thread info
|
||||
// TODO: Start with the current thread so that we can find the group faster
|
||||
for( asUINT n = 0; n < m_threads.size(); n++ )
|
||||
{
|
||||
if( m_threads[n]->coRoutines[m_threads[n]->currentCoRoutine] == currCtx )
|
||||
{
|
||||
// Add the coRoutine to the list
|
||||
m_threads[n]->coRoutines.push_back(coctx);
|
||||
}
|
||||
}
|
||||
|
||||
return coctx;
|
||||
}
|
||||
|
||||
void CContextMgr::SetSleeping(asIScriptContext *ctx, asUINT milliSeconds)
|
||||
{
|
||||
assert( m_getTimeFunc != 0 );
|
||||
|
||||
// Find the context and update the timeStamp
|
||||
// for when the context is to be continued
|
||||
|
||||
// TODO: Start with the current thread
|
||||
|
||||
for( asUINT n = 0; n < m_threads.size(); n++ )
|
||||
{
|
||||
if( m_threads[n]->coRoutines[m_threads[n]->currentCoRoutine] == ctx )
|
||||
{
|
||||
m_threads[n]->sleepUntil = (m_getTimeFunc ? m_getTimeFunc() : 0) + milliSeconds;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CContextMgr::RegisterThreadSupport(asIScriptEngine *engine)
|
||||
{
|
||||
int r;
|
||||
|
||||
// Must set the get time callback function for this to work
|
||||
assert( m_getTimeFunc != 0 );
|
||||
|
||||
// Register the sleep function
|
||||
r = engine->RegisterGlobalFunction("void sleep(uint)", asFUNCTION(ScriptSleep), asCALL_CDECL); assert( r >= 0 );
|
||||
|
||||
// TODO: Add support for spawning new threads, waiting for signals, etc
|
||||
}
|
||||
|
||||
void CContextMgr::RegisterCoRoutineSupport(asIScriptEngine *engine)
|
||||
{
|
||||
int r;
|
||||
|
||||
// The dictionary add-on must have been registered already
|
||||
assert( engine->GetTypeInfoByDecl("dictionary") );
|
||||
|
||||
#ifndef AS_MAX_PORTABILITY
|
||||
r = engine->RegisterGlobalFunction("void yield()", asFUNCTION(ScriptYield), asCALL_CDECL); assert( r >= 0 );
|
||||
r = engine->RegisterFuncdef("void coroutine(dictionary@)");
|
||||
r = engine->RegisterGlobalFunction("void createCoRoutine(coroutine @+, dictionary @+)", asFUNCTION(ScriptCreateCoRoutine), asCALL_CDECL); assert( r >= 0 );
|
||||
#else
|
||||
r = engine->RegisterGlobalFunction("void yield()", asFUNCTION(ScriptYield_generic), asCALL_GENERIC); assert( r >= 0 );
|
||||
r = engine->RegisterFuncdef("void coroutine(dictionary@)");
|
||||
r = engine->RegisterGlobalFunction("void createCoRoutine(coroutine @+, dictionary @+)", asFUNCTION(ScriptCreateCoRoutine_generic), asCALL_GENERIC); assert( r >= 0 );
|
||||
#endif
|
||||
}
|
||||
|
||||
void CContextMgr::SetGetTimeCallback(TIMEFUNC_t func)
|
||||
{
|
||||
m_getTimeFunc = func;
|
||||
}
|
||||
|
||||
END_AS_NAMESPACE
|
||||
99
add_on/contextmgr/contextmgr.h
Normal file
99
add_on/contextmgr/contextmgr.h
Normal file
@@ -0,0 +1,99 @@
|
||||
#ifndef CONTEXTMGR_H
|
||||
#define CONTEXTMGR_H
|
||||
|
||||
// The context manager simplifies the management of multiple concurrent scripts
|
||||
|
||||
// More than one context manager can be used, if you wish to control different
|
||||
// groups of scripts separately, e.g. game object scripts, and GUI scripts.
|
||||
|
||||
// OBSERVATION: This class is currently not thread safe.
|
||||
|
||||
#ifndef ANGELSCRIPT_H
|
||||
// Avoid having to inform include path if header is already include before
|
||||
#include <angelscript.h>
|
||||
#endif
|
||||
|
||||
#include <vector>
|
||||
|
||||
BEGIN_AS_NAMESPACE
|
||||
|
||||
class CScriptDictionary;
|
||||
|
||||
// The internal structure for holding contexts
|
||||
struct SContextInfo;
|
||||
|
||||
// The signature of the get time callback function
|
||||
typedef asUINT (*TIMEFUNC_t)();
|
||||
|
||||
class CContextMgr
|
||||
{
|
||||
public:
|
||||
CContextMgr();
|
||||
~CContextMgr();
|
||||
|
||||
// Set the function that the manager will use to obtain the time in milliseconds
|
||||
void SetGetTimeCallback(TIMEFUNC_t func);
|
||||
|
||||
// Registers the following:
|
||||
//
|
||||
// void sleep(uint milliseconds)
|
||||
//
|
||||
// The application must set the get time callback for this to work
|
||||
void RegisterThreadSupport(asIScriptEngine *engine);
|
||||
|
||||
// Registers the following:
|
||||
//
|
||||
// funcdef void coroutine(dictionary@)
|
||||
// void createCoRoutine(coroutine @func, dictionary @args)
|
||||
// void yield()
|
||||
void RegisterCoRoutineSupport(asIScriptEngine *engine);
|
||||
|
||||
// Create a new context, prepare it with the function id, then return
|
||||
// it so that the application can pass the argument values. The context
|
||||
// will be released by the manager after the execution has completed.
|
||||
// Set keepCtxAfterExecution to true if the application needs to retrieve
|
||||
// information from the context after it the script has finished.
|
||||
asIScriptContext *AddContext(asIScriptEngine *engine, asIScriptFunction *func, bool keepCtxAfterExecution = false);
|
||||
|
||||
// If the context was kept after the execution, this method must be
|
||||
// called when the application is done with the context so it can be
|
||||
// returned to the pool for reuse.
|
||||
void DoneWithContext(asIScriptContext *ctx);
|
||||
|
||||
// Create a new context, prepare it with the function id, then return
|
||||
// it so that the application can pass the argument values. The context
|
||||
// will be added as a co-routine in the same thread as the currCtx.
|
||||
asIScriptContext *AddContextForCoRoutine(asIScriptContext *currCtx, asIScriptFunction *func);
|
||||
|
||||
// Execute each script that is not currently sleeping. The function returns after
|
||||
// each script has been executed once. The application should call this function
|
||||
// for each iteration of the message pump, or game loop, or whatever.
|
||||
// Returns the number of scripts still in execution.
|
||||
int ExecuteScripts();
|
||||
|
||||
// Put a script to sleep for a while
|
||||
void SetSleeping(asIScriptContext *ctx, asUINT milliSeconds);
|
||||
|
||||
// Switch the execution to the next co-routine in the group.
|
||||
// Returns true if the switch was successful.
|
||||
void NextCoRoutine();
|
||||
|
||||
// Abort all scripts
|
||||
void AbortAll();
|
||||
|
||||
protected:
|
||||
std::vector<SContextInfo*> m_threads;
|
||||
std::vector<SContextInfo*> m_freeThreads;
|
||||
asUINT m_currentThread;
|
||||
TIMEFUNC_t m_getTimeFunc;
|
||||
|
||||
// Statistics for Garbage Collection
|
||||
asUINT m_numExecutions;
|
||||
asUINT m_numGCObjectsCreated;
|
||||
asUINT m_numGCObjectsDestroyed;
|
||||
};
|
||||
|
||||
|
||||
END_AS_NAMESPACE
|
||||
|
||||
#endif
|
||||
270
add_on/datetime/datetime.cpp
Normal file
270
add_on/datetime/datetime.cpp
Normal file
@@ -0,0 +1,270 @@
|
||||
#include "datetime.h"
|
||||
#include "../autowrapper/aswrappedcall.h"
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
#include <new>
|
||||
|
||||
using namespace std;
|
||||
using namespace std::chrono;
|
||||
|
||||
BEGIN_AS_NAMESPACE
|
||||
|
||||
// TODO: Allow setting the timezone to use
|
||||
|
||||
static tm time_point_to_tm(const std::chrono::time_point<std::chrono::system_clock> &tp)
|
||||
{
|
||||
time_t t = system_clock::to_time_t(tp);
|
||||
tm local;
|
||||
|
||||
// Use the universal timezone
|
||||
#ifdef _MSC_VER
|
||||
gmtime_s(&local, &t);
|
||||
#else
|
||||
local = *gmtime(&t);
|
||||
#endif
|
||||
return local;
|
||||
}
|
||||
|
||||
// Returns true if successful. Doesn't modify tp if not successful
|
||||
static bool tm_to_time_point(const tm &_tm, std::chrono::time_point<std::chrono::system_clock> &tp)
|
||||
{
|
||||
tm localTm = _tm;
|
||||
|
||||
// Do not rely on timezone, as it is not portable
|
||||
// ref: https://stackoverflow.com/questions/38298261/why-there-is-no-inverse-function-for-gmtime-in-libc
|
||||
// ref: https://stackoverflow.com/questions/8558919/mktime-and-tm-isdst
|
||||
localTm.tm_isdst = -1; // Always use current settings, so mktime doesn't modify the time for daylight savings
|
||||
time_t t = mktime(&localTm);
|
||||
if (t == -1)
|
||||
return false;
|
||||
|
||||
// Adjust the time_t since epoch with the difference of the local timezone to the universal timezone
|
||||
t += (mktime(localtime(&t)) - mktime(gmtime(&t)));
|
||||
|
||||
// Verify if the members were modified, indicating an out-of-range value in input
|
||||
if (localTm.tm_year != _tm.tm_year ||
|
||||
localTm.tm_mon != _tm.tm_mon ||
|
||||
localTm.tm_mday != _tm.tm_mday ||
|
||||
localTm.tm_hour != _tm.tm_hour ||
|
||||
localTm.tm_min != _tm.tm_min ||
|
||||
localTm.tm_sec != _tm.tm_sec)
|
||||
return false;
|
||||
|
||||
tp = system_clock::from_time_t(t);
|
||||
return true;
|
||||
}
|
||||
|
||||
CDateTime::CDateTime() : tp(std::chrono::system_clock::now())
|
||||
{
|
||||
}
|
||||
|
||||
CDateTime::CDateTime(const CDateTime &o) : tp(o.tp)
|
||||
{
|
||||
}
|
||||
|
||||
CDateTime &CDateTime::operator=(const CDateTime &o)
|
||||
{
|
||||
tp = o.tp;
|
||||
return *this;
|
||||
}
|
||||
|
||||
asUINT CDateTime::getYear() const
|
||||
{
|
||||
tm local = time_point_to_tm(tp);
|
||||
return local.tm_year + 1900;
|
||||
}
|
||||
|
||||
asUINT CDateTime::getMonth() const
|
||||
{
|
||||
tm local = time_point_to_tm(tp);
|
||||
return local.tm_mon + 1;
|
||||
}
|
||||
|
||||
asUINT CDateTime::getDay() const
|
||||
{
|
||||
tm local = time_point_to_tm(tp);
|
||||
return local.tm_mday;
|
||||
}
|
||||
|
||||
asUINT CDateTime::getHour() const
|
||||
{
|
||||
tm local = time_point_to_tm(tp);
|
||||
return local.tm_hour;
|
||||
}
|
||||
|
||||
asUINT CDateTime::getMinute() const
|
||||
{
|
||||
tm local = time_point_to_tm(tp);
|
||||
return local.tm_min;
|
||||
}
|
||||
|
||||
asUINT CDateTime::getSecond() const
|
||||
{
|
||||
tm local = time_point_to_tm(tp);
|
||||
return local.tm_sec;
|
||||
}
|
||||
|
||||
bool CDateTime::setDate(asUINT year, asUINT month, asUINT day)
|
||||
{
|
||||
tm local = time_point_to_tm(tp);
|
||||
local.tm_year = int(year) - 1900;
|
||||
local.tm_mon = month - 1;
|
||||
local.tm_mday = day;
|
||||
|
||||
return tm_to_time_point(local, tp);
|
||||
}
|
||||
|
||||
bool CDateTime::setTime(asUINT hour, asUINT minute, asUINT second)
|
||||
{
|
||||
tm local = time_point_to_tm(tp);
|
||||
local.tm_hour = hour;
|
||||
local.tm_min = minute;
|
||||
local.tm_sec = second;
|
||||
|
||||
return tm_to_time_point(local, tp);
|
||||
}
|
||||
|
||||
CDateTime::CDateTime(asUINT year, asUINT month, asUINT day, asUINT hour, asUINT minute, asUINT second)
|
||||
{
|
||||
tp = std::chrono::system_clock::now();
|
||||
setDate(year, month, day);
|
||||
setTime(hour, minute, second);
|
||||
}
|
||||
|
||||
asINT64 CDateTime::operator-(const CDateTime &dt) const
|
||||
{
|
||||
return (tp - dt.tp).count() / std::chrono::system_clock::period::den * std::chrono::system_clock::period::num;
|
||||
}
|
||||
|
||||
CDateTime CDateTime::operator+(asINT64 seconds) const
|
||||
{
|
||||
CDateTime dt(*this);
|
||||
dt.tp += std::chrono::system_clock::duration(seconds * std::chrono::system_clock::period::den / std::chrono::system_clock::period::num);
|
||||
return dt;
|
||||
}
|
||||
|
||||
CDateTime &CDateTime::operator+=(asINT64 seconds)
|
||||
{
|
||||
tp += std::chrono::system_clock::duration(seconds * std::chrono::system_clock::period::den / std::chrono::system_clock::period::num);
|
||||
return *this;
|
||||
}
|
||||
|
||||
CDateTime operator+(asINT64 seconds, const CDateTime &other)
|
||||
{
|
||||
return other + seconds;
|
||||
}
|
||||
|
||||
CDateTime CDateTime::operator-(asINT64 seconds) const
|
||||
{
|
||||
return *this + -seconds;
|
||||
}
|
||||
|
||||
CDateTime &CDateTime::operator-=(asINT64 seconds)
|
||||
{
|
||||
return *this += -seconds;
|
||||
}
|
||||
|
||||
CDateTime operator-(asINT64 seconds, const CDateTime &other)
|
||||
{
|
||||
return other + -seconds;
|
||||
}
|
||||
|
||||
bool CDateTime::operator==(const CDateTime &other) const
|
||||
{
|
||||
return tp == other.tp;
|
||||
}
|
||||
|
||||
bool CDateTime::operator<(const CDateTime &other) const
|
||||
{
|
||||
return tp < other.tp;
|
||||
}
|
||||
|
||||
static int opCmp(const CDateTime &a, const CDateTime &b)
|
||||
{
|
||||
if (a < b) return -1;
|
||||
if (a == b) return 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void Construct(CDateTime *mem)
|
||||
{
|
||||
new(mem) CDateTime();
|
||||
}
|
||||
|
||||
static void ConstructCopy(CDateTime *mem, const CDateTime &o)
|
||||
{
|
||||
new(mem) CDateTime(o);
|
||||
}
|
||||
|
||||
static void ConstructSet(CDateTime *mem, asUINT year, asUINT month, asUINT day, asUINT hour, asUINT minute, asUINT second)
|
||||
{
|
||||
new(mem) CDateTime(year, month, day, hour, minute, second);
|
||||
}
|
||||
|
||||
static void ConstructSet_Generic(asIScriptGeneric *gen)
|
||||
{
|
||||
CDateTime *date = (CDateTime*)gen->GetObject();
|
||||
asUINT year = *(asUINT*)gen->GetAddressOfArg(0);
|
||||
asUINT month = *(asUINT*)gen->GetAddressOfArg(1);
|
||||
asUINT day = *(asUINT*)gen->GetAddressOfArg(2);
|
||||
asUINT hour = *(asUINT*)gen->GetAddressOfArg(3);
|
||||
asUINT minute = *(asUINT*)gen->GetAddressOfArg(4);
|
||||
asUINT second = *(asUINT*)gen->GetAddressOfArg(5);
|
||||
ConstructSet(date, year, month, day, hour, minute, second);
|
||||
}
|
||||
|
||||
void RegisterScriptDateTime(asIScriptEngine *engine)
|
||||
{
|
||||
int r = engine->RegisterObjectType("datetime", sizeof(CDateTime), asOBJ_VALUE | asOBJ_POD | asGetTypeTraits<CDateTime>()); assert(r >= 0);
|
||||
|
||||
if(strstr(asGetLibraryOptions(), "AS_MAX_PORTABILITY")==0)
|
||||
{
|
||||
r = engine->RegisterObjectBehaviour("datetime", asBEHAVE_CONSTRUCT, "void f()", asFUNCTION(Construct), asCALL_CDECL_OBJLAST); assert(r >= 0);
|
||||
r = engine->RegisterObjectBehaviour("datetime", asBEHAVE_CONSTRUCT, "void f(const datetime &in)", asFUNCTION(ConstructCopy), asCALL_CDECL_OBJFIRST); assert(r >= 0);
|
||||
r = engine->RegisterObjectBehaviour("datetime", asBEHAVE_CONSTRUCT, "void f(uint, uint, uint, uint = 0, uint = 0, uint = 0)", asFUNCTION(ConstructSet), asCALL_CDECL_OBJFIRST); assert(r >= 0);
|
||||
r = engine->RegisterObjectMethod("datetime", "datetime &opAssign(const datetime &in)", asMETHOD(CDateTime, operator=), asCALL_THISCALL); assert(r >= 0);
|
||||
r = engine->RegisterObjectMethod("datetime", "uint get_year() const property", asMETHOD(CDateTime, getYear), asCALL_THISCALL); assert(r >= 0);
|
||||
r = engine->RegisterObjectMethod("datetime", "uint get_month() const property", asMETHOD(CDateTime, getMonth), asCALL_THISCALL); assert(r >= 0);
|
||||
r = engine->RegisterObjectMethod("datetime", "uint get_day() const property", asMETHOD(CDateTime, getDay), asCALL_THISCALL); assert(r >= 0);
|
||||
r = engine->RegisterObjectMethod("datetime", "uint get_hour() const property", asMETHOD(CDateTime, getHour), asCALL_THISCALL); assert(r >= 0);
|
||||
r = engine->RegisterObjectMethod("datetime", "uint get_minute() const property", asMETHOD(CDateTime, getMinute), asCALL_THISCALL); assert(r >= 0);
|
||||
r = engine->RegisterObjectMethod("datetime", "uint get_second() const property", asMETHOD(CDateTime, getSecond), asCALL_THISCALL); assert(r >= 0);
|
||||
r = engine->RegisterObjectMethod("datetime", "bool setDate(uint year, uint month, uint day)", asMETHOD(CDateTime, setDate), asCALL_THISCALL); assert(r >= 0);
|
||||
r = engine->RegisterObjectMethod("datetime", "bool setTime(uint hour, uint minute, uint second)", asMETHOD(CDateTime, setTime), asCALL_THISCALL); assert(r >= 0);
|
||||
r = engine->RegisterObjectMethod("datetime", "int64 opSub(const datetime &in) const", asMETHODPR(CDateTime, operator-, (const CDateTime &other) const, asINT64), asCALL_THISCALL); assert(r >= 0);
|
||||
r = engine->RegisterObjectMethod("datetime", "datetime opAdd(int64 seconds) const", asMETHOD(CDateTime, operator+), asCALL_THISCALL); assert(r >= 0);
|
||||
r = engine->RegisterObjectMethod("datetime", "datetime opAdd_r(int64 seconds) const", asFUNCTIONPR(operator+, (asINT64 seconds, const CDateTime &other), CDateTime), asCALL_CDECL_OBJLAST); assert(r >= 0);
|
||||
r = engine->RegisterObjectMethod("datetime", "datetime &opAddAssign(int64 seconds)", asMETHOD(CDateTime, operator+=), asCALL_THISCALL); assert(r >= 0);
|
||||
r = engine->RegisterObjectMethod("datetime", "datetime opSub(int64 seconds) const", asMETHODPR(CDateTime, operator-, (asINT64) const, CDateTime), asCALL_THISCALL); assert(r >= 0);
|
||||
r = engine->RegisterObjectMethod("datetime", "datetime opSub_r(int64 seconds) const", asFUNCTIONPR(operator-, (asINT64 seconds, const CDateTime &other), CDateTime), asCALL_CDECL_OBJLAST); assert(r >= 0);
|
||||
r = engine->RegisterObjectMethod("datetime", "datetime &opSubAssign(int64 seconds)", asMETHOD(CDateTime, operator-=), asCALL_THISCALL); assert(r >= 0);
|
||||
r = engine->RegisterObjectMethod("datetime", "bool opEquals(const datetime &in) const", asMETHODPR(CDateTime, operator==, (const CDateTime &other) const, bool), asCALL_THISCALL); assert(r >= 0);
|
||||
r = engine->RegisterObjectMethod("datetime", "int opCmp(const datetime &in) const", asFUNCTION(opCmp), asCALL_CDECL_OBJFIRST); assert(r >= 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
r = engine->RegisterObjectBehaviour("datetime", asBEHAVE_CONSTRUCT, "void f()", WRAP_OBJ_LAST(Construct), asCALL_GENERIC); assert(r >= 0);
|
||||
r = engine->RegisterObjectBehaviour("datetime", asBEHAVE_CONSTRUCT, "void f(const datetime &in)", WRAP_OBJ_FIRST(ConstructCopy), asCALL_GENERIC); assert(r >= 0);
|
||||
r = engine->RegisterObjectBehaviour("datetime", asBEHAVE_CONSTRUCT, "void f(uint, uint, uint, uint = 0, uint = 0, uint = 0)", asFUNCTION(ConstructSet_Generic), asCALL_GENERIC); assert(r >= 0);
|
||||
r = engine->RegisterObjectMethod("datetime", "datetime &opAssign(const datetime &in)", WRAP_MFN(CDateTime, operator=), asCALL_GENERIC); assert(r >= 0);
|
||||
r = engine->RegisterObjectMethod("datetime", "uint get_year() const property", WRAP_MFN(CDateTime, getYear), asCALL_GENERIC); assert(r >= 0);
|
||||
r = engine->RegisterObjectMethod("datetime", "uint get_month() const property", WRAP_MFN(CDateTime, getMonth), asCALL_GENERIC); assert(r >= 0);
|
||||
r = engine->RegisterObjectMethod("datetime", "uint get_day() const property", WRAP_MFN(CDateTime, getDay), asCALL_GENERIC); assert(r >= 0);
|
||||
r = engine->RegisterObjectMethod("datetime", "uint get_hour() const property", WRAP_MFN(CDateTime, getHour), asCALL_GENERIC); assert(r >= 0);
|
||||
r = engine->RegisterObjectMethod("datetime", "uint get_minute() const property", WRAP_MFN(CDateTime, getMinute), asCALL_GENERIC); assert(r >= 0);
|
||||
r = engine->RegisterObjectMethod("datetime", "uint get_second() const property", WRAP_MFN(CDateTime, getSecond), asCALL_GENERIC); assert(r >= 0);
|
||||
r = engine->RegisterObjectMethod("datetime", "bool setDate(uint year, uint month, uint day)", WRAP_MFN(CDateTime, setDate), asCALL_GENERIC); assert(r >= 0);
|
||||
r = engine->RegisterObjectMethod("datetime", "bool setTime(uint hour, uint minute, uint second)", WRAP_MFN(CDateTime, setTime), asCALL_GENERIC); assert(r >= 0);
|
||||
r = engine->RegisterObjectMethod("datetime", "int64 opSub(const datetime &in) const", WRAP_MFN_PR(CDateTime, operator-, (const CDateTime &other) const, asINT64), asCALL_GENERIC); assert(r >= 0);
|
||||
r = engine->RegisterObjectMethod("datetime", "datetime opAdd(int64 seconds) const", WRAP_MFN(CDateTime, operator+), asCALL_GENERIC); assert(r >= 0);
|
||||
r = engine->RegisterObjectMethod("datetime", "datetime opAdd_r(int64 seconds) const", WRAP_OBJ_LAST_PR(operator+, (asINT64 seconds, const CDateTime &other), CDateTime), asCALL_GENERIC); assert(r >= 0);
|
||||
r = engine->RegisterObjectMethod("datetime", "datetime &opAddAssign(int64 seconds)", WRAP_MFN(CDateTime, operator+=), asCALL_GENERIC); assert(r >= 0);
|
||||
r = engine->RegisterObjectMethod("datetime", "datetime opSub(int64 seconds) const", WRAP_MFN_PR(CDateTime, operator-, (asINT64) const, CDateTime), asCALL_GENERIC); assert(r >= 0);
|
||||
r = engine->RegisterObjectMethod("datetime", "datetime opSub_r(int64 seconds) const", WRAP_OBJ_LAST_PR(operator-, (asINT64 seconds, const CDateTime &other), CDateTime), asCALL_GENERIC); assert(r >= 0);
|
||||
r = engine->RegisterObjectMethod("datetime", "datetime &opSubAssign(int64 seconds)", WRAP_MFN(CDateTime, operator-=), asCALL_GENERIC); assert(r >= 0);
|
||||
r = engine->RegisterObjectMethod("datetime", "bool opEquals(const datetime &in) const", WRAP_MFN_PR(CDateTime, operator==, (const CDateTime &other) const, bool), asCALL_GENERIC); assert(r >= 0);
|
||||
r = engine->RegisterObjectMethod("datetime", "int opCmp(const datetime &in) const", WRAP_OBJ_FIRST(opCmp), asCALL_GENERIC); assert(r >= 0);
|
||||
}
|
||||
}
|
||||
|
||||
END_AS_NAMESPACE
|
||||
61
add_on/datetime/datetime.h
Normal file
61
add_on/datetime/datetime.h
Normal file
@@ -0,0 +1,61 @@
|
||||
#ifndef SCRIPTDATETIME_H
|
||||
#define SCRIPTDATETIME_H
|
||||
|
||||
#ifndef ANGELSCRIPT_H
|
||||
// Avoid having to inform include path if header is already include before
|
||||
#include <angelscript.h>
|
||||
#endif
|
||||
|
||||
#ifdef AS_CAN_USE_CPP11
|
||||
#include <chrono>
|
||||
#else
|
||||
#error Sorry, this requires C++11 which your compiler doesnt appear to support
|
||||
#endif
|
||||
|
||||
BEGIN_AS_NAMESPACE
|
||||
|
||||
class CDateTime
|
||||
{
|
||||
public:
|
||||
// Constructors
|
||||
CDateTime();
|
||||
CDateTime(const CDateTime &other);
|
||||
CDateTime(asUINT year, asUINT month, asUINT day, asUINT hour, asUINT minute, asUINT second);
|
||||
|
||||
// Copy the stored value from another any object
|
||||
CDateTime &operator=(const CDateTime &other);
|
||||
|
||||
// Accessors
|
||||
asUINT getYear() const;
|
||||
asUINT getMonth() const;
|
||||
asUINT getDay() const;
|
||||
asUINT getHour() const;
|
||||
asUINT getMinute() const;
|
||||
asUINT getSecond() const;
|
||||
|
||||
// Setters
|
||||
// Returns true if valid
|
||||
bool setDate(asUINT year, asUINT month, asUINT day);
|
||||
bool setTime(asUINT hour, asUINT minute, asUINT second);
|
||||
|
||||
// Operators
|
||||
// Return difference in seconds
|
||||
asINT64 operator-(const CDateTime &other) const;
|
||||
CDateTime operator+(asINT64 seconds) const;
|
||||
friend CDateTime operator+(asINT64 seconds, const CDateTime &other);
|
||||
CDateTime & operator+=(asINT64 seconds);
|
||||
CDateTime operator-(asINT64 seconds) const;
|
||||
friend CDateTime operator-(asINT64 seconds, const CDateTime &other);
|
||||
CDateTime & operator-=(asINT64 seconds);
|
||||
bool operator==(const CDateTime &other) const;
|
||||
bool operator<(const CDateTime &other) const;
|
||||
|
||||
protected:
|
||||
std::chrono::system_clock::time_point tp;
|
||||
};
|
||||
|
||||
void RegisterScriptDateTime(asIScriptEngine *engine);
|
||||
|
||||
END_AS_NAMESPACE
|
||||
|
||||
#endif
|
||||
852
add_on/debugger/debugger.cpp
Normal file
852
add_on/debugger/debugger.cpp
Normal file
@@ -0,0 +1,852 @@
|
||||
#include "debugger.h"
|
||||
#include <iostream> // cout
|
||||
#include <sstream> // stringstream
|
||||
#include <stdlib.h> // atoi
|
||||
#include <assert.h> // assert
|
||||
|
||||
using namespace std;
|
||||
|
||||
BEGIN_AS_NAMESPACE
|
||||
|
||||
CDebugger::CDebugger()
|
||||
{
|
||||
m_action = CONTINUE;
|
||||
m_lastFunction = 0;
|
||||
m_engine = 0;
|
||||
}
|
||||
|
||||
CDebugger::~CDebugger()
|
||||
{
|
||||
SetEngine(0);
|
||||
}
|
||||
|
||||
string CDebugger::ToString(void *value, asUINT typeId, int expandMembers, asIScriptEngine *engine)
|
||||
{
|
||||
if( value == 0 )
|
||||
return "<null>";
|
||||
|
||||
// If no engine pointer was provided use the default
|
||||
if( engine == 0 )
|
||||
engine = m_engine;
|
||||
|
||||
stringstream s;
|
||||
if( typeId == asTYPEID_VOID )
|
||||
return "<void>";
|
||||
else if( typeId == asTYPEID_BOOL )
|
||||
return *(bool*)value ? "true" : "false";
|
||||
else if( typeId == asTYPEID_INT8 )
|
||||
s << (int)*(signed char*)value;
|
||||
else if( typeId == asTYPEID_INT16 )
|
||||
s << (int)*(signed short*)value;
|
||||
else if( typeId == asTYPEID_INT32 )
|
||||
s << *(signed int*)value;
|
||||
else if( typeId == asTYPEID_INT64 )
|
||||
#if defined(_MSC_VER) && _MSC_VER <= 1200
|
||||
s << "{...}"; // MSVC6 doesn't like the << operator for 64bit integer
|
||||
#else
|
||||
s << *(asINT64*)value;
|
||||
#endif
|
||||
else if( typeId == asTYPEID_UINT8 )
|
||||
s << (unsigned int)*(unsigned char*)value;
|
||||
else if( typeId == asTYPEID_UINT16 )
|
||||
s << (unsigned int)*(unsigned short*)value;
|
||||
else if( typeId == asTYPEID_UINT32 )
|
||||
s << *(unsigned int*)value;
|
||||
else if( typeId == asTYPEID_UINT64 )
|
||||
#if defined(_MSC_VER) && _MSC_VER <= 1200
|
||||
s << "{...}"; // MSVC6 doesn't like the << operator for 64bit integer
|
||||
#else
|
||||
s << *(asQWORD*)value;
|
||||
#endif
|
||||
else if( typeId == asTYPEID_FLOAT )
|
||||
s << *(float*)value;
|
||||
else if( typeId == asTYPEID_DOUBLE )
|
||||
s << *(double*)value;
|
||||
else if( (typeId & asTYPEID_MASK_OBJECT) == 0 )
|
||||
{
|
||||
// The type is an enum
|
||||
s << *(asUINT*)value;
|
||||
|
||||
// Check if the value matches one of the defined enums
|
||||
if( engine )
|
||||
{
|
||||
asITypeInfo *t = engine->GetTypeInfoById(typeId);
|
||||
for( int n = t->GetEnumValueCount(); n-- > 0; )
|
||||
{
|
||||
int enumVal;
|
||||
const char *enumName = t->GetEnumValueByIndex(n, &enumVal);
|
||||
if( enumVal == *(int*)value )
|
||||
{
|
||||
s << ", " << enumName;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if( typeId & asTYPEID_SCRIPTOBJECT )
|
||||
{
|
||||
// Dereference handles, so we can see what it points to
|
||||
if( typeId & asTYPEID_OBJHANDLE )
|
||||
value = *(void**)value;
|
||||
|
||||
asIScriptObject *obj = (asIScriptObject *)value;
|
||||
|
||||
// Print the address of the object
|
||||
s << "{" << obj << "}";
|
||||
|
||||
// Print the members
|
||||
if( obj && expandMembers > 0 )
|
||||
{
|
||||
asITypeInfo *type = obj->GetObjectType();
|
||||
for( asUINT n = 0; n < obj->GetPropertyCount(); n++ )
|
||||
{
|
||||
if( n == 0 )
|
||||
s << " ";
|
||||
else
|
||||
s << ", ";
|
||||
|
||||
s << type->GetPropertyDeclaration(n) << " = " << ToString(obj->GetAddressOfProperty(n), obj->GetPropertyTypeId(n), expandMembers - 1, type->GetEngine());
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Dereference handles, so we can see what it points to
|
||||
if( typeId & asTYPEID_OBJHANDLE )
|
||||
value = *(void**)value;
|
||||
|
||||
// Print the address for reference types so it will be
|
||||
// possible to see when handles point to the same object
|
||||
if( engine )
|
||||
{
|
||||
asITypeInfo *type = engine->GetTypeInfoById(typeId);
|
||||
if( type->GetFlags() & asOBJ_REF )
|
||||
s << "{" << value << "}";
|
||||
|
||||
if( value )
|
||||
{
|
||||
// Check if there is a registered to-string callback
|
||||
map<const asITypeInfo*, ToStringCallback>::iterator it = m_toStringCallbacks.find(type);
|
||||
if( it == m_toStringCallbacks.end() )
|
||||
{
|
||||
// If the type is a template instance, there might be a
|
||||
// to-string callback for the generic template type
|
||||
if( type->GetFlags() & asOBJ_TEMPLATE )
|
||||
{
|
||||
asITypeInfo *tmplType = engine->GetTypeInfoByName(type->GetName());
|
||||
it = m_toStringCallbacks.find(tmplType);
|
||||
}
|
||||
}
|
||||
|
||||
if( it != m_toStringCallbacks.end() )
|
||||
{
|
||||
if( type->GetFlags() & asOBJ_REF )
|
||||
s << " ";
|
||||
|
||||
// Invoke the callback to get the string representation of this type
|
||||
string str = it->second(value, expandMembers, this);
|
||||
s << str;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
s << "{no engine}";
|
||||
}
|
||||
|
||||
return s.str();
|
||||
}
|
||||
|
||||
void CDebugger::RegisterToStringCallback(const asITypeInfo *ot, ToStringCallback callback)
|
||||
{
|
||||
if( m_toStringCallbacks.find(ot) == m_toStringCallbacks.end() )
|
||||
m_toStringCallbacks.insert(map<const asITypeInfo*, ToStringCallback>::value_type(ot, callback));
|
||||
}
|
||||
|
||||
void CDebugger::LineCallback(asIScriptContext *ctx)
|
||||
{
|
||||
assert( ctx );
|
||||
|
||||
// This should never happen, but it doesn't hurt to validate it
|
||||
if( ctx == 0 )
|
||||
return;
|
||||
|
||||
// By default we ignore callbacks when the context is not active.
|
||||
// An application might override this to for example disconnect the
|
||||
// debugger as the execution finished.
|
||||
if( ctx->GetState() != asEXECUTION_ACTIVE )
|
||||
return;
|
||||
|
||||
if( m_action == CONTINUE )
|
||||
{
|
||||
if( !CheckBreakPoint(ctx) )
|
||||
return;
|
||||
}
|
||||
else if( m_action == STEP_OVER )
|
||||
{
|
||||
if( ctx->GetCallstackSize() > m_lastCommandAtStackLevel )
|
||||
{
|
||||
if( !CheckBreakPoint(ctx) )
|
||||
return;
|
||||
}
|
||||
}
|
||||
else if( m_action == STEP_OUT )
|
||||
{
|
||||
if( ctx->GetCallstackSize() >= m_lastCommandAtStackLevel )
|
||||
{
|
||||
if( !CheckBreakPoint(ctx) )
|
||||
return;
|
||||
}
|
||||
}
|
||||
else if( m_action == STEP_INTO )
|
||||
{
|
||||
CheckBreakPoint(ctx);
|
||||
|
||||
// Always break, but we call the check break point anyway
|
||||
// to tell user when break point has been reached
|
||||
}
|
||||
|
||||
stringstream s;
|
||||
const char *file = 0;
|
||||
int lineNbr = ctx->GetLineNumber(0, 0, &file);
|
||||
s << (file ? file : "{unnamed}") << ":" << lineNbr << "; " << ctx->GetFunction()->GetDeclaration() << endl;
|
||||
Output(s.str());
|
||||
|
||||
TakeCommands(ctx);
|
||||
}
|
||||
|
||||
bool CDebugger::CheckBreakPoint(asIScriptContext *ctx)
|
||||
{
|
||||
if( ctx == 0 )
|
||||
return false;
|
||||
|
||||
// TODO: Should cache the break points in a function by checking which possible break points
|
||||
// can be hit when entering a function. If there are no break points in the current function
|
||||
// then there is no need to check every line.
|
||||
|
||||
const char *tmp = 0;
|
||||
int lineNbr = ctx->GetLineNumber(0, 0, &tmp);
|
||||
|
||||
// Consider just filename, not the full path
|
||||
string file = tmp ? tmp : "";
|
||||
size_t r = file.find_last_of("\\/");
|
||||
if( r != string::npos )
|
||||
file = file.substr(r+1);
|
||||
|
||||
// Did we move into a new function?
|
||||
asIScriptFunction *func = ctx->GetFunction();
|
||||
if( m_lastFunction != func )
|
||||
{
|
||||
// Check if any breakpoints need adjusting
|
||||
for( size_t n = 0; n < m_breakPoints.size(); n++ )
|
||||
{
|
||||
// We need to check for a breakpoint at entering the function
|
||||
if( m_breakPoints[n].func )
|
||||
{
|
||||
if( m_breakPoints[n].name == func->GetName() )
|
||||
{
|
||||
stringstream s;
|
||||
s << "Entering function '" << m_breakPoints[n].name << "'. Transforming it into break point" << endl;
|
||||
Output(s.str());
|
||||
|
||||
// Transform the function breakpoint into a file breakpoint
|
||||
m_breakPoints[n].name = file;
|
||||
m_breakPoints[n].lineNbr = lineNbr;
|
||||
m_breakPoints[n].func = false;
|
||||
m_breakPoints[n].needsAdjusting = false;
|
||||
}
|
||||
}
|
||||
// Check if a given breakpoint fall on a line with code or else adjust it to the next line
|
||||
else if( m_breakPoints[n].needsAdjusting &&
|
||||
m_breakPoints[n].name == file )
|
||||
{
|
||||
int line = func->FindNextLineWithCode(m_breakPoints[n].lineNbr);
|
||||
if( line >= 0 )
|
||||
{
|
||||
m_breakPoints[n].needsAdjusting = false;
|
||||
if( line != m_breakPoints[n].lineNbr )
|
||||
{
|
||||
stringstream s;
|
||||
s << "Moving break point " << n << " in file '" << file << "' to next line with code at line " << line << endl;
|
||||
Output(s.str());
|
||||
|
||||
// Move the breakpoint to the next line
|
||||
m_breakPoints[n].lineNbr = line;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
m_lastFunction = func;
|
||||
|
||||
// Determine if there is a breakpoint at the current line
|
||||
for( size_t n = 0; n < m_breakPoints.size(); n++ )
|
||||
{
|
||||
// TODO: do case-less comparison for file name
|
||||
|
||||
// Should we break?
|
||||
if( !m_breakPoints[n].func &&
|
||||
m_breakPoints[n].lineNbr == lineNbr &&
|
||||
m_breakPoints[n].name == file )
|
||||
{
|
||||
stringstream s;
|
||||
s << "Reached break point " << n << " in file '" << file << "' at line " << lineNbr << endl;
|
||||
Output(s.str());
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void CDebugger::TakeCommands(asIScriptContext *ctx)
|
||||
{
|
||||
for(;;)
|
||||
{
|
||||
char buf[512];
|
||||
|
||||
Output("[dbg]> ");
|
||||
cin.getline(buf, 512);
|
||||
|
||||
if( InterpretCommand(string(buf), ctx) )
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
bool CDebugger::InterpretCommand(const string &cmd, asIScriptContext *ctx)
|
||||
{
|
||||
if( cmd.length() == 0 ) return true;
|
||||
|
||||
switch( cmd[0] )
|
||||
{
|
||||
case 'c':
|
||||
m_action = CONTINUE;
|
||||
break;
|
||||
|
||||
case 's':
|
||||
m_action = STEP_INTO;
|
||||
break;
|
||||
|
||||
case 'n':
|
||||
m_action = STEP_OVER;
|
||||
m_lastCommandAtStackLevel = ctx ? ctx->GetCallstackSize() : 1;
|
||||
break;
|
||||
|
||||
case 'o':
|
||||
m_action = STEP_OUT;
|
||||
m_lastCommandAtStackLevel = ctx ? ctx->GetCallstackSize() : 0;
|
||||
break;
|
||||
|
||||
case 'b':
|
||||
{
|
||||
// Set break point
|
||||
size_t p = cmd.find_first_not_of(" \t", 1);
|
||||
size_t div = cmd.find(':');
|
||||
if( div != string::npos && div > 2 && p > 1 )
|
||||
{
|
||||
string file = cmd.substr(2, div-2);
|
||||
string line = cmd.substr(div+1);
|
||||
|
||||
int nbr = atoi(line.c_str());
|
||||
|
||||
AddFileBreakPoint(file, nbr);
|
||||
}
|
||||
else if( div == string::npos && p != string::npos && p > 1 )
|
||||
{
|
||||
string func = cmd.substr(p);
|
||||
|
||||
AddFuncBreakPoint(func);
|
||||
}
|
||||
else
|
||||
{
|
||||
Output("Incorrect format for setting break point, expected one of:\n"
|
||||
" b <file name>:<line number>\n"
|
||||
" b <function name>\n");
|
||||
}
|
||||
}
|
||||
// take more commands
|
||||
return false;
|
||||
|
||||
case 'r':
|
||||
{
|
||||
// Remove break point
|
||||
size_t p = cmd.find_first_not_of(" \t", 1);
|
||||
if( cmd.length() > 2 && p != string::npos && p > 1 )
|
||||
{
|
||||
string br = cmd.substr(2);
|
||||
if( br == "all" )
|
||||
{
|
||||
m_breakPoints.clear();
|
||||
Output("All break points have been removed\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
int nbr = atoi(br.c_str());
|
||||
if( nbr >= 0 && nbr < (int)m_breakPoints.size() )
|
||||
m_breakPoints.erase(m_breakPoints.begin()+nbr);
|
||||
ListBreakPoints();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Output("Incorrect format for removing break points, expected:\n"
|
||||
" r <all|number of break point>\n");
|
||||
}
|
||||
}
|
||||
// take more commands
|
||||
return false;
|
||||
|
||||
case 'l':
|
||||
{
|
||||
// List something
|
||||
bool printHelp = false;
|
||||
size_t p = cmd.find_first_not_of(" \t", 1);
|
||||
if( p != string::npos && p > 1 )
|
||||
{
|
||||
if( cmd[p] == 'b' )
|
||||
{
|
||||
ListBreakPoints();
|
||||
}
|
||||
else if( cmd[p] == 'v' )
|
||||
{
|
||||
ListLocalVariables(ctx);
|
||||
}
|
||||
else if( cmd[p] == 'g' )
|
||||
{
|
||||
ListGlobalVariables(ctx);
|
||||
}
|
||||
else if( cmd[p] == 'm' )
|
||||
{
|
||||
ListMemberProperties(ctx);
|
||||
}
|
||||
else if( cmd[p] == 's' )
|
||||
{
|
||||
ListStatistics(ctx);
|
||||
}
|
||||
else
|
||||
{
|
||||
Output("Unknown list option.\n");
|
||||
printHelp = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Output("Incorrect format for list command.\n");
|
||||
printHelp = true;
|
||||
}
|
||||
|
||||
if( printHelp )
|
||||
{
|
||||
Output("Expected format: \n"
|
||||
" l <list option>\n"
|
||||
"Available options: \n"
|
||||
" b - breakpoints\n"
|
||||
" v - local variables\n"
|
||||
" m - member properties\n"
|
||||
" g - global variables\n"
|
||||
" s - statistics\n");
|
||||
}
|
||||
}
|
||||
// take more commands
|
||||
return false;
|
||||
|
||||
case 'h':
|
||||
PrintHelp();
|
||||
// take more commands
|
||||
return false;
|
||||
|
||||
case 'p':
|
||||
{
|
||||
// Print a value
|
||||
size_t p = cmd.find_first_not_of(" \t", 1);
|
||||
if( p != string::npos && p > 1 )
|
||||
{
|
||||
PrintValue(cmd.substr(p), ctx);
|
||||
}
|
||||
else
|
||||
{
|
||||
Output("Incorrect format for print, expected:\n"
|
||||
" p <expression>\n");
|
||||
}
|
||||
}
|
||||
// take more commands
|
||||
return false;
|
||||
|
||||
case 'w':
|
||||
// Where am I?
|
||||
PrintCallstack(ctx);
|
||||
// take more commands
|
||||
return false;
|
||||
|
||||
case 'a':
|
||||
// abort the execution
|
||||
if( ctx == 0 )
|
||||
{
|
||||
Output("No script is running\n");
|
||||
return false;
|
||||
}
|
||||
ctx->Abort();
|
||||
break;
|
||||
|
||||
default:
|
||||
Output("Unknown command\n");
|
||||
// take more commands
|
||||
return false;
|
||||
}
|
||||
|
||||
// Continue execution
|
||||
return true;
|
||||
}
|
||||
|
||||
void CDebugger::PrintValue(const std::string &expr, asIScriptContext *ctx)
|
||||
{
|
||||
if( ctx == 0 )
|
||||
{
|
||||
Output("No script is running\n");
|
||||
return;
|
||||
}
|
||||
|
||||
asIScriptEngine *engine = ctx->GetEngine();
|
||||
|
||||
// Tokenize the input string to get the variable scope and name
|
||||
asUINT len = 0;
|
||||
string scope;
|
||||
string name;
|
||||
string str = expr;
|
||||
asETokenClass t = engine->ParseToken(str.c_str(), 0, &len);
|
||||
while( t == asTC_IDENTIFIER || (t == asTC_KEYWORD && len == 2 && str.compare(0, 2, "::") == 0) )
|
||||
{
|
||||
if( t == asTC_KEYWORD )
|
||||
{
|
||||
if( scope == "" && name == "" )
|
||||
scope = "::"; // global scope
|
||||
else if( scope == "::" || scope == "" )
|
||||
scope = name; // namespace
|
||||
else
|
||||
scope += "::" + name; // nested namespace
|
||||
name = "";
|
||||
}
|
||||
else if( t == asTC_IDENTIFIER )
|
||||
name.assign(str.c_str(), len);
|
||||
|
||||
// Skip the parsed token and get the next one
|
||||
str = str.substr(len);
|
||||
t = engine->ParseToken(str.c_str(), 0, &len);
|
||||
}
|
||||
|
||||
if( name.size() )
|
||||
{
|
||||
// Find the variable
|
||||
void *ptr = 0;
|
||||
int typeId = 0;
|
||||
|
||||
asIScriptFunction *func = ctx->GetFunction();
|
||||
if( !func ) return;
|
||||
|
||||
// skip local variables if a scope was informed
|
||||
if( scope == "" )
|
||||
{
|
||||
// We start from the end, in case the same name is reused in different scopes
|
||||
for( asUINT n = func->GetVarCount(); n-- > 0; )
|
||||
{
|
||||
if( ctx->IsVarInScope(n) && name == ctx->GetVarName(n) )
|
||||
{
|
||||
ptr = ctx->GetAddressOfVar(n);
|
||||
typeId = ctx->GetVarTypeId(n);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Look for class members, if we're in a class method
|
||||
if( !ptr && func->GetObjectType() )
|
||||
{
|
||||
if( name == "this" )
|
||||
{
|
||||
ptr = ctx->GetThisPointer();
|
||||
typeId = ctx->GetThisTypeId();
|
||||
}
|
||||
else
|
||||
{
|
||||
asITypeInfo *type = engine->GetTypeInfoById(ctx->GetThisTypeId());
|
||||
for( asUINT n = 0; n < type->GetPropertyCount(); n++ )
|
||||
{
|
||||
const char *propName = 0;
|
||||
int offset = 0;
|
||||
bool isReference = 0;
|
||||
int compositeOffset = 0;
|
||||
bool isCompositeIndirect = false;
|
||||
type->GetProperty(n, &propName, &typeId, 0, 0, &offset, &isReference, 0, &compositeOffset, &isCompositeIndirect);
|
||||
if( name == propName )
|
||||
{
|
||||
ptr = (void*)(((asBYTE*)ctx->GetThisPointer())+compositeOffset);
|
||||
if (isCompositeIndirect) ptr = *(void**)ptr;
|
||||
ptr = (void*)(((asBYTE*)ptr) + offset);
|
||||
if( isReference ) ptr = *(void**)ptr;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Look for global variables
|
||||
if( !ptr )
|
||||
{
|
||||
if( scope == "" )
|
||||
{
|
||||
// If no explicit scope was informed then use the namespace of the current function by default
|
||||
scope = func->GetNamespace();
|
||||
}
|
||||
else if( scope == "::" )
|
||||
{
|
||||
// The global namespace will be empty
|
||||
scope = "";
|
||||
}
|
||||
|
||||
asIScriptModule *mod = func->GetModule();
|
||||
if( mod )
|
||||
{
|
||||
for( asUINT n = 0; n < mod->GetGlobalVarCount(); n++ )
|
||||
{
|
||||
const char *varName = 0, *nameSpace = 0;
|
||||
mod->GetGlobalVar(n, &varName, &nameSpace, &typeId);
|
||||
|
||||
// Check if both name and namespace match
|
||||
if( name == varName && scope == nameSpace )
|
||||
{
|
||||
ptr = mod->GetAddressOfGlobalVar(n);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if( ptr )
|
||||
{
|
||||
// TODO: If there is a . after the identifier, check for members
|
||||
// TODO: If there is a [ after the identifier try to call the 'opIndex(expr) const' method
|
||||
if( str != "" )
|
||||
{
|
||||
Output("Invalid expression. Expression doesn't end after symbol\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
stringstream s;
|
||||
// TODO: Allow user to set if members should be expanded
|
||||
// Expand members by default to 3 recursive levels only
|
||||
s << ToString(ptr, typeId, 3, engine) << endl;
|
||||
Output(s.str());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Output("Invalid expression. No matching symbol\n");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Output("Invalid expression. Expected identifier\n");
|
||||
}
|
||||
}
|
||||
|
||||
void CDebugger::ListBreakPoints()
|
||||
{
|
||||
// List all break points
|
||||
stringstream s;
|
||||
for( size_t b = 0; b < m_breakPoints.size(); b++ )
|
||||
if( m_breakPoints[b].func )
|
||||
s << b << " - " << m_breakPoints[b].name << endl;
|
||||
else
|
||||
s << b << " - " << m_breakPoints[b].name << ":" << m_breakPoints[b].lineNbr << endl;
|
||||
Output(s.str());
|
||||
}
|
||||
|
||||
void CDebugger::ListMemberProperties(asIScriptContext *ctx)
|
||||
{
|
||||
if( ctx == 0 )
|
||||
{
|
||||
Output("No script is running\n");
|
||||
return;
|
||||
}
|
||||
|
||||
void *ptr = ctx->GetThisPointer();
|
||||
if( ptr )
|
||||
{
|
||||
stringstream s;
|
||||
// TODO: Allow user to define if members should be expanded or not
|
||||
// Expand members by default to 3 recursive levels only
|
||||
s << "this = " << ToString(ptr, ctx->GetThisTypeId(), 3, ctx->GetEngine()) << endl;
|
||||
Output(s.str());
|
||||
}
|
||||
}
|
||||
|
||||
void CDebugger::ListLocalVariables(asIScriptContext *ctx)
|
||||
{
|
||||
if( ctx == 0 )
|
||||
{
|
||||
Output("No script is running\n");
|
||||
return;
|
||||
}
|
||||
|
||||
asIScriptFunction *func = ctx->GetFunction();
|
||||
if( !func ) return;
|
||||
|
||||
stringstream s;
|
||||
for( asUINT n = 0; n < func->GetVarCount(); n++ )
|
||||
{
|
||||
if( ctx->IsVarInScope(n) )
|
||||
{
|
||||
// TODO: Allow user to set if members should be expanded or not
|
||||
// Expand members by default to 3 recursive levels only
|
||||
s << func->GetVarDecl(n) << " = " << ToString(ctx->GetAddressOfVar(n), ctx->GetVarTypeId(n), 3, ctx->GetEngine()) << endl;
|
||||
}
|
||||
}
|
||||
Output(s.str());
|
||||
}
|
||||
|
||||
void CDebugger::ListGlobalVariables(asIScriptContext *ctx)
|
||||
{
|
||||
if( ctx == 0 )
|
||||
{
|
||||
Output("No script is running\n");
|
||||
return;
|
||||
}
|
||||
|
||||
// Determine the current module from the function
|
||||
asIScriptFunction *func = ctx->GetFunction();
|
||||
if( !func ) return;
|
||||
|
||||
asIScriptModule *mod = func->GetModule();
|
||||
if( !mod ) return;
|
||||
|
||||
stringstream s;
|
||||
for( asUINT n = 0; n < mod->GetGlobalVarCount(); n++ )
|
||||
{
|
||||
int typeId = 0;
|
||||
mod->GetGlobalVar(n, 0, 0, &typeId);
|
||||
// TODO: Allow user to set how many recursive expansions should be done
|
||||
// Expand members by default to 3 recursive levels only
|
||||
s << mod->GetGlobalVarDeclaration(n) << " = " << ToString(mod->GetAddressOfGlobalVar(n), typeId, 3, ctx->GetEngine()) << endl;
|
||||
}
|
||||
Output(s.str());
|
||||
}
|
||||
|
||||
void CDebugger::ListStatistics(asIScriptContext *ctx)
|
||||
{
|
||||
if( ctx == 0 )
|
||||
{
|
||||
Output("No script is running\n");
|
||||
return;
|
||||
}
|
||||
|
||||
asIScriptEngine *engine = ctx->GetEngine();
|
||||
|
||||
asUINT gcCurrSize, gcTotalDestr, gcTotalDet, gcNewObjects, gcTotalNewDestr;
|
||||
engine->GetGCStatistics(&gcCurrSize, &gcTotalDestr, &gcTotalDet, &gcNewObjects, &gcTotalNewDestr);
|
||||
|
||||
stringstream s;
|
||||
s << "Garbage collector:" << endl;
|
||||
s << " current size: " << gcCurrSize << endl;
|
||||
s << " total destroyed: " << gcTotalDestr << endl;
|
||||
s << " total detected: " << gcTotalDet << endl;
|
||||
s << " new objects: " << gcNewObjects << endl;
|
||||
s << " new objects destroyed: " << gcTotalNewDestr << endl;
|
||||
|
||||
Output(s.str());
|
||||
}
|
||||
|
||||
void CDebugger::PrintCallstack(asIScriptContext *ctx)
|
||||
{
|
||||
if( ctx == 0 )
|
||||
{
|
||||
Output("No script is running\n");
|
||||
return;
|
||||
}
|
||||
|
||||
stringstream s;
|
||||
const char *file = 0;
|
||||
int lineNbr = 0;
|
||||
for( asUINT n = 0; n < ctx->GetCallstackSize(); n++ )
|
||||
{
|
||||
lineNbr = ctx->GetLineNumber(n, 0, &file);
|
||||
s << (file ? file : "{unnamed}") << ":" << lineNbr << "; " << ctx->GetFunction(n)->GetDeclaration() << endl;
|
||||
}
|
||||
Output(s.str());
|
||||
}
|
||||
|
||||
void CDebugger::AddFuncBreakPoint(const string &func)
|
||||
{
|
||||
// Trim the function name
|
||||
size_t b = func.find_first_not_of(" \t");
|
||||
size_t e = func.find_last_not_of(" \t");
|
||||
string actual = func.substr(b, e != string::npos ? e-b+1 : string::npos);
|
||||
|
||||
stringstream s;
|
||||
s << "Adding deferred break point for function '" << actual << "'" << endl;
|
||||
Output(s.str());
|
||||
|
||||
BreakPoint bp(actual, 0, true);
|
||||
m_breakPoints.push_back(bp);
|
||||
}
|
||||
|
||||
void CDebugger::AddFileBreakPoint(const string &file, int lineNbr)
|
||||
{
|
||||
// Store just file name, not entire path
|
||||
size_t r = file.find_last_of("\\/");
|
||||
string actual;
|
||||
if( r != string::npos )
|
||||
actual = file.substr(r+1);
|
||||
else
|
||||
actual = file;
|
||||
|
||||
// Trim the file name
|
||||
size_t b = actual.find_first_not_of(" \t");
|
||||
size_t e = actual.find_last_not_of(" \t");
|
||||
actual = actual.substr(b, e != string::npos ? e-b+1 : string::npos);
|
||||
|
||||
stringstream s;
|
||||
s << "Setting break point in file '" << actual << "' at line " << lineNbr << endl;
|
||||
Output(s.str());
|
||||
|
||||
BreakPoint bp(actual, lineNbr, false);
|
||||
m_breakPoints.push_back(bp);
|
||||
}
|
||||
|
||||
void CDebugger::PrintHelp()
|
||||
{
|
||||
Output(" c - Continue\n"
|
||||
" s - Step into\n"
|
||||
" n - Next step\n"
|
||||
" o - Step out\n"
|
||||
" b - Set break point\n"
|
||||
" l - List various things\n"
|
||||
" r - Remove break point\n"
|
||||
" p - Print value\n"
|
||||
" w - Where am I?\n"
|
||||
" a - Abort execution\n"
|
||||
" h - Print this help text\n");
|
||||
}
|
||||
|
||||
void CDebugger::Output(const string &str)
|
||||
{
|
||||
// By default we just output to stdout
|
||||
cout << str;
|
||||
}
|
||||
|
||||
void CDebugger::SetEngine(asIScriptEngine *engine)
|
||||
{
|
||||
if( m_engine != engine )
|
||||
{
|
||||
if( m_engine )
|
||||
m_engine->Release();
|
||||
m_engine = engine;
|
||||
if( m_engine )
|
||||
m_engine->AddRef();
|
||||
}
|
||||
}
|
||||
|
||||
asIScriptEngine *CDebugger::GetEngine()
|
||||
{
|
||||
return m_engine;
|
||||
}
|
||||
|
||||
END_AS_NAMESPACE
|
||||
87
add_on/debugger/debugger.h
Normal file
87
add_on/debugger/debugger.h
Normal file
@@ -0,0 +1,87 @@
|
||||
#ifndef DEBUGGER_H
|
||||
#define DEBUGGER_H
|
||||
|
||||
#ifndef ANGELSCRIPT_H
|
||||
// Avoid having to inform include path if header is already include before
|
||||
#include <angelscript.h>
|
||||
#endif
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <map>
|
||||
|
||||
BEGIN_AS_NAMESPACE
|
||||
|
||||
class CDebugger
|
||||
{
|
||||
public:
|
||||
CDebugger();
|
||||
virtual ~CDebugger();
|
||||
|
||||
// Register callbacks to handle to-string conversions of application types
|
||||
// The expandMembersLevel is a counter for how many recursive levels the members should be expanded.
|
||||
// If the object that is being converted to a string has members of its own the callback should call
|
||||
// the debugger's ToString passing in expandMembersLevel - 1.
|
||||
typedef std::string (*ToStringCallback)(void *obj, int expandMembersLevel, CDebugger *dbg);
|
||||
virtual void RegisterToStringCallback(const asITypeInfo *ti, ToStringCallback callback);
|
||||
|
||||
// User interaction
|
||||
virtual void TakeCommands(asIScriptContext *ctx);
|
||||
virtual void Output(const std::string &str);
|
||||
|
||||
// Line callback invoked by context
|
||||
virtual void LineCallback(asIScriptContext *ctx);
|
||||
|
||||
// Commands
|
||||
virtual void PrintHelp();
|
||||
virtual void AddFileBreakPoint(const std::string &file, int lineNbr);
|
||||
virtual void AddFuncBreakPoint(const std::string &func);
|
||||
virtual void ListBreakPoints();
|
||||
virtual void ListLocalVariables(asIScriptContext *ctx);
|
||||
virtual void ListGlobalVariables(asIScriptContext *ctx);
|
||||
virtual void ListMemberProperties(asIScriptContext *ctx);
|
||||
virtual void ListStatistics(asIScriptContext *ctx);
|
||||
virtual void PrintCallstack(asIScriptContext *ctx);
|
||||
virtual void PrintValue(const std::string &expr, asIScriptContext *ctx);
|
||||
|
||||
// Helpers
|
||||
virtual bool InterpretCommand(const std::string &cmd, asIScriptContext *ctx);
|
||||
virtual bool CheckBreakPoint(asIScriptContext *ctx);
|
||||
virtual std::string ToString(void *value, asUINT typeId, int expandMembersLevel, asIScriptEngine *engine);
|
||||
|
||||
// Optionally set the engine pointer in the debugger so it can be retrieved
|
||||
// by callbacks that need it. This will hold a reference to the engine.
|
||||
virtual void SetEngine(asIScriptEngine *engine);
|
||||
virtual asIScriptEngine *GetEngine();
|
||||
|
||||
protected:
|
||||
enum DebugAction
|
||||
{
|
||||
CONTINUE, // continue until next break point
|
||||
STEP_INTO, // stop at next instruction
|
||||
STEP_OVER, // stop at next instruction, skipping called functions
|
||||
STEP_OUT // run until returning from current function
|
||||
};
|
||||
DebugAction m_action;
|
||||
asUINT m_lastCommandAtStackLevel;
|
||||
asIScriptFunction *m_lastFunction;
|
||||
|
||||
struct BreakPoint
|
||||
{
|
||||
BreakPoint(std::string f, int n, bool _func) : name(f), lineNbr(n), func(_func), needsAdjusting(true) {}
|
||||
std::string name;
|
||||
int lineNbr;
|
||||
bool func;
|
||||
bool needsAdjusting;
|
||||
};
|
||||
std::vector<BreakPoint> m_breakPoints;
|
||||
|
||||
asIScriptEngine *m_engine;
|
||||
|
||||
// Registered callbacks for converting types to strings
|
||||
std::map<const asITypeInfo*, ToStringCallback> m_toStringCallbacks;
|
||||
};
|
||||
|
||||
END_AS_NAMESPACE
|
||||
|
||||
#endif
|
||||
490
add_on/scriptany/scriptany.cpp
Normal file
490
add_on/scriptany/scriptany.cpp
Normal file
@@ -0,0 +1,490 @@
|
||||
#include "scriptany.h"
|
||||
#include <new>
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
|
||||
BEGIN_AS_NAMESPACE
|
||||
|
||||
// We'll use the generic interface for the factories as we need the engine pointer
|
||||
static void ScriptAnyFactory_Generic(asIScriptGeneric *gen)
|
||||
{
|
||||
asIScriptEngine *engine = gen->GetEngine();
|
||||
|
||||
*(CScriptAny**)gen->GetAddressOfReturnLocation() = new CScriptAny(engine);
|
||||
}
|
||||
|
||||
static void ScriptAnyFactory2_Generic(asIScriptGeneric *gen)
|
||||
{
|
||||
asIScriptEngine *engine = gen->GetEngine();
|
||||
void *ref = (void*)gen->GetArgAddress(0);
|
||||
int refType = gen->GetArgTypeId(0);
|
||||
|
||||
*(CScriptAny**)gen->GetAddressOfReturnLocation() = new CScriptAny(ref,refType,engine);
|
||||
}
|
||||
|
||||
static CScriptAny &ScriptAnyAssignment(CScriptAny *other, CScriptAny *self)
|
||||
{
|
||||
return *self = *other;
|
||||
}
|
||||
|
||||
static void ScriptAnyAssignment_Generic(asIScriptGeneric *gen)
|
||||
{
|
||||
CScriptAny *other = (CScriptAny*)gen->GetArgObject(0);
|
||||
CScriptAny *self = (CScriptAny*)gen->GetObject();
|
||||
|
||||
*self = *other;
|
||||
|
||||
gen->SetReturnObject(self);
|
||||
}
|
||||
|
||||
static void ScriptAny_Store_Generic(asIScriptGeneric *gen)
|
||||
{
|
||||
void *ref = (void*)gen->GetArgAddress(0);
|
||||
int refTypeId = gen->GetArgTypeId(0);
|
||||
CScriptAny *self = (CScriptAny*)gen->GetObject();
|
||||
|
||||
self->Store(ref, refTypeId);
|
||||
}
|
||||
|
||||
static void ScriptAny_StoreInt_Generic(asIScriptGeneric *gen)
|
||||
{
|
||||
asINT64 *ref = (asINT64*)gen->GetArgAddress(0);
|
||||
CScriptAny *self = (CScriptAny*)gen->GetObject();
|
||||
|
||||
self->Store(*ref);
|
||||
}
|
||||
|
||||
static void ScriptAny_StoreFlt_Generic(asIScriptGeneric *gen)
|
||||
{
|
||||
double *ref = (double*)gen->GetArgAddress(0);
|
||||
CScriptAny *self = (CScriptAny*)gen->GetObject();
|
||||
|
||||
self->Store(*ref);
|
||||
}
|
||||
|
||||
static void ScriptAny_Retrieve_Generic(asIScriptGeneric *gen)
|
||||
{
|
||||
void *ref = (void*)gen->GetArgAddress(0);
|
||||
int refTypeId = gen->GetArgTypeId(0);
|
||||
CScriptAny *self = (CScriptAny*)gen->GetObject();
|
||||
|
||||
*(bool*)gen->GetAddressOfReturnLocation() = self->Retrieve(ref, refTypeId);
|
||||
}
|
||||
|
||||
static void ScriptAny_RetrieveInt_Generic(asIScriptGeneric *gen)
|
||||
{
|
||||
asINT64 *ref = (asINT64*)gen->GetArgAddress(0);
|
||||
CScriptAny *self = (CScriptAny*)gen->GetObject();
|
||||
|
||||
*(bool*)gen->GetAddressOfReturnLocation() = self->Retrieve(*ref);
|
||||
}
|
||||
|
||||
static void ScriptAny_RetrieveFlt_Generic(asIScriptGeneric *gen)
|
||||
{
|
||||
double *ref = (double*)gen->GetArgAddress(0);
|
||||
CScriptAny *self = (CScriptAny*)gen->GetObject();
|
||||
|
||||
*(bool*)gen->GetAddressOfReturnLocation() = self->Retrieve(*ref);
|
||||
}
|
||||
|
||||
static void ScriptAny_AddRef_Generic(asIScriptGeneric *gen)
|
||||
{
|
||||
CScriptAny *self = (CScriptAny*)gen->GetObject();
|
||||
self->AddRef();
|
||||
}
|
||||
|
||||
static void ScriptAny_Release_Generic(asIScriptGeneric *gen)
|
||||
{
|
||||
CScriptAny *self = (CScriptAny*)gen->GetObject();
|
||||
self->Release();
|
||||
}
|
||||
|
||||
static void ScriptAny_GetRefCount_Generic(asIScriptGeneric *gen)
|
||||
{
|
||||
CScriptAny *self = (CScriptAny*)gen->GetObject();
|
||||
*(int*)gen->GetAddressOfReturnLocation() = self->GetRefCount();
|
||||
}
|
||||
|
||||
static void ScriptAny_SetFlag_Generic(asIScriptGeneric *gen)
|
||||
{
|
||||
CScriptAny *self = (CScriptAny*)gen->GetObject();
|
||||
self->SetFlag();
|
||||
}
|
||||
|
||||
static void ScriptAny_GetFlag_Generic(asIScriptGeneric *gen)
|
||||
{
|
||||
CScriptAny *self = (CScriptAny*)gen->GetObject();
|
||||
*(bool*)gen->GetAddressOfReturnLocation() = self->GetFlag();
|
||||
}
|
||||
|
||||
static void ScriptAny_EnumReferences_Generic(asIScriptGeneric *gen)
|
||||
{
|
||||
CScriptAny *self = (CScriptAny*)gen->GetObject();
|
||||
asIScriptEngine *engine = *(asIScriptEngine**)gen->GetAddressOfArg(0);
|
||||
self->EnumReferences(engine);
|
||||
}
|
||||
|
||||
static void ScriptAny_ReleaseAllHandles_Generic(asIScriptGeneric *gen)
|
||||
{
|
||||
CScriptAny *self = (CScriptAny*)gen->GetObject();
|
||||
asIScriptEngine *engine = *(asIScriptEngine**)gen->GetAddressOfArg(0);
|
||||
self->ReleaseAllHandles(engine);
|
||||
}
|
||||
|
||||
void RegisterScriptAny(asIScriptEngine *engine)
|
||||
{
|
||||
if( strstr(asGetLibraryOptions(), "AS_MAX_PORTABILITY") )
|
||||
RegisterScriptAny_Generic(engine);
|
||||
else
|
||||
RegisterScriptAny_Native(engine);
|
||||
}
|
||||
|
||||
void RegisterScriptAny_Native(asIScriptEngine *engine)
|
||||
{
|
||||
int r;
|
||||
r = engine->RegisterObjectType("any", sizeof(CScriptAny), asOBJ_REF | asOBJ_GC); assert( r >= 0 );
|
||||
|
||||
// We'll use the generic interface for the constructor as we need the engine pointer
|
||||
r = engine->RegisterObjectBehaviour("any", asBEHAVE_FACTORY, "any@ f()", asFUNCTION(ScriptAnyFactory_Generic), asCALL_GENERIC); assert( r >= 0 );
|
||||
r = engine->RegisterObjectBehaviour("any", asBEHAVE_FACTORY, "any@ f(?&in) explicit", asFUNCTION(ScriptAnyFactory2_Generic), asCALL_GENERIC); assert( r >= 0 );
|
||||
r = engine->RegisterObjectBehaviour("any", asBEHAVE_FACTORY, "any@ f(const int64&in) explicit", asFUNCTION(ScriptAnyFactory2_Generic), asCALL_GENERIC); assert(r >= 0);
|
||||
r = engine->RegisterObjectBehaviour("any", asBEHAVE_FACTORY, "any@ f(const double&in) explicit", asFUNCTION(ScriptAnyFactory2_Generic), asCALL_GENERIC); assert(r >= 0);
|
||||
|
||||
r = engine->RegisterObjectBehaviour("any", asBEHAVE_ADDREF, "void f()", asMETHOD(CScriptAny,AddRef), asCALL_THISCALL); assert( r >= 0 );
|
||||
r = engine->RegisterObjectBehaviour("any", asBEHAVE_RELEASE, "void f()", asMETHOD(CScriptAny,Release), asCALL_THISCALL); assert( r >= 0 );
|
||||
r = engine->RegisterObjectMethod("any", "any &opAssign(any&in)", asFUNCTION(ScriptAnyAssignment), asCALL_CDECL_OBJLAST); assert( r >= 0 );
|
||||
r = engine->RegisterObjectMethod("any", "void store(?&in)", asMETHODPR(CScriptAny,Store,(void*,int),void), asCALL_THISCALL); assert( r >= 0 );
|
||||
r = engine->RegisterObjectMethod("any", "void store(const int64&in)", asMETHODPR(CScriptAny,Store,(asINT64&),void), asCALL_THISCALL); assert( r >= 0 );
|
||||
r = engine->RegisterObjectMethod("any", "void store(const double&in)", asMETHODPR(CScriptAny,Store,(double&),void), asCALL_THISCALL); assert( r >= 0 );
|
||||
r = engine->RegisterObjectMethod("any", "bool retrieve(?&out)", asMETHODPR(CScriptAny,Retrieve,(void*,int) const,bool), asCALL_THISCALL); assert( r >= 0 );
|
||||
r = engine->RegisterObjectMethod("any", "bool retrieve(int64&out)", asMETHODPR(CScriptAny,Retrieve,(asINT64&) const,bool), asCALL_THISCALL); assert( r >= 0 );
|
||||
r = engine->RegisterObjectMethod("any", "bool retrieve(double&out)", asMETHODPR(CScriptAny,Retrieve,(double&) const,bool), asCALL_THISCALL); assert( r >= 0 );
|
||||
|
||||
// Register GC behaviours
|
||||
r = engine->RegisterObjectBehaviour("any", asBEHAVE_GETREFCOUNT, "int f()", asMETHOD(CScriptAny,GetRefCount), asCALL_THISCALL); assert( r >= 0 );
|
||||
r = engine->RegisterObjectBehaviour("any", asBEHAVE_SETGCFLAG, "void f()", asMETHOD(CScriptAny,SetFlag), asCALL_THISCALL); assert( r >= 0 );
|
||||
r = engine->RegisterObjectBehaviour("any", asBEHAVE_GETGCFLAG, "bool f()", asMETHOD(CScriptAny,GetFlag), asCALL_THISCALL); assert( r >= 0 );
|
||||
r = engine->RegisterObjectBehaviour("any", asBEHAVE_ENUMREFS, "void f(int&in)", asMETHOD(CScriptAny,EnumReferences), asCALL_THISCALL); assert( r >= 0 );
|
||||
r = engine->RegisterObjectBehaviour("any", asBEHAVE_RELEASEREFS, "void f(int&in)", asMETHOD(CScriptAny,ReleaseAllHandles), asCALL_THISCALL); assert( r >= 0 );
|
||||
}
|
||||
|
||||
void RegisterScriptAny_Generic(asIScriptEngine *engine)
|
||||
{
|
||||
int r;
|
||||
r = engine->RegisterObjectType("any", sizeof(CScriptAny), asOBJ_REF | asOBJ_GC); assert( r >= 0 );
|
||||
|
||||
// We'll use the generic interface for the constructor as we need the engine pointer
|
||||
r = engine->RegisterObjectBehaviour("any", asBEHAVE_FACTORY, "any@ f()", asFUNCTION(ScriptAnyFactory_Generic), asCALL_GENERIC); assert( r >= 0 );
|
||||
r = engine->RegisterObjectBehaviour("any", asBEHAVE_FACTORY, "any@ f(?&in) explicit", asFUNCTION(ScriptAnyFactory2_Generic), asCALL_GENERIC); assert( r >= 0 );
|
||||
r = engine->RegisterObjectBehaviour("any", asBEHAVE_FACTORY, "any@ f(const int64&in) explicit", asFUNCTION(ScriptAnyFactory2_Generic), asCALL_GENERIC); assert(r >= 0);
|
||||
r = engine->RegisterObjectBehaviour("any", asBEHAVE_FACTORY, "any@ f(const double&in) explicit", asFUNCTION(ScriptAnyFactory2_Generic), asCALL_GENERIC); assert(r >= 0);
|
||||
|
||||
r = engine->RegisterObjectBehaviour("any", asBEHAVE_ADDREF, "void f()", asFUNCTION(ScriptAny_AddRef_Generic), asCALL_GENERIC); assert( r >= 0 );
|
||||
r = engine->RegisterObjectBehaviour("any", asBEHAVE_RELEASE, "void f()", asFUNCTION(ScriptAny_Release_Generic), asCALL_GENERIC); assert( r >= 0 );
|
||||
r = engine->RegisterObjectMethod("any", "any &opAssign(any&in)", asFUNCTION(ScriptAnyAssignment_Generic), asCALL_GENERIC); assert( r >= 0 );
|
||||
r = engine->RegisterObjectMethod("any", "void store(?&in)", asFUNCTION(ScriptAny_Store_Generic), asCALL_GENERIC); assert( r >= 0 );
|
||||
r = engine->RegisterObjectMethod("any", "void store(const int64&in)", asFUNCTION(ScriptAny_StoreInt_Generic), asCALL_GENERIC); assert( r >= 0 );
|
||||
r = engine->RegisterObjectMethod("any", "void store(const double&in)", asFUNCTION(ScriptAny_StoreFlt_Generic), asCALL_GENERIC); assert( r >= 0 );
|
||||
r = engine->RegisterObjectMethod("any", "bool retrieve(?&out) const", asFUNCTION(ScriptAny_Retrieve_Generic), asCALL_GENERIC); assert( r >= 0 );
|
||||
r = engine->RegisterObjectMethod("any", "bool retrieve(int64&out) const", asFUNCTION(ScriptAny_RetrieveInt_Generic), asCALL_GENERIC); assert( r >= 0 );
|
||||
r = engine->RegisterObjectMethod("any", "bool retrieve(double&out) const", asFUNCTION(ScriptAny_RetrieveFlt_Generic), asCALL_GENERIC); assert( r >= 0 );
|
||||
|
||||
// Register GC behaviours
|
||||
r = engine->RegisterObjectBehaviour("any", asBEHAVE_GETREFCOUNT, "int f()", asFUNCTION(ScriptAny_GetRefCount_Generic), asCALL_GENERIC); assert( r >= 0 );
|
||||
r = engine->RegisterObjectBehaviour("any", asBEHAVE_SETGCFLAG, "void f()", asFUNCTION(ScriptAny_SetFlag_Generic), asCALL_GENERIC); assert( r >= 0 );
|
||||
r = engine->RegisterObjectBehaviour("any", asBEHAVE_GETGCFLAG, "bool f()", asFUNCTION(ScriptAny_GetFlag_Generic), asCALL_GENERIC); assert( r >= 0 );
|
||||
r = engine->RegisterObjectBehaviour("any", asBEHAVE_ENUMREFS, "void f(int&in)", asFUNCTION(ScriptAny_EnumReferences_Generic), asCALL_GENERIC); assert( r >= 0 );
|
||||
r = engine->RegisterObjectBehaviour("any", asBEHAVE_RELEASEREFS, "void f(int&in)", asFUNCTION(ScriptAny_ReleaseAllHandles_Generic), asCALL_GENERIC); assert( r >= 0 );
|
||||
}
|
||||
|
||||
|
||||
CScriptAny &CScriptAny::operator=(const CScriptAny &other)
|
||||
{
|
||||
// Hold on to the object type reference so it isn't destroyed too early
|
||||
if( (other.value.typeId & asTYPEID_MASK_OBJECT) )
|
||||
{
|
||||
asITypeInfo *ti = engine->GetTypeInfoById(other.value.typeId);
|
||||
if( ti )
|
||||
ti->AddRef();
|
||||
}
|
||||
|
||||
FreeObject();
|
||||
|
||||
value.typeId = other.value.typeId;
|
||||
if( value.typeId & asTYPEID_OBJHANDLE )
|
||||
{
|
||||
// For handles, copy the pointer and increment the reference count
|
||||
value.valueObj = other.value.valueObj;
|
||||
engine->AddRefScriptObject(value.valueObj, engine->GetTypeInfoById(value.typeId));
|
||||
}
|
||||
else if( value.typeId & asTYPEID_MASK_OBJECT )
|
||||
{
|
||||
// Create a copy of the object
|
||||
value.valueObj = engine->CreateScriptObjectCopy(other.value.valueObj, engine->GetTypeInfoById(value.typeId));
|
||||
}
|
||||
else
|
||||
{
|
||||
// Primitives can be copied directly
|
||||
value.valueInt = other.value.valueInt;
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
int CScriptAny::CopyFrom(const CScriptAny *other)
|
||||
{
|
||||
if( other == 0 ) return asINVALID_ARG;
|
||||
|
||||
*this = *(CScriptAny*)other;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
CScriptAny::CScriptAny(asIScriptEngine *engine)
|
||||
{
|
||||
this->engine = engine;
|
||||
refCount = 1;
|
||||
gcFlag = false;
|
||||
|
||||
value.typeId = 0;
|
||||
value.valueInt = 0;
|
||||
|
||||
// Notify the garbage collector of this object
|
||||
engine->NotifyGarbageCollectorOfNewObject(this, engine->GetTypeInfoByName("any"));
|
||||
}
|
||||
|
||||
CScriptAny::CScriptAny(void *ref, int refTypeId, asIScriptEngine *engine)
|
||||
{
|
||||
this->engine = engine;
|
||||
refCount = 1;
|
||||
gcFlag = false;
|
||||
|
||||
value.typeId = 0;
|
||||
value.valueInt = 0;
|
||||
|
||||
// Notify the garbage collector of this object
|
||||
engine->NotifyGarbageCollectorOfNewObject(this, engine->GetTypeInfoByName("any"));
|
||||
|
||||
Store(ref, refTypeId);
|
||||
}
|
||||
|
||||
CScriptAny::~CScriptAny()
|
||||
{
|
||||
FreeObject();
|
||||
}
|
||||
|
||||
void CScriptAny::Store(void *ref, int refTypeId)
|
||||
{
|
||||
// This method is not expected to be used for primitive types, except for bool, int64, or double
|
||||
assert( refTypeId > asTYPEID_DOUBLE || refTypeId == asTYPEID_VOID || refTypeId == asTYPEID_BOOL || refTypeId == asTYPEID_INT64 || refTypeId == asTYPEID_DOUBLE );
|
||||
|
||||
// Hold on to the object type reference so it isn't destroyed too early
|
||||
if( (refTypeId & asTYPEID_MASK_OBJECT) )
|
||||
{
|
||||
asITypeInfo *ti = engine->GetTypeInfoById(refTypeId);
|
||||
if( ti )
|
||||
ti->AddRef();
|
||||
}
|
||||
|
||||
FreeObject();
|
||||
|
||||
value.typeId = refTypeId;
|
||||
if( value.typeId & asTYPEID_OBJHANDLE )
|
||||
{
|
||||
// We're receiving a reference to the handle, so we need to dereference it
|
||||
value.valueObj = *(void**)ref;
|
||||
engine->AddRefScriptObject(value.valueObj, engine->GetTypeInfoById(value.typeId));
|
||||
}
|
||||
else if( value.typeId & asTYPEID_MASK_OBJECT )
|
||||
{
|
||||
// Create a copy of the object
|
||||
value.valueObj = engine->CreateScriptObjectCopy(ref, engine->GetTypeInfoById(value.typeId));
|
||||
}
|
||||
else
|
||||
{
|
||||
// Primitives can be copied directly
|
||||
value.valueInt = 0;
|
||||
|
||||
// Copy the primitive value
|
||||
// We receive a pointer to the value.
|
||||
int size = engine->GetSizeOfPrimitiveType(value.typeId);
|
||||
memcpy(&value.valueInt, ref, size);
|
||||
}
|
||||
}
|
||||
|
||||
void CScriptAny::Store(double &ref)
|
||||
{
|
||||
Store(&ref, asTYPEID_DOUBLE);
|
||||
}
|
||||
|
||||
void CScriptAny::Store(asINT64 &ref)
|
||||
{
|
||||
Store(&ref, asTYPEID_INT64);
|
||||
}
|
||||
|
||||
|
||||
bool CScriptAny::Retrieve(void *ref, int refTypeId) const
|
||||
{
|
||||
// This method is not expected to be used for primitive types, except for bool, int64, or double
|
||||
assert( refTypeId > asTYPEID_DOUBLE || refTypeId == asTYPEID_BOOL || refTypeId == asTYPEID_INT64 || refTypeId == asTYPEID_DOUBLE );
|
||||
|
||||
if( refTypeId & asTYPEID_OBJHANDLE )
|
||||
{
|
||||
// Is the handle type compatible with the stored value?
|
||||
|
||||
// A handle can be retrieved if the stored type is a handle of same or compatible type
|
||||
// or if the stored type is an object that implements the interface that the handle refer to.
|
||||
if( (value.typeId & asTYPEID_MASK_OBJECT) )
|
||||
{
|
||||
// Don't allow the retrieval if the stored handle is to a const object but not the wanted handle
|
||||
if( (value.typeId & asTYPEID_HANDLETOCONST) && !(refTypeId & asTYPEID_HANDLETOCONST) )
|
||||
return false;
|
||||
|
||||
// RefCastObject will increment the refCount of the returned pointer if successful
|
||||
engine->RefCastObject(value.valueObj, engine->GetTypeInfoById(value.typeId), engine->GetTypeInfoById(refTypeId), reinterpret_cast<void**>(ref));
|
||||
if( *(asPWORD*)ref == 0 )
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else if( refTypeId & asTYPEID_MASK_OBJECT )
|
||||
{
|
||||
// Is the object type compatible with the stored value?
|
||||
|
||||
// Copy the object into the given reference
|
||||
if( value.typeId == refTypeId )
|
||||
{
|
||||
engine->AssignScriptObject(ref, value.valueObj, engine->GetTypeInfoById(value.typeId));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Is the primitive type compatible with the stored value?
|
||||
|
||||
if( value.typeId == refTypeId )
|
||||
{
|
||||
int size = engine->GetSizeOfPrimitiveType(refTypeId);
|
||||
memcpy(ref, &value.valueInt, size);
|
||||
return true;
|
||||
}
|
||||
|
||||
// We know all numbers are stored as either int64 or double, since we register overloaded functions for those
|
||||
if( value.typeId == asTYPEID_INT64 && refTypeId == asTYPEID_DOUBLE )
|
||||
{
|
||||
*(double*)ref = double(value.valueInt);
|
||||
return true;
|
||||
}
|
||||
else if( value.typeId == asTYPEID_DOUBLE && refTypeId == asTYPEID_INT64 )
|
||||
{
|
||||
*(asINT64*)ref = asINT64(value.valueFlt);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool CScriptAny::Retrieve(asINT64 &outValue) const
|
||||
{
|
||||
return Retrieve(&outValue, asTYPEID_INT64);
|
||||
}
|
||||
|
||||
bool CScriptAny::Retrieve(double &outValue) const
|
||||
{
|
||||
return Retrieve(&outValue, asTYPEID_DOUBLE);
|
||||
}
|
||||
|
||||
int CScriptAny::GetTypeId() const
|
||||
{
|
||||
return value.typeId;
|
||||
}
|
||||
|
||||
void CScriptAny::FreeObject()
|
||||
{
|
||||
// If it is a handle or a ref counted object, call release
|
||||
if( value.typeId & asTYPEID_MASK_OBJECT )
|
||||
{
|
||||
// Let the engine release the object
|
||||
asITypeInfo *ti = engine->GetTypeInfoById(value.typeId);
|
||||
engine->ReleaseScriptObject(value.valueObj, ti);
|
||||
|
||||
// Release the object type info
|
||||
if( ti )
|
||||
ti->Release();
|
||||
|
||||
value.valueObj = 0;
|
||||
value.typeId = 0;
|
||||
}
|
||||
|
||||
// For primitives, there's nothing to do
|
||||
}
|
||||
|
||||
|
||||
void CScriptAny::EnumReferences(asIScriptEngine *inEngine)
|
||||
{
|
||||
// If we're holding a reference, we'll notify the garbage collector of it
|
||||
if (value.valueObj && (value.typeId & asTYPEID_MASK_OBJECT))
|
||||
{
|
||||
asITypeInfo *subType = engine->GetTypeInfoById(value.typeId);
|
||||
if ((subType->GetFlags() & asOBJ_REF))
|
||||
{
|
||||
inEngine->GCEnumCallback(value.valueObj);
|
||||
}
|
||||
else if ((subType->GetFlags() & asOBJ_VALUE) && (subType->GetFlags() & asOBJ_GC))
|
||||
{
|
||||
// For value types we need to forward the enum callback
|
||||
// to the object so it can decide what to do
|
||||
engine->ForwardGCEnumReferences(value.valueObj, subType);
|
||||
}
|
||||
|
||||
// The object type itself is also garbage collected
|
||||
asITypeInfo *ti = inEngine->GetTypeInfoById(value.typeId);
|
||||
if (ti)
|
||||
inEngine->GCEnumCallback(ti);
|
||||
}
|
||||
}
|
||||
|
||||
void CScriptAny::ReleaseAllHandles(asIScriptEngine * /*engine*/)
|
||||
{
|
||||
FreeObject();
|
||||
}
|
||||
|
||||
int CScriptAny::AddRef() const
|
||||
{
|
||||
// Increase counter and clear flag set by GC
|
||||
gcFlag = false;
|
||||
return asAtomicInc(refCount);
|
||||
}
|
||||
|
||||
int CScriptAny::Release() const
|
||||
{
|
||||
// Decrease the ref counter
|
||||
gcFlag = false;
|
||||
if( asAtomicDec(refCount) == 0 )
|
||||
{
|
||||
// Delete this object as no more references to it exists
|
||||
delete this;
|
||||
return 0;
|
||||
}
|
||||
|
||||
return refCount;
|
||||
}
|
||||
|
||||
int CScriptAny::GetRefCount()
|
||||
{
|
||||
return refCount;
|
||||
}
|
||||
|
||||
void CScriptAny::SetFlag()
|
||||
{
|
||||
gcFlag = true;
|
||||
}
|
||||
|
||||
bool CScriptAny::GetFlag()
|
||||
{
|
||||
return gcFlag;
|
||||
}
|
||||
|
||||
|
||||
END_AS_NAMESPACE
|
||||
76
add_on/scriptany/scriptany.h
Normal file
76
add_on/scriptany/scriptany.h
Normal file
@@ -0,0 +1,76 @@
|
||||
#ifndef SCRIPTANY_H
|
||||
#define SCRIPTANY_H
|
||||
|
||||
#ifndef ANGELSCRIPT_H
|
||||
// Avoid having to inform include path if header is already include before
|
||||
#include <angelscript.h>
|
||||
#endif
|
||||
|
||||
|
||||
BEGIN_AS_NAMESPACE
|
||||
|
||||
class CScriptAny
|
||||
{
|
||||
public:
|
||||
// Constructors
|
||||
CScriptAny(asIScriptEngine *engine);
|
||||
CScriptAny(void *ref, int refTypeId, asIScriptEngine *engine);
|
||||
|
||||
// Memory management
|
||||
int AddRef() const;
|
||||
int Release() const;
|
||||
|
||||
// Copy the stored value from another any object
|
||||
CScriptAny &operator=(const CScriptAny&);
|
||||
int CopyFrom(const CScriptAny *other);
|
||||
|
||||
// Store the value, either as variable type, integer number, or real number
|
||||
void Store(void *ref, int refTypeId);
|
||||
void Store(asINT64 &value);
|
||||
void Store(double &value);
|
||||
|
||||
// Retrieve the stored value, either as variable type, integer number, or real number
|
||||
bool Retrieve(void *ref, int refTypeId) const;
|
||||
bool Retrieve(asINT64 &value) const;
|
||||
bool Retrieve(double &value) const;
|
||||
|
||||
// Get the type id of the stored value
|
||||
int GetTypeId() const;
|
||||
|
||||
// GC methods
|
||||
int GetRefCount();
|
||||
void SetFlag();
|
||||
bool GetFlag();
|
||||
void EnumReferences(asIScriptEngine *engine);
|
||||
void ReleaseAllHandles(asIScriptEngine *engine);
|
||||
|
||||
protected:
|
||||
virtual ~CScriptAny();
|
||||
void FreeObject();
|
||||
|
||||
mutable int refCount;
|
||||
mutable bool gcFlag;
|
||||
asIScriptEngine *engine;
|
||||
|
||||
// The structure for holding the values
|
||||
struct valueStruct
|
||||
{
|
||||
union
|
||||
{
|
||||
asINT64 valueInt;
|
||||
double valueFlt;
|
||||
void *valueObj;
|
||||
};
|
||||
int typeId;
|
||||
};
|
||||
|
||||
valueStruct value;
|
||||
};
|
||||
|
||||
void RegisterScriptAny(asIScriptEngine *engine);
|
||||
void RegisterScriptAny_Native(asIScriptEngine *engine);
|
||||
void RegisterScriptAny_Generic(asIScriptEngine *engine);
|
||||
|
||||
END_AS_NAMESPACE
|
||||
|
||||
#endif
|
||||
2186
add_on/scriptarray/scriptarray.cpp
Normal file
2186
add_on/scriptarray/scriptarray.cpp
Normal file
File diff suppressed because it is too large
Load Diff
142
add_on/scriptarray/scriptarray.h
Normal file
142
add_on/scriptarray/scriptarray.h
Normal file
@@ -0,0 +1,142 @@
|
||||
#ifndef SCRIPTARRAY_H
|
||||
#define SCRIPTARRAY_H
|
||||
|
||||
#ifndef ANGELSCRIPT_H
|
||||
// Avoid having to inform include path if header is already include before
|
||||
#include <angelscript.h>
|
||||
#endif
|
||||
|
||||
// Sometimes it may be desired to use the same method names as used by C++ STL.
|
||||
// This may for example reduce time when converting code from script to C++ or
|
||||
// back.
|
||||
//
|
||||
// 0 = off
|
||||
// 1 = on
|
||||
#ifndef AS_USE_STLNAMES
|
||||
#define AS_USE_STLNAMES 0
|
||||
#endif
|
||||
|
||||
// Some prefer to use property accessors to get/set the length of the array
|
||||
// This option registers the accessors instead of the method length()
|
||||
#ifndef AS_USE_ACCESSORS
|
||||
#define AS_USE_ACCESSORS 0
|
||||
#endif
|
||||
|
||||
BEGIN_AS_NAMESPACE
|
||||
|
||||
struct SArrayBuffer;
|
||||
struct SArrayCache;
|
||||
|
||||
class CScriptArray
|
||||
{
|
||||
public:
|
||||
// Set the memory functions that should be used by all CScriptArrays
|
||||
static void SetMemoryFunctions(asALLOCFUNC_t allocFunc, asFREEFUNC_t freeFunc);
|
||||
|
||||
// Factory functions
|
||||
static CScriptArray *Create(asITypeInfo *ot);
|
||||
static CScriptArray *Create(asITypeInfo *ot, asUINT length);
|
||||
static CScriptArray *Create(asITypeInfo *ot, asUINT length, void *defaultValue);
|
||||
static CScriptArray *Create(asITypeInfo *ot, void *listBuffer);
|
||||
|
||||
// Memory management
|
||||
void AddRef() const;
|
||||
void Release() const;
|
||||
|
||||
// Type information
|
||||
asITypeInfo *GetArrayObjectType() const;
|
||||
int GetArrayTypeId() const;
|
||||
int GetElementTypeId() const;
|
||||
|
||||
// Get the current size
|
||||
asUINT GetSize() const;
|
||||
|
||||
// Returns true if the array is empty
|
||||
bool IsEmpty() const;
|
||||
|
||||
// Pre-allocates memory for elements
|
||||
void Reserve(asUINT maxElements);
|
||||
|
||||
// Resize the array
|
||||
void Resize(asUINT numElements);
|
||||
|
||||
// Get a pointer to an element. Returns 0 if out of bounds
|
||||
void *At(asUINT index);
|
||||
const void *At(asUINT index) const;
|
||||
|
||||
// Set value of an element.
|
||||
// The value arg should be a pointer to the value that will be copied to the element.
|
||||
// Remember, if the array holds handles the value parameter should be the
|
||||
// address of the handle. The refCount of the object will also be incremented
|
||||
void SetValue(asUINT index, void *value);
|
||||
|
||||
// Copy the contents of one array to another (only if the types are the same)
|
||||
CScriptArray &operator=(const CScriptArray&);
|
||||
|
||||
// Compare two arrays
|
||||
bool operator==(const CScriptArray &) const;
|
||||
|
||||
// Array manipulation
|
||||
void InsertAt(asUINT index, void *value);
|
||||
void InsertAt(asUINT index, const CScriptArray &arr);
|
||||
void InsertLast(void *value);
|
||||
void RemoveAt(asUINT index);
|
||||
void RemoveLast();
|
||||
void RemoveRange(asUINT start, asUINT count);
|
||||
void SortAsc();
|
||||
void SortDesc();
|
||||
void SortAsc(asUINT startAt, asUINT count);
|
||||
void SortDesc(asUINT startAt, asUINT count);
|
||||
void Sort(asUINT startAt, asUINT count, bool asc);
|
||||
void Sort(asIScriptFunction *less, asUINT startAt, asUINT count);
|
||||
void Reverse();
|
||||
int Find(void *value) const;
|
||||
int Find(asUINT startAt, void *value) const;
|
||||
int FindByRef(void *ref) const;
|
||||
int FindByRef(asUINT startAt, void *ref) const;
|
||||
|
||||
// Return the address of internal buffer for direct manipulation of elements
|
||||
void *GetBuffer();
|
||||
|
||||
// GC methods
|
||||
int GetRefCount();
|
||||
void SetFlag();
|
||||
bool GetFlag();
|
||||
void EnumReferences(asIScriptEngine *engine);
|
||||
void ReleaseAllHandles(asIScriptEngine *engine);
|
||||
|
||||
protected:
|
||||
mutable int refCount;
|
||||
mutable bool gcFlag;
|
||||
asITypeInfo *objType;
|
||||
SArrayBuffer *buffer;
|
||||
int elementSize;
|
||||
int subTypeId;
|
||||
|
||||
// Constructors
|
||||
CScriptArray(asITypeInfo *ot, void *initBuf); // Called from script when initialized with list
|
||||
CScriptArray(asUINT length, asITypeInfo *ot);
|
||||
CScriptArray(asUINT length, void *defVal, asITypeInfo *ot);
|
||||
CScriptArray(const CScriptArray &other);
|
||||
virtual ~CScriptArray();
|
||||
|
||||
bool Less(const void *a, const void *b, bool asc);
|
||||
void *GetArrayItemPointer(int index);
|
||||
void *GetDataPointer(void *buffer);
|
||||
void Copy(void *dst, void *src);
|
||||
void Precache();
|
||||
bool CheckMaxSize(asUINT numElements);
|
||||
void Resize(int delta, asUINT at);
|
||||
void CreateBuffer(SArrayBuffer **buf, asUINT numElements);
|
||||
void DeleteBuffer(SArrayBuffer *buf);
|
||||
void CopyBuffer(SArrayBuffer *dst, SArrayBuffer *src);
|
||||
void Construct(SArrayBuffer *buf, asUINT start, asUINT end);
|
||||
void Destruct(SArrayBuffer *buf, asUINT start, asUINT end);
|
||||
bool Equals(const void *a, const void *b, asIScriptContext *ctx, SArrayCache *cache) const;
|
||||
};
|
||||
|
||||
void RegisterScriptArray(asIScriptEngine *engine, bool defaultArray);
|
||||
|
||||
END_AS_NAMESPACE
|
||||
|
||||
#endif
|
||||
1167
add_on/scriptbuilder/scriptbuilder.cpp
Normal file
1167
add_on/scriptbuilder/scriptbuilder.cpp
Normal file
File diff suppressed because it is too large
Load Diff
216
add_on/scriptbuilder/scriptbuilder.h
Normal file
216
add_on/scriptbuilder/scriptbuilder.h
Normal file
@@ -0,0 +1,216 @@
|
||||
#ifndef SCRIPTBUILDER_H
|
||||
#define SCRIPTBUILDER_H
|
||||
|
||||
//---------------------------
|
||||
// Compilation settings
|
||||
//
|
||||
|
||||
// Set this flag to turn on/off metadata processing
|
||||
// 0 = off
|
||||
// 1 = on
|
||||
#ifndef AS_PROCESS_METADATA
|
||||
#define AS_PROCESS_METADATA 1
|
||||
#endif
|
||||
|
||||
// TODO: Implement flags for turning on/off include directives and conditional programming
|
||||
|
||||
|
||||
|
||||
//---------------------------
|
||||
// Declaration
|
||||
//
|
||||
|
||||
#ifndef ANGELSCRIPT_H
|
||||
// Avoid having to inform include path if header is already include before
|
||||
#include <angelscript.h>
|
||||
#endif
|
||||
|
||||
|
||||
#if defined(_MSC_VER) && _MSC_VER <= 1200
|
||||
// disable the annoying warnings on MSVC 6
|
||||
#pragma warning (disable:4786)
|
||||
#endif
|
||||
|
||||
#include <string>
|
||||
#include <map>
|
||||
#include <set>
|
||||
#include <vector>
|
||||
#include <string.h> // _strcmpi
|
||||
|
||||
BEGIN_AS_NAMESPACE
|
||||
|
||||
class CScriptBuilder;
|
||||
|
||||
// This callback will be called for each #include directive encountered by the
|
||||
// builder. The callback should call the AddSectionFromFile or AddSectionFromMemory
|
||||
// to add the included section to the script. If the include cannot be resolved
|
||||
// then the function should return a negative value to abort the compilation.
|
||||
typedef int (*INCLUDECALLBACK_t)(const char *include, const char *from, CScriptBuilder *builder, void *userParam);
|
||||
|
||||
// This callback will be called for each #pragma directive encountered by the builder.
|
||||
// The application can interpret the pragmaText and decide what do to based on that.
|
||||
// If the callback returns a negative value the builder will report an error and abort the compilation.
|
||||
typedef int(*PRAGMACALLBACK_t)(const std::string &pragmaText, CScriptBuilder &builder, void *userParam);
|
||||
|
||||
// Helper class for loading and pre-processing script files to
|
||||
// support include directives and metadata declarations
|
||||
class CScriptBuilder
|
||||
{
|
||||
public:
|
||||
CScriptBuilder();
|
||||
|
||||
// Start a new module
|
||||
int StartNewModule(asIScriptEngine *engine, const char *moduleName);
|
||||
|
||||
// Load a script section from a file on disk
|
||||
// Returns 1 if the file was included
|
||||
// 0 if the file had already been included before
|
||||
// <0 on error
|
||||
int AddSectionFromFile(const char *filename);
|
||||
|
||||
// Load a script section from memory
|
||||
// Returns 1 if the section was included
|
||||
// 0 if a section with the same name had already been included before
|
||||
// <0 on error
|
||||
int AddSectionFromMemory(const char *sectionName,
|
||||
const char *scriptCode,
|
||||
unsigned int scriptLength = 0,
|
||||
int lineOffset = 0);
|
||||
|
||||
// Build the added script sections
|
||||
int BuildModule();
|
||||
|
||||
// Returns the engine
|
||||
asIScriptEngine *GetEngine();
|
||||
|
||||
// Returns the current module
|
||||
asIScriptModule *GetModule();
|
||||
|
||||
// Register the callback for resolving include directive
|
||||
void SetIncludeCallback(INCLUDECALLBACK_t callback, void *userParam);
|
||||
|
||||
// Register the callback for resolving pragma directive
|
||||
void SetPragmaCallback(PRAGMACALLBACK_t callback, void *userParam);
|
||||
|
||||
// Add a pre-processor define for conditional compilation
|
||||
void DefineWord(const char *word);
|
||||
|
||||
// Enumerate included script sections
|
||||
unsigned int GetSectionCount() const;
|
||||
std::string GetSectionName(unsigned int idx) const;
|
||||
|
||||
#if AS_PROCESS_METADATA == 1
|
||||
// Get metadata declared for classes, interfaces, and enums
|
||||
std::vector<std::string> GetMetadataForType(int typeId);
|
||||
|
||||
// Get metadata declared for functions
|
||||
std::vector<std::string> GetMetadataForFunc(asIScriptFunction *func);
|
||||
|
||||
// Get metadata declared for global variables
|
||||
std::vector<std::string> GetMetadataForVar(int varIdx);
|
||||
|
||||
// Get metadata declared for class variables
|
||||
std::vector<std::string> GetMetadataForTypeProperty(int typeId, int varIdx);
|
||||
|
||||
// Get metadata declared for class methods
|
||||
std::vector<std::string> GetMetadataForTypeMethod(int typeId, asIScriptFunction *method);
|
||||
#endif
|
||||
|
||||
protected:
|
||||
void ClearAll();
|
||||
int Build();
|
||||
int ProcessScriptSection(const char *script, unsigned int length, const char *sectionname, int lineOffset);
|
||||
int LoadScriptSection(const char *filename);
|
||||
bool IncludeIfNotAlreadyIncluded(const char *filename);
|
||||
|
||||
int SkipStatement(int pos);
|
||||
|
||||
int ExcludeCode(int start);
|
||||
void OverwriteCode(int start, int len);
|
||||
|
||||
asIScriptEngine *engine;
|
||||
asIScriptModule *module;
|
||||
std::string modifiedScript;
|
||||
|
||||
INCLUDECALLBACK_t includeCallback;
|
||||
void *includeParam;
|
||||
|
||||
PRAGMACALLBACK_t pragmaCallback;
|
||||
void *pragmaParam;
|
||||
|
||||
#if AS_PROCESS_METADATA == 1
|
||||
int ExtractMetadata(int pos, std::vector<std::string> &outMetadata);
|
||||
int ExtractDeclaration(int pos, std::string &outName, std::string &outDeclaration, int &outType);
|
||||
|
||||
enum METADATATYPE
|
||||
{
|
||||
MDT_TYPE = 1,
|
||||
MDT_FUNC = 2,
|
||||
MDT_VAR = 3,
|
||||
MDT_VIRTPROP = 4,
|
||||
MDT_FUNC_OR_VAR = 5
|
||||
};
|
||||
|
||||
// Temporary structure for storing metadata and declaration
|
||||
struct SMetadataDecl
|
||||
{
|
||||
SMetadataDecl(std::vector<std::string> m, std::string n, std::string d, int t, std::string c, std::string ns) : metadata(m), name(n), declaration(d), type(t), parentClass(c), nameSpace(ns) {}
|
||||
std::vector<std::string> metadata;
|
||||
std::string name;
|
||||
std::string declaration;
|
||||
int type;
|
||||
std::string parentClass;
|
||||
std::string nameSpace;
|
||||
};
|
||||
std::vector<SMetadataDecl> foundDeclarations;
|
||||
std::string currentClass;
|
||||
std::string currentNamespace;
|
||||
|
||||
// Storage of metadata for global declarations
|
||||
std::map<int, std::vector<std::string> > typeMetadataMap;
|
||||
std::map<int, std::vector<std::string> > funcMetadataMap;
|
||||
std::map<int, std::vector<std::string> > varMetadataMap;
|
||||
|
||||
// Storage of metadata for class member declarations
|
||||
struct SClassMetadata
|
||||
{
|
||||
SClassMetadata(const std::string& aName) : className(aName) {}
|
||||
std::string className;
|
||||
std::map<int, std::vector<std::string> > funcMetadataMap;
|
||||
std::map<int, std::vector<std::string> > varMetadataMap;
|
||||
};
|
||||
std::map<int, SClassMetadata> classMetadataMap;
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef _WIN32
|
||||
// On Windows the filenames are case insensitive so the comparisons to
|
||||
// avoid duplicate includes must also be case insensitive. True case insensitive
|
||||
// is not easy as it must be language aware, but a simple implementation such
|
||||
// as strcmpi should suffice in almost all cases.
|
||||
//
|
||||
// ref: http://www.gotw.ca/gotw/029.htm
|
||||
// ref: https://msdn.microsoft.com/en-us/library/windows/desktop/dd317761(v=vs.85).aspx
|
||||
// ref: http://site.icu-project.org/
|
||||
|
||||
// TODO: Strings by default are treated as UTF8 encoded. If the application choses to
|
||||
// use a different encoding, the comparison algorithm should be adjusted as well
|
||||
|
||||
struct ci_less
|
||||
{
|
||||
bool operator()(const std::string &a, const std::string &b) const
|
||||
{
|
||||
return _stricmp(a.c_str(), b.c_str()) < 0;
|
||||
}
|
||||
};
|
||||
std::set<std::string, ci_less> includedScripts;
|
||||
#else
|
||||
std::set<std::string> includedScripts;
|
||||
#endif
|
||||
|
||||
std::set<std::string> definedWords;
|
||||
};
|
||||
|
||||
END_AS_NAMESPACE
|
||||
|
||||
#endif
|
||||
1299
add_on/scriptdictionary/scriptdictionary.cpp
Normal file
1299
add_on/scriptdictionary/scriptdictionary.cpp
Normal file
File diff suppressed because it is too large
Load Diff
240
add_on/scriptdictionary/scriptdictionary.h
Normal file
240
add_on/scriptdictionary/scriptdictionary.h
Normal file
@@ -0,0 +1,240 @@
|
||||
#ifndef SCRIPTDICTIONARY_H
|
||||
#define SCRIPTDICTIONARY_H
|
||||
|
||||
// The dictionary class relies on the script string object, thus the script
|
||||
// string type must be registered with the engine before registering the
|
||||
// dictionary type
|
||||
|
||||
#ifndef ANGELSCRIPT_H
|
||||
// Avoid having to inform include path if header is already include before
|
||||
#include <angelscript.h>
|
||||
#endif
|
||||
|
||||
// By default the CScriptDictionary use the std::string for the keys.
|
||||
// If the application uses a custom string type, then this typedef
|
||||
// can be changed accordingly.
|
||||
#include <string>
|
||||
typedef std::string dictKey_t;
|
||||
|
||||
// Forward declare CScriptDictValue so we can typedef the internal map type
|
||||
BEGIN_AS_NAMESPACE
|
||||
class CScriptDictValue;
|
||||
END_AS_NAMESPACE
|
||||
|
||||
// C++11 introduced the std::unordered_map which is a hash map which is
|
||||
// is generally more performatic for lookups than the std::map which is a
|
||||
// binary tree.
|
||||
// TODO: memory: The map allocator should use the asAllocMem and asFreeMem
|
||||
#if AS_CAN_USE_CPP11
|
||||
#include <unordered_map>
|
||||
typedef std::unordered_map<dictKey_t, AS_NAMESPACE_QUALIFIER CScriptDictValue> dictMap_t;
|
||||
#else
|
||||
#include <map>
|
||||
typedef std::map<dictKey_t, AS_NAMESPACE_QUALIFIER CScriptDictValue> dictMap_t;
|
||||
#endif
|
||||
|
||||
|
||||
#ifdef _MSC_VER
|
||||
// Turn off annoying warnings about truncated symbol names
|
||||
#pragma warning (disable:4786)
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
|
||||
// Sometimes it may be desired to use the same method names as used by C++ STL.
|
||||
// This may for example reduce time when converting code from script to C++ or
|
||||
// back.
|
||||
//
|
||||
// 0 = off
|
||||
// 1 = on
|
||||
|
||||
#ifndef AS_USE_STLNAMES
|
||||
#define AS_USE_STLNAMES 0
|
||||
#endif
|
||||
|
||||
|
||||
BEGIN_AS_NAMESPACE
|
||||
|
||||
class CScriptArray;
|
||||
class CScriptDictionary;
|
||||
|
||||
class CScriptDictValue
|
||||
{
|
||||
public:
|
||||
// This class must not be declared as local variable in C++, because it needs
|
||||
// to receive the script engine pointer in all operations. The engine pointer
|
||||
// is not kept as member in order to keep the size down
|
||||
CScriptDictValue();
|
||||
CScriptDictValue(asIScriptEngine *engine, void *value, int typeId);
|
||||
|
||||
// Destructor must not be called without first calling FreeValue, otherwise a memory leak will occur
|
||||
~CScriptDictValue();
|
||||
|
||||
// Replace the stored value
|
||||
void Set(asIScriptEngine *engine, void *value, int typeId);
|
||||
void Set(asIScriptEngine *engine, const asINT64 &value);
|
||||
void Set(asIScriptEngine *engine, const double &value);
|
||||
void Set(asIScriptEngine *engine, CScriptDictValue &value);
|
||||
|
||||
// Gets the stored value. Returns false if the value isn't compatible with the informed typeId
|
||||
bool Get(asIScriptEngine *engine, void *value, int typeId) const;
|
||||
bool Get(asIScriptEngine *engine, asINT64 &value) const;
|
||||
bool Get(asIScriptEngine *engine, double &value) const;
|
||||
|
||||
// Returns the address of the stored value for inspection
|
||||
const void *GetAddressOfValue() const;
|
||||
|
||||
// Returns the type id of the stored value
|
||||
int GetTypeId() const;
|
||||
|
||||
// Free the stored value
|
||||
void FreeValue(asIScriptEngine *engine);
|
||||
|
||||
// GC callback
|
||||
void EnumReferences(asIScriptEngine *engine);
|
||||
|
||||
protected:
|
||||
friend class CScriptDictionary;
|
||||
|
||||
union
|
||||
{
|
||||
asINT64 m_valueInt;
|
||||
double m_valueFlt;
|
||||
void *m_valueObj;
|
||||
};
|
||||
int m_typeId;
|
||||
};
|
||||
|
||||
class CScriptDictionary
|
||||
{
|
||||
public:
|
||||
// Factory functions
|
||||
static CScriptDictionary *Create(asIScriptEngine *engine);
|
||||
|
||||
// Called from the script to instantiate a dictionary from an initialization list
|
||||
static CScriptDictionary *Create(asBYTE *buffer);
|
||||
|
||||
// Reference counting
|
||||
void AddRef() const;
|
||||
void Release() const;
|
||||
|
||||
// Reassign the dictionary
|
||||
CScriptDictionary &operator =(const CScriptDictionary &other);
|
||||
|
||||
// Sets a key/value pair
|
||||
void Set(const dictKey_t &key, void *value, int typeId);
|
||||
void Set(const dictKey_t &key, const asINT64 &value);
|
||||
void Set(const dictKey_t &key, const double &value);
|
||||
|
||||
// Gets the stored value. Returns false if the value isn't compatible with the informed typeId
|
||||
bool Get(const dictKey_t &key, void *value, int typeId) const;
|
||||
bool Get(const dictKey_t &key, asINT64 &value) const;
|
||||
bool Get(const dictKey_t &key, double &value) const;
|
||||
|
||||
// Index accessors. If the dictionary is not const it inserts the value if it doesn't already exist
|
||||
// If the dictionary is const then a script exception is set if it doesn't exist and a null pointer is returned
|
||||
CScriptDictValue *operator[](const dictKey_t &key);
|
||||
const CScriptDictValue *operator[](const dictKey_t &key) const;
|
||||
|
||||
// Returns the type id of the stored value, or negative if it doesn't exist
|
||||
int GetTypeId(const dictKey_t &key) const;
|
||||
|
||||
// Returns true if the key is set
|
||||
bool Exists(const dictKey_t &key) const;
|
||||
|
||||
// Returns true if there are no key/value pairs in the dictionary
|
||||
bool IsEmpty() const;
|
||||
|
||||
// Returns the number of key/value pairs in the dictionary
|
||||
asUINT GetSize() const;
|
||||
|
||||
// Deletes the key
|
||||
bool Delete(const dictKey_t &key);
|
||||
|
||||
// Deletes all keys
|
||||
void DeleteAll();
|
||||
|
||||
// Get an array of all keys
|
||||
CScriptArray *GetKeys() const;
|
||||
|
||||
// STL style iterator
|
||||
class CIterator
|
||||
{
|
||||
public:
|
||||
void operator++(); // Pre-increment
|
||||
void operator++(int); // Post-increment
|
||||
|
||||
// This is needed to support C++11 range-for
|
||||
CIterator &operator*();
|
||||
|
||||
bool operator==(const CIterator &other) const;
|
||||
bool operator!=(const CIterator &other) const;
|
||||
|
||||
// Accessors
|
||||
const dictKey_t &GetKey() const;
|
||||
int GetTypeId() const;
|
||||
bool GetValue(asINT64 &value) const;
|
||||
bool GetValue(double &value) const;
|
||||
bool GetValue(void *value, int typeId) const;
|
||||
const void * GetAddressOfValue() const;
|
||||
|
||||
protected:
|
||||
friend class CScriptDictionary;
|
||||
|
||||
CIterator();
|
||||
CIterator(const CScriptDictionary &dict,
|
||||
dictMap_t::const_iterator it);
|
||||
|
||||
CIterator &operator=(const CIterator &) {return *this;} // Not used
|
||||
|
||||
dictMap_t::const_iterator m_it;
|
||||
const CScriptDictionary &m_dict;
|
||||
};
|
||||
|
||||
CIterator begin() const;
|
||||
CIterator end() const;
|
||||
CIterator find(const dictKey_t &key) const;
|
||||
|
||||
// Garbage collections behaviours
|
||||
int GetRefCount();
|
||||
void SetGCFlag();
|
||||
bool GetGCFlag();
|
||||
void EnumReferences(asIScriptEngine *engine);
|
||||
void ReleaseAllReferences(asIScriptEngine *engine);
|
||||
|
||||
protected:
|
||||
// Since the dictionary uses the asAllocMem and asFreeMem functions to allocate memory
|
||||
// the constructors are made protected so that the application cannot allocate it
|
||||
// manually in a different way
|
||||
CScriptDictionary(asIScriptEngine *engine);
|
||||
CScriptDictionary(asBYTE *buffer);
|
||||
|
||||
// We don't want anyone to call the destructor directly, it should be called through the Release method
|
||||
virtual ~CScriptDictionary();
|
||||
|
||||
// Cache the object types needed
|
||||
void Init(asIScriptEngine *engine);
|
||||
|
||||
// Our properties
|
||||
asIScriptEngine *engine;
|
||||
mutable int refCount;
|
||||
mutable bool gcFlag;
|
||||
dictMap_t dict;
|
||||
};
|
||||
|
||||
// This function will determine the configuration of the engine
|
||||
// and use one of the two functions below to register the dictionary object
|
||||
void RegisterScriptDictionary(asIScriptEngine *engine);
|
||||
|
||||
// Call this function to register the math functions
|
||||
// using native calling conventions
|
||||
void RegisterScriptDictionary_Native(asIScriptEngine *engine);
|
||||
|
||||
// Use this one instead if native calling conventions
|
||||
// are not supported on the target platform
|
||||
void RegisterScriptDictionary_Generic(asIScriptEngine *engine);
|
||||
|
||||
END_AS_NAMESPACE
|
||||
|
||||
#endif
|
||||
660
add_on/scriptfile/scriptfile.cpp
Normal file
660
add_on/scriptfile/scriptfile.cpp
Normal file
@@ -0,0 +1,660 @@
|
||||
#include "scriptfile.h"
|
||||
#include <new>
|
||||
#include <assert.h>
|
||||
#include <string>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#ifdef _WIN32_WCE
|
||||
#include <windows.h> // For GetModuleFileName
|
||||
#ifdef GetObject
|
||||
#undef GetObject
|
||||
#endif
|
||||
#endif
|
||||
|
||||
using namespace std;
|
||||
|
||||
BEGIN_AS_NAMESPACE
|
||||
|
||||
CScriptFile *ScriptFile_Factory()
|
||||
{
|
||||
return new CScriptFile();
|
||||
}
|
||||
|
||||
void ScriptFile_Factory_Generic(asIScriptGeneric *gen)
|
||||
{
|
||||
*(CScriptFile**)gen->GetAddressOfReturnLocation() = ScriptFile_Factory();
|
||||
}
|
||||
|
||||
void ScriptFile_AddRef_Generic(asIScriptGeneric *gen)
|
||||
{
|
||||
CScriptFile *file = (CScriptFile*)gen->GetObject();
|
||||
file->AddRef();
|
||||
}
|
||||
|
||||
void ScriptFile_Release_Generic(asIScriptGeneric *gen)
|
||||
{
|
||||
CScriptFile *file = (CScriptFile*)gen->GetObject();
|
||||
file->Release();
|
||||
}
|
||||
|
||||
void ScriptFile_Open_Generic(asIScriptGeneric *gen)
|
||||
{
|
||||
CScriptFile *file = (CScriptFile*)gen->GetObject();
|
||||
std::string *f = (std::string*)gen->GetArgAddress(0);
|
||||
std::string *m = (std::string*)gen->GetArgAddress(1);
|
||||
int r = file->Open(*f, *m);
|
||||
gen->SetReturnDWord(r);
|
||||
}
|
||||
|
||||
void ScriptFile_Close_Generic(asIScriptGeneric *gen)
|
||||
{
|
||||
CScriptFile *file = (CScriptFile*)gen->GetObject();
|
||||
int r = file->Close();
|
||||
gen->SetReturnDWord(r);
|
||||
}
|
||||
|
||||
void ScriptFile_GetSize_Generic(asIScriptGeneric *gen)
|
||||
{
|
||||
CScriptFile *file = (CScriptFile*)gen->GetObject();
|
||||
int r = file->GetSize();
|
||||
gen->SetReturnDWord(r);
|
||||
}
|
||||
|
||||
void ScriptFile_ReadString_Generic(asIScriptGeneric *gen)
|
||||
{
|
||||
CScriptFile *file = (CScriptFile*)gen->GetObject();
|
||||
int len = gen->GetArgDWord(0);
|
||||
string str = file->ReadString(len);
|
||||
gen->SetReturnObject(&str);
|
||||
}
|
||||
|
||||
void ScriptFile_ReadLine_Generic(asIScriptGeneric *gen)
|
||||
{
|
||||
CScriptFile *file = (CScriptFile*)gen->GetObject();
|
||||
std::string str = file->ReadLine();
|
||||
gen->SetReturnObject(&str);
|
||||
}
|
||||
|
||||
void ScriptFile_ReadInt_Generic(asIScriptGeneric *gen)
|
||||
{
|
||||
CScriptFile *file = (CScriptFile*)gen->GetObject();
|
||||
asUINT bytes = *(asUINT*)gen->GetAddressOfArg(0);
|
||||
*(asINT64*)gen->GetAddressOfReturnLocation() = file->ReadInt(bytes);
|
||||
}
|
||||
|
||||
void ScriptFile_ReadUInt_Generic(asIScriptGeneric *gen)
|
||||
{
|
||||
CScriptFile *file = (CScriptFile*)gen->GetObject();
|
||||
asUINT bytes = *(asUINT*)gen->GetAddressOfArg(0);
|
||||
*(asQWORD*)gen->GetAddressOfReturnLocation() = file->ReadUInt(bytes);
|
||||
}
|
||||
|
||||
void ScriptFile_ReadFloat_Generic(asIScriptGeneric *gen)
|
||||
{
|
||||
CScriptFile *file = (CScriptFile*)gen->GetObject();
|
||||
*(float*)gen->GetAddressOfReturnLocation() = file->ReadFloat();
|
||||
}
|
||||
|
||||
void ScriptFile_ReadDouble_Generic(asIScriptGeneric *gen)
|
||||
{
|
||||
CScriptFile *file = (CScriptFile*)gen->GetObject();
|
||||
*(double*)gen->GetAddressOfReturnLocation() = file->ReadDouble();
|
||||
}
|
||||
|
||||
void ScriptFile_WriteString_Generic(asIScriptGeneric *gen)
|
||||
{
|
||||
CScriptFile *file = (CScriptFile*)gen->GetObject();
|
||||
std::string *str = (std::string*)gen->GetArgAddress(0);
|
||||
gen->SetReturnDWord(file->WriteString(*str));
|
||||
}
|
||||
|
||||
void ScriptFile_WriteInt_Generic(asIScriptGeneric *gen)
|
||||
{
|
||||
CScriptFile *file = (CScriptFile*)gen->GetObject();
|
||||
asINT64 val = *(asINT64*)gen->GetAddressOfArg(0);
|
||||
asUINT bytes = *(asUINT*)gen->GetAddressOfArg(1);
|
||||
*(int*)gen->GetAddressOfReturnLocation() = file->WriteInt(val, bytes);
|
||||
}
|
||||
|
||||
void ScriptFile_WriteUInt_Generic(asIScriptGeneric *gen)
|
||||
{
|
||||
CScriptFile *file = (CScriptFile*)gen->GetObject();
|
||||
asQWORD val = *(asQWORD*)gen->GetAddressOfArg(0);
|
||||
asUINT bytes = *(asUINT*)gen->GetAddressOfArg(1);
|
||||
*(int*)gen->GetAddressOfReturnLocation() = file->WriteUInt(val, bytes);
|
||||
}
|
||||
|
||||
void ScriptFile_WriteFloat_Generic(asIScriptGeneric *gen)
|
||||
{
|
||||
CScriptFile *file = (CScriptFile*)gen->GetObject();
|
||||
float val = *(float*)gen->GetAddressOfArg(0);
|
||||
*(int*)gen->GetAddressOfReturnLocation() = file->WriteFloat(val);
|
||||
}
|
||||
|
||||
void ScriptFile_WriteDouble_Generic(asIScriptGeneric *gen)
|
||||
{
|
||||
CScriptFile *file = (CScriptFile*)gen->GetObject();
|
||||
double val = *(double*)gen->GetAddressOfArg(0);
|
||||
*(int*)gen->GetAddressOfReturnLocation() = file->WriteDouble(val);
|
||||
}
|
||||
|
||||
void ScriptFile_IsEOF_Generic(asIScriptGeneric *gen)
|
||||
{
|
||||
CScriptFile *file = (CScriptFile*)gen->GetObject();
|
||||
bool r = file->IsEOF();
|
||||
gen->SetReturnByte(r);
|
||||
}
|
||||
|
||||
void ScriptFile_GetPos_Generic(asIScriptGeneric *gen)
|
||||
{
|
||||
CScriptFile *file = (CScriptFile*)gen->GetObject();
|
||||
gen->SetReturnDWord(file->GetPos());
|
||||
}
|
||||
|
||||
void ScriptFile_SetPos_Generic(asIScriptGeneric *gen)
|
||||
{
|
||||
CScriptFile *file = (CScriptFile*)gen->GetObject();
|
||||
int pos = (int)gen->GetArgDWord(0);
|
||||
gen->SetReturnDWord(file->SetPos(pos));
|
||||
}
|
||||
|
||||
void ScriptFile_MovePos_Generic(asIScriptGeneric *gen)
|
||||
{
|
||||
CScriptFile *file = (CScriptFile*)gen->GetObject();
|
||||
int delta = (int)gen->GetArgDWord(0);
|
||||
gen->SetReturnDWord(file->MovePos(delta));
|
||||
}
|
||||
|
||||
void RegisterScriptFile_Native(asIScriptEngine *engine)
|
||||
{
|
||||
int r;
|
||||
|
||||
r = engine->RegisterObjectType("file", 0, asOBJ_REF); assert( r >= 0 );
|
||||
r = engine->RegisterObjectBehaviour("file", asBEHAVE_FACTORY, "file @f()", asFUNCTION(ScriptFile_Factory), asCALL_CDECL); assert( r >= 0 );
|
||||
r = engine->RegisterObjectBehaviour("file", asBEHAVE_ADDREF, "void f()", asMETHOD(CScriptFile,AddRef), asCALL_THISCALL); assert( r >= 0 );
|
||||
r = engine->RegisterObjectBehaviour("file", asBEHAVE_RELEASE, "void f()", asMETHOD(CScriptFile,Release), asCALL_THISCALL); assert( r >= 0 );
|
||||
|
||||
r = engine->RegisterObjectMethod("file", "int open(const string &in, const string &in)", asMETHOD(CScriptFile,Open), asCALL_THISCALL); assert( r >= 0 );
|
||||
r = engine->RegisterObjectMethod("file", "int close()", asMETHOD(CScriptFile,Close), asCALL_THISCALL); assert( r >= 0 );
|
||||
r = engine->RegisterObjectMethod("file", "int getSize() const", asMETHOD(CScriptFile,GetSize), asCALL_THISCALL); assert( r >= 0 );
|
||||
r = engine->RegisterObjectMethod("file", "bool isEndOfFile() const", asMETHOD(CScriptFile,IsEOF), asCALL_THISCALL); assert( r >= 0 );
|
||||
r = engine->RegisterObjectMethod("file", "string readString(uint)", asMETHOD(CScriptFile,ReadString), asCALL_THISCALL); assert( r >= 0 );
|
||||
r = engine->RegisterObjectMethod("file", "string readLine()", asMETHOD(CScriptFile,ReadLine), asCALL_THISCALL); assert( r >= 0 );
|
||||
r = engine->RegisterObjectMethod("file", "int64 readInt(uint)", asMETHOD(CScriptFile,ReadInt), asCALL_THISCALL); assert( r >= 0 );
|
||||
r = engine->RegisterObjectMethod("file", "uint64 readUInt(uint)", asMETHOD(CScriptFile,ReadUInt), asCALL_THISCALL); assert( r >= 0 );
|
||||
r = engine->RegisterObjectMethod("file", "float readFloat()", asMETHOD(CScriptFile,ReadFloat), asCALL_THISCALL); assert( r >= 0 );
|
||||
r = engine->RegisterObjectMethod("file", "double readDouble()", asMETHOD(CScriptFile,ReadDouble), asCALL_THISCALL); assert( r >= 0 );
|
||||
#if AS_WRITE_OPS == 1
|
||||
r = engine->RegisterObjectMethod("file", "int writeString(const string &in)", asMETHOD(CScriptFile,WriteString), asCALL_THISCALL); assert( r >= 0 );
|
||||
r = engine->RegisterObjectMethod("file", "int writeInt(int64, uint)", asMETHOD(CScriptFile,WriteInt), asCALL_THISCALL); assert( r >= 0 );
|
||||
r = engine->RegisterObjectMethod("file", "int writeUInt(uint64, uint)", asMETHOD(CScriptFile,WriteUInt), asCALL_THISCALL); assert( r >= 0 );
|
||||
r = engine->RegisterObjectMethod("file", "int writeFloat(float)", asMETHOD(CScriptFile,WriteFloat), asCALL_THISCALL); assert( r >= 0 );
|
||||
r = engine->RegisterObjectMethod("file", "int writeDouble(double)", asMETHOD(CScriptFile,WriteDouble), asCALL_THISCALL); assert( r >= 0 );
|
||||
#endif
|
||||
r = engine->RegisterObjectMethod("file", "int getPos() const", asMETHOD(CScriptFile,GetPos), asCALL_THISCALL); assert( r >= 0 );
|
||||
r = engine->RegisterObjectMethod("file", "int setPos(int)", asMETHOD(CScriptFile,SetPos), asCALL_THISCALL); assert( r >= 0 );
|
||||
r = engine->RegisterObjectMethod("file", "int movePos(int)", asMETHOD(CScriptFile,MovePos), asCALL_THISCALL); assert( r >= 0 );
|
||||
|
||||
r = engine->RegisterObjectProperty("file", "bool mostSignificantByteFirst", asOFFSET(CScriptFile, mostSignificantByteFirst)); assert( r >= 0 );
|
||||
}
|
||||
|
||||
void RegisterScriptFile_Generic(asIScriptEngine *engine)
|
||||
{
|
||||
int r;
|
||||
|
||||
r = engine->RegisterObjectType("file", 0, asOBJ_REF); assert( r >= 0 );
|
||||
r = engine->RegisterObjectBehaviour("file", asBEHAVE_FACTORY, "file @f()", asFUNCTION(ScriptFile_Factory_Generic), asCALL_GENERIC); assert( r >= 0 );
|
||||
r = engine->RegisterObjectBehaviour("file", asBEHAVE_ADDREF, "void f()", asFUNCTION(ScriptFile_AddRef_Generic), asCALL_GENERIC); assert( r >= 0 );
|
||||
r = engine->RegisterObjectBehaviour("file", asBEHAVE_RELEASE, "void f()", asFUNCTION(ScriptFile_Release_Generic), asCALL_GENERIC); assert( r >= 0 );
|
||||
|
||||
r = engine->RegisterObjectMethod("file", "int open(const string &in, const string &in)", asFUNCTION(ScriptFile_Open_Generic), asCALL_GENERIC); assert( r >= 0 );
|
||||
r = engine->RegisterObjectMethod("file", "int close()", asFUNCTION(ScriptFile_Close_Generic), asCALL_GENERIC); assert( r >= 0 );
|
||||
r = engine->RegisterObjectMethod("file", "int getSize() const", asFUNCTION(ScriptFile_GetSize_Generic), asCALL_GENERIC); assert( r >= 0 );
|
||||
r = engine->RegisterObjectMethod("file", "bool isEndOfFile() const", asFUNCTION(ScriptFile_IsEOF_Generic), asCALL_GENERIC); assert( r >= 0 );
|
||||
r = engine->RegisterObjectMethod("file", "string readString(uint)", asFUNCTION(ScriptFile_ReadString_Generic), asCALL_GENERIC); assert( r >= 0 );
|
||||
r = engine->RegisterObjectMethod("file", "string readLine()", asFUNCTION(ScriptFile_ReadLine_Generic), asCALL_GENERIC); assert( r >= 0 );
|
||||
r = engine->RegisterObjectMethod("file", "int64 readInt(uint)", asFUNCTION(ScriptFile_ReadInt_Generic), asCALL_GENERIC); assert( r >= 0 );
|
||||
r = engine->RegisterObjectMethod("file", "uint64 readUInt(uint)", asFUNCTION(ScriptFile_ReadUInt_Generic), asCALL_GENERIC); assert( r >= 0 );
|
||||
r = engine->RegisterObjectMethod("file", "float readFloat()", asFUNCTION(ScriptFile_ReadFloat_Generic), asCALL_GENERIC); assert( r >= 0 );
|
||||
r = engine->RegisterObjectMethod("file", "double readDouble()", asFUNCTION(ScriptFile_ReadDouble_Generic), asCALL_GENERIC); assert( r >= 0 );
|
||||
#if AS_WRITE_OPS == 1
|
||||
r = engine->RegisterObjectMethod("file", "int writeString(const string &in)", asFUNCTION(ScriptFile_WriteString_Generic), asCALL_GENERIC); assert( r >= 0 );
|
||||
r = engine->RegisterObjectMethod("file", "int writeInt(int64, uint)", asFUNCTION(ScriptFile_WriteInt_Generic), asCALL_GENERIC); assert( r >= 0 );
|
||||
r = engine->RegisterObjectMethod("file", "int writeUInt(uint64, uint)", asFUNCTION(ScriptFile_WriteUInt_Generic), asCALL_GENERIC); assert( r >= 0 );
|
||||
r = engine->RegisterObjectMethod("file", "int writeFloat(float)", asFUNCTION(ScriptFile_WriteFloat_Generic), asCALL_GENERIC); assert( r >= 0 );
|
||||
r = engine->RegisterObjectMethod("file", "int writeDouble(double)", asFUNCTION(ScriptFile_WriteDouble_Generic), asCALL_GENERIC); assert( r >= 0 );
|
||||
#endif
|
||||
r = engine->RegisterObjectMethod("file", "int getPos() const", asFUNCTION(ScriptFile_GetPos_Generic), asCALL_GENERIC); assert( r >= 0 );
|
||||
r = engine->RegisterObjectMethod("file", "int setPos(int)", asFUNCTION(ScriptFile_SetPos_Generic), asCALL_GENERIC); assert( r >= 0 );
|
||||
r = engine->RegisterObjectMethod("file", "int movePos(int)", asFUNCTION(ScriptFile_MovePos_Generic), asCALL_GENERIC); assert( r >= 0 );
|
||||
|
||||
r = engine->RegisterObjectProperty("file", "bool mostSignificantByteFirst", asOFFSET(CScriptFile, mostSignificantByteFirst)); assert( r >= 0 );
|
||||
}
|
||||
|
||||
void RegisterScriptFile(asIScriptEngine *engine)
|
||||
{
|
||||
if( strstr(asGetLibraryOptions(), "AS_MAX_PORTABILITY") )
|
||||
RegisterScriptFile_Generic(engine);
|
||||
else
|
||||
RegisterScriptFile_Native(engine);
|
||||
}
|
||||
|
||||
CScriptFile::CScriptFile()
|
||||
{
|
||||
refCount = 1;
|
||||
file = 0;
|
||||
mostSignificantByteFirst = false;
|
||||
}
|
||||
|
||||
CScriptFile::~CScriptFile()
|
||||
{
|
||||
Close();
|
||||
}
|
||||
|
||||
void CScriptFile::AddRef() const
|
||||
{
|
||||
asAtomicInc(refCount);
|
||||
}
|
||||
|
||||
void CScriptFile::Release() const
|
||||
{
|
||||
if( asAtomicDec(refCount) == 0 )
|
||||
delete this;
|
||||
}
|
||||
|
||||
int CScriptFile::Open(const std::string &filename, const std::string &mode)
|
||||
{
|
||||
// Close the previously opened file handle
|
||||
if( file )
|
||||
Close();
|
||||
|
||||
std::string myFilename = filename;
|
||||
|
||||
// Validate the mode
|
||||
string m;
|
||||
#if AS_WRITE_OPS == 1
|
||||
if( mode != "r" && mode != "w" && mode != "a" )
|
||||
#else
|
||||
if( mode != "r" )
|
||||
#endif
|
||||
return -1;
|
||||
else
|
||||
m = mode;
|
||||
|
||||
#ifdef _WIN32_WCE
|
||||
// no relative pathing on CE
|
||||
char buf[MAX_PATH];
|
||||
static TCHAR apppath[MAX_PATH] = TEXT("");
|
||||
if (!apppath[0])
|
||||
{
|
||||
GetModuleFileName(NULL, apppath, MAX_PATH);
|
||||
|
||||
int appLen = _tcslen(apppath);
|
||||
while (appLen > 1)
|
||||
{
|
||||
if (apppath[appLen-1] == TEXT('\\'))
|
||||
break;
|
||||
appLen--;
|
||||
}
|
||||
|
||||
// Terminate the string after the trailing backslash
|
||||
apppath[appLen] = TEXT('\0');
|
||||
}
|
||||
#ifdef _UNICODE
|
||||
wcstombs(buf, apppath, wcslen(apppath)+1);
|
||||
#else
|
||||
memcpy(buf, apppath, strlen(apppath));
|
||||
#endif
|
||||
myFilename = buf + myFilename;
|
||||
#endif
|
||||
|
||||
|
||||
// By default windows translates "\r\n" to "\n", but we want to read the file as-is.
|
||||
m += "b";
|
||||
|
||||
// Open the file
|
||||
#if _MSC_VER >= 1400 && !defined(__S3E__)
|
||||
// MSVC 8.0 / 2005 introduced new functions
|
||||
// Marmalade doesn't use these, even though it uses the MSVC compiler
|
||||
fopen_s(&file, myFilename.c_str(), m.c_str());
|
||||
#else
|
||||
file = fopen(myFilename.c_str(), m.c_str());
|
||||
#endif
|
||||
if( file == 0 )
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int CScriptFile::Close()
|
||||
{
|
||||
if( file == 0 )
|
||||
return -1;
|
||||
|
||||
fclose(file);
|
||||
file = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int CScriptFile::GetSize() const
|
||||
{
|
||||
if( file == 0 )
|
||||
return -1;
|
||||
|
||||
int pos = ftell(file);
|
||||
fseek(file, 0, SEEK_END);
|
||||
int size = ftell(file);
|
||||
fseek(file, pos, SEEK_SET);
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
int CScriptFile::GetPos() const
|
||||
{
|
||||
if( file == 0 )
|
||||
return -1;
|
||||
|
||||
return ftell(file);
|
||||
}
|
||||
|
||||
int CScriptFile::SetPos(int pos)
|
||||
{
|
||||
if( file == 0 )
|
||||
return -1;
|
||||
|
||||
int r = fseek(file, pos, SEEK_SET);
|
||||
|
||||
// Return -1 on error
|
||||
return r ? -1 : 0;
|
||||
}
|
||||
|
||||
int CScriptFile::MovePos(int delta)
|
||||
{
|
||||
if( file == 0 )
|
||||
return -1;
|
||||
|
||||
int r = fseek(file, delta, SEEK_CUR);
|
||||
|
||||
// Return -1 on error
|
||||
return r ? -1 : 0;
|
||||
}
|
||||
|
||||
string CScriptFile::ReadString(unsigned int length)
|
||||
{
|
||||
if( file == 0 )
|
||||
return "";
|
||||
|
||||
// Read the string
|
||||
string str;
|
||||
str.resize(length);
|
||||
int size = (int)fread(&str[0], 1, length, file);
|
||||
str.resize(size);
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
string CScriptFile::ReadLine()
|
||||
{
|
||||
if( file == 0 )
|
||||
return "";
|
||||
|
||||
// Read until the first new-line character
|
||||
string str;
|
||||
char buf[256];
|
||||
|
||||
do
|
||||
{
|
||||
// Get the current position so we can determine how many characters were read
|
||||
int start = ftell(file);
|
||||
|
||||
// Set the last byte to something different that 0, so that we can check if the buffer was filled up
|
||||
buf[255] = 1;
|
||||
|
||||
// Read the line (or first 255 characters, which ever comes first)
|
||||
char *r = fgets(buf, 256, file);
|
||||
if( r == 0 ) break;
|
||||
|
||||
// Get the position after the read
|
||||
int end = ftell(file);
|
||||
|
||||
// Add the read characters to the output buffer
|
||||
str.append(buf, end-start);
|
||||
}
|
||||
while( !feof(file) && buf[255] == 0 && buf[254] != '\n' );
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
asINT64 CScriptFile::ReadInt(asUINT bytes)
|
||||
{
|
||||
if( file == 0 )
|
||||
return 0;
|
||||
|
||||
if( bytes > 8 ) bytes = 8;
|
||||
if( bytes == 0 ) return 0;
|
||||
|
||||
unsigned char buf[8];
|
||||
size_t r = fread(buf, bytes, 1, file);
|
||||
if( r == 0 ) return 0;
|
||||
|
||||
asINT64 val = 0;
|
||||
if( mostSignificantByteFirst )
|
||||
{
|
||||
unsigned int n = 0;
|
||||
for( ; n < bytes; n++ )
|
||||
val |= asQWORD(buf[n]) << ((bytes-n-1)*8);
|
||||
|
||||
// Check the most significant byte to determine if the rest
|
||||
// of the qword must be filled to give a negative value
|
||||
if( buf[0] & 0x80 )
|
||||
for( ; n < 8; n++ )
|
||||
val |= asQWORD(0xFF) << (n*8);
|
||||
}
|
||||
else
|
||||
{
|
||||
unsigned int n = 0;
|
||||
for( ; n < bytes; n++ )
|
||||
val |= asQWORD(buf[n]) << (n*8);
|
||||
|
||||
// Check the most significant byte to determine if the rest
|
||||
// of the qword must be filled to give a negative value
|
||||
if( buf[bytes-1] & 0x80 )
|
||||
for( ; n < 8; n++ )
|
||||
val |= asQWORD(0xFF) << (n*8);
|
||||
}
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
asQWORD CScriptFile::ReadUInt(asUINT bytes)
|
||||
{
|
||||
if( file == 0 )
|
||||
return 0;
|
||||
|
||||
if( bytes > 8 ) bytes = 8;
|
||||
if( bytes == 0 ) return 0;
|
||||
|
||||
unsigned char buf[8];
|
||||
size_t r = fread(buf, bytes, 1, file);
|
||||
if( r == 0 ) return 0;
|
||||
|
||||
asQWORD val = 0;
|
||||
if( mostSignificantByteFirst )
|
||||
{
|
||||
unsigned int n = 0;
|
||||
for( ; n < bytes; n++ )
|
||||
val |= asQWORD(buf[n]) << ((bytes-n-1)*8);
|
||||
}
|
||||
else
|
||||
{
|
||||
unsigned int n = 0;
|
||||
for( ; n < bytes; n++ )
|
||||
val |= asQWORD(buf[n]) << (n*8);
|
||||
}
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
float CScriptFile::ReadFloat()
|
||||
{
|
||||
if( file == 0 )
|
||||
return 0;
|
||||
|
||||
unsigned char buf[4];
|
||||
size_t r = fread(buf, 4, 1, file);
|
||||
if( r == 0 ) return 0;
|
||||
|
||||
asUINT val = 0;
|
||||
if( mostSignificantByteFirst )
|
||||
{
|
||||
unsigned int n = 0;
|
||||
for( ; n < 4; n++ )
|
||||
val |= asUINT(buf[n]) << ((3-n)*8);
|
||||
}
|
||||
else
|
||||
{
|
||||
unsigned int n = 0;
|
||||
for( ; n < 4; n++ )
|
||||
val |= asUINT(buf[n]) << (n*8);
|
||||
}
|
||||
|
||||
return *reinterpret_cast<float*>(&val);
|
||||
}
|
||||
|
||||
double CScriptFile::ReadDouble()
|
||||
{
|
||||
if( file == 0 )
|
||||
return 0;
|
||||
|
||||
unsigned char buf[8];
|
||||
size_t r = fread(buf, 8, 1, file);
|
||||
if( r == 0 ) return 0;
|
||||
|
||||
asQWORD val = 0;
|
||||
if( mostSignificantByteFirst )
|
||||
{
|
||||
unsigned int n = 0;
|
||||
for( ; n < 8; n++ )
|
||||
val |= asQWORD(buf[n]) << ((7-n)*8);
|
||||
}
|
||||
else
|
||||
{
|
||||
unsigned int n = 0;
|
||||
for( ; n < 8; n++ )
|
||||
val |= asQWORD(buf[n]) << (n*8);
|
||||
}
|
||||
|
||||
return *reinterpret_cast<double*>(&val);
|
||||
}
|
||||
|
||||
bool CScriptFile::IsEOF() const
|
||||
{
|
||||
if( file == 0 )
|
||||
return true;
|
||||
|
||||
return feof(file) ? true : false;
|
||||
}
|
||||
|
||||
#if AS_WRITE_OPS == 1
|
||||
int CScriptFile::WriteString(const std::string &str)
|
||||
{
|
||||
if( file == 0 )
|
||||
return -1;
|
||||
|
||||
// Write the entire string
|
||||
size_t r = fwrite(&str[0], 1, str.length(), file);
|
||||
|
||||
return int(r);
|
||||
}
|
||||
|
||||
int CScriptFile::WriteInt(asINT64 val, asUINT bytes)
|
||||
{
|
||||
if( file == 0 )
|
||||
return 0;
|
||||
|
||||
unsigned char buf[8];
|
||||
if( mostSignificantByteFirst )
|
||||
{
|
||||
for( unsigned int n = 0; n < bytes; n++ )
|
||||
buf[n] = (val >> ((bytes-n-1)*8)) & 0xFF;
|
||||
}
|
||||
else
|
||||
{
|
||||
for( unsigned int n = 0; n < bytes; n++ )
|
||||
buf[n] = (val >> (n*8)) & 0xFF;
|
||||
}
|
||||
|
||||
size_t r = fwrite(&buf, bytes, 1, file);
|
||||
return int(r);
|
||||
}
|
||||
|
||||
int CScriptFile::WriteUInt(asQWORD val, asUINT bytes)
|
||||
{
|
||||
if( file == 0 )
|
||||
return 0;
|
||||
|
||||
unsigned char buf[8];
|
||||
if( mostSignificantByteFirst )
|
||||
{
|
||||
for( unsigned int n = 0; n < bytes; n++ )
|
||||
buf[n] = (val >> ((bytes-n-1)*8)) & 0xFF;
|
||||
}
|
||||
else
|
||||
{
|
||||
for( unsigned int n = 0; n < bytes; n++ )
|
||||
buf[n] = (val >> (n*8)) & 0xFF;
|
||||
}
|
||||
|
||||
size_t r = fwrite(&buf, bytes, 1, file);
|
||||
return int(r);
|
||||
}
|
||||
|
||||
int CScriptFile::WriteFloat(float f)
|
||||
{
|
||||
if( file == 0 )
|
||||
return 0;
|
||||
|
||||
unsigned char buf[4];
|
||||
asUINT val = *reinterpret_cast<asUINT*>(&f);
|
||||
if( mostSignificantByteFirst )
|
||||
{
|
||||
for( unsigned int n = 0; n < 4; n++ )
|
||||
buf[n] = (val >> ((3-n)*4)) & 0xFF;
|
||||
}
|
||||
else
|
||||
{
|
||||
for( unsigned int n = 0; n < 4; n++ )
|
||||
buf[n] = (val >> (n*8)) & 0xFF;
|
||||
}
|
||||
|
||||
size_t r = fwrite(&buf, 4, 1, file);
|
||||
return int(r);
|
||||
}
|
||||
|
||||
int CScriptFile::WriteDouble(double d)
|
||||
{
|
||||
if( file == 0 )
|
||||
return 0;
|
||||
|
||||
unsigned char buf[8];
|
||||
asQWORD val = *reinterpret_cast<asQWORD*>(&d);
|
||||
if( mostSignificantByteFirst )
|
||||
{
|
||||
for( unsigned int n = 0; n < 8; n++ )
|
||||
buf[n] = (val >> ((7-n)*8)) & 0xFF;
|
||||
}
|
||||
else
|
||||
{
|
||||
for( unsigned int n = 0; n < 8; n++ )
|
||||
buf[n] = (val >> (n*8)) & 0xFF;
|
||||
}
|
||||
|
||||
size_t r = fwrite(&buf, 8, 1, file);
|
||||
return int(r);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
END_AS_NAMESPACE
|
||||
101
add_on/scriptfile/scriptfile.h
Normal file
101
add_on/scriptfile/scriptfile.h
Normal file
@@ -0,0 +1,101 @@
|
||||
//
|
||||
// CScriptFile
|
||||
//
|
||||
// This class encapsulates a FILE pointer in a reference counted class for
|
||||
// use within AngelScript.
|
||||
//
|
||||
|
||||
#ifndef SCRIPTFILE_H
|
||||
#define SCRIPTFILE_H
|
||||
|
||||
//---------------------------
|
||||
// Compilation settings
|
||||
//
|
||||
|
||||
// Set this flag to turn on/off write support
|
||||
// 0 = off
|
||||
// 1 = on
|
||||
|
||||
#ifndef AS_WRITE_OPS
|
||||
#define AS_WRITE_OPS 1
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
|
||||
//---------------------------
|
||||
// Declaration
|
||||
//
|
||||
|
||||
#ifndef ANGELSCRIPT_H
|
||||
// Avoid having to inform include path if header is already include before
|
||||
#include <angelscript.h>
|
||||
#endif
|
||||
|
||||
#include <string>
|
||||
#include <stdio.h>
|
||||
|
||||
BEGIN_AS_NAMESPACE
|
||||
|
||||
class CScriptFile
|
||||
{
|
||||
public:
|
||||
CScriptFile();
|
||||
|
||||
void AddRef() const;
|
||||
void Release() const;
|
||||
|
||||
// TODO: Implement the "r+", "w+" and "a+" modes
|
||||
// mode = "r" -> open the file for reading
|
||||
// "w" -> open the file for writing (overwrites existing file)
|
||||
// "a" -> open the file for appending
|
||||
int Open(const std::string &filename, const std::string &mode);
|
||||
int Close();
|
||||
int GetSize() const;
|
||||
bool IsEOF() const;
|
||||
|
||||
// Reading
|
||||
std::string ReadString(unsigned int length);
|
||||
std::string ReadLine();
|
||||
asINT64 ReadInt(asUINT bytes);
|
||||
asQWORD ReadUInt(asUINT bytes);
|
||||
float ReadFloat();
|
||||
double ReadDouble();
|
||||
|
||||
// Writing
|
||||
int WriteString(const std::string &str);
|
||||
int WriteInt(asINT64 v, asUINT bytes);
|
||||
int WriteUInt(asQWORD v, asUINT bytes);
|
||||
int WriteFloat(float v);
|
||||
int WriteDouble(double v);
|
||||
|
||||
// Cursor
|
||||
int GetPos() const;
|
||||
int SetPos(int pos);
|
||||
int MovePos(int delta);
|
||||
|
||||
// Big-endian = most significant byte first
|
||||
bool mostSignificantByteFirst;
|
||||
|
||||
protected:
|
||||
~CScriptFile();
|
||||
|
||||
mutable int refCount;
|
||||
FILE *file;
|
||||
};
|
||||
|
||||
// This function will determine the configuration of the engine
|
||||
// and use one of the two functions below to register the file type
|
||||
void RegisterScriptFile(asIScriptEngine *engine);
|
||||
|
||||
// Call this function to register the file type
|
||||
// using native calling conventions
|
||||
void RegisterScriptFile_Native(asIScriptEngine *engine);
|
||||
|
||||
// Use this one instead if native calling conventions
|
||||
// are not supported on the target platform
|
||||
void RegisterScriptFile_Generic(asIScriptEngine *engine);
|
||||
|
||||
END_AS_NAMESPACE
|
||||
|
||||
#endif
|
||||
629
add_on/scriptfile/scriptfilesystem.cpp
Normal file
629
add_on/scriptfile/scriptfilesystem.cpp
Normal file
@@ -0,0 +1,629 @@
|
||||
#include "scriptfilesystem.h"
|
||||
#include "../autowrapper/aswrappedcall.h"
|
||||
|
||||
#if defined(_WIN32)
|
||||
#include <direct.h> // _getcwd
|
||||
#include <Windows.h> // FindFirstFile, GetFileAttributes
|
||||
|
||||
#undef DeleteFile
|
||||
#undef CopyFile
|
||||
|
||||
#else
|
||||
#include <unistd.h> // getcwd
|
||||
#include <dirent.h> // opendir, readdir, closedir
|
||||
#include <sys/stat.h> // stat
|
||||
#endif
|
||||
#include <assert.h> // assert
|
||||
|
||||
using namespace std;
|
||||
|
||||
BEGIN_AS_NAMESPACE
|
||||
|
||||
// TODO: The file system should have a way to allow the application to define in
|
||||
// which sub directories it is allowed to make changes and/or read
|
||||
|
||||
CScriptFileSystem *ScriptFileSystem_Factory()
|
||||
{
|
||||
return new CScriptFileSystem();
|
||||
}
|
||||
|
||||
void RegisterScriptFileSystem_Native(asIScriptEngine *engine)
|
||||
{
|
||||
int r;
|
||||
|
||||
assert( engine->GetTypeInfoByName("string") );
|
||||
assert( engine->GetTypeInfoByDecl("array<string>") );
|
||||
assert( engine->GetTypeInfoByName("datetime") );
|
||||
|
||||
r = engine->RegisterObjectType("filesystem", 0, asOBJ_REF); assert( r >= 0 );
|
||||
r = engine->RegisterObjectBehaviour("filesystem", asBEHAVE_FACTORY, "filesystem @f()", asFUNCTION(ScriptFileSystem_Factory), asCALL_CDECL); assert( r >= 0 );
|
||||
r = engine->RegisterObjectBehaviour("filesystem", asBEHAVE_ADDREF, "void f()", asMETHOD(CScriptFileSystem,AddRef), asCALL_THISCALL); assert( r >= 0 );
|
||||
r = engine->RegisterObjectBehaviour("filesystem", asBEHAVE_RELEASE, "void f()", asMETHOD(CScriptFileSystem,Release), asCALL_THISCALL); assert( r >= 0 );
|
||||
|
||||
r = engine->RegisterObjectMethod("filesystem", "bool changeCurrentPath(const string &in)", asMETHOD(CScriptFileSystem, ChangeCurrentPath), asCALL_THISCALL); assert( r >= 0 );
|
||||
r = engine->RegisterObjectMethod("filesystem", "string getCurrentPath() const", asMETHOD(CScriptFileSystem, GetCurrentPath), asCALL_THISCALL); assert( r >= 0 );
|
||||
r = engine->RegisterObjectMethod("filesystem", "array<string> @getDirs() const", asMETHOD(CScriptFileSystem, GetDirs), asCALL_THISCALL); assert( r >= 0 );
|
||||
r = engine->RegisterObjectMethod("filesystem", "array<string> @getFiles() const", asMETHOD(CScriptFileSystem, GetFiles), asCALL_THISCALL); assert( r >= 0 );
|
||||
r = engine->RegisterObjectMethod("filesystem", "bool isDir(const string &in) const", asMETHOD(CScriptFileSystem, IsDir), asCALL_THISCALL); assert( r >= 0 );
|
||||
r = engine->RegisterObjectMethod("filesystem", "bool isLink(const string &in) const", asMETHOD(CScriptFileSystem, IsLink), asCALL_THISCALL); assert(r >= 0);
|
||||
r = engine->RegisterObjectMethod("filesystem", "int64 getSize(const string &in) const", asMETHOD(CScriptFileSystem, GetSize), asCALL_THISCALL); assert(r >= 0);
|
||||
r = engine->RegisterObjectMethod("filesystem", "int makeDir(const string &in)", asMETHOD(CScriptFileSystem, MakeDir), asCALL_THISCALL); assert(r >= 0);
|
||||
r = engine->RegisterObjectMethod("filesystem", "int removeDir(const string &in)", asMETHOD(CScriptFileSystem, RemoveDir), asCALL_THISCALL); assert(r >= 0);
|
||||
r = engine->RegisterObjectMethod("filesystem", "int deleteFile(const string &in)", asMETHOD(CScriptFileSystem, DeleteFile), asCALL_THISCALL); assert(r >= 0);
|
||||
r = engine->RegisterObjectMethod("filesystem", "int copyFile(const string &in, const string &in)", asMETHOD(CScriptFileSystem, CopyFile), asCALL_THISCALL); assert(r >= 0);
|
||||
r = engine->RegisterObjectMethod("filesystem", "int move(const string &in, const string &in)", asMETHOD(CScriptFileSystem, Move), asCALL_THISCALL); assert(r >= 0);
|
||||
r = engine->RegisterObjectMethod("filesystem", "datetime getCreateDateTime(const string &in) const", asMETHOD(CScriptFileSystem, GetCreateDateTime), asCALL_THISCALL); assert(r >= 0);
|
||||
r = engine->RegisterObjectMethod("filesystem", "datetime getModifyDateTime(const string &in) const", asMETHOD(CScriptFileSystem, GetModifyDateTime), asCALL_THISCALL); assert(r >= 0);
|
||||
}
|
||||
|
||||
void RegisterScriptFileSystem_Generic(asIScriptEngine *engine)
|
||||
{
|
||||
int r;
|
||||
|
||||
assert( engine->GetTypeInfoByName("string") );
|
||||
assert( engine->GetTypeInfoByDecl("array<string>") );
|
||||
assert( engine->GetTypeInfoByName("datetime") );
|
||||
|
||||
r = engine->RegisterObjectType("filesystem", 0, asOBJ_REF); assert( r >= 0 );
|
||||
r = engine->RegisterObjectBehaviour("filesystem", asBEHAVE_FACTORY, "filesystem @f()", WRAP_FN(ScriptFileSystem_Factory), asCALL_GENERIC); assert( r >= 0 );
|
||||
r = engine->RegisterObjectBehaviour("filesystem", asBEHAVE_ADDREF, "void f()", WRAP_MFN(CScriptFileSystem,AddRef), asCALL_GENERIC); assert( r >= 0 );
|
||||
r = engine->RegisterObjectBehaviour("filesystem", asBEHAVE_RELEASE, "void f()", WRAP_MFN(CScriptFileSystem,Release), asCALL_GENERIC); assert( r >= 0 );
|
||||
|
||||
r = engine->RegisterObjectMethod("filesystem", "bool changeCurrentPath(const string &in)", WRAP_MFN(CScriptFileSystem, ChangeCurrentPath), asCALL_GENERIC); assert( r >= 0 );
|
||||
r = engine->RegisterObjectMethod("filesystem", "string getCurrentPath() const", WRAP_MFN(CScriptFileSystem, GetCurrentPath), asCALL_GENERIC); assert( r >= 0 );
|
||||
r = engine->RegisterObjectMethod("filesystem", "array<string> @getDirs() const", WRAP_MFN(CScriptFileSystem, GetDirs), asCALL_GENERIC); assert( r >= 0 );
|
||||
r = engine->RegisterObjectMethod("filesystem", "array<string> @getFiles() const", WRAP_MFN(CScriptFileSystem, GetFiles), asCALL_GENERIC); assert( r >= 0 );
|
||||
r = engine->RegisterObjectMethod("filesystem", "bool isDir(const string &in) const", WRAP_MFN(CScriptFileSystem, IsDir), asCALL_GENERIC); assert( r >= 0 );
|
||||
r = engine->RegisterObjectMethod("filesystem", "bool isLink(const string &in) const", WRAP_MFN(CScriptFileSystem, IsLink), asCALL_GENERIC); assert(r >= 0);
|
||||
r = engine->RegisterObjectMethod("filesystem", "int64 getSize(const string &in) const", WRAP_MFN(CScriptFileSystem, GetSize), asCALL_GENERIC); assert(r >= 0);
|
||||
r = engine->RegisterObjectMethod("filesystem", "int makeDir(const string &in)", WRAP_MFN(CScriptFileSystem, MakeDir), asCALL_GENERIC); assert(r >= 0);
|
||||
r = engine->RegisterObjectMethod("filesystem", "int removeDir(const string &in)", WRAP_MFN(CScriptFileSystem, RemoveDir), asCALL_GENERIC); assert(r >= 0);
|
||||
r = engine->RegisterObjectMethod("filesystem", "int deleteFile(const string &in)", WRAP_MFN(CScriptFileSystem, DeleteFile), asCALL_GENERIC); assert(r >= 0);
|
||||
r = engine->RegisterObjectMethod("filesystem", "int copyFile(const string &in, const string &in)", WRAP_MFN(CScriptFileSystem, CopyFile), asCALL_GENERIC); assert(r >= 0);
|
||||
r = engine->RegisterObjectMethod("filesystem", "int move(const string &in, const string &in)", WRAP_MFN(CScriptFileSystem, Move), asCALL_GENERIC); assert(r >= 0);
|
||||
r = engine->RegisterObjectMethod("filesystem", "datetime getCreateDateTime(const string &in) const", WRAP_MFN(CScriptFileSystem, GetCreateDateTime), asCALL_GENERIC); assert(r >= 0);
|
||||
r = engine->RegisterObjectMethod("filesystem", "datetime getModifyDateTime(const string &in) const", WRAP_MFN(CScriptFileSystem, GetModifyDateTime), asCALL_GENERIC); assert(r >= 0);
|
||||
}
|
||||
|
||||
void RegisterScriptFileSystem(asIScriptEngine *engine)
|
||||
{
|
||||
if( strstr(asGetLibraryOptions(), "AS_MAX_PORTABILITY") )
|
||||
RegisterScriptFileSystem_Generic(engine);
|
||||
else
|
||||
RegisterScriptFileSystem_Native(engine);
|
||||
}
|
||||
|
||||
CScriptFileSystem::CScriptFileSystem()
|
||||
{
|
||||
refCount = 1;
|
||||
|
||||
// Gets the application's current working directory as the starting point
|
||||
// TODO: Replace backslash with slash to keep a unified naming convention
|
||||
char buffer[1000];
|
||||
#if defined(_WIN32)
|
||||
currentPath = _getcwd(buffer, 1000);
|
||||
#else
|
||||
currentPath = getcwd(buffer, 1000);
|
||||
#endif
|
||||
}
|
||||
|
||||
CScriptFileSystem::~CScriptFileSystem()
|
||||
{
|
||||
}
|
||||
|
||||
void CScriptFileSystem::AddRef() const
|
||||
{
|
||||
asAtomicInc(refCount);
|
||||
}
|
||||
|
||||
void CScriptFileSystem::Release() const
|
||||
{
|
||||
if( asAtomicDec(refCount) == 0 )
|
||||
delete this;
|
||||
}
|
||||
|
||||
CScriptArray *CScriptFileSystem::GetFiles() const
|
||||
{
|
||||
// Obtain a pointer to the engine
|
||||
asIScriptContext *ctx = asGetActiveContext();
|
||||
asIScriptEngine *engine = ctx->GetEngine();
|
||||
|
||||
// TODO: This should only be done once
|
||||
// TODO: This assumes that CScriptArray was already registered
|
||||
asITypeInfo *arrayType = engine->GetTypeInfoByDecl("array<string>");
|
||||
|
||||
// Create the array object
|
||||
CScriptArray *array = CScriptArray::Create(arrayType);
|
||||
|
||||
#if defined(_WIN32)
|
||||
// Windows uses UTF16 so it is necessary to convert the string
|
||||
wchar_t bufUTF16[10000];
|
||||
string searchPattern = currentPath + "/*";
|
||||
MultiByteToWideChar(CP_UTF8, 0, searchPattern.c_str(), -1, bufUTF16, 10000);
|
||||
|
||||
WIN32_FIND_DATAW ffd;
|
||||
HANDLE hFind = FindFirstFileW(bufUTF16, &ffd);
|
||||
if( INVALID_HANDLE_VALUE == hFind )
|
||||
return array;
|
||||
|
||||
do
|
||||
{
|
||||
// Skip directories
|
||||
if( (ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) )
|
||||
continue;
|
||||
|
||||
// Convert the file name back to UTF8
|
||||
char bufUTF8[10000];
|
||||
WideCharToMultiByte(CP_UTF8, 0, ffd.cFileName, -1, bufUTF8, 10000, 0, 0);
|
||||
|
||||
// Add the file to the array
|
||||
array->Resize(array->GetSize()+1);
|
||||
((string*)(array->At(array->GetSize()-1)))->assign(bufUTF8);
|
||||
}
|
||||
while( FindNextFileW(hFind, &ffd) != 0 );
|
||||
|
||||
FindClose(hFind);
|
||||
#else
|
||||
dirent *ent = 0;
|
||||
DIR *dir = opendir(currentPath.c_str());
|
||||
while( (ent = readdir(dir)) != NULL )
|
||||
{
|
||||
const string filename = ent->d_name;
|
||||
|
||||
// Skip . and ..
|
||||
if( filename[0] == '.' )
|
||||
continue;
|
||||
|
||||
// Skip sub directories
|
||||
const string fullname = currentPath + "/" + filename;
|
||||
struct stat st;
|
||||
if( stat(fullname.c_str(), &st) == -1 )
|
||||
continue;
|
||||
if( (st.st_mode & S_IFDIR) != 0 )
|
||||
continue;
|
||||
|
||||
// Add the file to the array
|
||||
array->Resize(array->GetSize()+1);
|
||||
((string*)(array->At(array->GetSize()-1)))->assign(filename);
|
||||
}
|
||||
closedir(dir);
|
||||
#endif
|
||||
|
||||
return array;
|
||||
}
|
||||
|
||||
CScriptArray *CScriptFileSystem::GetDirs() const
|
||||
{
|
||||
// Obtain a pointer to the engine
|
||||
asIScriptContext *ctx = asGetActiveContext();
|
||||
asIScriptEngine *engine = ctx->GetEngine();
|
||||
|
||||
// TODO: This should only be done once
|
||||
// TODO: This assumes that CScriptArray was already registered
|
||||
asITypeInfo *arrayType = engine->GetTypeInfoByDecl("array<string>");
|
||||
|
||||
// Create the array object
|
||||
CScriptArray *array = CScriptArray::Create(arrayType);
|
||||
|
||||
#if defined(_WIN32)
|
||||
// Windows uses UTF16 so it is necessary to convert the string
|
||||
wchar_t bufUTF16[10000];
|
||||
string searchPattern = currentPath + "/*";
|
||||
MultiByteToWideChar(CP_UTF8, 0, searchPattern.c_str(), -1, bufUTF16, 10000);
|
||||
|
||||
WIN32_FIND_DATAW ffd;
|
||||
HANDLE hFind = FindFirstFileW(bufUTF16, &ffd);
|
||||
if( INVALID_HANDLE_VALUE == hFind )
|
||||
return array;
|
||||
|
||||
do
|
||||
{
|
||||
// Skip files
|
||||
if( !(ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) )
|
||||
continue;
|
||||
|
||||
// Convert the file name back to UTF8
|
||||
char bufUTF8[10000];
|
||||
WideCharToMultiByte(CP_UTF8, 0, ffd.cFileName, -1, bufUTF8, 10000, 0, 0);
|
||||
|
||||
if( strcmp(bufUTF8, ".") == 0 || strcmp(bufUTF8, "..") == 0 )
|
||||
continue;
|
||||
|
||||
// Add the dir to the array
|
||||
array->Resize(array->GetSize()+1);
|
||||
((string*)(array->At(array->GetSize()-1)))->assign(bufUTF8);
|
||||
}
|
||||
while( FindNextFileW(hFind, &ffd) != 0 );
|
||||
|
||||
FindClose(hFind);
|
||||
#else
|
||||
dirent *ent = 0;
|
||||
DIR *dir = opendir(currentPath.c_str());
|
||||
while( (ent = readdir(dir)) != NULL )
|
||||
{
|
||||
const string filename = ent->d_name;
|
||||
|
||||
// Skip . and ..
|
||||
if( filename[0] == '.' )
|
||||
continue;
|
||||
|
||||
// Skip files
|
||||
const string fullname = currentPath + "/" + filename;
|
||||
struct stat st;
|
||||
if( stat(fullname.c_str(), &st) == -1 )
|
||||
continue;
|
||||
if( (st.st_mode & S_IFDIR) == 0 )
|
||||
continue;
|
||||
|
||||
// Add the dir to the array
|
||||
array->Resize(array->GetSize()+1);
|
||||
((string*)(array->At(array->GetSize()-1)))->assign(filename);
|
||||
}
|
||||
closedir(dir);
|
||||
#endif
|
||||
|
||||
return array;
|
||||
}
|
||||
|
||||
// Doesn't change anything if the new path is not valid
|
||||
bool CScriptFileSystem::ChangeCurrentPath(const string &path)
|
||||
{
|
||||
string newPath;
|
||||
if( path.find(":") != string::npos || path.find("/") == 0 || path.find("\\") == 0 )
|
||||
newPath = path;
|
||||
else
|
||||
newPath = currentPath + "/" + path;
|
||||
|
||||
// TODO: Resolve internal /./ and /../
|
||||
// TODO: Replace backslash with slash to keep a unified naming convention
|
||||
|
||||
// Remove trailing slashes from the path
|
||||
while(newPath.length() && (newPath[newPath.length()-1] == '/' || newPath[newPath.length()-1] == '\\') )
|
||||
newPath.resize(newPath.length()-1);
|
||||
|
||||
if (!IsDir(newPath))
|
||||
return false;
|
||||
|
||||
currentPath = newPath;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CScriptFileSystem::IsDir(const string &path) const
|
||||
{
|
||||
string search;
|
||||
if( path.find(":") != string::npos || path.find("/") == 0 || path.find("\\") == 0 )
|
||||
search = path;
|
||||
else
|
||||
search = currentPath + "/" + path;
|
||||
|
||||
#if defined(_WIN32)
|
||||
// Windows uses UTF16 so it is necessary to convert the string
|
||||
wchar_t bufUTF16[10000];
|
||||
MultiByteToWideChar(CP_UTF8, 0, search.c_str(), -1, bufUTF16, 10000);
|
||||
|
||||
// Check if the path exists and is a directory
|
||||
DWORD attrib = GetFileAttributesW(bufUTF16);
|
||||
if( attrib == INVALID_FILE_ATTRIBUTES ||
|
||||
!(attrib & FILE_ATTRIBUTE_DIRECTORY) )
|
||||
return false;
|
||||
#else
|
||||
// Check if the path exists and is a directory
|
||||
struct stat st;
|
||||
if( stat(search.c_str(), &st) == -1 )
|
||||
return false;
|
||||
if( (st.st_mode & S_IFDIR) == 0 )
|
||||
return false;
|
||||
#endif
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CScriptFileSystem::IsLink(const string &path) const
|
||||
{
|
||||
string search;
|
||||
if (path.find(":") != string::npos || path.find("/") == 0 || path.find("\\") == 0)
|
||||
search = path;
|
||||
else
|
||||
search = currentPath + "/" + path;
|
||||
|
||||
#if defined(_WIN32)
|
||||
// Windows uses UTF16 so it is necessary to convert the string
|
||||
wchar_t bufUTF16[10000];
|
||||
MultiByteToWideChar(CP_UTF8, 0, search.c_str(), -1, bufUTF16, 10000);
|
||||
|
||||
// Check if the path exists and is a link
|
||||
DWORD attrib = GetFileAttributesW(bufUTF16);
|
||||
if (attrib == INVALID_FILE_ATTRIBUTES ||
|
||||
!(attrib & FILE_ATTRIBUTE_REPARSE_POINT))
|
||||
return false;
|
||||
#else
|
||||
// Check if the path exists and is a link
|
||||
struct stat st;
|
||||
if (stat(search.c_str(), &st) == -1)
|
||||
return false;
|
||||
if ((st.st_mode & S_IFLNK) == 0)
|
||||
return false;
|
||||
#endif
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
asINT64 CScriptFileSystem::GetSize(const string &path) const
|
||||
{
|
||||
string search;
|
||||
if (path.find(":") != string::npos || path.find("/") == 0 || path.find("\\") == 0)
|
||||
search = path;
|
||||
else
|
||||
search = currentPath + "/" + path;
|
||||
|
||||
#if defined(_WIN32)
|
||||
// Windows uses UTF16 so it is necessary to convert the string
|
||||
wchar_t bufUTF16[10000];
|
||||
MultiByteToWideChar(CP_UTF8, 0, search.c_str(), -1, bufUTF16, 10000);
|
||||
|
||||
// Get the size of the file
|
||||
LARGE_INTEGER largeInt;
|
||||
HANDLE file = CreateFileW(bufUTF16, GENERIC_READ, FILE_SHARE_READ, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
|
||||
BOOL success = GetFileSizeEx(file, &largeInt);
|
||||
CloseHandle(file);
|
||||
if( !success )
|
||||
return -1;
|
||||
return asINT64(largeInt.QuadPart);
|
||||
#else
|
||||
// Get the size of the file
|
||||
struct stat st;
|
||||
if (stat(search.c_str(), &st) == -1)
|
||||
return -1;
|
||||
return asINT64(st.st_size);
|
||||
#endif
|
||||
}
|
||||
|
||||
// TODO: Should be able to return different codes for
|
||||
// - directory exists
|
||||
// - path not found
|
||||
// - access denied
|
||||
// TODO: Should be able to define the permissions for the directory
|
||||
int CScriptFileSystem::MakeDir(const string &path)
|
||||
{
|
||||
string search;
|
||||
if (path.find(":") != string::npos || path.find("/") == 0 || path.find("\\") == 0)
|
||||
search = path;
|
||||
else
|
||||
search = currentPath + "/" + path;
|
||||
|
||||
#if defined(_WIN32)
|
||||
// Windows uses UTF16 so it is necessary to convert the string
|
||||
wchar_t bufUTF16[10000];
|
||||
MultiByteToWideChar(CP_UTF8, 0, search.c_str(), -1, bufUTF16, 10000);
|
||||
|
||||
// Create the directory
|
||||
BOOL success = CreateDirectoryW(bufUTF16, 0);
|
||||
return success ? 0 : -1;
|
||||
#else
|
||||
// Create the directory
|
||||
int failure = mkdir(search.c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);
|
||||
return !failure ? 0 : -1;
|
||||
#endif
|
||||
}
|
||||
|
||||
// TODO: Should be able to return different codes for
|
||||
// - directory doesn't exist
|
||||
// - directory is not empty
|
||||
// - access denied
|
||||
// TODO: Should have an option to remove the directory and all content recursively
|
||||
int CScriptFileSystem::RemoveDir(const string &path)
|
||||
{
|
||||
string search;
|
||||
if (path.find(":") != string::npos || path.find("/") == 0 || path.find("\\") == 0)
|
||||
search = path;
|
||||
else
|
||||
search = currentPath + "/" + path;
|
||||
|
||||
#if defined(_WIN32)
|
||||
// Windows uses UTF16 so it is necessary to convert the string
|
||||
wchar_t bufUTF16[10000];
|
||||
MultiByteToWideChar(CP_UTF8, 0, search.c_str(), -1, bufUTF16, 10000);
|
||||
|
||||
// Remove the directory
|
||||
BOOL success = RemoveDirectoryW(bufUTF16);
|
||||
return success ? 0 : -1;
|
||||
#else
|
||||
// Remove the directory
|
||||
int failure = rmdir(search.c_str());
|
||||
return !failure ? 0 : -1;
|
||||
#endif
|
||||
}
|
||||
|
||||
int CScriptFileSystem::DeleteFile(const string &path)
|
||||
{
|
||||
string search;
|
||||
if (path.find(":") != string::npos || path.find("/") == 0 || path.find("\\") == 0)
|
||||
search = path;
|
||||
else
|
||||
search = currentPath + "/" + path;
|
||||
|
||||
#if defined(_WIN32)
|
||||
// Windows uses UTF16 so it is necessary to convert the string
|
||||
wchar_t bufUTF16[10000];
|
||||
MultiByteToWideChar(CP_UTF8, 0, search.c_str(), -1, bufUTF16, 10000);
|
||||
|
||||
// Remove the file
|
||||
BOOL success = DeleteFileW(bufUTF16);
|
||||
return success ? 0 : -1;
|
||||
#else
|
||||
// Remove the file
|
||||
int failure = unlink(search.c_str());
|
||||
return !failure ? 0 : -1;
|
||||
#endif
|
||||
}
|
||||
|
||||
int CScriptFileSystem::CopyFile(const string &source, const string &target)
|
||||
{
|
||||
string search1;
|
||||
if (source.find(":") != string::npos || source.find("/") == 0 || source.find("\\") == 0)
|
||||
search1 = source;
|
||||
else
|
||||
search1 = currentPath + "/" + source;
|
||||
|
||||
string search2;
|
||||
if (target.find(":") != string::npos || target.find("/") == 0 || target.find("\\") == 0)
|
||||
search2 = target;
|
||||
else
|
||||
search2 = currentPath + "/" + target;
|
||||
|
||||
#if defined(_WIN32)
|
||||
// Windows uses UTF16 so it is necessary to convert the string
|
||||
wchar_t bufUTF16_1[10000];
|
||||
MultiByteToWideChar(CP_UTF8, 0, search1.c_str(), -1, bufUTF16_1, 10000);
|
||||
|
||||
wchar_t bufUTF16_2[10000];
|
||||
MultiByteToWideChar(CP_UTF8, 0, search2.c_str(), -1, bufUTF16_2, 10000);
|
||||
|
||||
// Copy the file
|
||||
BOOL success = CopyFileW(bufUTF16_1, bufUTF16_2, TRUE);
|
||||
return success ? 0 : -1;
|
||||
#else
|
||||
// Copy the file manually as there is no posix function for this
|
||||
bool failure = false;
|
||||
FILE *src = 0, *tgt = 0;
|
||||
src = fopen(search1.c_str(), "r");
|
||||
if (src == 0) failure = true;
|
||||
if( !failure ) tgt = fopen(search2.c_str(), "w");
|
||||
if (tgt == 0) failure = true;
|
||||
char buf[1024];
|
||||
size_t n;
|
||||
while (!failure && (n = fread(buf, sizeof(char), sizeof(buf), src)) > 0)
|
||||
{
|
||||
if (fwrite(buf, sizeof(char), n, tgt) != n)
|
||||
failure = true;
|
||||
}
|
||||
if (src) fclose(src);
|
||||
if (tgt) fclose(tgt);
|
||||
return !failure ? 0 : -1;
|
||||
#endif
|
||||
}
|
||||
|
||||
int CScriptFileSystem::Move(const string &source, const string &target)
|
||||
{
|
||||
string search1;
|
||||
if (source.find(":") != string::npos || source.find("/") == 0 || source.find("\\") == 0)
|
||||
search1 = source;
|
||||
else
|
||||
search1 = currentPath + "/" + source;
|
||||
|
||||
string search2;
|
||||
if (target.find(":") != string::npos || target.find("/") == 0 || target.find("\\") == 0)
|
||||
search2 = target;
|
||||
else
|
||||
search2 = currentPath + "/" + target;
|
||||
|
||||
#if defined(_WIN32)
|
||||
// Windows uses UTF16 so it is necessary to convert the string
|
||||
wchar_t bufUTF16_1[10000];
|
||||
MultiByteToWideChar(CP_UTF8, 0, search1.c_str(), -1, bufUTF16_1, 10000);
|
||||
|
||||
wchar_t bufUTF16_2[10000];
|
||||
MultiByteToWideChar(CP_UTF8, 0, search2.c_str(), -1, bufUTF16_2, 10000);
|
||||
|
||||
// Move the file or directory
|
||||
BOOL success = MoveFileW(bufUTF16_1, bufUTF16_2);
|
||||
return success ? 0 : -1;
|
||||
#else
|
||||
// Move the file or directory
|
||||
int failure = rename(search1.c_str(), search2.c_str());
|
||||
return !failure ? 0 : -1;
|
||||
#endif
|
||||
}
|
||||
|
||||
string CScriptFileSystem::GetCurrentPath() const
|
||||
{
|
||||
return currentPath;
|
||||
}
|
||||
|
||||
CDateTime CScriptFileSystem::GetCreateDateTime(const string &path) const
|
||||
{
|
||||
string search;
|
||||
if (path.find(":") != string::npos || path.find("/") == 0 || path.find("\\") == 0)
|
||||
search = path;
|
||||
else
|
||||
search = currentPath + "/" + path;
|
||||
|
||||
#if defined(_WIN32)
|
||||
// Windows uses UTF16 so it is necessary to convert the string
|
||||
wchar_t bufUTF16[10000];
|
||||
MultiByteToWideChar(CP_UTF8, 0, search.c_str(), -1, bufUTF16, 10000);
|
||||
|
||||
// Get the create date/time of the file
|
||||
FILETIME createTm;
|
||||
HANDLE file = CreateFileW(bufUTF16, GENERIC_READ, FILE_SHARE_READ, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
|
||||
BOOL success = GetFileTime(file, &createTm, 0, 0);
|
||||
CloseHandle(file);
|
||||
if( !success )
|
||||
{
|
||||
asIScriptContext *ctx = asGetActiveContext();
|
||||
if( ctx )
|
||||
ctx->SetException("Failed to get file creation date/time");
|
||||
return CDateTime();
|
||||
}
|
||||
SYSTEMTIME tm;
|
||||
FileTimeToSystemTime(&createTm, &tm);
|
||||
return CDateTime(tm.wYear, tm.wMonth, tm.wDay, tm.wHour, tm.wMinute, tm.wSecond);
|
||||
#else
|
||||
// Get the create date/time of the file
|
||||
struct stat st;
|
||||
if (stat(search.c_str(), &st) == -1)
|
||||
{
|
||||
asIScriptContext *ctx = asGetActiveContext();
|
||||
if( ctx )
|
||||
ctx->SetException("Failed to get file creation date/time");
|
||||
return CDateTime();
|
||||
}
|
||||
tm *t = localtime(&st.st_ctime);
|
||||
return CDateTime(t->tm_year + 1900, t->tm_mon + 1, t->tm_mday, t->tm_hour, t->tm_min, t->tm_sec);
|
||||
#endif
|
||||
}
|
||||
|
||||
CDateTime CScriptFileSystem::GetModifyDateTime(const string &path) const
|
||||
{
|
||||
string search;
|
||||
if (path.find(":") != string::npos || path.find("/") == 0 || path.find("\\") == 0)
|
||||
search = path;
|
||||
else
|
||||
search = currentPath + "/" + path;
|
||||
|
||||
#if defined(_WIN32)
|
||||
// Windows uses UTF16 so it is necessary to convert the string
|
||||
wchar_t bufUTF16[10000];
|
||||
MultiByteToWideChar(CP_UTF8, 0, search.c_str(), -1, bufUTF16, 10000);
|
||||
|
||||
// Get the last modify date/time of the file
|
||||
FILETIME modifyTm;
|
||||
HANDLE file = CreateFileW(bufUTF16, GENERIC_READ, FILE_SHARE_READ, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
|
||||
BOOL success = GetFileTime(file, 0, 0, &modifyTm);
|
||||
CloseHandle(file);
|
||||
if( !success )
|
||||
{
|
||||
asIScriptContext *ctx = asGetActiveContext();
|
||||
if( ctx )
|
||||
ctx->SetException("Failed to get file modify date/time");
|
||||
return CDateTime();
|
||||
}
|
||||
SYSTEMTIME tm;
|
||||
FileTimeToSystemTime(&modifyTm, &tm);
|
||||
return CDateTime(tm.wYear, tm.wMonth, tm.wDay, tm.wHour, tm.wMinute, tm.wSecond);
|
||||
#else
|
||||
// Get the last modify date/time of the file
|
||||
struct stat st;
|
||||
if (stat(search.c_str(), &st) == -1)
|
||||
{
|
||||
asIScriptContext *ctx = asGetActiveContext();
|
||||
if( ctx )
|
||||
ctx->SetException("Failed to get file modify date/time");
|
||||
return CDateTime();
|
||||
}
|
||||
tm *t = localtime(&st.st_mtime);
|
||||
return CDateTime(t->tm_year + 1900, t->tm_mon + 1, t->tm_mday, t->tm_hour, t->tm_min, t->tm_sec);
|
||||
#endif
|
||||
}
|
||||
|
||||
END_AS_NAMESPACE
|
||||
78
add_on/scriptfile/scriptfilesystem.h
Normal file
78
add_on/scriptfile/scriptfilesystem.h
Normal file
@@ -0,0 +1,78 @@
|
||||
#ifndef AS_SCRIPTFILESYSTEM_H
|
||||
#define AS_SCRIPTFILESYSTEM_H
|
||||
|
||||
#ifndef ANGELSCRIPT_H
|
||||
// Avoid having to inform include path if header is already include before
|
||||
#include <angelscript.h>
|
||||
#endif
|
||||
|
||||
#include <string>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "../scriptarray/scriptarray.h"
|
||||
#include "../datetime/datetime.h"
|
||||
|
||||
BEGIN_AS_NAMESPACE
|
||||
|
||||
class CScriptFileSystem
|
||||
{
|
||||
public:
|
||||
CScriptFileSystem();
|
||||
|
||||
void AddRef() const;
|
||||
void Release() const;
|
||||
|
||||
// Sets the current path that should be used in other calls when using relative paths
|
||||
// It can use relative paths too, so moving up a directory is used by passing in ".."
|
||||
bool ChangeCurrentPath(const std::string &path);
|
||||
std::string GetCurrentPath() const;
|
||||
|
||||
// Returns true if the path is a directory. Input can be either a full path or a relative path.
|
||||
// This method does not return the dirs '.' and '..'
|
||||
bool IsDir(const std::string &path) const;
|
||||
|
||||
// Returns true if the path is a link. Input can be either a full path or a relative path
|
||||
bool IsLink(const std::string &path) const;
|
||||
|
||||
// Returns the size of file. Input can be either a full path or a relative path
|
||||
asINT64 GetSize(const std::string &path) const;
|
||||
|
||||
// Returns a list of the files in the current path
|
||||
CScriptArray *GetFiles() const;
|
||||
|
||||
// Returns a list of the directories in the current path
|
||||
CScriptArray *GetDirs() const;
|
||||
|
||||
// Creates a new directory. Returns 0 on success
|
||||
int MakeDir(const std::string &path);
|
||||
|
||||
// Removes a directory. Will only remove the directory if it is empty. Returns 0 on success
|
||||
int RemoveDir(const std::string &path);
|
||||
|
||||
// Deletes a file. Returns 0 on success
|
||||
int DeleteFile(const std::string &path);
|
||||
|
||||
// Copies a file. Returns 0 on success
|
||||
int CopyFile(const std::string &source, const std::string &target);
|
||||
|
||||
// Moves or renames a file or directory. Returns 0 on success
|
||||
int Move(const std::string &source, const std::string &target);
|
||||
|
||||
// Gets the date and time of the file/dir creation
|
||||
CDateTime GetCreateDateTime(const std::string &path) const;
|
||||
|
||||
// Gets the date and time of the file/dir modification
|
||||
CDateTime GetModifyDateTime(const std::string &path) const;
|
||||
|
||||
protected:
|
||||
~CScriptFileSystem();
|
||||
|
||||
mutable int refCount;
|
||||
std::string currentPath;
|
||||
};
|
||||
|
||||
void RegisterScriptFileSystem(asIScriptEngine *engine);
|
||||
|
||||
END_AS_NAMESPACE
|
||||
|
||||
#endif
|
||||
807
add_on/scriptgrid/scriptgrid.cpp
Normal file
807
add_on/scriptgrid/scriptgrid.cpp
Normal file
@@ -0,0 +1,807 @@
|
||||
#include <new>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
#include <stdio.h> // sprintf
|
||||
|
||||
#include "scriptgrid.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
BEGIN_AS_NAMESPACE
|
||||
|
||||
// Set the default memory routines
|
||||
// Use the angelscript engine's memory routines by default
|
||||
static asALLOCFUNC_t userAlloc = asAllocMem;
|
||||
static asFREEFUNC_t userFree = asFreeMem;
|
||||
|
||||
// Allows the application to set which memory routines should be used by the array object
|
||||
void CScriptGrid::SetMemoryFunctions(asALLOCFUNC_t allocFunc, asFREEFUNC_t freeFunc)
|
||||
{
|
||||
userAlloc = allocFunc;
|
||||
userFree = freeFunc;
|
||||
}
|
||||
|
||||
static void RegisterScriptGrid_Native(asIScriptEngine *engine);
|
||||
|
||||
struct SGridBuffer
|
||||
{
|
||||
asDWORD width;
|
||||
asDWORD height;
|
||||
asBYTE data[1];
|
||||
};
|
||||
|
||||
CScriptGrid *CScriptGrid::Create(asITypeInfo *ti)
|
||||
{
|
||||
return CScriptGrid::Create(ti, 0, 0);
|
||||
}
|
||||
|
||||
CScriptGrid *CScriptGrid::Create(asITypeInfo *ti, asUINT w, asUINT h)
|
||||
{
|
||||
// Allocate the memory
|
||||
void *mem = userAlloc(sizeof(CScriptGrid));
|
||||
if( mem == 0 )
|
||||
{
|
||||
asIScriptContext *ctx = asGetActiveContext();
|
||||
if( ctx )
|
||||
ctx->SetException("Out of memory");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Initialize the object
|
||||
CScriptGrid *a = new(mem) CScriptGrid(w, h, ti);
|
||||
|
||||
return a;
|
||||
}
|
||||
|
||||
CScriptGrid *CScriptGrid::Create(asITypeInfo *ti, void *initList)
|
||||
{
|
||||
// Allocate the memory
|
||||
void *mem = userAlloc(sizeof(CScriptGrid));
|
||||
if( mem == 0 )
|
||||
{
|
||||
asIScriptContext *ctx = asGetActiveContext();
|
||||
if( ctx )
|
||||
ctx->SetException("Out of memory");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Initialize the object
|
||||
CScriptGrid *a = new(mem) CScriptGrid(ti, initList);
|
||||
|
||||
return a;
|
||||
}
|
||||
|
||||
CScriptGrid *CScriptGrid::Create(asITypeInfo *ti, asUINT w, asUINT h, void *defVal)
|
||||
{
|
||||
// Allocate the memory
|
||||
void *mem = userAlloc(sizeof(CScriptGrid));
|
||||
if( mem == 0 )
|
||||
{
|
||||
asIScriptContext *ctx = asGetActiveContext();
|
||||
if( ctx )
|
||||
ctx->SetException("Out of memory");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Initialize the object
|
||||
CScriptGrid *a = new(mem) CScriptGrid(w, h, defVal, ti);
|
||||
|
||||
return a;
|
||||
}
|
||||
|
||||
// This optional callback is called when the template type is first used by the compiler.
|
||||
// It allows the application to validate if the template can be instantiated for the requested
|
||||
// subtype at compile time, instead of at runtime. The output argument dontGarbageCollect
|
||||
// allow the callback to tell the engine if the template instance type shouldn't be garbage collected,
|
||||
// i.e. no asOBJ_GC flag.
|
||||
static bool ScriptGridTemplateCallback(asITypeInfo *ti, bool &dontGarbageCollect)
|
||||
{
|
||||
// Make sure the subtype can be instantiated with a default factory/constructor,
|
||||
// otherwise we won't be able to instantiate the elements.
|
||||
int typeId = ti->GetSubTypeId();
|
||||
if( typeId == asTYPEID_VOID )
|
||||
return false;
|
||||
if( (typeId & asTYPEID_MASK_OBJECT) && !(typeId & asTYPEID_OBJHANDLE) )
|
||||
{
|
||||
asITypeInfo *subtype = ti->GetEngine()->GetTypeInfoById(typeId);
|
||||
asDWORD flags = subtype->GetFlags();
|
||||
if( (flags & asOBJ_VALUE) && !(flags & asOBJ_POD) )
|
||||
{
|
||||
// Verify that there is a default constructor
|
||||
bool found = false;
|
||||
for( asUINT n = 0; n < subtype->GetBehaviourCount(); n++ )
|
||||
{
|
||||
asEBehaviours beh;
|
||||
asIScriptFunction *func = subtype->GetBehaviourByIndex(n, &beh);
|
||||
if( beh != asBEHAVE_CONSTRUCT ) continue;
|
||||
|
||||
if( func->GetParamCount() == 0 )
|
||||
{
|
||||
// Found the default constructor
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if( !found )
|
||||
{
|
||||
// There is no default constructor
|
||||
ti->GetEngine()->WriteMessage("array", 0, 0, asMSGTYPE_ERROR, "The subtype has no default constructor");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else if( (flags & asOBJ_REF) )
|
||||
{
|
||||
bool found = false;
|
||||
|
||||
// If value assignment for ref type has been disabled then the array
|
||||
// can be created if the type has a default factory function
|
||||
if( !ti->GetEngine()->GetEngineProperty(asEP_DISALLOW_VALUE_ASSIGN_FOR_REF_TYPE) )
|
||||
{
|
||||
// Verify that there is a default factory
|
||||
for( asUINT n = 0; n < subtype->GetFactoryCount(); n++ )
|
||||
{
|
||||
asIScriptFunction *func = subtype->GetFactoryByIndex(n);
|
||||
if( func->GetParamCount() == 0 )
|
||||
{
|
||||
// Found the default factory
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if( !found )
|
||||
{
|
||||
// No default factory
|
||||
ti->GetEngine()->WriteMessage("array", 0, 0, asMSGTYPE_ERROR, "The subtype has no default factory");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// If the object type is not garbage collected then the array also doesn't need to be
|
||||
if( !(flags & asOBJ_GC) )
|
||||
dontGarbageCollect = true;
|
||||
}
|
||||
else if( !(typeId & asTYPEID_OBJHANDLE) )
|
||||
{
|
||||
// Arrays with primitives cannot form circular references,
|
||||
// thus there is no need to garbage collect them
|
||||
dontGarbageCollect = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
assert( typeId & asTYPEID_OBJHANDLE );
|
||||
|
||||
// It is not necessary to set the array as garbage collected for all handle types.
|
||||
// If it is possible to determine that the handle cannot refer to an object type
|
||||
// that can potentially form a circular reference with the array then it is not
|
||||
// necessary to make the array garbage collected.
|
||||
asITypeInfo *subtype = ti->GetEngine()->GetTypeInfoById(typeId);
|
||||
asDWORD flags = subtype->GetFlags();
|
||||
if( !(flags & asOBJ_GC) )
|
||||
{
|
||||
if( (flags & asOBJ_SCRIPT_OBJECT) )
|
||||
{
|
||||
// Even if a script class is by itself not garbage collected, it is possible
|
||||
// that classes that derive from it may be, so it is not possible to know
|
||||
// that no circular reference can occur.
|
||||
if( (flags & asOBJ_NOINHERIT) )
|
||||
{
|
||||
// A script class declared as final cannot be inherited from, thus
|
||||
// we can be certain that the object cannot be garbage collected.
|
||||
dontGarbageCollect = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// For application registered classes we assume the application knows
|
||||
// what it is doing and don't mark the array as garbage collected unless
|
||||
// the type is also garbage collected.
|
||||
dontGarbageCollect = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// The type is ok
|
||||
return true;
|
||||
}
|
||||
|
||||
// Registers the template array type
|
||||
void RegisterScriptGrid(asIScriptEngine *engine)
|
||||
{
|
||||
// TODO: Implement the generic calling convention
|
||||
RegisterScriptGrid_Native(engine);
|
||||
}
|
||||
|
||||
static void RegisterScriptGrid_Native(asIScriptEngine *engine)
|
||||
{
|
||||
int r;
|
||||
|
||||
// Register the grid type as a template
|
||||
r = engine->RegisterObjectType("grid<class T>", 0, asOBJ_REF | asOBJ_GC | asOBJ_TEMPLATE); assert( r >= 0 );
|
||||
|
||||
// Register a callback for validating the subtype before it is used
|
||||
r = engine->RegisterObjectBehaviour("grid<T>", asBEHAVE_TEMPLATE_CALLBACK, "bool f(int&in, bool&out)", asFUNCTION(ScriptGridTemplateCallback), asCALL_CDECL); assert( r >= 0 );
|
||||
|
||||
// Templates receive the object type as the first parameter. To the script writer this is hidden
|
||||
r = engine->RegisterObjectBehaviour("grid<T>", asBEHAVE_FACTORY, "grid<T>@ f(int&in)", asFUNCTIONPR(CScriptGrid::Create, (asITypeInfo*), CScriptGrid*), asCALL_CDECL); assert( r >= 0 );
|
||||
r = engine->RegisterObjectBehaviour("grid<T>", asBEHAVE_FACTORY, "grid<T>@ f(int&in, uint, uint)", asFUNCTIONPR(CScriptGrid::Create, (asITypeInfo*, asUINT, asUINT), CScriptGrid*), asCALL_CDECL); assert( r >= 0 );
|
||||
r = engine->RegisterObjectBehaviour("grid<T>", asBEHAVE_FACTORY, "grid<T>@ f(int&in, uint, uint, const T &in)", asFUNCTIONPR(CScriptGrid::Create, (asITypeInfo*, asUINT, asUINT, void *), CScriptGrid*), asCALL_CDECL); assert( r >= 0 );
|
||||
|
||||
// Register the factory that will be used for initialization lists
|
||||
r = engine->RegisterObjectBehaviour("grid<T>", asBEHAVE_LIST_FACTORY, "grid<T>@ f(int&in type, int&in list) {repeat {repeat_same T}}", asFUNCTIONPR(CScriptGrid::Create, (asITypeInfo*, void*), CScriptGrid*), asCALL_CDECL); assert( r >= 0 );
|
||||
|
||||
// The memory management methods
|
||||
r = engine->RegisterObjectBehaviour("grid<T>", asBEHAVE_ADDREF, "void f()", asMETHOD(CScriptGrid,AddRef), asCALL_THISCALL); assert( r >= 0 );
|
||||
r = engine->RegisterObjectBehaviour("grid<T>", asBEHAVE_RELEASE, "void f()", asMETHOD(CScriptGrid,Release), asCALL_THISCALL); assert( r >= 0 );
|
||||
|
||||
// The index operator returns the template subtype
|
||||
r = engine->RegisterObjectMethod("grid<T>", "T &opIndex(uint, uint)", asMETHODPR(CScriptGrid, At, (asUINT, asUINT), void*), asCALL_THISCALL); assert( r >= 0 );
|
||||
r = engine->RegisterObjectMethod("grid<T>", "const T &opIndex(uint, uint) const", asMETHODPR(CScriptGrid, At, (asUINT, asUINT) const, const void*), asCALL_THISCALL); assert( r >= 0 );
|
||||
|
||||
// Other methods
|
||||
r = engine->RegisterObjectMethod("grid<T>", "void resize(uint width, uint height)", asMETHODPR(CScriptGrid, Resize, (asUINT, asUINT), void), asCALL_THISCALL); assert( r >= 0 );
|
||||
r = engine->RegisterObjectMethod("grid<T>", "uint width() const", asMETHOD(CScriptGrid, GetWidth), asCALL_THISCALL); assert( r >= 0 );
|
||||
r = engine->RegisterObjectMethod("grid<T>", "uint height() const", asMETHOD(CScriptGrid, GetHeight), asCALL_THISCALL); assert( r >= 0 );
|
||||
|
||||
// Register GC behaviours in case the array needs to be garbage collected
|
||||
r = engine->RegisterObjectBehaviour("grid<T>", asBEHAVE_GETREFCOUNT, "int f()", asMETHOD(CScriptGrid, GetRefCount), asCALL_THISCALL); assert( r >= 0 );
|
||||
r = engine->RegisterObjectBehaviour("grid<T>", asBEHAVE_SETGCFLAG, "void f()", asMETHOD(CScriptGrid, SetFlag), asCALL_THISCALL); assert( r >= 0 );
|
||||
r = engine->RegisterObjectBehaviour("grid<T>", asBEHAVE_GETGCFLAG, "bool f()", asMETHOD(CScriptGrid, GetFlag), asCALL_THISCALL); assert( r >= 0 );
|
||||
r = engine->RegisterObjectBehaviour("grid<T>", asBEHAVE_ENUMREFS, "void f(int&in)", asMETHOD(CScriptGrid, EnumReferences), asCALL_THISCALL); assert( r >= 0 );
|
||||
r = engine->RegisterObjectBehaviour("grid<T>", asBEHAVE_RELEASEREFS, "void f(int&in)", asMETHOD(CScriptGrid, ReleaseAllHandles), asCALL_THISCALL); assert( r >= 0 );
|
||||
}
|
||||
|
||||
CScriptGrid::CScriptGrid(asITypeInfo *ti, void *buf)
|
||||
{
|
||||
refCount = 1;
|
||||
gcFlag = false;
|
||||
objType = ti;
|
||||
objType->AddRef();
|
||||
buffer = 0;
|
||||
subTypeId = objType->GetSubTypeId();
|
||||
|
||||
asIScriptEngine *engine = ti->GetEngine();
|
||||
|
||||
// Determine element size
|
||||
if( subTypeId & asTYPEID_MASK_OBJECT )
|
||||
elementSize = sizeof(asPWORD);
|
||||
else
|
||||
elementSize = engine->GetSizeOfPrimitiveType(subTypeId);
|
||||
|
||||
// Determine the initial size from the buffer
|
||||
asUINT height = *(asUINT*)buf;
|
||||
asUINT width = height ? *(asUINT*)((char*)(buf)+4) : 0;
|
||||
|
||||
// Make sure the grid size isn't too large for us to handle
|
||||
if( !CheckMaxSize(width, height) )
|
||||
{
|
||||
// Don't continue with the initialization
|
||||
return;
|
||||
}
|
||||
|
||||
// Skip the height value at the start of the buffer
|
||||
buf = (asUINT*)(buf)+1;
|
||||
|
||||
// Copy the values of the grid elements from the buffer
|
||||
if( (ti->GetSubTypeId() & asTYPEID_MASK_OBJECT) == 0 )
|
||||
{
|
||||
CreateBuffer(&buffer, width, height);
|
||||
|
||||
// Copy the values of the primitive type into the internal buffer
|
||||
for( asUINT y = 0; y < height; y++ )
|
||||
{
|
||||
// Skip the length value at the start of each row
|
||||
buf = (asUINT*)(buf)+1;
|
||||
|
||||
// Copy the line
|
||||
if( width > 0 )
|
||||
memcpy(At(0,y), buf, width*elementSize);
|
||||
|
||||
// Move to next line
|
||||
buf = (char*)(buf) + width*elementSize;
|
||||
|
||||
// Align to 4 byte boundary
|
||||
if( asPWORD(buf) & 0x3 )
|
||||
buf = (char*)(buf) + 4 - (asPWORD(buf) & 0x3);
|
||||
}
|
||||
}
|
||||
else if( ti->GetSubTypeId() & asTYPEID_OBJHANDLE )
|
||||
{
|
||||
CreateBuffer(&buffer, width, height);
|
||||
|
||||
// Copy the handles into the internal buffer
|
||||
for( asUINT y = 0; y < height; y++ )
|
||||
{
|
||||
// Skip the length value at the start of each row
|
||||
buf = (asUINT*)(buf)+1;
|
||||
|
||||
// Copy the line
|
||||
if( width > 0 )
|
||||
memcpy(At(0,y), buf, width*elementSize);
|
||||
|
||||
// With object handles it is safe to clear the memory in the received buffer
|
||||
// instead of increasing the ref count. It will save time both by avoiding the
|
||||
// call the increase ref, and also relieve the engine from having to release
|
||||
// its references too
|
||||
memset(buf, 0, width*elementSize);
|
||||
|
||||
// Move to next line
|
||||
buf = (char*)(buf) + width*elementSize;
|
||||
|
||||
// Align to 4 byte boundary
|
||||
if( asPWORD(buf) & 0x3 )
|
||||
buf = (char*)(buf) + 4 - (asPWORD(buf) & 0x3);
|
||||
}
|
||||
}
|
||||
else if( ti->GetSubType()->GetFlags() & asOBJ_REF )
|
||||
{
|
||||
// Only allocate the buffer, but not the objects
|
||||
subTypeId |= asTYPEID_OBJHANDLE;
|
||||
CreateBuffer(&buffer, width, height);
|
||||
subTypeId &= ~asTYPEID_OBJHANDLE;
|
||||
|
||||
// Copy the handles into the internal buffer
|
||||
for( asUINT y = 0; y < height; y++ )
|
||||
{
|
||||
// Skip the length value at the start of each row
|
||||
buf = (asUINT*)(buf)+1;
|
||||
|
||||
// Copy the line
|
||||
if( width > 0 )
|
||||
memcpy(At(0,y), buf, width*elementSize);
|
||||
|
||||
// With object handles it is safe to clear the memory in the received buffer
|
||||
// instead of increasing the ref count. It will save time both by avoiding the
|
||||
// call the increase ref, and also relieve the engine from having to release
|
||||
// its references too
|
||||
memset(buf, 0, width*elementSize);
|
||||
|
||||
// Move to next line
|
||||
buf = (char*)(buf) + width*elementSize;
|
||||
|
||||
// Align to 4 byte boundary
|
||||
if( asPWORD(buf) & 0x3 )
|
||||
buf = (char*)(buf) + 4 - (asPWORD(buf) & 0x3);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// TODO: Optimize by calling the copy constructor of the object instead of
|
||||
// constructing with the default constructor and then assigning the value
|
||||
// TODO: With C++11 ideally we should be calling the move constructor, instead
|
||||
// of the copy constructor as the engine will just discard the objects in the
|
||||
// buffer afterwards.
|
||||
CreateBuffer(&buffer, width, height);
|
||||
|
||||
// For value types we need to call the opAssign for each individual object
|
||||
asITypeInfo *subType = ti->GetSubType();
|
||||
asUINT subTypeSize = subType->GetSize();
|
||||
for( asUINT y = 0;y < height; y++ )
|
||||
{
|
||||
// Skip the length value at the start of each row
|
||||
buf = (asUINT*)(buf)+1;
|
||||
|
||||
// Call opAssign for each of the objects on the row
|
||||
for( asUINT x = 0; x < width; x++ )
|
||||
{
|
||||
void *obj = At(x,y);
|
||||
asBYTE *srcObj = (asBYTE*)(buf) + x*subTypeSize;
|
||||
engine->AssignScriptObject(obj, srcObj, subType);
|
||||
}
|
||||
|
||||
// Move to next line
|
||||
buf = (char*)(buf) + width*subTypeSize;
|
||||
|
||||
// Align to 4 byte boundary
|
||||
if( asPWORD(buf) & 0x3 )
|
||||
buf = (char*)(buf) + 4 - (asPWORD(buf) & 0x3);
|
||||
}
|
||||
}
|
||||
|
||||
// Notify the GC of the successful creation
|
||||
if( objType->GetFlags() & asOBJ_GC )
|
||||
objType->GetEngine()->NotifyGarbageCollectorOfNewObject(this, objType);
|
||||
}
|
||||
|
||||
CScriptGrid::CScriptGrid(asUINT width, asUINT height, asITypeInfo *ti)
|
||||
{
|
||||
refCount = 1;
|
||||
gcFlag = false;
|
||||
objType = ti;
|
||||
objType->AddRef();
|
||||
buffer = 0;
|
||||
subTypeId = objType->GetSubTypeId();
|
||||
|
||||
// Determine element size
|
||||
if( subTypeId & asTYPEID_MASK_OBJECT )
|
||||
elementSize = sizeof(asPWORD);
|
||||
else
|
||||
elementSize = objType->GetEngine()->GetSizeOfPrimitiveType(subTypeId);
|
||||
|
||||
// Make sure the array size isn't too large for us to handle
|
||||
if( !CheckMaxSize(width, height) )
|
||||
{
|
||||
// Don't continue with the initialization
|
||||
return;
|
||||
}
|
||||
|
||||
CreateBuffer(&buffer, width, height);
|
||||
|
||||
// Notify the GC of the successful creation
|
||||
if( objType->GetFlags() & asOBJ_GC )
|
||||
objType->GetEngine()->NotifyGarbageCollectorOfNewObject(this, objType);
|
||||
}
|
||||
|
||||
void CScriptGrid::Resize(asUINT width, asUINT height)
|
||||
{
|
||||
// Make sure the size isn't too large for us to handle
|
||||
if( !CheckMaxSize(width, height) )
|
||||
return;
|
||||
|
||||
// Create a new buffer
|
||||
SGridBuffer *tmpBuffer = 0;
|
||||
CreateBuffer(&tmpBuffer, width, height);
|
||||
if( tmpBuffer == 0 )
|
||||
return;
|
||||
|
||||
if( buffer )
|
||||
{
|
||||
// Copy the existing values to the new buffer
|
||||
asUINT w = width > buffer->width ? buffer->width : width;
|
||||
asUINT h = height > buffer->height ? buffer->height : height;
|
||||
for( asUINT y = 0; y < h; y++ )
|
||||
for( asUINT x = 0; x < w; x++ )
|
||||
SetValue(tmpBuffer, x, y, At(buffer, x, y));
|
||||
|
||||
// Replace the internal buffer
|
||||
DeleteBuffer(buffer);
|
||||
}
|
||||
|
||||
buffer = tmpBuffer;
|
||||
}
|
||||
|
||||
CScriptGrid::CScriptGrid(asUINT width, asUINT height, void *defVal, asITypeInfo *ti)
|
||||
{
|
||||
refCount = 1;
|
||||
gcFlag = false;
|
||||
objType = ti;
|
||||
objType->AddRef();
|
||||
buffer = 0;
|
||||
subTypeId = objType->GetSubTypeId();
|
||||
|
||||
// Determine element size
|
||||
if( subTypeId & asTYPEID_MASK_OBJECT )
|
||||
elementSize = sizeof(asPWORD);
|
||||
else
|
||||
elementSize = objType->GetEngine()->GetSizeOfPrimitiveType(subTypeId);
|
||||
|
||||
// Make sure the array size isn't too large for us to handle
|
||||
if( !CheckMaxSize(width, height) )
|
||||
{
|
||||
// Don't continue with the initialization
|
||||
return;
|
||||
}
|
||||
|
||||
CreateBuffer(&buffer, width, height);
|
||||
|
||||
// Notify the GC of the successful creation
|
||||
if( objType->GetFlags() & asOBJ_GC )
|
||||
objType->GetEngine()->NotifyGarbageCollectorOfNewObject(this, objType);
|
||||
|
||||
// Initialize the elements with the default value
|
||||
for( asUINT y = 0; y < GetHeight(); y++ )
|
||||
for( asUINT x = 0; x < GetWidth(); x++ )
|
||||
SetValue(x, y, defVal);
|
||||
}
|
||||
|
||||
void CScriptGrid::SetValue(asUINT x, asUINT y, void *value)
|
||||
{
|
||||
SetValue(buffer, x, y, value);
|
||||
}
|
||||
|
||||
void CScriptGrid::SetValue(SGridBuffer *buf, asUINT x, asUINT y, void *value)
|
||||
{
|
||||
// At() will take care of the out-of-bounds checking, though
|
||||
// if called from the application then nothing will be done
|
||||
void *ptr = At(buf, x, y);
|
||||
if( ptr == 0 ) return;
|
||||
|
||||
if( (subTypeId & ~asTYPEID_MASK_SEQNBR) && !(subTypeId & asTYPEID_OBJHANDLE) )
|
||||
objType->GetEngine()->AssignScriptObject(ptr, value, objType->GetSubType());
|
||||
else if( subTypeId & asTYPEID_OBJHANDLE )
|
||||
{
|
||||
void *tmp = *(void**)ptr;
|
||||
*(void**)ptr = *(void**)value;
|
||||
objType->GetEngine()->AddRefScriptObject(*(void**)value, objType->GetSubType());
|
||||
if( tmp )
|
||||
objType->GetEngine()->ReleaseScriptObject(tmp, objType->GetSubType());
|
||||
}
|
||||
else if( subTypeId == asTYPEID_BOOL ||
|
||||
subTypeId == asTYPEID_INT8 ||
|
||||
subTypeId == asTYPEID_UINT8 )
|
||||
*(char*)ptr = *(char*)value;
|
||||
else if( subTypeId == asTYPEID_INT16 ||
|
||||
subTypeId == asTYPEID_UINT16 )
|
||||
*(short*)ptr = *(short*)value;
|
||||
else if( subTypeId == asTYPEID_INT32 ||
|
||||
subTypeId == asTYPEID_UINT32 ||
|
||||
subTypeId == asTYPEID_FLOAT ||
|
||||
subTypeId > asTYPEID_DOUBLE ) // enums have a type id larger than doubles
|
||||
*(int*)ptr = *(int*)value;
|
||||
else if( subTypeId == asTYPEID_INT64 ||
|
||||
subTypeId == asTYPEID_UINT64 ||
|
||||
subTypeId == asTYPEID_DOUBLE )
|
||||
*(double*)ptr = *(double*)value;
|
||||
}
|
||||
|
||||
CScriptGrid::~CScriptGrid()
|
||||
{
|
||||
if( buffer )
|
||||
{
|
||||
DeleteBuffer(buffer);
|
||||
buffer = 0;
|
||||
}
|
||||
if( objType ) objType->Release();
|
||||
}
|
||||
|
||||
asUINT CScriptGrid::GetWidth() const
|
||||
{
|
||||
if( buffer )
|
||||
return buffer->width;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
asUINT CScriptGrid::GetHeight() const
|
||||
{
|
||||
if( buffer )
|
||||
return buffer->height;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// internal
|
||||
bool CScriptGrid::CheckMaxSize(asUINT width, asUINT height)
|
||||
{
|
||||
// This code makes sure the size of the buffer that is allocated
|
||||
// for the array doesn't overflow and becomes smaller than requested
|
||||
|
||||
asUINT maxSize = 0xFFFFFFFFul - sizeof(SGridBuffer) + 1;
|
||||
if( elementSize > 0 )
|
||||
maxSize /= elementSize;
|
||||
|
||||
asINT64 numElements = width * height;
|
||||
|
||||
if( (numElements >> 32) || numElements > maxSize )
|
||||
{
|
||||
asIScriptContext *ctx = asGetActiveContext();
|
||||
if( ctx )
|
||||
ctx->SetException("Too large grid size");
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// OK
|
||||
return true;
|
||||
}
|
||||
|
||||
asITypeInfo *CScriptGrid::GetGridObjectType() const
|
||||
{
|
||||
return objType;
|
||||
}
|
||||
|
||||
int CScriptGrid::GetGridTypeId() const
|
||||
{
|
||||
return objType->GetTypeId();
|
||||
}
|
||||
|
||||
int CScriptGrid::GetElementTypeId() const
|
||||
{
|
||||
return subTypeId;
|
||||
}
|
||||
|
||||
void *CScriptGrid::At(asUINT x, asUINT y)
|
||||
{
|
||||
return At(buffer, x, y);
|
||||
}
|
||||
|
||||
// Return a pointer to the array element. Returns 0 if the index is out of bounds
|
||||
void *CScriptGrid::At(SGridBuffer *buf, asUINT x, asUINT y)
|
||||
{
|
||||
if( buf == 0 || x >= buf->width || y >= buf->height )
|
||||
{
|
||||
// If this is called from a script we raise a script exception
|
||||
asIScriptContext *ctx = asGetActiveContext();
|
||||
if( ctx )
|
||||
ctx->SetException("Index out of bounds");
|
||||
return 0;
|
||||
}
|
||||
|
||||
asUINT index = x+y*buf->width;
|
||||
if( (subTypeId & asTYPEID_MASK_OBJECT) && !(subTypeId & asTYPEID_OBJHANDLE) )
|
||||
return *(void**)(buf->data + elementSize*index);
|
||||
else
|
||||
return buf->data + elementSize*index;
|
||||
}
|
||||
const void *CScriptGrid::At(asUINT x, asUINT y) const
|
||||
{
|
||||
return const_cast<CScriptGrid*>(this)->At(const_cast<SGridBuffer*>(buffer), x, y);
|
||||
}
|
||||
|
||||
|
||||
// internal
|
||||
void CScriptGrid::CreateBuffer(SGridBuffer **buf, asUINT w, asUINT h)
|
||||
{
|
||||
asUINT numElements = w * h;
|
||||
|
||||
*buf = reinterpret_cast<SGridBuffer*>(userAlloc(sizeof(SGridBuffer)-1+elementSize*numElements));
|
||||
|
||||
if( *buf )
|
||||
{
|
||||
(*buf)->width = w;
|
||||
(*buf)->height = h;
|
||||
Construct(*buf);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Oops, out of memory
|
||||
asIScriptContext *ctx = asGetActiveContext();
|
||||
if( ctx )
|
||||
ctx->SetException("Out of memory");
|
||||
}
|
||||
}
|
||||
|
||||
// internal
|
||||
void CScriptGrid::DeleteBuffer(SGridBuffer *buf)
|
||||
{
|
||||
assert( buf );
|
||||
|
||||
Destruct(buf);
|
||||
|
||||
// Free the buffer
|
||||
userFree(buf);
|
||||
}
|
||||
|
||||
// internal
|
||||
void CScriptGrid::Construct(SGridBuffer *buf)
|
||||
{
|
||||
assert( buf );
|
||||
|
||||
if( subTypeId & asTYPEID_OBJHANDLE )
|
||||
{
|
||||
// Set all object handles to null
|
||||
void *d = (void*)(buf->data);
|
||||
memset(d, 0, (buf->width*buf->height)*sizeof(void*));
|
||||
}
|
||||
else if( subTypeId & asTYPEID_MASK_OBJECT )
|
||||
{
|
||||
void **max = (void**)(buf->data + (buf->width*buf->height) * sizeof(void*));
|
||||
void **d = (void**)(buf->data);
|
||||
|
||||
asIScriptEngine *engine = objType->GetEngine();
|
||||
asITypeInfo *subType = objType->GetSubType();
|
||||
|
||||
for( ; d < max; d++ )
|
||||
{
|
||||
*d = (void*)engine->CreateScriptObject(subType);
|
||||
if( *d == 0 )
|
||||
{
|
||||
// Set the remaining entries to null so the destructor
|
||||
// won't attempt to destroy invalid objects later
|
||||
memset(d, 0, sizeof(void*)*(max-d));
|
||||
|
||||
// There is no need to set an exception on the context,
|
||||
// as CreateScriptObject has already done that
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// internal
|
||||
void CScriptGrid::Destruct(SGridBuffer *buf)
|
||||
{
|
||||
assert( buf );
|
||||
|
||||
if( subTypeId & asTYPEID_MASK_OBJECT )
|
||||
{
|
||||
asIScriptEngine *engine = objType->GetEngine();
|
||||
|
||||
void **max = (void**)(buf->data + (buf->width*buf->height) * sizeof(void*));
|
||||
void **d = (void**)(buf->data);
|
||||
|
||||
for( ; d < max; d++ )
|
||||
{
|
||||
if( *d )
|
||||
engine->ReleaseScriptObject(*d, objType->GetSubType());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// GC behaviour
|
||||
void CScriptGrid::EnumReferences(asIScriptEngine *engine)
|
||||
{
|
||||
if( buffer == 0 ) return;
|
||||
|
||||
// If the grid is holding handles, then we need to notify the GC of them
|
||||
if (subTypeId & asTYPEID_MASK_OBJECT)
|
||||
{
|
||||
asUINT numElements = buffer->width * buffer->height;
|
||||
void **d = (void**)buffer->data;
|
||||
asITypeInfo *subType = engine->GetTypeInfoById(subTypeId);
|
||||
if ((subType->GetFlags() & asOBJ_REF))
|
||||
{
|
||||
// For reference types we need to notify the GC of each instance
|
||||
for (asUINT n = 0; n < numElements; n++)
|
||||
{
|
||||
if (d[n])
|
||||
engine->GCEnumCallback(d[n]);
|
||||
}
|
||||
}
|
||||
else if ((subType->GetFlags() & asOBJ_VALUE) && (subType->GetFlags() & asOBJ_GC))
|
||||
{
|
||||
// For value types we need to forward the enum callback
|
||||
// to the object so it can decide what to do
|
||||
for (asUINT n = 0; n < numElements; n++)
|
||||
{
|
||||
if (d[n])
|
||||
engine->ForwardGCEnumReferences(d[n], subType);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// GC behaviour
|
||||
void CScriptGrid::ReleaseAllHandles(asIScriptEngine*)
|
||||
{
|
||||
if( buffer == 0 ) return;
|
||||
|
||||
DeleteBuffer(buffer);
|
||||
buffer = 0;
|
||||
}
|
||||
|
||||
void CScriptGrid::AddRef() const
|
||||
{
|
||||
// Clear the GC flag then increase the counter
|
||||
gcFlag = false;
|
||||
asAtomicInc(refCount);
|
||||
}
|
||||
|
||||
void CScriptGrid::Release() const
|
||||
{
|
||||
// Clearing the GC flag then descrease the counter
|
||||
gcFlag = false;
|
||||
if( asAtomicDec(refCount) == 0 )
|
||||
{
|
||||
// When reaching 0 no more references to this instance
|
||||
// exists and the object should be destroyed
|
||||
this->~CScriptGrid();
|
||||
userFree(const_cast<CScriptGrid*>(this));
|
||||
}
|
||||
}
|
||||
|
||||
// GC behaviour
|
||||
int CScriptGrid::GetRefCount()
|
||||
{
|
||||
return refCount;
|
||||
}
|
||||
|
||||
// GC behaviour
|
||||
void CScriptGrid::SetFlag()
|
||||
{
|
||||
gcFlag = true;
|
||||
}
|
||||
|
||||
// GC behaviour
|
||||
bool CScriptGrid::GetFlag()
|
||||
{
|
||||
return gcFlag;
|
||||
}
|
||||
|
||||
END_AS_NAMESPACE
|
||||
82
add_on/scriptgrid/scriptgrid.h
Normal file
82
add_on/scriptgrid/scriptgrid.h
Normal file
@@ -0,0 +1,82 @@
|
||||
#ifndef SCRIPTGRID_H
|
||||
#define SCRIPTGRID_H
|
||||
|
||||
#ifndef ANGELSCRIPT_H
|
||||
// Avoid having to inform include path if header is already include before
|
||||
#include <angelscript.h>
|
||||
#endif
|
||||
|
||||
BEGIN_AS_NAMESPACE
|
||||
|
||||
struct SGridBuffer;
|
||||
|
||||
class CScriptGrid
|
||||
{
|
||||
public:
|
||||
// Set the memory functions that should be used by all CScriptGrids
|
||||
static void SetMemoryFunctions(asALLOCFUNC_t allocFunc, asFREEFUNC_t freeFunc);
|
||||
|
||||
// Factory functions
|
||||
static CScriptGrid *Create(asITypeInfo *ot);
|
||||
static CScriptGrid *Create(asITypeInfo *ot, asUINT width, asUINT height);
|
||||
static CScriptGrid *Create(asITypeInfo *ot, asUINT width, asUINT height, void *defaultValue);
|
||||
static CScriptGrid *Create(asITypeInfo *ot, void *listBuffer);
|
||||
|
||||
// Memory management
|
||||
void AddRef() const;
|
||||
void Release() const;
|
||||
|
||||
// Type information
|
||||
asITypeInfo *GetGridObjectType() const;
|
||||
int GetGridTypeId() const;
|
||||
int GetElementTypeId() const;
|
||||
|
||||
// Size
|
||||
asUINT GetWidth() const;
|
||||
asUINT GetHeight() const;
|
||||
void Resize(asUINT width, asUINT height);
|
||||
|
||||
// Get a pointer to an element. Returns 0 if out of bounds
|
||||
void *At(asUINT x, asUINT y);
|
||||
const void *At(asUINT x, asUINT y) const;
|
||||
|
||||
// Set value of an element
|
||||
// Remember, if the grid holds handles the value parameter should be the
|
||||
// address of the handle. The refCount of the object will also be incremented
|
||||
void SetValue(asUINT x, asUINT y, void *value);
|
||||
|
||||
// GC methods
|
||||
int GetRefCount();
|
||||
void SetFlag();
|
||||
bool GetFlag();
|
||||
void EnumReferences(asIScriptEngine *engine);
|
||||
void ReleaseAllHandles(asIScriptEngine *engine);
|
||||
|
||||
protected:
|
||||
mutable int refCount;
|
||||
mutable bool gcFlag;
|
||||
asITypeInfo *objType;
|
||||
SGridBuffer *buffer;
|
||||
int elementSize;
|
||||
int subTypeId;
|
||||
|
||||
// Constructors
|
||||
CScriptGrid(asITypeInfo *ot, void *initBuf); // Called from script when initialized with list
|
||||
CScriptGrid(asUINT w, asUINT h, asITypeInfo *ot);
|
||||
CScriptGrid(asUINT w, asUINT h, void *defVal, asITypeInfo *ot);
|
||||
virtual ~CScriptGrid();
|
||||
|
||||
bool CheckMaxSize(asUINT x, asUINT y);
|
||||
void CreateBuffer(SGridBuffer **buf, asUINT w, asUINT h);
|
||||
void DeleteBuffer(SGridBuffer *buf);
|
||||
void Construct(SGridBuffer *buf);
|
||||
void Destruct(SGridBuffer *buf);
|
||||
void SetValue(SGridBuffer *buf, asUINT x, asUINT y, void *value);
|
||||
void *At(SGridBuffer *buf, asUINT x, asUINT y);
|
||||
};
|
||||
|
||||
void RegisterScriptGrid(asIScriptEngine *engine);
|
||||
|
||||
END_AS_NAMESPACE
|
||||
|
||||
#endif
|
||||
360
add_on/scripthandle/scripthandle.cpp
Normal file
360
add_on/scripthandle/scripthandle.cpp
Normal file
@@ -0,0 +1,360 @@
|
||||
#include "scripthandle.h"
|
||||
#include <new>
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
|
||||
BEGIN_AS_NAMESPACE
|
||||
|
||||
static void Construct(CScriptHandle *self) { new(self) CScriptHandle(); }
|
||||
static void Construct(CScriptHandle *self, const CScriptHandle &o) { new(self) CScriptHandle(o); }
|
||||
// This one is not static because it needs to be friend with the CScriptHandle class
|
||||
void Construct(CScriptHandle *self, void *ref, int typeId) { new(self) CScriptHandle(ref, typeId); }
|
||||
static void Destruct(CScriptHandle *self) { self->~CScriptHandle(); }
|
||||
|
||||
CScriptHandle::CScriptHandle()
|
||||
{
|
||||
m_ref = 0;
|
||||
m_type = 0;
|
||||
}
|
||||
|
||||
CScriptHandle::CScriptHandle(const CScriptHandle &other)
|
||||
{
|
||||
m_ref = other.m_ref;
|
||||
m_type = other.m_type;
|
||||
|
||||
AddRefHandle();
|
||||
}
|
||||
|
||||
CScriptHandle::CScriptHandle(void *ref, asITypeInfo *type)
|
||||
{
|
||||
m_ref = ref;
|
||||
m_type = type;
|
||||
|
||||
AddRefHandle();
|
||||
}
|
||||
|
||||
// This constructor shouldn't be called from the application
|
||||
// directly as it requires an active script context
|
||||
CScriptHandle::CScriptHandle(void *ref, int typeId)
|
||||
{
|
||||
m_ref = 0;
|
||||
m_type = 0;
|
||||
|
||||
Assign(ref, typeId);
|
||||
}
|
||||
|
||||
CScriptHandle::~CScriptHandle()
|
||||
{
|
||||
ReleaseHandle();
|
||||
}
|
||||
|
||||
void CScriptHandle::ReleaseHandle()
|
||||
{
|
||||
if( m_ref && m_type )
|
||||
{
|
||||
asIScriptEngine *engine = m_type->GetEngine();
|
||||
engine->ReleaseScriptObject(m_ref, m_type);
|
||||
|
||||
engine->Release();
|
||||
|
||||
m_ref = 0;
|
||||
m_type = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void CScriptHandle::AddRefHandle()
|
||||
{
|
||||
if( m_ref && m_type )
|
||||
{
|
||||
asIScriptEngine *engine = m_type->GetEngine();
|
||||
engine->AddRefScriptObject(m_ref, m_type);
|
||||
|
||||
// Hold on to the engine so it isn't destroyed while
|
||||
// a reference to a script object is still held
|
||||
engine->AddRef();
|
||||
}
|
||||
}
|
||||
|
||||
CScriptHandle &CScriptHandle::operator =(const CScriptHandle &other)
|
||||
{
|
||||
Set(other.m_ref, other.m_type);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
void CScriptHandle::Set(void *ref, asITypeInfo *type)
|
||||
{
|
||||
if( m_ref == ref ) return;
|
||||
|
||||
ReleaseHandle();
|
||||
|
||||
m_ref = ref;
|
||||
m_type = type;
|
||||
|
||||
AddRefHandle();
|
||||
}
|
||||
|
||||
void *CScriptHandle::GetRef()
|
||||
{
|
||||
return m_ref;
|
||||
}
|
||||
|
||||
asITypeInfo *CScriptHandle::GetType() const
|
||||
{
|
||||
return m_type;
|
||||
}
|
||||
|
||||
int CScriptHandle::GetTypeId() const
|
||||
{
|
||||
if( m_type == 0 ) return 0;
|
||||
|
||||
return m_type->GetTypeId() | asTYPEID_OBJHANDLE;
|
||||
}
|
||||
|
||||
// This method shouldn't be called from the application
|
||||
// directly as it requires an active script context
|
||||
CScriptHandle &CScriptHandle::Assign(void *ref, int typeId)
|
||||
{
|
||||
// When receiving a null handle we just clear our memory
|
||||
if( typeId == 0 )
|
||||
{
|
||||
Set(0, 0);
|
||||
return *this;
|
||||
}
|
||||
|
||||
// Dereference received handles to get the object
|
||||
if( typeId & asTYPEID_OBJHANDLE )
|
||||
{
|
||||
// Store the actual reference
|
||||
ref = *(void**)ref;
|
||||
typeId &= ~asTYPEID_OBJHANDLE;
|
||||
}
|
||||
|
||||
// Get the object type
|
||||
asIScriptContext *ctx = asGetActiveContext();
|
||||
asIScriptEngine *engine = ctx->GetEngine();
|
||||
asITypeInfo *type = engine->GetTypeInfoById(typeId);
|
||||
|
||||
// If the argument is another CScriptHandle, we should copy the content instead
|
||||
if( type && strcmp(type->GetName(), "ref") == 0 )
|
||||
{
|
||||
CScriptHandle *r = (CScriptHandle*)ref;
|
||||
ref = r->m_ref;
|
||||
type = r->m_type;
|
||||
}
|
||||
|
||||
Set(ref, type);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool CScriptHandle::operator==(const CScriptHandle &o) const
|
||||
{
|
||||
if( m_ref == o.m_ref &&
|
||||
m_type == o.m_type )
|
||||
return true;
|
||||
|
||||
// TODO: If type is not the same, we should attempt to do a dynamic cast,
|
||||
// which may change the pointer for application registered classes
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool CScriptHandle::operator!=(const CScriptHandle &o) const
|
||||
{
|
||||
return !(*this == o);
|
||||
}
|
||||
|
||||
bool CScriptHandle::Equals(void *ref, int typeId) const
|
||||
{
|
||||
// Null handles are received as reference to a null handle
|
||||
if( typeId == 0 )
|
||||
ref = 0;
|
||||
|
||||
// Dereference handles to get the object
|
||||
if( typeId & asTYPEID_OBJHANDLE )
|
||||
{
|
||||
// Compare the actual reference
|
||||
ref = *(void**)ref;
|
||||
typeId &= ~asTYPEID_OBJHANDLE;
|
||||
}
|
||||
|
||||
// TODO: If typeId is not the same, we should attempt to do a dynamic cast,
|
||||
// which may change the pointer for application registered classes
|
||||
|
||||
if( ref == m_ref ) return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// AngelScript: used as '@obj = cast<obj>(ref);'
|
||||
void CScriptHandle::Cast(void **outRef, int typeId)
|
||||
{
|
||||
// If we hold a null handle, then just return null
|
||||
if( m_type == 0 )
|
||||
{
|
||||
*outRef = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
// It is expected that the outRef is always a handle
|
||||
assert( typeId & asTYPEID_OBJHANDLE );
|
||||
|
||||
// Compare the type id of the actual object
|
||||
typeId &= ~asTYPEID_OBJHANDLE;
|
||||
asIScriptEngine *engine = m_type->GetEngine();
|
||||
asITypeInfo *type = engine->GetTypeInfoById(typeId);
|
||||
|
||||
*outRef = 0;
|
||||
|
||||
// RefCastObject will increment the refCount of the returned object if successful
|
||||
engine->RefCastObject(m_ref, m_type, type, outRef);
|
||||
}
|
||||
|
||||
void CScriptHandle::EnumReferences(asIScriptEngine *inEngine)
|
||||
{
|
||||
// If we're holding a reference, we'll notify the garbage collector of it
|
||||
if (m_ref)
|
||||
inEngine->GCEnumCallback(m_ref);
|
||||
|
||||
// The object type itself is also garbage collected
|
||||
if( m_type)
|
||||
inEngine->GCEnumCallback(m_type);
|
||||
}
|
||||
|
||||
void CScriptHandle::ReleaseReferences(asIScriptEngine *inEngine)
|
||||
{
|
||||
// Simply clear the content to release the references
|
||||
Set(0, 0);
|
||||
}
|
||||
|
||||
void RegisterScriptHandle_Native(asIScriptEngine *engine)
|
||||
{
|
||||
int r;
|
||||
|
||||
#if AS_CAN_USE_CPP11
|
||||
// With C++11 it is possible to use asGetTypeTraits to automatically determine the flags that represent the C++ class
|
||||
r = engine->RegisterObjectType("ref", sizeof(CScriptHandle), asOBJ_VALUE | asOBJ_ASHANDLE | asOBJ_GC | asGetTypeTraits<CScriptHandle>()); assert( r >= 0 );
|
||||
#else
|
||||
r = engine->RegisterObjectType("ref", sizeof(CScriptHandle), asOBJ_VALUE | asOBJ_ASHANDLE | asOBJ_GC | asOBJ_APP_CLASS_CDAK); assert( r >= 0 );
|
||||
#endif
|
||||
r = engine->RegisterObjectBehaviour("ref", asBEHAVE_CONSTRUCT, "void f()", asFUNCTIONPR(Construct, (CScriptHandle *), void), asCALL_CDECL_OBJFIRST); assert( r >= 0 );
|
||||
r = engine->RegisterObjectBehaviour("ref", asBEHAVE_CONSTRUCT, "void f(const ref &in)", asFUNCTIONPR(Construct, (CScriptHandle *, const CScriptHandle &), void), asCALL_CDECL_OBJFIRST); assert( r >= 0 );
|
||||
r = engine->RegisterObjectBehaviour("ref", asBEHAVE_CONSTRUCT, "void f(const ?&in)", asFUNCTIONPR(Construct, (CScriptHandle *, void *, int), void), asCALL_CDECL_OBJFIRST); assert( r >= 0 );
|
||||
r = engine->RegisterObjectBehaviour("ref", asBEHAVE_DESTRUCT, "void f()", asFUNCTIONPR(Destruct, (CScriptHandle *), void), asCALL_CDECL_OBJFIRST); assert( r >= 0 );
|
||||
r = engine->RegisterObjectBehaviour("ref", asBEHAVE_ENUMREFS, "void f(int&in)", asMETHOD(CScriptHandle,EnumReferences), asCALL_THISCALL); assert(r >= 0);
|
||||
r = engine->RegisterObjectBehaviour("ref", asBEHAVE_RELEASEREFS, "void f(int&in)", asMETHOD(CScriptHandle, ReleaseReferences), asCALL_THISCALL); assert(r >= 0);
|
||||
r = engine->RegisterObjectMethod("ref", "void opCast(?&out)", asMETHODPR(CScriptHandle, Cast, (void **, int), void), asCALL_THISCALL); assert( r >= 0 );
|
||||
r = engine->RegisterObjectMethod("ref", "ref &opHndlAssign(const ref &in)", asMETHOD(CScriptHandle, operator=), asCALL_THISCALL); assert( r >= 0 );
|
||||
r = engine->RegisterObjectMethod("ref", "ref &opHndlAssign(const ?&in)", asMETHOD(CScriptHandle, Assign), asCALL_THISCALL); assert( r >= 0 );
|
||||
r = engine->RegisterObjectMethod("ref", "bool opEquals(const ref &in) const", asMETHODPR(CScriptHandle, operator==, (const CScriptHandle &) const, bool), asCALL_THISCALL); assert( r >= 0 );
|
||||
r = engine->RegisterObjectMethod("ref", "bool opEquals(const ?&in) const", asMETHODPR(CScriptHandle, Equals, (void*, int) const, bool), asCALL_THISCALL); assert( r >= 0 );
|
||||
}
|
||||
|
||||
void CScriptHandle_Construct_Generic(asIScriptGeneric *gen)
|
||||
{
|
||||
CScriptHandle *self = reinterpret_cast<CScriptHandle*>(gen->GetObject());
|
||||
new(self) CScriptHandle();
|
||||
}
|
||||
|
||||
void CScriptHandle_ConstructCopy_Generic(asIScriptGeneric *gen)
|
||||
{
|
||||
CScriptHandle *other = reinterpret_cast<CScriptHandle*>(gen->GetArgAddress(0));
|
||||
CScriptHandle *self = reinterpret_cast<CScriptHandle*>(gen->GetObject());
|
||||
new(self) CScriptHandle(*other);
|
||||
}
|
||||
|
||||
void CScriptHandle_ConstructVar_Generic(asIScriptGeneric *gen)
|
||||
{
|
||||
void *ref = gen->GetArgAddress(0);
|
||||
int typeId = gen->GetArgTypeId(0);
|
||||
CScriptHandle *self = reinterpret_cast<CScriptHandle*>(gen->GetObject());
|
||||
Construct(self, ref, typeId);
|
||||
}
|
||||
|
||||
void CScriptHandle_Destruct_Generic(asIScriptGeneric *gen)
|
||||
{
|
||||
CScriptHandle *self = reinterpret_cast<CScriptHandle*>(gen->GetObject());
|
||||
self->~CScriptHandle();
|
||||
}
|
||||
|
||||
void CScriptHandle_Cast_Generic(asIScriptGeneric *gen)
|
||||
{
|
||||
void **ref = reinterpret_cast<void**>(gen->GetArgAddress(0));
|
||||
int typeId = gen->GetArgTypeId(0);
|
||||
CScriptHandle *self = reinterpret_cast<CScriptHandle*>(gen->GetObject());
|
||||
self->Cast(ref, typeId);
|
||||
}
|
||||
|
||||
void CScriptHandle_Assign_Generic(asIScriptGeneric *gen)
|
||||
{
|
||||
CScriptHandle *other = reinterpret_cast<CScriptHandle*>(gen->GetArgAddress(0));
|
||||
CScriptHandle *self = reinterpret_cast<CScriptHandle*>(gen->GetObject());
|
||||
*self = *other;
|
||||
gen->SetReturnAddress(self);
|
||||
}
|
||||
|
||||
void CScriptHandle_AssignVar_Generic(asIScriptGeneric *gen)
|
||||
{
|
||||
void *ref = gen->GetArgAddress(0);
|
||||
int typeId = gen->GetArgTypeId(0);
|
||||
CScriptHandle *self = reinterpret_cast<CScriptHandle*>(gen->GetObject());
|
||||
self->Assign(ref, typeId);
|
||||
gen->SetReturnAddress(self);
|
||||
}
|
||||
|
||||
void CScriptHandle_Equals_Generic(asIScriptGeneric *gen)
|
||||
{
|
||||
CScriptHandle *other = reinterpret_cast<CScriptHandle*>(gen->GetArgAddress(0));
|
||||
CScriptHandle *self = reinterpret_cast<CScriptHandle*>(gen->GetObject());
|
||||
gen->SetReturnByte(*self == *other);
|
||||
}
|
||||
|
||||
void CScriptHandle_EqualsVar_Generic(asIScriptGeneric *gen)
|
||||
{
|
||||
void *ref = gen->GetArgAddress(0);
|
||||
int typeId = gen->GetArgTypeId(0);
|
||||
CScriptHandle *self = reinterpret_cast<CScriptHandle*>(gen->GetObject());
|
||||
gen->SetReturnByte(self->Equals(ref, typeId));
|
||||
}
|
||||
|
||||
void CScriptHandle_EnumReferences_Generic(asIScriptGeneric *gen)
|
||||
{
|
||||
CScriptHandle *self = reinterpret_cast<CScriptHandle*>(gen->GetObject());
|
||||
self->EnumReferences(gen->GetEngine());
|
||||
}
|
||||
|
||||
void CScriptHandle_ReleaseReferences_Generic(asIScriptGeneric *gen)
|
||||
{
|
||||
CScriptHandle *self = reinterpret_cast<CScriptHandle*>(gen->GetObject());
|
||||
self->ReleaseReferences(gen->GetEngine());
|
||||
}
|
||||
|
||||
void RegisterScriptHandle_Generic(asIScriptEngine *engine)
|
||||
{
|
||||
int r;
|
||||
|
||||
r = engine->RegisterObjectType("ref", sizeof(CScriptHandle), asOBJ_VALUE | asOBJ_ASHANDLE | asOBJ_GC | asOBJ_APP_CLASS_CDAK); assert( r >= 0 );
|
||||
r = engine->RegisterObjectBehaviour("ref", asBEHAVE_CONSTRUCT, "void f()", asFUNCTION(CScriptHandle_Construct_Generic), asCALL_GENERIC); assert( r >= 0 );
|
||||
r = engine->RegisterObjectBehaviour("ref", asBEHAVE_CONSTRUCT, "void f(const ref &in)", asFUNCTION(CScriptHandle_ConstructCopy_Generic), asCALL_GENERIC); assert( r >= 0 );
|
||||
r = engine->RegisterObjectBehaviour("ref", asBEHAVE_CONSTRUCT, "void f(const ?&in)", asFUNCTION(CScriptHandle_ConstructVar_Generic), asCALL_GENERIC); assert( r >= 0 );
|
||||
r = engine->RegisterObjectBehaviour("ref", asBEHAVE_DESTRUCT, "void f()", asFUNCTION(CScriptHandle_Destruct_Generic), asCALL_GENERIC); assert( r >= 0 );
|
||||
r = engine->RegisterObjectBehaviour("ref", asBEHAVE_ENUMREFS, "void f(int&in)", asFUNCTION(CScriptHandle_EnumReferences_Generic), asCALL_GENERIC); assert(r >= 0);
|
||||
r = engine->RegisterObjectBehaviour("ref", asBEHAVE_RELEASEREFS, "void f(int&in)", asFUNCTION(CScriptHandle_ReleaseReferences_Generic), asCALL_GENERIC); assert(r >= 0);
|
||||
r = engine->RegisterObjectMethod("ref", "void opCast(?&out)", asFUNCTION(CScriptHandle_Cast_Generic), asCALL_GENERIC); assert( r >= 0 );
|
||||
r = engine->RegisterObjectMethod("ref", "ref &opHndlAssign(const ref &in)", asFUNCTION(CScriptHandle_Assign_Generic), asCALL_GENERIC); assert( r >= 0 );
|
||||
r = engine->RegisterObjectMethod("ref", "ref &opHndlAssign(const ?&in)", asFUNCTION(CScriptHandle_AssignVar_Generic), asCALL_GENERIC); assert( r >= 0 );
|
||||
r = engine->RegisterObjectMethod("ref", "bool opEquals(const ref &in) const", asFUNCTION(CScriptHandle_Equals_Generic), asCALL_GENERIC); assert( r >= 0 );
|
||||
r = engine->RegisterObjectMethod("ref", "bool opEquals(const ?&in) const", asFUNCTION(CScriptHandle_EqualsVar_Generic), asCALL_GENERIC); assert( r >= 0 );
|
||||
}
|
||||
|
||||
void RegisterScriptHandle(asIScriptEngine *engine)
|
||||
{
|
||||
if( strstr(asGetLibraryOptions(), "AS_MAX_PORTABILITY") )
|
||||
RegisterScriptHandle_Generic(engine);
|
||||
else
|
||||
RegisterScriptHandle_Native(engine);
|
||||
}
|
||||
|
||||
|
||||
END_AS_NAMESPACE
|
||||
69
add_on/scripthandle/scripthandle.h
Normal file
69
add_on/scripthandle/scripthandle.h
Normal file
@@ -0,0 +1,69 @@
|
||||
#ifndef SCRIPTHANDLE_H
|
||||
#define SCRIPTHANDLE_H
|
||||
|
||||
#ifndef ANGELSCRIPT_H
|
||||
// Avoid having to inform include path if header is already include before
|
||||
#include <angelscript.h>
|
||||
#endif
|
||||
|
||||
|
||||
BEGIN_AS_NAMESPACE
|
||||
|
||||
class CScriptHandle
|
||||
{
|
||||
public:
|
||||
// Constructors
|
||||
CScriptHandle();
|
||||
CScriptHandle(const CScriptHandle &other);
|
||||
CScriptHandle(void *ref, asITypeInfo *type);
|
||||
~CScriptHandle();
|
||||
|
||||
// Copy the stored value from another any object
|
||||
CScriptHandle &operator=(const CScriptHandle &other);
|
||||
|
||||
// Set the reference
|
||||
void Set(void *ref, asITypeInfo *type);
|
||||
|
||||
// Compare equalness
|
||||
bool operator==(const CScriptHandle &o) const;
|
||||
bool operator!=(const CScriptHandle &o) const;
|
||||
bool Equals(void *ref, int typeId) const;
|
||||
|
||||
// Dynamic cast to desired handle type
|
||||
void Cast(void **outRef, int typeId);
|
||||
|
||||
// Returns the type of the reference held
|
||||
asITypeInfo *GetType() const;
|
||||
int GetTypeId() const;
|
||||
|
||||
// Get the reference
|
||||
void *GetRef();
|
||||
|
||||
// GC callback
|
||||
void EnumReferences(asIScriptEngine *engine);
|
||||
void ReleaseReferences(asIScriptEngine *engine);
|
||||
|
||||
protected:
|
||||
// These functions need to have access to protected
|
||||
// members in order to call them from the script engine
|
||||
friend void Construct(CScriptHandle *self, void *ref, int typeId);
|
||||
friend void RegisterScriptHandle_Native(asIScriptEngine *engine);
|
||||
friend void CScriptHandle_AssignVar_Generic(asIScriptGeneric *gen);
|
||||
|
||||
void ReleaseHandle();
|
||||
void AddRefHandle();
|
||||
|
||||
// These shouldn't be called directly by the
|
||||
// application as they requires an active context
|
||||
CScriptHandle(void *ref, int typeId);
|
||||
CScriptHandle &Assign(void *ref, int typeId);
|
||||
|
||||
void *m_ref;
|
||||
asITypeInfo *m_type;
|
||||
};
|
||||
|
||||
void RegisterScriptHandle(asIScriptEngine *engine);
|
||||
|
||||
END_AS_NAMESPACE
|
||||
|
||||
#endif
|
||||
987
add_on/scripthelper/scripthelper.cpp
Normal file
987
add_on/scripthelper/scripthelper.cpp
Normal file
@@ -0,0 +1,987 @@
|
||||
#include <string.h>
|
||||
#include "scripthelper.h"
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
#include <fstream>
|
||||
#include <set>
|
||||
#include <stdlib.h>
|
||||
|
||||
using namespace std;
|
||||
|
||||
BEGIN_AS_NAMESPACE
|
||||
|
||||
int CompareRelation(asIScriptEngine *engine, void *lobj, void *robj, int typeId, int &result)
|
||||
{
|
||||
// TODO: If a lot of script objects are going to be compared, e.g. when sorting an array,
|
||||
// then the method id and context should be cached between calls.
|
||||
|
||||
int retval = -1;
|
||||
asIScriptFunction *func = 0;
|
||||
|
||||
asITypeInfo *ti = engine->GetTypeInfoById(typeId);
|
||||
if( ti )
|
||||
{
|
||||
// Check if the object type has a compatible opCmp method
|
||||
for( asUINT n = 0; n < ti->GetMethodCount(); n++ )
|
||||
{
|
||||
asIScriptFunction *f = ti->GetMethodByIndex(n);
|
||||
asDWORD flags;
|
||||
if( strcmp(f->GetName(), "opCmp") == 0 &&
|
||||
f->GetReturnTypeId(&flags) == asTYPEID_INT32 &&
|
||||
flags == asTM_NONE &&
|
||||
f->GetParamCount() == 1 )
|
||||
{
|
||||
int paramTypeId;
|
||||
f->GetParam(0, ¶mTypeId, &flags);
|
||||
|
||||
// The parameter must be an input reference of the same type
|
||||
// If the reference is a inout reference, then it must also be read-only
|
||||
if( !(flags & asTM_INREF) || typeId != paramTypeId || ((flags & asTM_OUTREF) && !(flags & asTM_CONST)) )
|
||||
break;
|
||||
|
||||
// Found the method
|
||||
func = f;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if( func )
|
||||
{
|
||||
// Call the method
|
||||
asIScriptContext *ctx = engine->CreateContext();
|
||||
ctx->Prepare(func);
|
||||
ctx->SetObject(lobj);
|
||||
ctx->SetArgAddress(0, robj);
|
||||
int r = ctx->Execute();
|
||||
if( r == asEXECUTION_FINISHED )
|
||||
{
|
||||
result = (int)ctx->GetReturnDWord();
|
||||
|
||||
// The comparison was successful
|
||||
retval = 0;
|
||||
}
|
||||
ctx->Release();
|
||||
}
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
int CompareEquality(asIScriptEngine *engine, void *lobj, void *robj, int typeId, bool &result)
|
||||
{
|
||||
// TODO: If a lot of script objects are going to be compared, e.g. when searching for an
|
||||
// entry in a set, then the method and context should be cached between calls.
|
||||
|
||||
int retval = -1;
|
||||
asIScriptFunction *func = 0;
|
||||
|
||||
asITypeInfo *ti = engine->GetTypeInfoById(typeId);
|
||||
if( ti )
|
||||
{
|
||||
// Check if the object type has a compatible opEquals method
|
||||
for( asUINT n = 0; n < ti->GetMethodCount(); n++ )
|
||||
{
|
||||
asIScriptFunction *f = ti->GetMethodByIndex(n);
|
||||
asDWORD flags;
|
||||
if( strcmp(f->GetName(), "opEquals") == 0 &&
|
||||
f->GetReturnTypeId(&flags) == asTYPEID_BOOL &&
|
||||
flags == asTM_NONE &&
|
||||
f->GetParamCount() == 1 )
|
||||
{
|
||||
int paramTypeId;
|
||||
f->GetParam(0, ¶mTypeId, &flags);
|
||||
|
||||
// The parameter must be an input reference of the same type
|
||||
// If the reference is a inout reference, then it must also be read-only
|
||||
if( !(flags & asTM_INREF) || typeId != paramTypeId || ((flags & asTM_OUTREF) && !(flags & asTM_CONST)) )
|
||||
break;
|
||||
|
||||
// Found the method
|
||||
func = f;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if( func )
|
||||
{
|
||||
// Call the method
|
||||
asIScriptContext *ctx = engine->CreateContext();
|
||||
ctx->Prepare(func);
|
||||
ctx->SetObject(lobj);
|
||||
ctx->SetArgAddress(0, robj);
|
||||
int r = ctx->Execute();
|
||||
if( r == asEXECUTION_FINISHED )
|
||||
{
|
||||
result = ctx->GetReturnByte() ? true : false;
|
||||
|
||||
// The comparison was successful
|
||||
retval = 0;
|
||||
}
|
||||
ctx->Release();
|
||||
}
|
||||
else
|
||||
{
|
||||
// If the opEquals method doesn't exist, then we try with opCmp instead
|
||||
int relation;
|
||||
retval = CompareRelation(engine, lobj, robj, typeId, relation);
|
||||
if( retval >= 0 )
|
||||
result = relation == 0 ? true : false;
|
||||
}
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
int ExecuteString(asIScriptEngine *engine, const char *code, asIScriptModule *mod, asIScriptContext *ctx)
|
||||
{
|
||||
return ExecuteString(engine, code, 0, asTYPEID_VOID, mod, ctx);
|
||||
}
|
||||
|
||||
int ExecuteString(asIScriptEngine *engine, const char *code, void *ref, int refTypeId, asIScriptModule *mod, asIScriptContext *ctx)
|
||||
{
|
||||
// Wrap the code in a function so that it can be compiled and executed
|
||||
string funcCode = " ExecuteString() {\n";
|
||||
funcCode += code;
|
||||
funcCode += "\n;}";
|
||||
|
||||
// Determine the return type based on the type of the ref arg
|
||||
funcCode = engine->GetTypeDeclaration(refTypeId, true) + funcCode;
|
||||
|
||||
// GetModule will free unused types, so to be on the safe side we'll hold on to a reference to the type
|
||||
asITypeInfo *type = 0;
|
||||
if( refTypeId & asTYPEID_MASK_OBJECT )
|
||||
{
|
||||
type = engine->GetTypeInfoById(refTypeId);
|
||||
if( type )
|
||||
type->AddRef();
|
||||
}
|
||||
|
||||
// If no module was provided, get a dummy from the engine
|
||||
asIScriptModule *execMod = mod ? mod : engine->GetModule("ExecuteString", asGM_ALWAYS_CREATE);
|
||||
|
||||
// Now it's ok to release the type
|
||||
if( type )
|
||||
type->Release();
|
||||
|
||||
// Compile the function that can be executed
|
||||
asIScriptFunction *func = 0;
|
||||
int r = execMod->CompileFunction("ExecuteString", funcCode.c_str(), -1, 0, &func);
|
||||
if( r < 0 )
|
||||
return r;
|
||||
|
||||
// If no context was provided, request a new one from the engine
|
||||
asIScriptContext *execCtx = ctx ? ctx : engine->RequestContext();
|
||||
r = execCtx->Prepare(func);
|
||||
if (r >= 0)
|
||||
{
|
||||
// Execute the function
|
||||
r = execCtx->Execute();
|
||||
|
||||
// Unless the provided type was void retrieve it's value
|
||||
if (ref != 0 && refTypeId != asTYPEID_VOID)
|
||||
{
|
||||
if (refTypeId & asTYPEID_OBJHANDLE)
|
||||
{
|
||||
// Expect the pointer to be null to start with
|
||||
assert(*reinterpret_cast<void**>(ref) == 0);
|
||||
*reinterpret_cast<void**>(ref) = *reinterpret_cast<void**>(execCtx->GetAddressOfReturnValue());
|
||||
engine->AddRefScriptObject(*reinterpret_cast<void**>(ref), engine->GetTypeInfoById(refTypeId));
|
||||
}
|
||||
else if (refTypeId & asTYPEID_MASK_OBJECT)
|
||||
{
|
||||
// Use the registered assignment operator to do a value assign.
|
||||
// This assumes that the ref is pointing to a valid object instance.
|
||||
engine->AssignScriptObject(ref, execCtx->GetAddressOfReturnValue(), engine->GetTypeInfoById(refTypeId));
|
||||
}
|
||||
else
|
||||
{
|
||||
// Copy the primitive value
|
||||
memcpy(ref, execCtx->GetAddressOfReturnValue(), engine->GetSizeOfPrimitiveType(refTypeId));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Clean up
|
||||
func->Release();
|
||||
if( !ctx ) engine->ReturnContext(execCtx);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
int WriteConfigToFile(asIScriptEngine *engine, const char *filename)
|
||||
{
|
||||
ofstream strm;
|
||||
strm.open(filename);
|
||||
|
||||
return WriteConfigToStream(engine, strm);
|
||||
}
|
||||
|
||||
int WriteConfigToStream(asIScriptEngine *engine, ostream &strm)
|
||||
{
|
||||
// A helper function for escaping quotes in default arguments
|
||||
struct Escape
|
||||
{
|
||||
static string Quotes(const char *decl)
|
||||
{
|
||||
string str = decl;
|
||||
size_t pos = 0;
|
||||
for(;;)
|
||||
{
|
||||
// Find " characters
|
||||
pos = str.find("\"",pos);
|
||||
if( pos == string::npos )
|
||||
break;
|
||||
|
||||
// Add a \ to escape them
|
||||
str.insert(pos, "\\");
|
||||
pos += 2;
|
||||
}
|
||||
|
||||
return str;
|
||||
}
|
||||
};
|
||||
|
||||
int c, n;
|
||||
|
||||
asDWORD currAccessMask = 0;
|
||||
string currNamespace = "";
|
||||
engine->SetDefaultNamespace("");
|
||||
|
||||
// Export the engine version, just for info
|
||||
strm << "// AngelScript " << asGetLibraryVersion() << "\n";
|
||||
strm << "// Lib options " << asGetLibraryOptions() << "\n";
|
||||
|
||||
// Export the relevant engine properties
|
||||
strm << "// Engine properties\n";
|
||||
for( n = 0; n < asEP_LAST_PROPERTY; n++ )
|
||||
strm << "ep " << n << " " << engine->GetEngineProperty(asEEngineProp(n)) << "\n";
|
||||
|
||||
// Make sure the default array type is expanded to the template form
|
||||
bool expandDefArrayToTempl = engine->GetEngineProperty(asEP_EXPAND_DEF_ARRAY_TO_TMPL) ? true : false;
|
||||
engine->SetEngineProperty(asEP_EXPAND_DEF_ARRAY_TO_TMPL, true);
|
||||
|
||||
// Write enum types and their values
|
||||
strm << "\n// Enums\n";
|
||||
c = engine->GetEnumCount();
|
||||
for( n = 0; n < c; n++ )
|
||||
{
|
||||
asITypeInfo *ti = engine->GetEnumByIndex(n);
|
||||
asDWORD accessMask = ti->GetAccessMask();
|
||||
if( accessMask != currAccessMask )
|
||||
{
|
||||
strm << "access " << hex << (unsigned int)(accessMask) << dec << "\n";
|
||||
currAccessMask = accessMask;
|
||||
}
|
||||
const char *nameSpace = ti->GetNamespace();
|
||||
if( nameSpace != currNamespace )
|
||||
{
|
||||
strm << "namespace \"" << nameSpace << "\"\n";
|
||||
currNamespace = nameSpace;
|
||||
engine->SetDefaultNamespace(currNamespace.c_str());
|
||||
}
|
||||
const char *enumName = ti->GetName();
|
||||
strm << "enum " << enumName << "\n";
|
||||
for( asUINT m = 0; m < ti->GetEnumValueCount(); m++ )
|
||||
{
|
||||
const char *valName;
|
||||
int val;
|
||||
valName = ti->GetEnumValueByIndex(m, &val);
|
||||
strm << "enumval " << enumName << " " << valName << " " << val << "\n";
|
||||
}
|
||||
}
|
||||
|
||||
// Enumerate all types
|
||||
strm << "\n// Types\n";
|
||||
|
||||
// Keep a list of the template types, as the methods for these need to be exported first
|
||||
set<asITypeInfo*> templateTypes;
|
||||
|
||||
c = engine->GetObjectTypeCount();
|
||||
for( n = 0; n < c; n++ )
|
||||
{
|
||||
asITypeInfo *type = engine->GetObjectTypeByIndex(n);
|
||||
asDWORD accessMask = type->GetAccessMask();
|
||||
if( accessMask != currAccessMask )
|
||||
{
|
||||
strm << "access " << hex << (unsigned int)(accessMask) << dec << "\n";
|
||||
currAccessMask = accessMask;
|
||||
}
|
||||
const char *nameSpace = type->GetNamespace();
|
||||
if( nameSpace != currNamespace )
|
||||
{
|
||||
strm << "namespace \"" << nameSpace << "\"\n";
|
||||
currNamespace = nameSpace;
|
||||
engine->SetDefaultNamespace(currNamespace.c_str());
|
||||
}
|
||||
if( type->GetFlags() & asOBJ_SCRIPT_OBJECT )
|
||||
{
|
||||
// This should only be interfaces
|
||||
assert( type->GetSize() == 0 );
|
||||
|
||||
strm << "intf " << type->GetName() << "\n";
|
||||
}
|
||||
else
|
||||
{
|
||||
// Only the type flags are necessary. The application flags are application
|
||||
// specific and doesn't matter to the offline compiler. The object size is also
|
||||
// unnecessary for the offline compiler
|
||||
strm << "objtype \"" << engine->GetTypeDeclaration(type->GetTypeId()) << "\" " << (unsigned int)(type->GetFlags() & asOBJ_MASK_VALID_FLAGS) << "\n";
|
||||
|
||||
// Store the template types (but not template instances)
|
||||
if( (type->GetFlags() & asOBJ_TEMPLATE) && type->GetSubType() && (type->GetSubType()->GetFlags() & asOBJ_TEMPLATE_SUBTYPE) )
|
||||
templateTypes.insert(type);
|
||||
}
|
||||
}
|
||||
|
||||
c = engine->GetTypedefCount();
|
||||
for( n = 0; n < c; n++ )
|
||||
{
|
||||
asITypeInfo *ti = engine->GetTypedefByIndex(n);
|
||||
const char *nameSpace = ti->GetNamespace();
|
||||
if( nameSpace != currNamespace )
|
||||
{
|
||||
strm << "namespace \"" << nameSpace << "\"\n";
|
||||
currNamespace = nameSpace;
|
||||
engine->SetDefaultNamespace(currNamespace.c_str());
|
||||
}
|
||||
asDWORD accessMask = ti->GetAccessMask();
|
||||
if( accessMask != currAccessMask )
|
||||
{
|
||||
strm << "access " << hex << (unsigned int)(accessMask) << dec << "\n";
|
||||
currAccessMask = accessMask;
|
||||
}
|
||||
strm << "typedef " << ti->GetName() << " \"" << engine->GetTypeDeclaration(ti->GetTypedefTypeId()) << "\"\n";
|
||||
}
|
||||
|
||||
c = engine->GetFuncdefCount();
|
||||
for( n = 0; n < c; n++ )
|
||||
{
|
||||
asITypeInfo *funcDef = engine->GetFuncdefByIndex(n);
|
||||
asDWORD accessMask = funcDef->GetAccessMask();
|
||||
const char *nameSpace = funcDef->GetNamespace();
|
||||
// Child funcdefs do not have any namespace, as they belong to the parent object
|
||||
if( nameSpace && nameSpace != currNamespace )
|
||||
{
|
||||
strm << "namespace \"" << nameSpace << "\"\n";
|
||||
currNamespace = nameSpace;
|
||||
engine->SetDefaultNamespace(currNamespace.c_str());
|
||||
}
|
||||
if( accessMask != currAccessMask )
|
||||
{
|
||||
strm << "access " << hex << (unsigned int)(accessMask) << dec << "\n";
|
||||
currAccessMask = accessMask;
|
||||
}
|
||||
strm << "funcdef \"" << funcDef->GetFuncdefSignature()->GetDeclaration() << "\"\n";
|
||||
}
|
||||
|
||||
// A helper for writing object type members
|
||||
struct TypeWriter
|
||||
{
|
||||
static void Write(asIScriptEngine *engine, ostream &strm, asITypeInfo *type, string &currNamespace, asDWORD &currAccessMask)
|
||||
{
|
||||
const char *nameSpace = type->GetNamespace();
|
||||
if( nameSpace != currNamespace )
|
||||
{
|
||||
strm << "namespace \"" << nameSpace << "\"\n";
|
||||
currNamespace = nameSpace;
|
||||
engine->SetDefaultNamespace(currNamespace.c_str());
|
||||
}
|
||||
string typeDecl = engine->GetTypeDeclaration(type->GetTypeId());
|
||||
if( type->GetFlags() & asOBJ_SCRIPT_OBJECT )
|
||||
{
|
||||
for( asUINT m = 0; m < type->GetMethodCount(); m++ )
|
||||
{
|
||||
asIScriptFunction *func = type->GetMethodByIndex(m);
|
||||
asDWORD accessMask = func->GetAccessMask();
|
||||
if( accessMask != currAccessMask )
|
||||
{
|
||||
strm << "access " << hex << (unsigned int)(accessMask) << dec << "\n";
|
||||
currAccessMask = accessMask;
|
||||
}
|
||||
strm << "intfmthd " << typeDecl.c_str() << " \"" << Escape::Quotes(func->GetDeclaration(false)).c_str() << (func->IsProperty() ? " property" : "") << "\"\n";
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
asUINT m;
|
||||
for( m = 0; m < type->GetFactoryCount(); m++ )
|
||||
{
|
||||
asIScriptFunction *func = type->GetFactoryByIndex(m);
|
||||
asDWORD accessMask = func->GetAccessMask();
|
||||
if( accessMask != currAccessMask )
|
||||
{
|
||||
strm << "access " << hex << (unsigned int)(accessMask) << dec << "\n";
|
||||
currAccessMask = accessMask;
|
||||
}
|
||||
strm << "objbeh \"" << typeDecl.c_str() << "\" " << asBEHAVE_FACTORY << " \"" << Escape::Quotes(func->GetDeclaration(false)).c_str() << "\"\n";
|
||||
}
|
||||
for( m = 0; m < type->GetBehaviourCount(); m++ )
|
||||
{
|
||||
asEBehaviours beh;
|
||||
asIScriptFunction *func = type->GetBehaviourByIndex(m, &beh);
|
||||
|
||||
if( beh == asBEHAVE_CONSTRUCT )
|
||||
// Prefix 'void'
|
||||
strm << "objbeh \"" << typeDecl.c_str() << "\" " << beh << " \"void " << Escape::Quotes(func->GetDeclaration(false)).c_str() << "\"\n";
|
||||
else if( beh == asBEHAVE_DESTRUCT )
|
||||
// Prefix 'void' and remove ~
|
||||
strm << "objbeh \"" << typeDecl.c_str() << "\" " << beh << " \"void " << Escape::Quotes(func->GetDeclaration(false)).c_str()+1 << "\"\n";
|
||||
else
|
||||
strm << "objbeh \"" << typeDecl.c_str() << "\" " << beh << " \"" << Escape::Quotes(func->GetDeclaration(false)).c_str() << "\"\n";
|
||||
}
|
||||
for( m = 0; m < type->GetMethodCount(); m++ )
|
||||
{
|
||||
asIScriptFunction *func = type->GetMethodByIndex(m);
|
||||
asDWORD accessMask = func->GetAccessMask();
|
||||
if( accessMask != currAccessMask )
|
||||
{
|
||||
strm << "access " << hex << (unsigned int)(accessMask) << dec << "\n";
|
||||
currAccessMask = accessMask;
|
||||
}
|
||||
strm << "objmthd \"" << typeDecl.c_str() << "\" \"" << Escape::Quotes(func->GetDeclaration(false)).c_str() << (func->IsProperty() ? " property" : "") << "\"\n";
|
||||
}
|
||||
for( m = 0; m < type->GetPropertyCount(); m++ )
|
||||
{
|
||||
asDWORD accessMask;
|
||||
type->GetProperty(m, 0, 0, 0, 0, 0, 0, &accessMask);
|
||||
if( accessMask != currAccessMask )
|
||||
{
|
||||
strm << "access " << hex << (unsigned int)(accessMask) << dec << "\n";
|
||||
currAccessMask = accessMask;
|
||||
}
|
||||
strm << "objprop \"" << typeDecl.c_str() << "\" \"" << type->GetPropertyDeclaration(m) << "\"";
|
||||
|
||||
// Save information about composite properties
|
||||
int compositeOffset;
|
||||
bool isCompositeIndirect;
|
||||
type->GetProperty(m, 0, 0, 0, 0, 0, 0, 0, &compositeOffset, &isCompositeIndirect);
|
||||
strm << " " << compositeOffset << " " << (isCompositeIndirect ? "1" : "0") << "\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Write the members of the template types, so they can be fully registered before any other type uses them
|
||||
// TODO: Order the template types based on dependency to avoid failure if one type uses instances of another
|
||||
strm << "\n// Template type members\n";
|
||||
for( set<asITypeInfo*>::iterator it = templateTypes.begin(); it != templateTypes.end(); ++it )
|
||||
{
|
||||
asITypeInfo *type = *it;
|
||||
TypeWriter::Write(engine, strm, type, currNamespace, currAccessMask);
|
||||
}
|
||||
|
||||
// Write the object types members
|
||||
strm << "\n// Type members\n";
|
||||
|
||||
c = engine->GetObjectTypeCount();
|
||||
for( n = 0; n < c; n++ )
|
||||
{
|
||||
asITypeInfo *type = engine->GetObjectTypeByIndex(n);
|
||||
if( templateTypes.find(type) == templateTypes.end() )
|
||||
TypeWriter::Write(engine, strm, type, currNamespace, currAccessMask);
|
||||
}
|
||||
|
||||
// Write functions
|
||||
strm << "\n// Functions\n";
|
||||
|
||||
c = engine->GetGlobalFunctionCount();
|
||||
for( n = 0; n < c; n++ )
|
||||
{
|
||||
asIScriptFunction *func = engine->GetGlobalFunctionByIndex(n);
|
||||
const char *nameSpace = func->GetNamespace();
|
||||
if( nameSpace != currNamespace )
|
||||
{
|
||||
strm << "namespace \"" << nameSpace << "\"\n";
|
||||
currNamespace = nameSpace;
|
||||
engine->SetDefaultNamespace(currNamespace.c_str());
|
||||
}
|
||||
asDWORD accessMask = func->GetAccessMask();
|
||||
if( accessMask != currAccessMask )
|
||||
{
|
||||
strm << "access " << hex << (unsigned int)(accessMask) << dec << "\n";
|
||||
currAccessMask = accessMask;
|
||||
}
|
||||
strm << "func \"" << Escape::Quotes(func->GetDeclaration()).c_str() << (func->IsProperty() ? " property" : "") << "\"\n";
|
||||
}
|
||||
|
||||
// Write global properties
|
||||
strm << "\n// Properties\n";
|
||||
|
||||
c = engine->GetGlobalPropertyCount();
|
||||
for( n = 0; n < c; n++ )
|
||||
{
|
||||
const char *name;
|
||||
int typeId;
|
||||
bool isConst;
|
||||
asDWORD accessMask;
|
||||
const char *nameSpace;
|
||||
engine->GetGlobalPropertyByIndex(n, &name, &nameSpace, &typeId, &isConst, 0, 0, &accessMask);
|
||||
if( accessMask != currAccessMask )
|
||||
{
|
||||
strm << "access " << hex << (unsigned int)(accessMask) << dec << "\n";
|
||||
currAccessMask = accessMask;
|
||||
}
|
||||
if( nameSpace != currNamespace )
|
||||
{
|
||||
strm << "namespace \"" << nameSpace << "\"\n";
|
||||
currNamespace = nameSpace;
|
||||
engine->SetDefaultNamespace(currNamespace.c_str());
|
||||
}
|
||||
strm << "prop \"" << (isConst ? "const " : "") << engine->GetTypeDeclaration(typeId) << " " << name << "\"\n";
|
||||
}
|
||||
|
||||
// Write string factory
|
||||
strm << "\n// String factory\n";
|
||||
|
||||
// Reset the namespace for the string factory and default array type
|
||||
if ("" != currNamespace)
|
||||
{
|
||||
strm << "namespace \"\"\n";
|
||||
currNamespace = "";
|
||||
engine->SetDefaultNamespace("");
|
||||
}
|
||||
|
||||
asDWORD flags = 0;
|
||||
int typeId = engine->GetStringFactoryReturnTypeId(&flags);
|
||||
if( typeId > 0 )
|
||||
strm << "strfactory \"" << ((flags & asTM_CONST) ? "const " : "") << engine->GetTypeDeclaration(typeId) << ((flags & asTM_INOUTREF) ? "&" : "") << "\"\n";
|
||||
|
||||
// Write default array type
|
||||
strm << "\n// Default array type\n";
|
||||
typeId = engine->GetDefaultArrayTypeId();
|
||||
if( typeId > 0 )
|
||||
strm << "defarray \"" << engine->GetTypeDeclaration(typeId) << "\"\n";
|
||||
|
||||
// Restore original settings
|
||||
engine->SetEngineProperty(asEP_EXPAND_DEF_ARRAY_TO_TMPL, expandDefArrayToTempl);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ConfigEngineFromStream(asIScriptEngine *engine, istream &strm, const char *configFile, asIStringFactory *stringFactory)
|
||||
{
|
||||
int r;
|
||||
|
||||
// Some helper functions for parsing the configuration
|
||||
struct in
|
||||
{
|
||||
static asETokenClass GetToken(asIScriptEngine *engine, string &token, const string &text, asUINT &pos)
|
||||
{
|
||||
asUINT len = 0;
|
||||
asETokenClass t = engine->ParseToken(&text[pos], text.length() - pos, &len);
|
||||
while( (t == asTC_WHITESPACE || t == asTC_COMMENT) && pos < text.length() )
|
||||
{
|
||||
pos += len;
|
||||
t = engine->ParseToken(&text[pos], text.length() - pos, &len);
|
||||
}
|
||||
|
||||
token.assign(&text[pos], len);
|
||||
|
||||
pos += len;
|
||||
|
||||
return t;
|
||||
}
|
||||
|
||||
static void ReplaceSlashQuote(string &str)
|
||||
{
|
||||
size_t pos = 0;
|
||||
for(;;)
|
||||
{
|
||||
// Search for \" in the string
|
||||
pos = str.find("\\\"", pos);
|
||||
if( pos == string::npos )
|
||||
break;
|
||||
|
||||
// Remove the \ character
|
||||
str.erase(pos, 1);
|
||||
}
|
||||
}
|
||||
|
||||
static asUINT GetLineNumber(const string &text, asUINT pos)
|
||||
{
|
||||
asUINT count = 1;
|
||||
for( asUINT n = 0; n < pos; n++ )
|
||||
if( text[n] == '\n' )
|
||||
count++;
|
||||
|
||||
return count;
|
||||
}
|
||||
};
|
||||
|
||||
// Since we are only going to compile the script and never actually execute it,
|
||||
// we turn off the initialization of global variables, so that the compiler can
|
||||
// just register dummy types and functions for the application interface.
|
||||
r = engine->SetEngineProperty(asEP_INIT_GLOBAL_VARS_AFTER_BUILD, false); assert( r >= 0 );
|
||||
|
||||
// Read the entire file
|
||||
char buffer[1000];
|
||||
string config;
|
||||
do {
|
||||
strm.getline(buffer, 1000);
|
||||
config += buffer;
|
||||
config += "\n";
|
||||
} while( !strm.eof() && strm.good() );
|
||||
|
||||
// Process the configuration file and register each entity
|
||||
asUINT pos = 0;
|
||||
while( pos < config.length() )
|
||||
{
|
||||
string token;
|
||||
// TODO: The position where the initial token is found should be stored for error messages
|
||||
in::GetToken(engine, token, config, pos);
|
||||
if( token == "ep" )
|
||||
{
|
||||
string tmp;
|
||||
in::GetToken(engine, tmp, config, pos);
|
||||
|
||||
asEEngineProp ep = asEEngineProp(atol(tmp.c_str()));
|
||||
|
||||
// Only set properties that affect the compiler
|
||||
if( ep != asEP_COPY_SCRIPT_SECTIONS &&
|
||||
ep != asEP_MAX_STACK_SIZE &&
|
||||
ep != asEP_INIT_GLOBAL_VARS_AFTER_BUILD &&
|
||||
ep != asEP_EXPAND_DEF_ARRAY_TO_TMPL &&
|
||||
ep != asEP_AUTO_GARBAGE_COLLECT )
|
||||
{
|
||||
// Get the value for the property
|
||||
in::GetToken(engine, tmp, config, pos);
|
||||
stringstream s(tmp);
|
||||
asPWORD value;
|
||||
|
||||
s >> value;
|
||||
|
||||
engine->SetEngineProperty(ep, value);
|
||||
}
|
||||
}
|
||||
else if( token == "namespace" )
|
||||
{
|
||||
string ns;
|
||||
in::GetToken(engine, ns, config, pos);
|
||||
ns = ns.substr(1, ns.length() - 2);
|
||||
|
||||
r = engine->SetDefaultNamespace(ns.c_str());
|
||||
if( r < 0 )
|
||||
{
|
||||
engine->WriteMessage(configFile, in::GetLineNumber(config, pos), 0, asMSGTYPE_ERROR, "Failed to set namespace");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
else if( token == "access" )
|
||||
{
|
||||
string maskStr;
|
||||
in::GetToken(engine, maskStr, config, pos);
|
||||
asDWORD mask = strtoul(maskStr.c_str(), 0, 16);
|
||||
engine->SetDefaultAccessMask(mask);
|
||||
}
|
||||
else if( token == "objtype" )
|
||||
{
|
||||
string name, flags;
|
||||
in::GetToken(engine, name, config, pos);
|
||||
name = name.substr(1, name.length() - 2);
|
||||
in::GetToken(engine, flags, config, pos);
|
||||
|
||||
// The size of the value type doesn't matter, because the
|
||||
// engine must adjust it anyway for different platforms
|
||||
r = engine->RegisterObjectType(name.c_str(), (atol(flags.c_str()) & asOBJ_VALUE) ? 1 : 0, atol(flags.c_str()));
|
||||
if( r < 0 )
|
||||
{
|
||||
engine->WriteMessage(configFile, in::GetLineNumber(config, pos), 0, asMSGTYPE_ERROR, "Failed to register object type");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
else if( token == "objbeh" )
|
||||
{
|
||||
string name, behaviour, decl;
|
||||
in::GetToken(engine, name, config, pos);
|
||||
name = name.substr(1, name.length() - 2);
|
||||
in::GetToken(engine, behaviour, config, pos);
|
||||
in::GetToken(engine, decl, config, pos);
|
||||
decl = decl.substr(1, decl.length() - 2);
|
||||
in::ReplaceSlashQuote(decl);
|
||||
|
||||
// Remove the $ that the engine prefixes the behaviours with
|
||||
size_t n = decl.find("$");
|
||||
if( n != string::npos )
|
||||
decl[n] = ' ';
|
||||
|
||||
asEBehaviours behave = static_cast<asEBehaviours>(atol(behaviour.c_str()));
|
||||
if( behave == asBEHAVE_TEMPLATE_CALLBACK )
|
||||
{
|
||||
// TODO: How can we let the compiler register this? Maybe through a plug-in system? Or maybe by implementing the callback as a script itself
|
||||
engine->WriteMessage(configFile, in::GetLineNumber(config, pos), 0, asMSGTYPE_WARNING, "Cannot register template callback without the actual implementation");
|
||||
}
|
||||
else
|
||||
{
|
||||
r = engine->RegisterObjectBehaviour(name.c_str(), behave, decl.c_str(), asFUNCTION(0), asCALL_GENERIC);
|
||||
if( r < 0 )
|
||||
{
|
||||
engine->WriteMessage(configFile, in::GetLineNumber(config, pos), 0, asMSGTYPE_ERROR, "Failed to register behaviour");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if( token == "objmthd" )
|
||||
{
|
||||
string name, decl;
|
||||
in::GetToken(engine, name, config, pos);
|
||||
name = name.substr(1, name.length() - 2);
|
||||
in::GetToken(engine, decl, config, pos);
|
||||
decl = decl.substr(1, decl.length() - 2);
|
||||
in::ReplaceSlashQuote(decl);
|
||||
|
||||
r = engine->RegisterObjectMethod(name.c_str(), decl.c_str(), asFUNCTION(0), asCALL_GENERIC);
|
||||
if( r < 0 )
|
||||
{
|
||||
engine->WriteMessage(configFile, in::GetLineNumber(config, pos), 0, asMSGTYPE_ERROR, "Failed to register object method");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
else if( token == "objprop" )
|
||||
{
|
||||
string name, decl, compositeOffset, isCompositeIndirect;
|
||||
in::GetToken(engine, name, config, pos);
|
||||
name = name.substr(1, name.length() - 2);
|
||||
in::GetToken(engine, decl, config, pos);
|
||||
decl = decl.substr(1, decl.length() - 2);
|
||||
in::GetToken(engine, compositeOffset, config, pos);
|
||||
in::GetToken(engine, isCompositeIndirect, config, pos);
|
||||
|
||||
asITypeInfo *type = engine->GetTypeInfoById(engine->GetTypeIdByDecl(name.c_str()));
|
||||
if( type == 0 )
|
||||
{
|
||||
engine->WriteMessage(configFile, in::GetLineNumber(config, pos), 0, asMSGTYPE_ERROR, "Type doesn't exist for property registration");
|
||||
return -1;
|
||||
}
|
||||
|
||||
// All properties must have different offsets in order to make them
|
||||
// distinct, so we simply register them with an incremental offset
|
||||
r = engine->RegisterObjectProperty(name.c_str(), decl.c_str(), type->GetPropertyCount(), compositeOffset != "0" ? type->GetPropertyCount() : 0, isCompositeIndirect != "0");
|
||||
if( r < 0 )
|
||||
{
|
||||
engine->WriteMessage(configFile, in::GetLineNumber(config, pos), 0, asMSGTYPE_ERROR, "Failed to register object property");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
else if( token == "intf" )
|
||||
{
|
||||
string name, size, flags;
|
||||
in::GetToken(engine, name, config, pos);
|
||||
|
||||
r = engine->RegisterInterface(name.c_str());
|
||||
if( r < 0 )
|
||||
{
|
||||
engine->WriteMessage(configFile, in::GetLineNumber(config, pos), 0, asMSGTYPE_ERROR, "Failed to register interface");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
else if( token == "intfmthd" )
|
||||
{
|
||||
string name, decl;
|
||||
in::GetToken(engine, name, config, pos);
|
||||
in::GetToken(engine, decl, config, pos);
|
||||
decl = decl.substr(1, decl.length() - 2);
|
||||
in::ReplaceSlashQuote(decl);
|
||||
|
||||
r = engine->RegisterInterfaceMethod(name.c_str(), decl.c_str());
|
||||
if( r < 0 )
|
||||
{
|
||||
engine->WriteMessage(configFile, in::GetLineNumber(config, pos), 0, asMSGTYPE_ERROR, "Failed to register interface method");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
else if( token == "func" )
|
||||
{
|
||||
string decl;
|
||||
in::GetToken(engine, decl, config, pos);
|
||||
decl = decl.substr(1, decl.length() - 2);
|
||||
in::ReplaceSlashQuote(decl);
|
||||
|
||||
r = engine->RegisterGlobalFunction(decl.c_str(), asFUNCTION(0), asCALL_GENERIC);
|
||||
if( r < 0 )
|
||||
{
|
||||
engine->WriteMessage(configFile, in::GetLineNumber(config, pos), 0, asMSGTYPE_ERROR, "Failed to register global function");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
else if( token == "prop" )
|
||||
{
|
||||
string decl;
|
||||
in::GetToken(engine, decl, config, pos);
|
||||
decl = decl.substr(1, decl.length() - 2);
|
||||
|
||||
// All properties must have different offsets in order to make them
|
||||
// distinct, so we simply register them with an incremental offset.
|
||||
// The pointer must also be non-null so we add 1 to have a value.
|
||||
r = engine->RegisterGlobalProperty(decl.c_str(), reinterpret_cast<void*>(asPWORD(engine->GetGlobalPropertyCount()+1)));
|
||||
if( r < 0 )
|
||||
{
|
||||
engine->WriteMessage(configFile, in::GetLineNumber(config, pos), 0, asMSGTYPE_ERROR, "Failed to register global property");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
else if( token == "strfactory" )
|
||||
{
|
||||
string type;
|
||||
in::GetToken(engine, type, config, pos);
|
||||
type = type.substr(1, type.length() - 2);
|
||||
|
||||
if (stringFactory == 0)
|
||||
{
|
||||
engine->WriteMessage(configFile, in::GetLineNumber(config, pos), 0, asMSGTYPE_WARNING, "Cannot register string factory without the actual implementation");
|
||||
return -1;
|
||||
}
|
||||
else
|
||||
{
|
||||
r = engine->RegisterStringFactory(type.c_str(), stringFactory);
|
||||
if (r < 0)
|
||||
{
|
||||
engine->WriteMessage(configFile, in::GetLineNumber(config, pos), 0, asMSGTYPE_ERROR, "Failed to register string factory");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if( token == "defarray" )
|
||||
{
|
||||
string type;
|
||||
in::GetToken(engine, type, config, pos);
|
||||
type = type.substr(1, type.length() - 2);
|
||||
|
||||
r = engine->RegisterDefaultArrayType(type.c_str());
|
||||
if( r < 0 )
|
||||
{
|
||||
engine->WriteMessage(configFile, in::GetLineNumber(config, pos), 0, asMSGTYPE_ERROR, "Failed to register the default array type");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
else if( token == "enum" )
|
||||
{
|
||||
string type;
|
||||
in::GetToken(engine, type, config, pos);
|
||||
|
||||
r = engine->RegisterEnum(type.c_str());
|
||||
if( r < 0 )
|
||||
{
|
||||
engine->WriteMessage(configFile, in::GetLineNumber(config, pos), 0, asMSGTYPE_ERROR, "Failed to register enum type");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
else if( token == "enumval" )
|
||||
{
|
||||
string type, name, value;
|
||||
in::GetToken(engine, type, config, pos);
|
||||
in::GetToken(engine, name, config, pos);
|
||||
in::GetToken(engine, value, config, pos);
|
||||
|
||||
r = engine->RegisterEnumValue(type.c_str(), name.c_str(), atol(value.c_str()));
|
||||
if( r < 0 )
|
||||
{
|
||||
engine->WriteMessage(configFile, in::GetLineNumber(config, pos), 0, asMSGTYPE_ERROR, "Failed to register enum value");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
else if( token == "typedef" )
|
||||
{
|
||||
string type, decl;
|
||||
in::GetToken(engine, type, config, pos);
|
||||
in::GetToken(engine, decl, config, pos);
|
||||
decl = decl.substr(1, decl.length() - 2);
|
||||
|
||||
r = engine->RegisterTypedef(type.c_str(), decl.c_str());
|
||||
if( r < 0 )
|
||||
{
|
||||
engine->WriteMessage(configFile, in::GetLineNumber(config, pos), 0, asMSGTYPE_ERROR, "Failed to register typedef");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
else if( token == "funcdef" )
|
||||
{
|
||||
string decl;
|
||||
in::GetToken(engine, decl, config, pos);
|
||||
decl = decl.substr(1, decl.length() - 2);
|
||||
|
||||
r = engine->RegisterFuncdef(decl.c_str());
|
||||
if( r < 0 )
|
||||
{
|
||||
engine->WriteMessage(configFile, in::GetLineNumber(config, pos), 0, asMSGTYPE_ERROR, "Failed to register funcdef");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
string GetExceptionInfo(asIScriptContext *ctx, bool showStack)
|
||||
{
|
||||
if( ctx->GetState() != asEXECUTION_EXCEPTION ) return "";
|
||||
|
||||
stringstream text;
|
||||
|
||||
const asIScriptFunction *function = ctx->GetExceptionFunction();
|
||||
text << "func: " << function->GetDeclaration() << "\n";
|
||||
text << "modl: " << (function->GetModuleName() ? function->GetModuleName() : "") << "\n";
|
||||
text << "sect: " << (function->GetScriptSectionName() ? function->GetScriptSectionName() : "") << "\n";
|
||||
text << "line: " << ctx->GetExceptionLineNumber() << "\n";
|
||||
text << "desc: " << ctx->GetExceptionString() << "\n";
|
||||
|
||||
if( showStack )
|
||||
{
|
||||
text << "--- call stack ---\n";
|
||||
for( asUINT n = 1; n < ctx->GetCallstackSize(); n++ )
|
||||
{
|
||||
function = ctx->GetFunction(n);
|
||||
if( function )
|
||||
{
|
||||
if( function->GetFuncType() == asFUNC_SCRIPT )
|
||||
{
|
||||
text << (function->GetScriptSectionName() ? function->GetScriptSectionName() : "") << " (" << ctx->GetLineNumber(n) << "): " << function->GetDeclaration() << "\n";
|
||||
}
|
||||
else
|
||||
{
|
||||
// The context is being reused by the application for a nested call
|
||||
text << "{...application...}: " << function->GetDeclaration() << "\n";
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// The context is being reused by the script engine for a nested call
|
||||
text << "{...script engine...}\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return text.str();
|
||||
}
|
||||
|
||||
void ScriptThrow(const string &msg)
|
||||
{
|
||||
asIScriptContext *ctx = asGetActiveContext();
|
||||
if (ctx)
|
||||
ctx->SetException(msg.c_str());
|
||||
}
|
||||
|
||||
string ScriptGetExceptionInfo()
|
||||
{
|
||||
asIScriptContext *ctx = asGetActiveContext();
|
||||
if (!ctx)
|
||||
return "";
|
||||
|
||||
const char *msg = ctx->GetExceptionString();
|
||||
if (msg == 0)
|
||||
return "";
|
||||
|
||||
return string(msg);
|
||||
}
|
||||
|
||||
void RegisterExceptionRoutines(asIScriptEngine *engine)
|
||||
{
|
||||
int r;
|
||||
|
||||
// The string type must be available
|
||||
assert(engine->GetTypeInfoByDecl("string"));
|
||||
|
||||
r = engine->RegisterGlobalFunction("void throw(const string &in)", asFUNCTION(ScriptThrow), asCALL_CDECL); assert(r >= 0);
|
||||
r = engine->RegisterGlobalFunction("string getExceptionInfo()", asFUNCTION(ScriptGetExceptionInfo), asCALL_CDECL); assert(r >= 0);
|
||||
}
|
||||
|
||||
END_AS_NAMESPACE
|
||||
53
add_on/scripthelper/scripthelper.h
Normal file
53
add_on/scripthelper/scripthelper.h
Normal file
@@ -0,0 +1,53 @@
|
||||
#ifndef SCRIPTHELPER_H
|
||||
#define SCRIPTHELPER_H
|
||||
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
|
||||
#ifndef ANGELSCRIPT_H
|
||||
// Avoid having to inform include path if header is already include before
|
||||
#include <angelscript.h>
|
||||
#endif
|
||||
|
||||
|
||||
BEGIN_AS_NAMESPACE
|
||||
|
||||
// Compare relation between two objects of the same type
|
||||
int CompareRelation(asIScriptEngine *engine, void *lobj, void *robj, int typeId, int &result);
|
||||
|
||||
// Compare equality between two objects of the same type
|
||||
int CompareEquality(asIScriptEngine *engine, void *lobj, void *robj, int typeId, bool &result);
|
||||
|
||||
// Compile and execute simple statements
|
||||
// The module is optional. If given the statements can access the entities compiled in the module.
|
||||
// The caller can optionally provide its own context, for example if a context should be reused.
|
||||
int ExecuteString(asIScriptEngine *engine, const char *code, asIScriptModule *mod = 0, asIScriptContext *ctx = 0);
|
||||
|
||||
// Compile and execute simple statements with option of return value
|
||||
// The module is optional. If given the statements can access the entitites compiled in the module.
|
||||
// The caller can optionally provide its own context, for example if a context should be reused.
|
||||
int ExecuteString(asIScriptEngine *engine, const char *code, void *ret, int retTypeId, asIScriptModule *mod = 0, asIScriptContext *ctx = 0);
|
||||
|
||||
// Write the registered application interface to a file for an offline compiler.
|
||||
// The format is compatible with the offline compiler in /sdk/samples/asbuild/.
|
||||
int WriteConfigToFile(asIScriptEngine *engine, const char *filename);
|
||||
|
||||
// Write the registered application interface to a text stream.
|
||||
int WriteConfigToStream(asIScriptEngine *engine, std::ostream &strm);
|
||||
|
||||
// Loads an interface from a text stream and configures the engine with it. This will not
|
||||
// set the correct function pointers, so it is not possible to use this engine to execute
|
||||
// scripts, but it can be used to compile scripts and save the byte code.
|
||||
int ConfigEngineFromStream(asIScriptEngine *engine, std::istream &strm, const char *nameOfStream = "config", asIStringFactory *stringFactory = 0);
|
||||
|
||||
// Format the details of the script exception into a human readable text
|
||||
std::string GetExceptionInfo(asIScriptContext *ctx, bool showStack = false);
|
||||
|
||||
// Register the exception routines
|
||||
// 'void throw(const string &msg)'
|
||||
// 'string getExceptionInfo()'
|
||||
void RegisterExceptionRoutines(asIScriptEngine *engine);
|
||||
|
||||
END_AS_NAMESPACE
|
||||
|
||||
#endif
|
||||
347
add_on/scriptmath/scriptmath.cpp
Normal file
347
add_on/scriptmath/scriptmath.cpp
Normal file
@@ -0,0 +1,347 @@
|
||||
#include <assert.h>
|
||||
#include <math.h>
|
||||
#include <float.h>
|
||||
#include <string.h>
|
||||
#include "scriptmath.h"
|
||||
|
||||
#ifdef __BORLANDC__
|
||||
#include <cmath>
|
||||
|
||||
// The C++Builder RTL doesn't pull the *f functions into the global namespace per default.
|
||||
using namespace std;
|
||||
|
||||
#if __BORLANDC__ < 0x580
|
||||
// C++Builder 6 and earlier don't come with any *f variants of the math functions at all.
|
||||
inline float cosf (float arg) { return std::cos (arg); }
|
||||
inline float sinf (float arg) { return std::sin (arg); }
|
||||
inline float tanf (float arg) { return std::tan (arg); }
|
||||
inline float atan2f (float y, float x) { return std::atan2 (y, x); }
|
||||
inline float logf (float arg) { return std::log (arg); }
|
||||
inline float powf (float x, float y) { return std::pow (x, y); }
|
||||
inline float sqrtf (float arg) { return std::sqrt (arg); }
|
||||
#endif
|
||||
|
||||
// C++Builder doesn't define most of the non-standard float-specific math functions with
|
||||
// "*f" suffix; instead it provides overloads for the standard math functions which take
|
||||
// "float" arguments.
|
||||
inline float acosf (float arg) { return std::acos (arg); }
|
||||
inline float asinf (float arg) { return std::asin (arg); }
|
||||
inline float atanf (float arg) { return std::atan (arg); }
|
||||
inline float coshf (float arg) { return std::cosh (arg); }
|
||||
inline float sinhf (float arg) { return std::sinh (arg); }
|
||||
inline float tanhf (float arg) { return std::tanh (arg); }
|
||||
inline float log10f (float arg) { return std::log10 (arg); }
|
||||
inline float ceilf (float arg) { return std::ceil (arg); }
|
||||
inline float fabsf (float arg) { return std::fabs (arg); }
|
||||
inline float floorf (float arg) { return std::floor (arg); }
|
||||
|
||||
// C++Builder doesn't define a non-standard "modff" function but rather an overload of "modf"
|
||||
// for float arguments. However, BCC's float overload of fmod() is broken (QC #74816; fixed
|
||||
// in C++Builder 2010).
|
||||
inline float modff (float x, float *y)
|
||||
{
|
||||
double d;
|
||||
float f = (float) modf((double) x, &d);
|
||||
*y = (float) d;
|
||||
return f;
|
||||
}
|
||||
#endif
|
||||
|
||||
BEGIN_AS_NAMESPACE
|
||||
|
||||
// Determine whether the float version should be registered, or the double version
|
||||
#ifndef AS_USE_FLOAT
|
||||
#if !defined(_WIN32_WCE) // WinCE doesn't have the float versions of the math functions
|
||||
#define AS_USE_FLOAT 1
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// The modf function doesn't seem very intuitive, so I'm writing this
|
||||
// function that simply returns the fractional part of the float value
|
||||
#if AS_USE_FLOAT
|
||||
float fractionf(float v)
|
||||
{
|
||||
float intPart;
|
||||
return modff(v, &intPart);
|
||||
}
|
||||
#else
|
||||
double fraction(double v)
|
||||
{
|
||||
double intPart;
|
||||
return modf(v, &intPart);
|
||||
}
|
||||
#endif
|
||||
|
||||
// As AngelScript doesn't allow bitwise manipulation of float types we'll provide a couple of
|
||||
// functions for converting float values to IEEE 754 formatted values etc. This also allow us to
|
||||
// provide a platform agnostic representation to the script so the scripts don't have to worry
|
||||
// about whether the CPU uses IEEE 754 floats or some other representation
|
||||
float fpFromIEEE(asUINT raw)
|
||||
{
|
||||
// TODO: Identify CPU family to provide proper conversion
|
||||
// if the CPU doesn't natively use IEEE style floats
|
||||
return *reinterpret_cast<float*>(&raw);
|
||||
}
|
||||
asUINT fpToIEEE(float fp)
|
||||
{
|
||||
return *reinterpret_cast<asUINT*>(&fp);
|
||||
}
|
||||
double fpFromIEEE(asQWORD raw)
|
||||
{
|
||||
return *reinterpret_cast<double*>(&raw);
|
||||
}
|
||||
asQWORD fpToIEEE(double fp)
|
||||
{
|
||||
return *reinterpret_cast<asQWORD*>(&fp);
|
||||
}
|
||||
|
||||
// closeTo() is used to determine if the binary representation of two numbers are
|
||||
// relatively close to each other. Numerical errors due to rounding errors build
|
||||
// up over many operations, so it is almost impossible to get exact numbers and
|
||||
// this is where closeTo() comes in.
|
||||
//
|
||||
// It shouldn't be used to determine if two numbers are mathematically close to
|
||||
// each other.
|
||||
//
|
||||
// ref: http://www.cygnus-software.com/papers/comparingfloats/comparingfloats.htm
|
||||
// ref: http://www.gamedev.net/topic/653449-scriptmath-and-closeto/
|
||||
bool closeTo(float a, float b, float epsilon)
|
||||
{
|
||||
// Equal numbers and infinity will return immediately
|
||||
if( a == b ) return true;
|
||||
|
||||
// When very close to 0, we can use the absolute comparison
|
||||
float diff = fabsf(a - b);
|
||||
if( (a == 0 || b == 0) && (diff < epsilon) )
|
||||
return true;
|
||||
|
||||
// Otherwise we need to use relative comparison to account for precision
|
||||
return diff / (fabs(a) + fabs(b)) < epsilon;
|
||||
}
|
||||
|
||||
bool closeTo(double a, double b, double epsilon)
|
||||
{
|
||||
if( a == b ) return true;
|
||||
|
||||
double diff = fabs(a - b);
|
||||
if( (a == 0 || b == 0) && (diff < epsilon) )
|
||||
return true;
|
||||
|
||||
return diff / (fabs(a) + fabs(b)) < epsilon;
|
||||
}
|
||||
|
||||
void RegisterScriptMath_Native(asIScriptEngine *engine)
|
||||
{
|
||||
int r;
|
||||
|
||||
// Conversion between floating point and IEEE bits representations
|
||||
r = engine->RegisterGlobalFunction("float fpFromIEEE(uint)", asFUNCTIONPR(fpFromIEEE, (asUINT), float), asCALL_CDECL); assert( r >= 0 );
|
||||
r = engine->RegisterGlobalFunction("uint fpToIEEE(float)", asFUNCTIONPR(fpToIEEE, (float), asUINT), asCALL_CDECL); assert( r >= 0 );
|
||||
r = engine->RegisterGlobalFunction("double fpFromIEEE(uint64)", asFUNCTIONPR(fpFromIEEE, (asQWORD), double), asCALL_CDECL); assert( r >= 0 );
|
||||
r = engine->RegisterGlobalFunction("uint64 fpToIEEE(double)", asFUNCTIONPR(fpToIEEE, (double), asQWORD), asCALL_CDECL); assert( r >= 0 );
|
||||
|
||||
// Close to comparison with epsilon
|
||||
r = engine->RegisterGlobalFunction("bool closeTo(float, float, float = 0.00001f)", asFUNCTIONPR(closeTo, (float, float, float), bool), asCALL_CDECL); assert( r >= 0 );
|
||||
r = engine->RegisterGlobalFunction("bool closeTo(double, double, double = 0.0000000001)", asFUNCTIONPR(closeTo, (double, double, double), bool), asCALL_CDECL); assert( r >= 0 );
|
||||
|
||||
#if AS_USE_FLOAT
|
||||
// Trigonometric functions
|
||||
r = engine->RegisterGlobalFunction("float cos(float)", asFUNCTIONPR(cosf, (float), float), asCALL_CDECL); assert( r >= 0 );
|
||||
r = engine->RegisterGlobalFunction("float sin(float)", asFUNCTIONPR(sinf, (float), float), asCALL_CDECL); assert( r >= 0 );
|
||||
r = engine->RegisterGlobalFunction("float tan(float)", asFUNCTIONPR(tanf, (float), float), asCALL_CDECL); assert( r >= 0 );
|
||||
|
||||
r = engine->RegisterGlobalFunction("float acos(float)", asFUNCTIONPR(acosf, (float), float), asCALL_CDECL); assert( r >= 0 );
|
||||
r = engine->RegisterGlobalFunction("float asin(float)", asFUNCTIONPR(asinf, (float), float), asCALL_CDECL); assert( r >= 0 );
|
||||
r = engine->RegisterGlobalFunction("float atan(float)", asFUNCTIONPR(atanf, (float), float), asCALL_CDECL); assert( r >= 0 );
|
||||
r = engine->RegisterGlobalFunction("float atan2(float,float)", asFUNCTIONPR(atan2f, (float, float), float), asCALL_CDECL); assert( r >= 0 );
|
||||
|
||||
// Hyberbolic functions
|
||||
r = engine->RegisterGlobalFunction("float cosh(float)", asFUNCTIONPR(coshf, (float), float), asCALL_CDECL); assert( r >= 0 );
|
||||
r = engine->RegisterGlobalFunction("float sinh(float)", asFUNCTIONPR(sinhf, (float), float), asCALL_CDECL); assert( r >= 0 );
|
||||
r = engine->RegisterGlobalFunction("float tanh(float)", asFUNCTIONPR(tanhf, (float), float), asCALL_CDECL); assert( r >= 0 );
|
||||
|
||||
// Exponential and logarithmic functions
|
||||
r = engine->RegisterGlobalFunction("float log(float)", asFUNCTIONPR(logf, (float), float), asCALL_CDECL); assert( r >= 0 );
|
||||
r = engine->RegisterGlobalFunction("float log10(float)", asFUNCTIONPR(log10f, (float), float), asCALL_CDECL); assert( r >= 0 );
|
||||
|
||||
// Power functions
|
||||
r = engine->RegisterGlobalFunction("float pow(float, float)", asFUNCTIONPR(powf, (float, float), float), asCALL_CDECL); assert( r >= 0 );
|
||||
r = engine->RegisterGlobalFunction("float sqrt(float)", asFUNCTIONPR(sqrtf, (float), float), asCALL_CDECL); assert( r >= 0 );
|
||||
|
||||
// Nearest integer, absolute value, and remainder functions
|
||||
r = engine->RegisterGlobalFunction("float ceil(float)", asFUNCTIONPR(ceilf, (float), float), asCALL_CDECL); assert( r >= 0 );
|
||||
r = engine->RegisterGlobalFunction("float abs(float)", asFUNCTIONPR(fabsf, (float), float), asCALL_CDECL); assert( r >= 0 );
|
||||
r = engine->RegisterGlobalFunction("float floor(float)", asFUNCTIONPR(floorf, (float), float), asCALL_CDECL); assert( r >= 0 );
|
||||
r = engine->RegisterGlobalFunction("float fraction(float)", asFUNCTIONPR(fractionf, (float), float), asCALL_CDECL); assert( r >= 0 );
|
||||
|
||||
// Don't register modf because AngelScript already supports the % operator
|
||||
#else
|
||||
// double versions of the same
|
||||
r = engine->RegisterGlobalFunction("double cos(double)", asFUNCTIONPR(cos, (double), double), asCALL_CDECL); assert( r >= 0 );
|
||||
r = engine->RegisterGlobalFunction("double sin(double)", asFUNCTIONPR(sin, (double), double), asCALL_CDECL); assert( r >= 0 );
|
||||
r = engine->RegisterGlobalFunction("double tan(double)", asFUNCTIONPR(tan, (double), double), asCALL_CDECL); assert( r >= 0 );
|
||||
r = engine->RegisterGlobalFunction("double acos(double)", asFUNCTIONPR(acos, (double), double), asCALL_CDECL); assert( r >= 0 );
|
||||
r = engine->RegisterGlobalFunction("double asin(double)", asFUNCTIONPR(asin, (double), double), asCALL_CDECL); assert( r >= 0 );
|
||||
r = engine->RegisterGlobalFunction("double atan(double)", asFUNCTIONPR(atan, (double), double), asCALL_CDECL); assert( r >= 0 );
|
||||
r = engine->RegisterGlobalFunction("double atan2(double,double)", asFUNCTIONPR(atan2, (double, double), double), asCALL_CDECL); assert( r >= 0 );
|
||||
r = engine->RegisterGlobalFunction("double cosh(double)", asFUNCTIONPR(cosh, (double), double), asCALL_CDECL); assert( r >= 0 );
|
||||
r = engine->RegisterGlobalFunction("double sinh(double)", asFUNCTIONPR(sinh, (double), double), asCALL_CDECL); assert( r >= 0 );
|
||||
r = engine->RegisterGlobalFunction("double tanh(double)", asFUNCTIONPR(tanh, (double), double), asCALL_CDECL); assert( r >= 0 );
|
||||
r = engine->RegisterGlobalFunction("double log(double)", asFUNCTIONPR(log, (double), double), asCALL_CDECL); assert( r >= 0 );
|
||||
r = engine->RegisterGlobalFunction("double log10(double)", asFUNCTIONPR(log10, (double), double), asCALL_CDECL); assert( r >= 0 );
|
||||
r = engine->RegisterGlobalFunction("double pow(double, double)", asFUNCTIONPR(pow, (double, double), double), asCALL_CDECL); assert( r >= 0 );
|
||||
r = engine->RegisterGlobalFunction("double sqrt(double)", asFUNCTIONPR(sqrt, (double), double), asCALL_CDECL); assert( r >= 0 );
|
||||
r = engine->RegisterGlobalFunction("double ceil(double)", asFUNCTIONPR(ceil, (double), double), asCALL_CDECL); assert( r >= 0 );
|
||||
r = engine->RegisterGlobalFunction("double abs(double)", asFUNCTIONPR(fabs, (double), double), asCALL_CDECL); assert( r >= 0 );
|
||||
r = engine->RegisterGlobalFunction("double floor(double)", asFUNCTIONPR(floor, (double), double), asCALL_CDECL); assert( r >= 0 );
|
||||
r = engine->RegisterGlobalFunction("double fraction(double)", asFUNCTIONPR(fraction, (double), double), asCALL_CDECL); assert( r >= 0 );
|
||||
#endif
|
||||
}
|
||||
|
||||
#if AS_USE_FLOAT
|
||||
// This macro creates simple generic wrappers for functions of type 'float func(float)'
|
||||
#define GENERICff(x) \
|
||||
void x##_generic(asIScriptGeneric *gen) \
|
||||
{ \
|
||||
float f = *(float*)gen->GetAddressOfArg(0); \
|
||||
*(float*)gen->GetAddressOfReturnLocation() = x(f); \
|
||||
}
|
||||
|
||||
GENERICff(cosf)
|
||||
GENERICff(sinf)
|
||||
GENERICff(tanf)
|
||||
GENERICff(acosf)
|
||||
GENERICff(asinf)
|
||||
GENERICff(atanf)
|
||||
GENERICff(coshf)
|
||||
GENERICff(sinhf)
|
||||
GENERICff(tanhf)
|
||||
GENERICff(logf)
|
||||
GENERICff(log10f)
|
||||
GENERICff(sqrtf)
|
||||
GENERICff(ceilf)
|
||||
GENERICff(fabsf)
|
||||
GENERICff(floorf)
|
||||
GENERICff(fractionf)
|
||||
|
||||
void powf_generic(asIScriptGeneric *gen)
|
||||
{
|
||||
float f1 = *(float*)gen->GetAddressOfArg(0);
|
||||
float f2 = *(float*)gen->GetAddressOfArg(1);
|
||||
*(float*)gen->GetAddressOfReturnLocation() = powf(f1, f2);
|
||||
}
|
||||
void atan2f_generic(asIScriptGeneric *gen)
|
||||
{
|
||||
float f1 = *(float*)gen->GetAddressOfArg(0);
|
||||
float f2 = *(float*)gen->GetAddressOfArg(1);
|
||||
*(float*)gen->GetAddressOfReturnLocation() = atan2f(f1, f2);
|
||||
}
|
||||
|
||||
#else
|
||||
// This macro creates simple generic wrappers for functions of type 'double func(double)'
|
||||
#define GENERICdd(x) \
|
||||
void x##_generic(asIScriptGeneric *gen) \
|
||||
{ \
|
||||
double f = *(double*)gen->GetAddressOfArg(0); \
|
||||
*(double*)gen->GetAddressOfReturnLocation() = x(f); \
|
||||
}
|
||||
|
||||
GENERICdd(cos)
|
||||
GENERICdd(sin)
|
||||
GENERICdd(tan)
|
||||
GENERICdd(acos)
|
||||
GENERICdd(asin)
|
||||
GENERICdd(atan)
|
||||
GENERICdd(cosh)
|
||||
GENERICdd(sinh)
|
||||
GENERICdd(tanh)
|
||||
GENERICdd(log)
|
||||
GENERICdd(log10)
|
||||
GENERICdd(sqrt)
|
||||
GENERICdd(ceil)
|
||||
GENERICdd(fabs)
|
||||
GENERICdd(floor)
|
||||
GENERICdd(fraction)
|
||||
|
||||
void pow_generic(asIScriptGeneric *gen)
|
||||
{
|
||||
double f1 = *(double*)gen->GetAddressOfArg(0);
|
||||
double f2 = *(double*)gen->GetAddressOfArg(1);
|
||||
*(double*)gen->GetAddressOfReturnLocation() = pow(f1, f2);
|
||||
}
|
||||
void atan2_generic(asIScriptGeneric *gen)
|
||||
{
|
||||
double f1 = *(double*)gen->GetAddressOfArg(0);
|
||||
double f2 = *(double*)gen->GetAddressOfArg(1);
|
||||
*(double*)gen->GetAddressOfReturnLocation() = atan2(f1, f2);
|
||||
}
|
||||
#endif
|
||||
void RegisterScriptMath_Generic(asIScriptEngine *engine)
|
||||
{
|
||||
int r;
|
||||
|
||||
#if AS_USE_FLOAT
|
||||
// Trigonometric functions
|
||||
r = engine->RegisterGlobalFunction("float cos(float)", asFUNCTION(cosf_generic), asCALL_GENERIC); assert( r >= 0 );
|
||||
r = engine->RegisterGlobalFunction("float sin(float)", asFUNCTION(sinf_generic), asCALL_GENERIC); assert( r >= 0 );
|
||||
r = engine->RegisterGlobalFunction("float tan(float)", asFUNCTION(tanf_generic), asCALL_GENERIC); assert( r >= 0 );
|
||||
|
||||
r = engine->RegisterGlobalFunction("float acos(float)", asFUNCTION(acosf_generic), asCALL_GENERIC); assert( r >= 0 );
|
||||
r = engine->RegisterGlobalFunction("float asin(float)", asFUNCTION(asinf_generic), asCALL_GENERIC); assert( r >= 0 );
|
||||
r = engine->RegisterGlobalFunction("float atan(float)", asFUNCTION(atanf_generic), asCALL_GENERIC); assert( r >= 0 );
|
||||
r = engine->RegisterGlobalFunction("float atan2(float,float)", asFUNCTION(atan2f_generic), asCALL_GENERIC); assert( r >= 0 );
|
||||
|
||||
// Hyberbolic functions
|
||||
r = engine->RegisterGlobalFunction("float cosh(float)", asFUNCTION(coshf_generic), asCALL_GENERIC); assert( r >= 0 );
|
||||
r = engine->RegisterGlobalFunction("float sinh(float)", asFUNCTION(sinhf_generic), asCALL_GENERIC); assert( r >= 0 );
|
||||
r = engine->RegisterGlobalFunction("float tanh(float)", asFUNCTION(tanhf_generic), asCALL_GENERIC); assert( r >= 0 );
|
||||
|
||||
// Exponential and logarithmic functions
|
||||
r = engine->RegisterGlobalFunction("float log(float)", asFUNCTION(logf_generic), asCALL_GENERIC); assert( r >= 0 );
|
||||
r = engine->RegisterGlobalFunction("float log10(float)", asFUNCTION(log10f_generic), asCALL_GENERIC); assert( r >= 0 );
|
||||
|
||||
// Power functions
|
||||
r = engine->RegisterGlobalFunction("float pow(float, float)", asFUNCTION(powf_generic), asCALL_GENERIC); assert( r >= 0 );
|
||||
r = engine->RegisterGlobalFunction("float sqrt(float)", asFUNCTION(sqrtf_generic), asCALL_GENERIC); assert( r >= 0 );
|
||||
|
||||
// Nearest integer, absolute value, and remainder functions
|
||||
r = engine->RegisterGlobalFunction("float ceil(float)", asFUNCTION(ceilf_generic), asCALL_GENERIC); assert( r >= 0 );
|
||||
r = engine->RegisterGlobalFunction("float abs(float)", asFUNCTION(fabsf_generic), asCALL_GENERIC); assert( r >= 0 );
|
||||
r = engine->RegisterGlobalFunction("float floor(float)", asFUNCTION(floorf_generic), asCALL_GENERIC); assert( r >= 0 );
|
||||
r = engine->RegisterGlobalFunction("float fraction(float)", asFUNCTION(fractionf_generic), asCALL_GENERIC); assert( r >= 0 );
|
||||
|
||||
// Don't register modf because AngelScript already supports the % operator
|
||||
#else
|
||||
// double versions of the same
|
||||
r = engine->RegisterGlobalFunction("double cos(double)", asFUNCTION(cos_generic), asCALL_GENERIC); assert( r >= 0 );
|
||||
r = engine->RegisterGlobalFunction("double sin(double)", asFUNCTION(sin_generic), asCALL_GENERIC); assert( r >= 0 );
|
||||
r = engine->RegisterGlobalFunction("double tan(double)", asFUNCTION(tan_generic), asCALL_GENERIC); assert( r >= 0 );
|
||||
r = engine->RegisterGlobalFunction("double acos(double)", asFUNCTION(acos_generic), asCALL_GENERIC); assert( r >= 0 );
|
||||
r = engine->RegisterGlobalFunction("double asin(double)", asFUNCTION(asin_generic), asCALL_GENERIC); assert( r >= 0 );
|
||||
r = engine->RegisterGlobalFunction("double atan(double)", asFUNCTION(atan_generic), asCALL_GENERIC); assert( r >= 0 );
|
||||
r = engine->RegisterGlobalFunction("double atan2(double,double)", asFUNCTION(atan2_generic), asCALL_GENERIC); assert( r >= 0 );
|
||||
r = engine->RegisterGlobalFunction("double cosh(double)", asFUNCTION(cosh_generic), asCALL_GENERIC); assert( r >= 0 );
|
||||
r = engine->RegisterGlobalFunction("double sinh(double)", asFUNCTION(sinh_generic), asCALL_GENERIC); assert( r >= 0 );
|
||||
r = engine->RegisterGlobalFunction("double tanh(double)", asFUNCTION(tanh_generic), asCALL_GENERIC); assert( r >= 0 );
|
||||
r = engine->RegisterGlobalFunction("double log(double)", asFUNCTION(log_generic), asCALL_GENERIC); assert( r >= 0 );
|
||||
r = engine->RegisterGlobalFunction("double log10(double)", asFUNCTION(log10_generic), asCALL_GENERIC); assert( r >= 0 );
|
||||
r = engine->RegisterGlobalFunction("double pow(double, double)", asFUNCTION(pow_generic), asCALL_GENERIC); assert( r >= 0 );
|
||||
r = engine->RegisterGlobalFunction("double sqrt(double)", asFUNCTION(sqrt_generic), asCALL_GENERIC); assert( r >= 0 );
|
||||
r = engine->RegisterGlobalFunction("double ceil(double)", asFUNCTION(ceil_generic), asCALL_GENERIC); assert( r >= 0 );
|
||||
r = engine->RegisterGlobalFunction("double abs(double)", asFUNCTION(fabs_generic), asCALL_GENERIC); assert( r >= 0 );
|
||||
r = engine->RegisterGlobalFunction("double floor(double)", asFUNCTION(floor_generic), asCALL_GENERIC); assert( r >= 0 );
|
||||
r = engine->RegisterGlobalFunction("double fraction(double)", asFUNCTION(fraction_generic), asCALL_GENERIC); assert( r >= 0 );
|
||||
#endif
|
||||
}
|
||||
|
||||
void RegisterScriptMath(asIScriptEngine *engine)
|
||||
{
|
||||
if( strstr(asGetLibraryOptions(), "AS_MAX_PORTABILITY") )
|
||||
RegisterScriptMath_Generic(engine);
|
||||
else
|
||||
RegisterScriptMath_Native(engine);
|
||||
}
|
||||
|
||||
END_AS_NAMESPACE
|
||||
|
||||
|
||||
26
add_on/scriptmath/scriptmath.h
Normal file
26
add_on/scriptmath/scriptmath.h
Normal file
@@ -0,0 +1,26 @@
|
||||
#ifndef SCRIPTMATH_H
|
||||
#define SCRIPTMATH_H
|
||||
|
||||
#ifndef ANGELSCRIPT_H
|
||||
// Avoid having to inform include path if header is already include before
|
||||
#include <angelscript.h>
|
||||
#endif
|
||||
|
||||
|
||||
BEGIN_AS_NAMESPACE
|
||||
|
||||
// This function will determine the configuration of the engine
|
||||
// and use one of the two functions below to register the math functions
|
||||
void RegisterScriptMath(asIScriptEngine *engine);
|
||||
|
||||
// Call this function to register the math functions
|
||||
// using native calling conventions
|
||||
void RegisterScriptMath_Native(asIScriptEngine *engine);
|
||||
|
||||
// Use this one instead if native calling conventions
|
||||
// are not supported on the target platform
|
||||
void RegisterScriptMath_Generic(asIScriptEngine *engine);
|
||||
|
||||
END_AS_NAMESPACE
|
||||
|
||||
#endif
|
||||
222
add_on/scriptmath/scriptmathcomplex.cpp
Normal file
222
add_on/scriptmath/scriptmathcomplex.cpp
Normal file
@@ -0,0 +1,222 @@
|
||||
#include <assert.h>
|
||||
#include <string.h> // strstr
|
||||
#include <new> // new()
|
||||
#include <math.h>
|
||||
#include "scriptmathcomplex.h"
|
||||
|
||||
#ifdef __BORLANDC__
|
||||
// C++Builder doesn't define a non-standard "sqrtf" function but rather an overload of "sqrt"
|
||||
// for float arguments.
|
||||
inline float sqrtf (float x) { return sqrt (x); }
|
||||
#endif
|
||||
|
||||
BEGIN_AS_NAMESPACE
|
||||
|
||||
Complex::Complex()
|
||||
{
|
||||
r = 0;
|
||||
i = 0;
|
||||
}
|
||||
|
||||
Complex::Complex(const Complex &other)
|
||||
{
|
||||
r = other.r;
|
||||
i = other.i;
|
||||
}
|
||||
|
||||
Complex::Complex(float _r, float _i)
|
||||
{
|
||||
r = _r;
|
||||
i = _i;
|
||||
}
|
||||
|
||||
bool Complex::operator==(const Complex &o) const
|
||||
{
|
||||
return (r == o.r) && (i == o.i);
|
||||
}
|
||||
|
||||
bool Complex::operator!=(const Complex &o) const
|
||||
{
|
||||
return !(*this == o);
|
||||
}
|
||||
|
||||
Complex &Complex::operator=(const Complex &other)
|
||||
{
|
||||
r = other.r;
|
||||
i = other.i;
|
||||
return *this;
|
||||
}
|
||||
|
||||
Complex &Complex::operator+=(const Complex &other)
|
||||
{
|
||||
r += other.r;
|
||||
i += other.i;
|
||||
return *this;
|
||||
}
|
||||
|
||||
Complex &Complex::operator-=(const Complex &other)
|
||||
{
|
||||
r -= other.r;
|
||||
i -= other.i;
|
||||
return *this;
|
||||
}
|
||||
|
||||
Complex &Complex::operator*=(const Complex &other)
|
||||
{
|
||||
*this = *this * other;
|
||||
return *this;
|
||||
}
|
||||
|
||||
Complex &Complex::operator/=(const Complex &other)
|
||||
{
|
||||
*this = *this / other;
|
||||
return *this;
|
||||
}
|
||||
|
||||
float Complex::squaredLength() const
|
||||
{
|
||||
return r*r + i*i;
|
||||
}
|
||||
|
||||
float Complex::length() const
|
||||
{
|
||||
return sqrtf(squaredLength());
|
||||
}
|
||||
|
||||
Complex Complex::operator+(const Complex &other) const
|
||||
{
|
||||
return Complex(r + other.r, i + other.i);
|
||||
}
|
||||
|
||||
Complex Complex::operator-(const Complex &other) const
|
||||
{
|
||||
return Complex(r - other.r, i + other.i);
|
||||
}
|
||||
|
||||
Complex Complex::operator*(const Complex &other) const
|
||||
{
|
||||
return Complex(r*other.r - i*other.i, r*other.i + i*other.r);
|
||||
}
|
||||
|
||||
Complex Complex::operator/(const Complex &other) const
|
||||
{
|
||||
float squaredLen = other.squaredLength();
|
||||
if( squaredLen == 0 ) return Complex(0,0);
|
||||
|
||||
return Complex((r*other.r + i*other.i)/squaredLen, (i*other.r - r*other.i)/squaredLen);
|
||||
}
|
||||
|
||||
//-----------------------
|
||||
// Swizzle operators
|
||||
//-----------------------
|
||||
|
||||
Complex Complex::get_ri() const
|
||||
{
|
||||
return *this;
|
||||
}
|
||||
Complex Complex::get_ir() const
|
||||
{
|
||||
return Complex(r,i);
|
||||
}
|
||||
void Complex::set_ri(const Complex &o)
|
||||
{
|
||||
*this = o;
|
||||
}
|
||||
void Complex::set_ir(const Complex &o)
|
||||
{
|
||||
r = o.i;
|
||||
i = o.r;
|
||||
}
|
||||
|
||||
//-----------------------
|
||||
// AngelScript functions
|
||||
//-----------------------
|
||||
|
||||
static void ComplexDefaultConstructor(Complex *self)
|
||||
{
|
||||
new(self) Complex();
|
||||
}
|
||||
|
||||
static void ComplexCopyConstructor(const Complex &other, Complex *self)
|
||||
{
|
||||
new(self) Complex(other);
|
||||
}
|
||||
|
||||
static void ComplexConvConstructor(float r, Complex *self)
|
||||
{
|
||||
new(self) Complex(r);
|
||||
}
|
||||
|
||||
static void ComplexInitConstructor(float r, float i, Complex *self)
|
||||
{
|
||||
new(self) Complex(r,i);
|
||||
}
|
||||
|
||||
static void ComplexListConstructor(float *list, Complex *self)
|
||||
{
|
||||
new(self) Complex(list[0], list[1]);
|
||||
}
|
||||
|
||||
//--------------------------------
|
||||
// Registration
|
||||
//-------------------------------------
|
||||
|
||||
static void RegisterScriptMathComplex_Native(asIScriptEngine *engine)
|
||||
{
|
||||
int r;
|
||||
|
||||
// Register the type
|
||||
#if AS_CAN_USE_CPP11
|
||||
// With C++11 it is possible to use asGetTypeTraits to determine the correct flags to represent the C++ class, except for the asOBJ_APP_CLASS_ALLFLOATS
|
||||
r = engine->RegisterObjectType("complex", sizeof(Complex), asOBJ_VALUE | asOBJ_POD | asGetTypeTraits<Complex>() | asOBJ_APP_CLASS_ALLFLOATS); assert( r >= 0 );
|
||||
#else
|
||||
r = engine->RegisterObjectType("complex", sizeof(Complex), asOBJ_VALUE | asOBJ_POD | asOBJ_APP_CLASS_CAK | asOBJ_APP_CLASS_ALLFLOATS); assert( r >= 0 );
|
||||
#endif
|
||||
|
||||
// Register the object properties
|
||||
r = engine->RegisterObjectProperty("complex", "float r", asOFFSET(Complex, r)); assert( r >= 0 );
|
||||
r = engine->RegisterObjectProperty("complex", "float i", asOFFSET(Complex, i)); assert( r >= 0 );
|
||||
|
||||
// Register the constructors
|
||||
r = engine->RegisterObjectBehaviour("complex", asBEHAVE_CONSTRUCT, "void f()", asFUNCTION(ComplexDefaultConstructor), asCALL_CDECL_OBJLAST); assert( r >= 0 );
|
||||
r = engine->RegisterObjectBehaviour("complex", asBEHAVE_CONSTRUCT, "void f(const complex &in)", asFUNCTION(ComplexCopyConstructor), asCALL_CDECL_OBJLAST); assert( r >= 0 );
|
||||
r = engine->RegisterObjectBehaviour("complex", asBEHAVE_CONSTRUCT, "void f(float)", asFUNCTION(ComplexConvConstructor), asCALL_CDECL_OBJLAST); assert( r >= 0 );
|
||||
r = engine->RegisterObjectBehaviour("complex", asBEHAVE_CONSTRUCT, "void f(float, float)", asFUNCTION(ComplexInitConstructor), asCALL_CDECL_OBJLAST); assert( r >= 0 );
|
||||
r = engine->RegisterObjectBehaviour("complex", asBEHAVE_LIST_CONSTRUCT, "void f(const int &in) {float, float}", asFUNCTION(ComplexListConstructor), asCALL_CDECL_OBJLAST); assert( r >= 0 );
|
||||
|
||||
// Register the operator overloads
|
||||
r = engine->RegisterObjectMethod("complex", "complex &opAddAssign(const complex &in)", asMETHODPR(Complex, operator+=, (const Complex &), Complex&), asCALL_THISCALL); assert( r >= 0 );
|
||||
r = engine->RegisterObjectMethod("complex", "complex &opSubAssign(const complex &in)", asMETHODPR(Complex, operator-=, (const Complex &), Complex&), asCALL_THISCALL); assert( r >= 0 );
|
||||
r = engine->RegisterObjectMethod("complex", "complex &opMulAssign(const complex &in)", asMETHODPR(Complex, operator*=, (const Complex &), Complex&), asCALL_THISCALL); assert( r >= 0 );
|
||||
r = engine->RegisterObjectMethod("complex", "complex &opDivAssign(const complex &in)", asMETHODPR(Complex, operator/=, (const Complex &), Complex&), asCALL_THISCALL); assert( r >= 0 );
|
||||
r = engine->RegisterObjectMethod("complex", "bool opEquals(const complex &in) const", asMETHODPR(Complex, operator==, (const Complex &) const, bool), asCALL_THISCALL); assert( r >= 0 );
|
||||
r = engine->RegisterObjectMethod("complex", "complex opAdd(const complex &in) const", asMETHODPR(Complex, operator+, (const Complex &) const, Complex), asCALL_THISCALL); assert( r >= 0 );
|
||||
r = engine->RegisterObjectMethod("complex", "complex opSub(const complex &in) const", asMETHODPR(Complex, operator-, (const Complex &) const, Complex), asCALL_THISCALL); assert( r >= 0 );
|
||||
r = engine->RegisterObjectMethod("complex", "complex opMul(const complex &in) const", asMETHODPR(Complex, operator*, (const Complex &) const, Complex), asCALL_THISCALL); assert( r >= 0 );
|
||||
r = engine->RegisterObjectMethod("complex", "complex opDiv(const complex &in) const", asMETHODPR(Complex, operator/, (const Complex &) const, Complex), asCALL_THISCALL); assert( r >= 0 );
|
||||
|
||||
// Register the object methods
|
||||
r = engine->RegisterObjectMethod("complex", "float abs() const", asMETHOD(Complex,length), asCALL_THISCALL); assert( r >= 0 );
|
||||
|
||||
// Register the swizzle operators
|
||||
r = engine->RegisterObjectMethod("complex", "complex get_ri() const property", asMETHOD(Complex, get_ri), asCALL_THISCALL); assert( r >= 0 );
|
||||
r = engine->RegisterObjectMethod("complex", "complex get_ir() const property", asMETHOD(Complex, get_ir), asCALL_THISCALL); assert( r >= 0 );
|
||||
r = engine->RegisterObjectMethod("complex", "void set_ri(const complex &in) property", asMETHOD(Complex, set_ri), asCALL_THISCALL); assert( r >= 0 );
|
||||
r = engine->RegisterObjectMethod("complex", "void set_ir(const complex &in) property", asMETHOD(Complex, set_ir), asCALL_THISCALL); assert( r >= 0 );
|
||||
}
|
||||
|
||||
void RegisterScriptMathComplex(asIScriptEngine *engine)
|
||||
{
|
||||
if( strstr(asGetLibraryOptions(), "AS_MAX_PORTABILITY") )
|
||||
{
|
||||
assert( false );
|
||||
// TODO: implement support for generic calling convention
|
||||
// RegisterScriptMathComplex_Generic(engine);
|
||||
}
|
||||
else
|
||||
RegisterScriptMathComplex_Native(engine);
|
||||
}
|
||||
|
||||
END_AS_NAMESPACE
|
||||
|
||||
|
||||
61
add_on/scriptmath/scriptmathcomplex.h
Normal file
61
add_on/scriptmath/scriptmathcomplex.h
Normal file
@@ -0,0 +1,61 @@
|
||||
#ifndef SCRIPTMATHCOMPLEX_H
|
||||
#define SCRIPTMATHCOMPLEX_H
|
||||
|
||||
#ifndef ANGELSCRIPT_H
|
||||
// Avoid having to inform include path if header is already include before
|
||||
#include <angelscript.h>
|
||||
#endif
|
||||
|
||||
|
||||
BEGIN_AS_NAMESPACE
|
||||
|
||||
// This class implements complex numbers and the common
|
||||
// operations that can be done with it.
|
||||
//
|
||||
// Ref: http://mathworld.wolfram.com/ComplexNumber.html
|
||||
|
||||
struct Complex
|
||||
{
|
||||
Complex();
|
||||
Complex(const Complex &other);
|
||||
Complex(float r, float i = 0);
|
||||
|
||||
// Assignment operator
|
||||
Complex &operator=(const Complex &other);
|
||||
|
||||
// Compound assigment operators
|
||||
Complex &operator+=(const Complex &other);
|
||||
Complex &operator-=(const Complex &other);
|
||||
Complex &operator*=(const Complex &other);
|
||||
Complex &operator/=(const Complex &other);
|
||||
|
||||
float length() const;
|
||||
float squaredLength() const;
|
||||
|
||||
// Swizzle operators
|
||||
Complex get_ri() const;
|
||||
void set_ri(const Complex &in);
|
||||
Complex get_ir() const;
|
||||
void set_ir(const Complex &in);
|
||||
|
||||
// Comparison
|
||||
bool operator==(const Complex &other) const;
|
||||
bool operator!=(const Complex &other) const;
|
||||
|
||||
// Math operators
|
||||
Complex operator+(const Complex &other) const;
|
||||
Complex operator-(const Complex &other) const;
|
||||
Complex operator*(const Complex &other) const;
|
||||
Complex operator/(const Complex &other) const;
|
||||
|
||||
float r;
|
||||
float i;
|
||||
};
|
||||
|
||||
// This function will determine the configuration of the engine
|
||||
// and use one of the two functions below to register the string type
|
||||
void RegisterScriptMathComplex(asIScriptEngine *engine);
|
||||
|
||||
END_AS_NAMESPACE
|
||||
|
||||
#endif
|
||||
1365
add_on/scriptstdstring/scriptstdstring.cpp
Normal file
1365
add_on/scriptstdstring/scriptstdstring.cpp
Normal file
File diff suppressed because it is too large
Load Diff
48
add_on/scriptstdstring/scriptstdstring.h
Normal file
48
add_on/scriptstdstring/scriptstdstring.h
Normal file
@@ -0,0 +1,48 @@
|
||||
//
|
||||
// Script std::string
|
||||
//
|
||||
// This function registers the std::string type with AngelScript to be used as the default string type.
|
||||
//
|
||||
// The string type is registered as a value type, thus may have performance issues if a lot of
|
||||
// string operations are performed in the script. However, for relatively few operations, this should
|
||||
// not cause any problem for most applications.
|
||||
//
|
||||
|
||||
#ifndef SCRIPTSTDSTRING_H
|
||||
#define SCRIPTSTDSTRING_H
|
||||
|
||||
#ifndef ANGELSCRIPT_H
|
||||
// Avoid having to inform include path if header is already include before
|
||||
#include <angelscript.h>
|
||||
#endif
|
||||
|
||||
#include <string>
|
||||
|
||||
//---------------------------
|
||||
// Compilation settings
|
||||
//
|
||||
|
||||
// Sometimes it may be desired to use the same method names as used by C++ STL.
|
||||
// This may for example reduce time when converting code from script to C++ or
|
||||
// back.
|
||||
//
|
||||
// 0 = off
|
||||
// 1 = on
|
||||
#ifndef AS_USE_STLNAMES
|
||||
#define AS_USE_STLNAMES 0
|
||||
#endif
|
||||
|
||||
// Some prefer to use property accessors to get/set the length of the string
|
||||
// This option registers the accessors instead of the method length()
|
||||
#ifndef AS_USE_ACCESSORS
|
||||
#define AS_USE_ACCESSORS 0
|
||||
#endif
|
||||
|
||||
BEGIN_AS_NAMESPACE
|
||||
|
||||
void RegisterStdString(asIScriptEngine *engine);
|
||||
void RegisterStdStringUtils(asIScriptEngine *engine);
|
||||
|
||||
END_AS_NAMESPACE
|
||||
|
||||
#endif
|
||||
129
add_on/scriptstdstring/scriptstdstring_utils.cpp
Normal file
129
add_on/scriptstdstring/scriptstdstring_utils.cpp
Normal file
@@ -0,0 +1,129 @@
|
||||
#include <assert.h>
|
||||
#include "scriptstdstring.h"
|
||||
#include "../scriptarray/scriptarray.h"
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
using namespace std;
|
||||
|
||||
BEGIN_AS_NAMESPACE
|
||||
|
||||
// This function takes an input string and splits it into parts by looking
|
||||
// for a specified delimiter. Example:
|
||||
//
|
||||
// string str = "A|B||D";
|
||||
// array<string>@ array = str.split("|");
|
||||
//
|
||||
// The resulting array has the following elements:
|
||||
//
|
||||
// {"A", "B", "", "D"}
|
||||
//
|
||||
// AngelScript signature:
|
||||
// array<string>@ string::split(const string &in delim) const
|
||||
static CScriptArray *StringSplit(const string &delim, const string &str)
|
||||
{
|
||||
// Obtain a pointer to the engine
|
||||
asIScriptContext *ctx = asGetActiveContext();
|
||||
asIScriptEngine *engine = ctx->GetEngine();
|
||||
|
||||
// TODO: This should only be done once
|
||||
// TODO: This assumes that CScriptArray was already registered
|
||||
asITypeInfo *arrayType = engine->GetTypeInfoByDecl("array<string>");
|
||||
|
||||
// Create the array object
|
||||
CScriptArray *array = CScriptArray::Create(arrayType);
|
||||
|
||||
// Find the existence of the delimiter in the input string
|
||||
int pos = 0, prev = 0, count = 0;
|
||||
while( (pos = (int)str.find(delim, prev)) != (int)string::npos )
|
||||
{
|
||||
// Add the part to the array
|
||||
array->Resize(array->GetSize()+1);
|
||||
((string*)array->At(count))->assign(&str[prev], pos-prev);
|
||||
|
||||
// Find the next part
|
||||
count++;
|
||||
prev = pos + (int)delim.length();
|
||||
}
|
||||
|
||||
// Add the remaining part
|
||||
array->Resize(array->GetSize()+1);
|
||||
((string*)array->At(count))->assign(&str[prev]);
|
||||
|
||||
return array;
|
||||
}
|
||||
|
||||
static void StringSplit_Generic(asIScriptGeneric *gen)
|
||||
{
|
||||
// Get the arguments
|
||||
string *str = (string*)gen->GetObject();
|
||||
string *delim = *(string**)gen->GetAddressOfArg(0);
|
||||
|
||||
// Return the array by handle
|
||||
*(CScriptArray**)gen->GetAddressOfReturnLocation() = StringSplit(*delim, *str);
|
||||
}
|
||||
|
||||
|
||||
|
||||
// This function takes as input an array of string handles as well as a
|
||||
// delimiter and concatenates the array elements into one delimited string.
|
||||
// Example:
|
||||
//
|
||||
// array<string> array = {"A", "B", "", "D"};
|
||||
// string str = join(array, "|");
|
||||
//
|
||||
// The resulting string is:
|
||||
//
|
||||
// "A|B||D"
|
||||
//
|
||||
// AngelScript signature:
|
||||
// string join(const array<string> &in array, const string &in delim)
|
||||
static string StringJoin(const CScriptArray &array, const string &delim)
|
||||
{
|
||||
// Create the new string
|
||||
string str = "";
|
||||
if( array.GetSize() )
|
||||
{
|
||||
int n;
|
||||
for( n = 0; n < (int)array.GetSize() - 1; n++ )
|
||||
{
|
||||
str += *(string*)array.At(n);
|
||||
str += delim;
|
||||
}
|
||||
|
||||
// Add the last part
|
||||
str += *(string*)array.At(n);
|
||||
}
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
static void StringJoin_Generic(asIScriptGeneric *gen)
|
||||
{
|
||||
// Get the arguments
|
||||
CScriptArray *array = *(CScriptArray**)gen->GetAddressOfArg(0);
|
||||
string *delim = *(string**)gen->GetAddressOfArg(1);
|
||||
|
||||
// Return the string
|
||||
new(gen->GetAddressOfReturnLocation()) string(StringJoin(*array, *delim));
|
||||
}
|
||||
|
||||
// This is where the utility functions are registered.
|
||||
// The string type must have been registered first.
|
||||
void RegisterStdStringUtils(asIScriptEngine *engine)
|
||||
{
|
||||
int r;
|
||||
|
||||
if( strstr(asGetLibraryOptions(), "AS_MAX_PORTABILITY") )
|
||||
{
|
||||
r = engine->RegisterObjectMethod("string", "array<string>@ split(const string &in) const", asFUNCTION(StringSplit_Generic), asCALL_GENERIC); assert(r >= 0);
|
||||
r = engine->RegisterGlobalFunction("string join(const array<string> &in, const string &in)", asFUNCTION(StringJoin_Generic), asCALL_GENERIC); assert(r >= 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
r = engine->RegisterObjectMethod("string", "array<string>@ split(const string &in) const", asFUNCTION(StringSplit), asCALL_CDECL_OBJLAST); assert(r >= 0);
|
||||
r = engine->RegisterGlobalFunction("string join(const array<string> &in, const string &in)", asFUNCTION(StringJoin), asCALL_CDECL); assert(r >= 0);
|
||||
}
|
||||
}
|
||||
|
||||
END_AS_NAMESPACE
|
||||
548
add_on/serializer/serializer.cpp
Normal file
548
add_on/serializer/serializer.cpp
Normal file
@@ -0,0 +1,548 @@
|
||||
//
|
||||
// CSerializer
|
||||
//
|
||||
// This code was based on the CScriptReloader written by FDsagizi
|
||||
// http://www.gamedev.net/topic/604890-dynamic-reloading-script/
|
||||
//
|
||||
|
||||
#include <assert.h>
|
||||
#include <string.h> // strstr
|
||||
#include <stdio.h> // sprintf
|
||||
#include "serializer.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
BEGIN_AS_NAMESPACE
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
CSerializer::CSerializer()
|
||||
{
|
||||
m_engine = 0;
|
||||
}
|
||||
|
||||
CSerializer::~CSerializer()
|
||||
{
|
||||
// Extra objects need to be released, since they are not stored in
|
||||
// the module and we cannot rely on the application releasing them
|
||||
for( size_t i = 0; i < m_extraObjects.size(); i++ )
|
||||
{
|
||||
SExtraObject &o = m_extraObjects[i];
|
||||
for( size_t i2 = 0; i2 < m_root.m_children.size(); i2++ )
|
||||
{
|
||||
if( m_root.m_children[i2]->m_originalPtr == o.originalObject && m_root.m_children[i2]->m_restorePtr )
|
||||
reinterpret_cast<asIScriptObject*>(m_root.m_children[i2]->m_restorePtr)->Release();
|
||||
}
|
||||
}
|
||||
|
||||
// Clean the serialized values before we remove the user types
|
||||
m_root.Uninit();
|
||||
|
||||
// Delete the user types
|
||||
std::map<std::string, CUserType*>::iterator it;
|
||||
for( it = m_userTypes.begin(); it != m_userTypes.end(); it++ )
|
||||
delete it->second;
|
||||
|
||||
if( m_engine )
|
||||
m_engine->Release();
|
||||
}
|
||||
|
||||
void CSerializer::AddUserType(CUserType *ref, const std::string &name)
|
||||
{
|
||||
m_userTypes[name] = ref;
|
||||
}
|
||||
|
||||
int CSerializer::Store(asIScriptModule *mod)
|
||||
{
|
||||
m_mod = mod;
|
||||
|
||||
// The engine must not be destroyed before we're completed, so we'll hold on to a reference
|
||||
mod->GetEngine()->AddRef();
|
||||
if( m_engine ) m_engine->Release();
|
||||
m_engine = mod->GetEngine();
|
||||
|
||||
m_root.m_serializer = this;
|
||||
|
||||
// First store global variables
|
||||
asUINT i;
|
||||
for( i = 0; i < mod->GetGlobalVarCount(); i++ )
|
||||
{
|
||||
const char *name, *nameSpace;
|
||||
int typeId;
|
||||
mod->GetGlobalVar(i, &name, &nameSpace, &typeId);
|
||||
m_root.m_children.push_back(new CSerializedValue(&m_root, name, nameSpace, mod->GetAddressOfGlobalVar(i), typeId));
|
||||
}
|
||||
|
||||
// Second store extra objects
|
||||
for( i = 0; i < m_extraObjects.size(); i++ )
|
||||
m_root.m_children.push_back(new CSerializedValue(&m_root, "", "", m_extraObjects[i].originalObject, m_extraObjects[i].originalTypeId));
|
||||
|
||||
// For the handles that were stored, we need to substitute the stored pointer
|
||||
// that is still pointing to the original object to an internal reference so
|
||||
// it can be restored later on.
|
||||
m_root.ReplaceHandles();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Retrieve all global variables after reload script.
|
||||
int CSerializer::Restore(asIScriptModule *mod)
|
||||
{
|
||||
m_mod = mod;
|
||||
|
||||
// The engine must not be destroyed before we're completed, so we'll hold on to a reference
|
||||
mod->GetEngine()->AddRef();
|
||||
if( m_engine ) m_engine->Release();
|
||||
m_engine = mod->GetEngine();
|
||||
|
||||
// First restore extra objects, i.e. the ones that are not directly seen from the module's global variables
|
||||
asUINT i;
|
||||
for( i = 0; i < m_extraObjects.size(); i++ )
|
||||
{
|
||||
SExtraObject &o = m_extraObjects[i];
|
||||
asITypeInfo *type = m_mod->GetTypeInfoByName( o.originalClassName.c_str() );
|
||||
if( type )
|
||||
{
|
||||
for( size_t i2 = 0; i2 < m_root.m_children.size(); i2++ )
|
||||
{
|
||||
if( m_root.m_children[i2]->m_originalPtr == o.originalObject )
|
||||
{
|
||||
// Create a new script object, but don't call its constructor as we will initialize the members.
|
||||
// Calling the constructor may have unwanted side effects if for example the constructor changes
|
||||
// any outside entities, such as setting global variables to point to new objects, etc.
|
||||
void *newPtr = m_engine->CreateUninitializedScriptObject( type );
|
||||
m_root.m_children[i2]->Restore( newPtr, type->GetTypeId() );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Second restore the global variables
|
||||
asUINT varCount = mod->GetGlobalVarCount();
|
||||
for( i = 0; i < varCount; i++ )
|
||||
{
|
||||
const char *name, *nameSpace;
|
||||
int typeId;
|
||||
mod->GetGlobalVar(i, &name, &nameSpace, &typeId);
|
||||
|
||||
CSerializedValue *v = m_root.FindByName(name, nameSpace);
|
||||
if( v )
|
||||
v->Restore(mod->GetAddressOfGlobalVar(i), typeId);
|
||||
}
|
||||
|
||||
// The handles that were restored needs to be
|
||||
// updated to point to their final objects.
|
||||
m_root.RestoreHandles();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void *CSerializer::GetPointerToRestoredObject(void *ptr)
|
||||
{
|
||||
return m_root.GetPointerToRestoredObject( ptr );
|
||||
}
|
||||
|
||||
void CSerializer::AddExtraObjectToStore( asIScriptObject *object )
|
||||
{
|
||||
if( !object )
|
||||
return;
|
||||
|
||||
// Check if the object hasn't been included already
|
||||
for( size_t i=0; i < m_extraObjects.size(); i++ )
|
||||
if( m_extraObjects[i].originalObject == object )
|
||||
return;
|
||||
|
||||
SExtraObject o;
|
||||
o.originalObject = object;
|
||||
o.originalClassName = object->GetObjectType()->GetName();
|
||||
o.originalTypeId = object->GetTypeId();
|
||||
|
||||
m_extraObjects.push_back( o );
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
CSerializedValue::CSerializedValue()
|
||||
{
|
||||
Init();
|
||||
}
|
||||
|
||||
CSerializedValue::CSerializedValue(CSerializedValue *parent, const std::string &name, const std::string &nameSpace, void *ref, int typeId)
|
||||
{
|
||||
Init();
|
||||
|
||||
m_name = name;
|
||||
m_nameSpace = nameSpace;
|
||||
m_serializer = parent->m_serializer;
|
||||
Store(ref, typeId);
|
||||
}
|
||||
|
||||
void CSerializedValue::Init()
|
||||
{
|
||||
m_handlePtr = 0;
|
||||
m_restorePtr = 0;
|
||||
m_typeId = 0;
|
||||
m_isInit = false;
|
||||
m_serializer = 0;
|
||||
m_userData = 0;
|
||||
m_originalPtr = 0;
|
||||
}
|
||||
|
||||
void CSerializedValue::Uninit()
|
||||
{
|
||||
m_isInit = false;
|
||||
|
||||
ClearChildren();
|
||||
|
||||
if( m_userData )
|
||||
{
|
||||
CUserType *type = m_serializer->m_userTypes[m_typeName];
|
||||
if( type )
|
||||
type->CleanupUserData(this);
|
||||
m_userData = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void CSerializedValue::ClearChildren()
|
||||
{
|
||||
// If this value is for an object handle that created an object during the restore
|
||||
// then it is necessary to release the handle here, so we won't get a memory leak
|
||||
if( (m_typeId & asTYPEID_OBJHANDLE) && m_children.size() == 1 && m_children[0]->m_restorePtr )
|
||||
{
|
||||
m_serializer->m_engine->ReleaseScriptObject(m_children[0]->m_restorePtr, m_serializer->m_engine->GetTypeInfoById(m_children[0]->m_typeId));
|
||||
}
|
||||
|
||||
for( size_t n = 0; n < m_children.size(); n++ )
|
||||
delete m_children[n];
|
||||
m_children.clear();
|
||||
}
|
||||
|
||||
CSerializedValue::~CSerializedValue()
|
||||
{
|
||||
Uninit();
|
||||
}
|
||||
|
||||
CSerializedValue *CSerializedValue::FindByName(const std::string &name, const std::string &nameSpace)
|
||||
{
|
||||
for( size_t i = 0; i < m_children.size(); i++ )
|
||||
if( m_children[i]->m_name == name &&
|
||||
m_children[i]->m_nameSpace == nameSpace )
|
||||
return m_children[i];
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void CSerializedValue::GetAllPointersOfChildren(std::vector<void*> *ptrs)
|
||||
{
|
||||
ptrs->push_back(m_originalPtr);
|
||||
|
||||
for( size_t i = 0; i < m_children.size(); ++i )
|
||||
m_children[i]->GetAllPointersOfChildren(ptrs);
|
||||
}
|
||||
|
||||
CSerializedValue *CSerializedValue::FindByPtr(void *ptr)
|
||||
{
|
||||
if( m_originalPtr == ptr )
|
||||
return this;
|
||||
|
||||
for( size_t i = 0; i < m_children.size(); i++ )
|
||||
{
|
||||
CSerializedValue *find = m_children[i]->FindByPtr(ptr);
|
||||
if( find )
|
||||
return find;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void *CSerializedValue::GetPointerToRestoredObject(void *ptr)
|
||||
{
|
||||
if( m_originalPtr == ptr )
|
||||
return m_restorePtr;
|
||||
|
||||
for( size_t i = 0; i < m_children.size(); ++i )
|
||||
{
|
||||
void *ret = m_children[i]->GetPointerToRestoredObject(ptr);
|
||||
if( ret )
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// find variable by ptr but looking only at those in the references, which will create a new object
|
||||
CSerializedValue *CSerializedValue::FindByPtrInHandles(void *ptr)
|
||||
{
|
||||
// if this handle created object
|
||||
if( (m_typeId & asTYPEID_OBJHANDLE) && m_children.size() == 1 )
|
||||
{
|
||||
if( m_children[0]->m_originalPtr == ptr )
|
||||
return this;
|
||||
}
|
||||
|
||||
if( !(m_typeId & asTYPEID_OBJHANDLE) )
|
||||
{
|
||||
for( size_t i = 0; i < m_children.size(); i++ )
|
||||
{
|
||||
CSerializedValue *find = m_children[i]->FindByPtrInHandles(ptr);
|
||||
if( find )
|
||||
return find;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void CSerializedValue::Store(void *ref, int typeId)
|
||||
{
|
||||
m_isInit = true;
|
||||
SetType(typeId);
|
||||
m_originalPtr = ref;
|
||||
|
||||
if( m_typeId & asTYPEID_OBJHANDLE )
|
||||
{
|
||||
m_handlePtr = *(void**)ref;
|
||||
}
|
||||
else if( m_typeId & asTYPEID_SCRIPTOBJECT )
|
||||
{
|
||||
asIScriptObject *obj = (asIScriptObject *)ref;
|
||||
asITypeInfo *type = obj->GetObjectType();
|
||||
SetType(type->GetTypeId());
|
||||
|
||||
// Store children
|
||||
for( asUINT i = 0; i < type->GetPropertyCount(); i++ )
|
||||
{
|
||||
int childId;
|
||||
const char *childName;
|
||||
type->GetProperty(i, &childName, &childId);
|
||||
|
||||
m_children.push_back(new CSerializedValue(this, childName, "", obj->GetAddressOfProperty(i), childId));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
int size = m_serializer->m_engine->GetSizeOfPrimitiveType(m_typeId);
|
||||
|
||||
if( size == 0 )
|
||||
{
|
||||
// if it is user type( string, array, etc ... )
|
||||
if( m_serializer->m_userTypes[m_typeName] )
|
||||
m_serializer->m_userTypes[m_typeName]->Store(this, m_originalPtr);
|
||||
else
|
||||
{
|
||||
// POD-types can be stored without need for user type
|
||||
asITypeInfo *type = GetType();
|
||||
if( type && (type->GetFlags() & asOBJ_POD) )
|
||||
size = GetType()->GetSize();
|
||||
|
||||
// It is not necessary to report an error here if it is not a POD-type as that will be done when restoring
|
||||
}
|
||||
}
|
||||
|
||||
if( size )
|
||||
{
|
||||
m_mem.resize(size);
|
||||
memcpy(&m_mem[0], ref, size);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CSerializedValue::Restore(void *ref, int typeId)
|
||||
{
|
||||
if( !this || !m_isInit || !ref )
|
||||
return;
|
||||
|
||||
// Verify that the stored type matched the new type of the value being restored
|
||||
if( typeId <= asTYPEID_DOUBLE && typeId != m_typeId ) return; // TODO: We may try to do a type conversion for primitives
|
||||
if( (typeId & ~asTYPEID_MASK_SEQNBR) ^ (m_typeId & ~asTYPEID_MASK_SEQNBR) ) return;
|
||||
asITypeInfo *type = m_serializer->m_engine->GetTypeInfoById(typeId);
|
||||
if( type && m_typeName != type->GetName() ) return;
|
||||
|
||||
// Set the new pointer and type
|
||||
m_restorePtr = ref;
|
||||
SetType(typeId);
|
||||
|
||||
// Restore the value
|
||||
if( m_typeId & asTYPEID_OBJHANDLE )
|
||||
{
|
||||
// if need create objects
|
||||
if( m_children.size() == 1 )
|
||||
{
|
||||
asITypeInfo *ctype = m_children[0]->GetType();
|
||||
|
||||
if( ctype->GetFactoryCount() == 0 )
|
||||
{
|
||||
// There are no factories, so assume the same pointer is going to be used
|
||||
m_children[0]->m_restorePtr = m_handlePtr;
|
||||
|
||||
// Increase the refCount for the object as it will be released upon clean-up
|
||||
m_serializer->m_engine->AddRefScriptObject(m_handlePtr, ctype);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Create a new script object, but don't call its constructor as we will initialize the members.
|
||||
// Calling the constructor may have unwanted side effects if for example the constructor changes
|
||||
// any outside entities, such as setting global variables to point to new objects, etc.
|
||||
void *newObject = m_serializer->m_engine->CreateUninitializedScriptObject(ctype);
|
||||
m_children[0]->Restore(newObject, ctype->GetTypeId());
|
||||
}
|
||||
}
|
||||
}
|
||||
else if( m_typeId & asTYPEID_SCRIPTOBJECT )
|
||||
{
|
||||
asIScriptObject *obj = (asIScriptObject *)ref;
|
||||
|
||||
// Retrieve children
|
||||
for( asUINT i = 0; i < type->GetPropertyCount() ; i++ )
|
||||
{
|
||||
const char *nameProperty;
|
||||
int ptypeId;
|
||||
type->GetProperty(i, &nameProperty, &ptypeId);
|
||||
|
||||
CSerializedValue *var = FindByName(nameProperty, "");
|
||||
if( var )
|
||||
var->Restore(obj->GetAddressOfProperty(i), ptypeId);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if( m_mem.size() )
|
||||
{
|
||||
// POD values can be restored with direct copy
|
||||
memcpy(ref, &m_mem[0], m_mem.size());
|
||||
}
|
||||
else if( m_serializer->m_userTypes[m_typeName] )
|
||||
{
|
||||
// user type restore
|
||||
m_serializer->m_userTypes[m_typeName]->Restore(this, m_restorePtr);
|
||||
}
|
||||
else
|
||||
{
|
||||
std::string str = "Cannot restore type '";
|
||||
str += type->GetName();
|
||||
str += "'";
|
||||
m_serializer->m_engine->WriteMessage("", 0, 0, asMSGTYPE_ERROR, str.c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CSerializedValue::CancelDuplicates(CSerializedValue *from)
|
||||
{
|
||||
std::vector<void*> ptrs;
|
||||
from->GetAllPointersOfChildren(&ptrs);
|
||||
|
||||
for( size_t i = 0; i < ptrs.size(); ++i )
|
||||
{
|
||||
CSerializedValue *find = m_serializer->m_root.FindByPtrInHandles(ptrs[i]);
|
||||
|
||||
while( find )
|
||||
{
|
||||
// cancel create object
|
||||
find->ClearChildren();
|
||||
|
||||
// Find next link to this ptr
|
||||
find = m_serializer->m_root.FindByPtrInHandles(ptrs[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CSerializedValue::ReplaceHandles()
|
||||
{
|
||||
if( m_handlePtr )
|
||||
{
|
||||
// Find the object that the handle is referring to
|
||||
CSerializedValue *handle_to = m_serializer->m_root.FindByPtr(m_handlePtr);
|
||||
|
||||
// If the object hasn't been stored yet...
|
||||
if( handle_to == 0 )
|
||||
{
|
||||
// Store the object now
|
||||
asITypeInfo *type = GetType();
|
||||
CSerializedValue *need_create = new CSerializedValue(this, m_name, m_nameSpace, m_handlePtr, type->GetTypeId());
|
||||
|
||||
// Make sure all other handles that point to the same object
|
||||
// are updated, so we don't end up creating duplicates
|
||||
CancelDuplicates(need_create);
|
||||
|
||||
m_children.push_back(need_create);
|
||||
}
|
||||
}
|
||||
|
||||
// Replace the handles in the children too
|
||||
for( size_t i = 0; i < m_children.size(); ++i )
|
||||
m_children[i]->ReplaceHandles();
|
||||
}
|
||||
|
||||
void CSerializedValue::RestoreHandles()
|
||||
{
|
||||
if( m_typeId & asTYPEID_OBJHANDLE )
|
||||
{
|
||||
if( m_handlePtr )
|
||||
{
|
||||
// Find the object the handle is supposed to point to
|
||||
CSerializedValue *handleTo = m_serializer->m_root.FindByPtr(m_handlePtr);
|
||||
|
||||
if( m_restorePtr && handleTo && handleTo->m_restorePtr )
|
||||
{
|
||||
asITypeInfo *type = m_serializer->m_engine->GetTypeInfoById(m_typeId);
|
||||
|
||||
// If the handle is already pointing to something it must be released first
|
||||
if( *(void**)m_restorePtr )
|
||||
m_serializer->m_engine->ReleaseScriptObject(*(void**)m_restorePtr, type);
|
||||
|
||||
// Update the internal pointer
|
||||
*(void**)m_restorePtr = handleTo->m_restorePtr;
|
||||
|
||||
// Increase the reference
|
||||
m_serializer->m_engine->AddRefScriptObject(handleTo->m_restorePtr, type);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// If the handle is pointing to something, we must release it to restore the null pointer
|
||||
if( m_restorePtr && *(void**)m_restorePtr )
|
||||
{
|
||||
m_serializer->m_engine->ReleaseScriptObject(*(void**)m_restorePtr, m_serializer->m_engine->GetTypeInfoById(m_typeId));
|
||||
*(void**)m_restorePtr = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Do the same for the children
|
||||
for( size_t i = 0; i < m_children.size(); ++i )
|
||||
m_children[i]->RestoreHandles();
|
||||
}
|
||||
|
||||
void CSerializedValue::SetType(int typeId)
|
||||
{
|
||||
m_typeId = typeId;
|
||||
|
||||
asITypeInfo *type = m_serializer->m_engine->GetTypeInfoById(typeId);
|
||||
|
||||
if( type )
|
||||
m_typeName = type->GetName();
|
||||
}
|
||||
|
||||
asITypeInfo *CSerializedValue::GetType()
|
||||
{
|
||||
if( !m_typeName.empty() )
|
||||
{
|
||||
int newTypeId = m_serializer->m_mod->GetTypeIdByDecl(m_typeName.c_str());
|
||||
return m_serializer->m_engine->GetTypeInfoById(newTypeId);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void CSerializedValue::SetUserData(void *data)
|
||||
{
|
||||
m_userData = data;
|
||||
}
|
||||
|
||||
void *CSerializedValue::GetUserData()
|
||||
{
|
||||
return m_userData;
|
||||
}
|
||||
|
||||
END_AS_NAMESPACE
|
||||
193
add_on/serializer/serializer.h
Normal file
193
add_on/serializer/serializer.h
Normal file
@@ -0,0 +1,193 @@
|
||||
//
|
||||
// CSerializer
|
||||
//
|
||||
// This code was based on the CScriptReloader written by FDsagizi
|
||||
// http://www.gamedev.net/topic/604890-dynamic-reloading-script/
|
||||
//
|
||||
|
||||
#ifndef SERIALIZER_H
|
||||
#define SERIALIZER_H
|
||||
|
||||
#ifndef ANGELSCRIPT_H
|
||||
// Avoid having to inform include path if header is already include before
|
||||
#include <angelscript.h>
|
||||
#endif
|
||||
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <map>
|
||||
|
||||
BEGIN_AS_NAMESPACE
|
||||
|
||||
class CSerializer;
|
||||
class CSerializedValue;
|
||||
|
||||
// Need for register user types objects
|
||||
// string, any, array... for all object
|
||||
// user ref type.
|
||||
struct CUserType
|
||||
{
|
||||
virtual ~CUserType() {};
|
||||
virtual void Store(CSerializedValue *val, void *ptr) = 0;
|
||||
virtual void Restore(CSerializedValue *val, void *ptr) = 0;
|
||||
virtual void CleanupUserData(CSerializedValue * /*val*/) {}
|
||||
};
|
||||
|
||||
|
||||
class CSerializedValue
|
||||
{
|
||||
public:
|
||||
CSerializedValue();
|
||||
CSerializedValue(CSerializedValue *parent, const std::string &name, const std::string &nameSpace, void *ref, int typeId);
|
||||
~CSerializedValue();
|
||||
|
||||
// Save the object and its children
|
||||
void Store(void *ref, int refTypeId);
|
||||
|
||||
// Restore the object and its children
|
||||
void Restore(void *ref, int refTypeId);
|
||||
|
||||
// Set type of this var
|
||||
void SetType(int typeId);
|
||||
|
||||
// Returns the object type for non-primitives
|
||||
asITypeInfo *GetType();
|
||||
|
||||
// Get child by name variable
|
||||
CSerializedValue *FindByName(const std::string &name, const std::string &nameSpace);
|
||||
|
||||
// Find variable by ptr
|
||||
CSerializedValue *FindByPtr(void *ptr);
|
||||
|
||||
// User data
|
||||
void *GetUserData();
|
||||
void SetUserData(void *data);
|
||||
|
||||
// Children, e.g. properties of a script class, or elements
|
||||
// of an array, or object pointed to by a handle unless it
|
||||
// is already a variable)
|
||||
std::vector<CSerializedValue*> m_children;
|
||||
|
||||
protected:
|
||||
friend class CSerializer;
|
||||
|
||||
void Init();
|
||||
void Uninit();
|
||||
|
||||
// you first need to save all the objects before you can save references to objects
|
||||
void ReplaceHandles();
|
||||
|
||||
// After the objects has been restored, the handles needs to
|
||||
// be updated to point to the right objects
|
||||
void RestoreHandles();
|
||||
|
||||
// Recursively get all ptrs of the children
|
||||
void GetAllPointersOfChildren(std::vector<void*> *ptrs);
|
||||
|
||||
// may be that the two references refer to the same variable.
|
||||
// But this variable is not available in the global list.
|
||||
// According to this reference will be restores it.
|
||||
// And so two links are not created 2 variables,
|
||||
// it is necessary to cancel the creation of one of them.
|
||||
void CancelDuplicates(CSerializedValue *from);
|
||||
|
||||
// Find variable by ptr but looking only at those in the references, which will create a new object
|
||||
CSerializedValue *FindByPtrInHandles(void *ptr);
|
||||
|
||||
// ptr - is a handle to class
|
||||
void *GetPointerToRestoredObject(void *ptr);
|
||||
|
||||
// Cleanup children
|
||||
void ClearChildren();
|
||||
|
||||
// The serializer object
|
||||
CSerializer *m_serializer;
|
||||
|
||||
// The user data can be used by CUserType to store extra information
|
||||
void *m_userData;
|
||||
|
||||
// The type id of the stored value
|
||||
int m_typeId;
|
||||
|
||||
// For non-primitives the typeId may change if the module is reloaded so
|
||||
// it is necessary to store the type name to determine the new type id
|
||||
std::string m_typeName;
|
||||
|
||||
// Name of variable or property
|
||||
std::string m_name;
|
||||
std::string m_nameSpace;
|
||||
|
||||
// Is initialized
|
||||
bool m_isInit;
|
||||
|
||||
// 'this' pointer to variable.
|
||||
// While storing, this points to the actual variable that was stored.
|
||||
// While restoring, it is just a unique identifier.
|
||||
void *m_originalPtr;
|
||||
|
||||
// where handle references
|
||||
// While storing, this points to the actual object.
|
||||
// While restoring, it is just a unique identifier.
|
||||
void *m_handlePtr;
|
||||
|
||||
// new address object, ie address the restoration
|
||||
// While storing this isn't used.
|
||||
// While restoring it will point to the actual variable/object that is restored.
|
||||
void *m_restorePtr;
|
||||
|
||||
// Serialized data for primitives
|
||||
std::vector<char> m_mem;
|
||||
};
|
||||
|
||||
|
||||
// This class keeps a list of variables, then restores them after the script is rebuilt.
|
||||
// But you have to be careful with the change of signature in classes, or
|
||||
// changing the types of objects. You can remove or add variables, functions,
|
||||
// methods, but you can not (yet) change the type of variables.
|
||||
//
|
||||
// You also need to understand that after a rebuild you should get
|
||||
// new functions and typeids from the module.
|
||||
class CSerializer
|
||||
{
|
||||
public:
|
||||
CSerializer();
|
||||
~CSerializer();
|
||||
|
||||
// Add implementation for serializing user types
|
||||
void AddUserType(CUserType *ref, const std::string &name);
|
||||
|
||||
// Store all global variables in the module
|
||||
int Store(asIScriptModule *mod);
|
||||
|
||||
// Restore all global variables after reloading script
|
||||
int Restore(asIScriptModule *mod);
|
||||
|
||||
// Store extra objects that are not seen from the module's global variables
|
||||
void AddExtraObjectToStore(asIScriptObject *object);
|
||||
|
||||
// Return new pointer to restored object
|
||||
void *GetPointerToRestoredObject(void *originalObject);
|
||||
|
||||
protected:
|
||||
friend class CSerializedValue;
|
||||
|
||||
CSerializedValue m_root;
|
||||
asIScriptEngine *m_engine;
|
||||
asIScriptModule *m_mod;
|
||||
|
||||
std::map<std::string, CUserType*> m_userTypes;
|
||||
|
||||
struct SExtraObject
|
||||
{
|
||||
asIScriptObject *originalObject;
|
||||
std::string originalClassName;
|
||||
int originalTypeId;
|
||||
};
|
||||
|
||||
std::vector<SExtraObject> m_extraObjects;
|
||||
};
|
||||
|
||||
|
||||
END_AS_NAMESPACE
|
||||
|
||||
#endif
|
||||
377
add_on/weakref/weakref.cpp
Normal file
377
add_on/weakref/weakref.cpp
Normal file
@@ -0,0 +1,377 @@
|
||||
|
||||
// The CScriptWeakRef class was originally implemented by vroad in March 2013
|
||||
|
||||
#include "weakref.h"
|
||||
#include <new>
|
||||
#include <assert.h>
|
||||
#include <string.h> // strstr()
|
||||
|
||||
BEGIN_AS_NAMESPACE
|
||||
|
||||
static void ScriptWeakRefConstruct(asITypeInfo *type, void *mem)
|
||||
{
|
||||
new(mem) CScriptWeakRef(type);
|
||||
}
|
||||
|
||||
static void ScriptWeakRefConstruct2(asITypeInfo *type, void *ref, void *mem)
|
||||
{
|
||||
new(mem) CScriptWeakRef(ref, type);
|
||||
|
||||
// It's possible the constructor raised a script exception, in which case we
|
||||
// need to call the destructor in order to cleanup the memory before returning
|
||||
asIScriptContext *ctx = asGetActiveContext();
|
||||
if( ctx && ctx->GetState() == asEXECUTION_EXCEPTION )
|
||||
reinterpret_cast<CScriptWeakRef*>(mem)->~CScriptWeakRef();
|
||||
}
|
||||
|
||||
static void ScriptWeakRefDestruct(CScriptWeakRef *obj)
|
||||
{
|
||||
obj->~CScriptWeakRef();
|
||||
}
|
||||
|
||||
static bool ScriptWeakRefTemplateCallback(asITypeInfo *ti, bool &/*dontGarbageCollect*/)
|
||||
{
|
||||
asITypeInfo *subType = ti->GetSubType();
|
||||
|
||||
// Weak references only work for reference types
|
||||
if( subType == 0 ) return false;
|
||||
if( !(subType->GetFlags() & asOBJ_REF) ) return false;
|
||||
|
||||
// The subtype shouldn't be a handle
|
||||
if( ti->GetSubTypeId() & asTYPEID_OBJHANDLE )
|
||||
return false;
|
||||
|
||||
// Make sure the type really supports weak references
|
||||
asUINT cnt = subType->GetBehaviourCount();
|
||||
for( asUINT n = 0; n < cnt; n++ )
|
||||
{
|
||||
asEBehaviours beh;
|
||||
subType->GetBehaviourByIndex(n, &beh);
|
||||
if( beh == asBEHAVE_GET_WEAKREF_FLAG )
|
||||
return true;
|
||||
}
|
||||
|
||||
ti->GetEngine()->WriteMessage("weakref", 0, 0, asMSGTYPE_ERROR, "The subtype doesn't support weak references");
|
||||
return false;
|
||||
}
|
||||
|
||||
CScriptWeakRef::CScriptWeakRef(asITypeInfo *type)
|
||||
{
|
||||
m_ref = 0;
|
||||
m_type = type;
|
||||
m_type->AddRef();
|
||||
m_weakRefFlag = 0;
|
||||
}
|
||||
|
||||
CScriptWeakRef::CScriptWeakRef(const CScriptWeakRef &other)
|
||||
{
|
||||
m_ref = other.m_ref;
|
||||
m_type = other.m_type;
|
||||
m_type->AddRef();
|
||||
m_weakRefFlag = other.m_weakRefFlag;
|
||||
if( m_weakRefFlag )
|
||||
m_weakRefFlag->AddRef();
|
||||
}
|
||||
|
||||
CScriptWeakRef::CScriptWeakRef(void *ref, asITypeInfo *type)
|
||||
{
|
||||
m_ref = ref;
|
||||
m_type = type;
|
||||
m_type->AddRef();
|
||||
|
||||
// The given type should be the weakref template instance
|
||||
assert( strcmp(type->GetName(), "weakref") == 0 ||
|
||||
strcmp(type->GetName(), "const_weakref") == 0 );
|
||||
|
||||
// Get the shared flag that will tell us when the object has been destroyed
|
||||
// This is threadsafe as we hold a strong reference to the object
|
||||
m_weakRefFlag = m_type->GetEngine()->GetWeakRefFlagOfScriptObject(m_ref, m_type->GetSubType());
|
||||
if( m_weakRefFlag )
|
||||
m_weakRefFlag->AddRef();
|
||||
}
|
||||
|
||||
CScriptWeakRef::~CScriptWeakRef()
|
||||
{
|
||||
if( m_type )
|
||||
m_type->Release();
|
||||
if( m_weakRefFlag )
|
||||
m_weakRefFlag->Release();
|
||||
}
|
||||
|
||||
CScriptWeakRef &CScriptWeakRef::operator =(const CScriptWeakRef &other)
|
||||
{
|
||||
// Don't do anything if it is the same reference
|
||||
// It is not enough to verify only the reference to the object, as the
|
||||
// address may be reused by another instance after the first has been freed.
|
||||
// By checking also the weakRefFlag we can be certain that it is the same
|
||||
// instance.
|
||||
if( m_ref == other.m_ref &&
|
||||
m_weakRefFlag == other.m_weakRefFlag )
|
||||
return *this;
|
||||
|
||||
// Must not allow changing the type
|
||||
if( m_type != other.m_type )
|
||||
{
|
||||
// We can allow a weakref to be assigned to a const_weakref
|
||||
if( !(strcmp(m_type->GetName(), "const_weakref") == 0 &&
|
||||
strcmp(other.m_type->GetName(), "weakref") == 0 &&
|
||||
m_type->GetSubType() == other.m_type->GetSubType()) )
|
||||
{
|
||||
assert( false );
|
||||
return *this;
|
||||
}
|
||||
}
|
||||
|
||||
m_ref = other.m_ref;
|
||||
|
||||
if( m_weakRefFlag )
|
||||
m_weakRefFlag->Release();
|
||||
m_weakRefFlag = other.m_weakRefFlag;
|
||||
if( m_weakRefFlag )
|
||||
m_weakRefFlag->AddRef();
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
CScriptWeakRef &CScriptWeakRef::Set(void *newRef)
|
||||
{
|
||||
// Release the previous weak ref
|
||||
if( m_weakRefFlag )
|
||||
m_weakRefFlag->Release();
|
||||
|
||||
// Retrieve the new weak ref
|
||||
m_ref = newRef;
|
||||
if( newRef )
|
||||
{
|
||||
m_weakRefFlag = m_type->GetEngine()->GetWeakRefFlagOfScriptObject(newRef, m_type->GetSubType());
|
||||
m_weakRefFlag->AddRef();
|
||||
}
|
||||
else
|
||||
m_weakRefFlag = 0;
|
||||
|
||||
// Release the newRef since we're only supposed to hold a weakref
|
||||
m_type->GetEngine()->ReleaseScriptObject(newRef, m_type->GetSubType());
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
asITypeInfo *CScriptWeakRef::GetRefType() const
|
||||
{
|
||||
return m_type->GetSubType();
|
||||
}
|
||||
|
||||
bool CScriptWeakRef::operator==(const CScriptWeakRef &o) const
|
||||
{
|
||||
// It is not enough to compare just the address of the object, as it may
|
||||
// be reused by another instance after the first has been freed. By verifying
|
||||
// also the weakRefFlag we can guarantee that it is indeed the same instance.
|
||||
if( m_ref == o.m_ref &&
|
||||
m_weakRefFlag == o.m_weakRefFlag &&
|
||||
m_type == o.m_type )
|
||||
return true;
|
||||
|
||||
// TODO: If type is not the same, we should attempt to do a dynamic cast,
|
||||
// which may change the pointer for application registered classes
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool CScriptWeakRef::operator!=(const CScriptWeakRef &o) const
|
||||
{
|
||||
return !(*this == o);
|
||||
}
|
||||
|
||||
// AngelScript: used as '@obj = ref.get();'
|
||||
void *CScriptWeakRef::Get() const
|
||||
{
|
||||
// If we hold a null handle, then just return null
|
||||
if( m_ref == 0 || m_weakRefFlag == 0 )
|
||||
return 0;
|
||||
|
||||
// Lock on the shared bool, so we can be certain it won't be changed to true
|
||||
// between the inspection of the flag and the increase of the ref count in the
|
||||
// owning object.
|
||||
m_weakRefFlag->Lock();
|
||||
if( !m_weakRefFlag->Get() )
|
||||
{
|
||||
m_type->GetEngine()->AddRefScriptObject(m_ref, m_type->GetSubType());
|
||||
m_weakRefFlag->Unlock();
|
||||
return m_ref;
|
||||
}
|
||||
m_weakRefFlag->Unlock();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool CScriptWeakRef::Equals(void *ref) const
|
||||
{
|
||||
if( m_ref != ref )
|
||||
return false;
|
||||
|
||||
// It is not enough to compare just the address, as another instance may
|
||||
// get the same address after the first instance has been freed. Verify the
|
||||
// weakref flag too to make sure it is the same instance
|
||||
asILockableSharedBool *flag = m_type->GetEngine()->GetWeakRefFlagOfScriptObject(ref, m_type->GetSubType());
|
||||
if (m_weakRefFlag != flag)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void RegisterScriptWeakRef_Native(asIScriptEngine *engine)
|
||||
{
|
||||
int r;
|
||||
|
||||
// Register a type for non-const handles
|
||||
r = engine->RegisterObjectType("weakref<class T>", sizeof(CScriptWeakRef), asOBJ_VALUE | asOBJ_ASHANDLE | asOBJ_TEMPLATE | asOBJ_APP_CLASS_DAK); assert( r >= 0 );
|
||||
|
||||
r = engine->RegisterObjectBehaviour("weakref<T>", asBEHAVE_CONSTRUCT, "void f(int&in)", asFUNCTION(ScriptWeakRefConstruct), asCALL_CDECL_OBJLAST); assert( r>= 0 );
|
||||
r = engine->RegisterObjectBehaviour("weakref<T>", asBEHAVE_CONSTRUCT, "void f(int&in, T@+)", asFUNCTION(ScriptWeakRefConstruct2), asCALL_CDECL_OBJLAST); assert( r>= 0 );
|
||||
r = engine->RegisterObjectBehaviour("weakref<T>", asBEHAVE_DESTRUCT, "void f()", asFUNCTION(ScriptWeakRefDestruct), asCALL_CDECL_OBJLAST); assert( r >= 0 );
|
||||
r = engine->RegisterObjectBehaviour("weakref<T>", asBEHAVE_TEMPLATE_CALLBACK, "bool f(int&in, bool&out)", asFUNCTION(ScriptWeakRefTemplateCallback), asCALL_CDECL); assert( r >= 0 );
|
||||
|
||||
r = engine->RegisterObjectMethod("weakref<T>", "T@ opImplCast()", asMETHOD(CScriptWeakRef, Get), asCALL_THISCALL); assert(r >= 0);
|
||||
r = engine->RegisterObjectMethod("weakref<T>", "T@ get() const", asMETHODPR(CScriptWeakRef, Get, () const, void*), asCALL_THISCALL); assert( r >= 0 );
|
||||
r = engine->RegisterObjectMethod("weakref<T>", "weakref<T> &opHndlAssign(const weakref<T> &in)", asMETHOD(CScriptWeakRef, operator=), asCALL_THISCALL); assert( r >= 0 );
|
||||
r = engine->RegisterObjectMethod("weakref<T>", "weakref<T> &opAssign(const weakref<T> &in)", asMETHOD(CScriptWeakRef, operator=), asCALL_THISCALL); assert(r >= 0);
|
||||
r = engine->RegisterObjectMethod("weakref<T>", "bool opEquals(const weakref<T> &in) const", asMETHODPR(CScriptWeakRef, operator==, (const CScriptWeakRef &) const, bool), asCALL_THISCALL); assert( r >= 0 );
|
||||
r = engine->RegisterObjectMethod("weakref<T>", "weakref<T> &opHndlAssign(T@)", asMETHOD(CScriptWeakRef, Set), asCALL_THISCALL); assert( r >= 0 );
|
||||
r = engine->RegisterObjectMethod("weakref<T>", "bool opEquals(const T@+) const", asMETHOD(CScriptWeakRef, Equals), asCALL_THISCALL); assert(r >= 0);
|
||||
|
||||
// Register another type for const handles
|
||||
r = engine->RegisterObjectType("const_weakref<class T>", sizeof(CScriptWeakRef), asOBJ_VALUE | asOBJ_ASHANDLE | asOBJ_TEMPLATE | asOBJ_APP_CLASS_DAK); assert( r >= 0 );
|
||||
|
||||
r = engine->RegisterObjectBehaviour("const_weakref<T>", asBEHAVE_CONSTRUCT, "void f(int&in)", asFUNCTION(ScriptWeakRefConstruct), asCALL_CDECL_OBJLAST); assert( r>= 0 );
|
||||
r = engine->RegisterObjectBehaviour("const_weakref<T>", asBEHAVE_CONSTRUCT, "void f(int&in, const T@+)", asFUNCTION(ScriptWeakRefConstruct2), asCALL_CDECL_OBJLAST); assert( r>= 0 );
|
||||
r = engine->RegisterObjectBehaviour("const_weakref<T>", asBEHAVE_DESTRUCT, "void f()", asFUNCTION(ScriptWeakRefDestruct), asCALL_CDECL_OBJLAST); assert( r >= 0 );
|
||||
r = engine->RegisterObjectBehaviour("const_weakref<T>", asBEHAVE_TEMPLATE_CALLBACK, "bool f(int&in, bool&out)", asFUNCTION(ScriptWeakRefTemplateCallback), asCALL_CDECL); assert( r >= 0 );
|
||||
|
||||
r = engine->RegisterObjectMethod("const_weakref<T>", "const T@ opImplCast() const", asMETHOD(CScriptWeakRef, Get), asCALL_THISCALL); assert(r >= 0);
|
||||
r = engine->RegisterObjectMethod("const_weakref<T>", "const T@ get() const", asMETHODPR(CScriptWeakRef, Get, () const, void*), asCALL_THISCALL); assert( r >= 0 );
|
||||
r = engine->RegisterObjectMethod("const_weakref<T>", "const_weakref<T> &opHndlAssign(const const_weakref<T> &in)", asMETHOD(CScriptWeakRef, operator=), asCALL_THISCALL); assert( r >= 0 );
|
||||
r = engine->RegisterObjectMethod("const_weakref<T>", "const_weakref<T> &opAssign(const const_weakref<T> &in)", asMETHOD(CScriptWeakRef, operator=), asCALL_THISCALL); assert(r >= 0);
|
||||
r = engine->RegisterObjectMethod("const_weakref<T>", "bool opEquals(const const_weakref<T> &in) const", asMETHODPR(CScriptWeakRef, operator==, (const CScriptWeakRef &) const, bool), asCALL_THISCALL); assert( r >= 0 );
|
||||
r = engine->RegisterObjectMethod("const_weakref<T>", "const_weakref<T> &opHndlAssign(const T@)", asMETHOD(CScriptWeakRef, Set), asCALL_THISCALL); assert( r >= 0 );
|
||||
r = engine->RegisterObjectMethod("const_weakref<T>", "bool opEquals(const T@+) const", asMETHOD(CScriptWeakRef, Equals), asCALL_THISCALL); assert(r >= 0);
|
||||
|
||||
// Allow non-const weak references to be converted to const weak references
|
||||
r = engine->RegisterObjectMethod("const_weakref<T>", "const_weakref<T> &opHndlAssign(const weakref<T> &in)", asMETHOD(CScriptWeakRef, operator=), asCALL_THISCALL); assert( r >= 0 );
|
||||
r = engine->RegisterObjectMethod("const_weakref<T>", "bool opEquals(const weakref<T> &in) const", asMETHODPR(CScriptWeakRef, operator==, (const CScriptWeakRef &) const, bool), asCALL_THISCALL); assert( r >= 0 );
|
||||
}
|
||||
|
||||
static void ScriptWeakRefConstruct_Generic(asIScriptGeneric *gen)
|
||||
{
|
||||
asITypeInfo *ti = *reinterpret_cast<asITypeInfo**>(gen->GetAddressOfArg(0));
|
||||
|
||||
ScriptWeakRefConstruct(ti, gen->GetObject());
|
||||
}
|
||||
|
||||
static void ScriptWeakRefConstruct2_Generic(asIScriptGeneric *gen)
|
||||
{
|
||||
asITypeInfo *ti = *reinterpret_cast<asITypeInfo**>(gen->GetAddressOfArg(0));
|
||||
void *ref = gen->GetArgAddress(1);
|
||||
|
||||
ScriptWeakRefConstruct2(ti, ref, gen->GetObject());
|
||||
}
|
||||
|
||||
static void ScriptWeakRefDestruct_Generic(asIScriptGeneric *gen)
|
||||
{
|
||||
CScriptWeakRef *self = reinterpret_cast<CScriptWeakRef*>(gen->GetObject());
|
||||
self->~CScriptWeakRef();
|
||||
}
|
||||
|
||||
void CScriptWeakRef_Get_Generic(asIScriptGeneric *gen)
|
||||
{
|
||||
CScriptWeakRef *self = reinterpret_cast<CScriptWeakRef*>(gen->GetObject());
|
||||
gen->SetReturnAddress(self->Get());
|
||||
}
|
||||
|
||||
void CScriptWeakRef_Assign_Generic(asIScriptGeneric *gen)
|
||||
{
|
||||
CScriptWeakRef *other = reinterpret_cast<CScriptWeakRef*>(gen->GetArgAddress(0));
|
||||
CScriptWeakRef *self = reinterpret_cast<CScriptWeakRef*>(gen->GetObject());
|
||||
*self = *other;
|
||||
gen->SetReturnAddress(self);
|
||||
}
|
||||
|
||||
void CScriptWeakRef_Assign2_Generic(asIScriptGeneric *gen)
|
||||
{
|
||||
void *other = gen->GetArgAddress(0);
|
||||
CScriptWeakRef *self = reinterpret_cast<CScriptWeakRef*>(gen->GetObject());
|
||||
self->Set(other);
|
||||
gen->SetReturnAddress(self);
|
||||
}
|
||||
|
||||
void CScriptWeakRef_Equals_Generic(asIScriptGeneric *gen)
|
||||
{
|
||||
CScriptWeakRef *other = reinterpret_cast<CScriptWeakRef*>(gen->GetArgAddress(0));
|
||||
CScriptWeakRef *self = reinterpret_cast<CScriptWeakRef*>(gen->GetObject());
|
||||
gen->SetReturnByte(*self == *other);
|
||||
}
|
||||
|
||||
void CScriptWeakRef_Equals2_Generic(asIScriptGeneric *gen)
|
||||
{
|
||||
void *other = gen->GetArgAddress(0);
|
||||
CScriptWeakRef *self = reinterpret_cast<CScriptWeakRef*>(gen->GetObject());
|
||||
|
||||
gen->SetReturnByte(self->Equals(other));
|
||||
}
|
||||
|
||||
static void ScriptWeakRefTemplateCallback_Generic(asIScriptGeneric *gen)
|
||||
{
|
||||
asITypeInfo *ti = *reinterpret_cast<asITypeInfo**>(gen->GetAddressOfArg(0));
|
||||
bool *dontGarbageCollect = *reinterpret_cast<bool**>(gen->GetAddressOfArg(1));
|
||||
*reinterpret_cast<bool*>(gen->GetAddressOfReturnLocation()) = ScriptWeakRefTemplateCallback(ti, *dontGarbageCollect);
|
||||
}
|
||||
|
||||
void RegisterScriptWeakRef_Generic(asIScriptEngine *engine)
|
||||
{
|
||||
int r;
|
||||
|
||||
// Register a type for non-const handles
|
||||
r = engine->RegisterObjectType("weakref<class T>", sizeof(CScriptWeakRef), asOBJ_VALUE | asOBJ_ASHANDLE | asOBJ_TEMPLATE | asOBJ_APP_CLASS_DAK); assert( r >= 0 );
|
||||
|
||||
r = engine->RegisterObjectBehaviour("weakref<T>", asBEHAVE_CONSTRUCT, "void f(int&in)", asFUNCTION(ScriptWeakRefConstruct_Generic), asCALL_GENERIC); assert( r>= 0 );
|
||||
r = engine->RegisterObjectBehaviour("weakref<T>", asBEHAVE_CONSTRUCT, "void f(int&in, T@+)", asFUNCTION(ScriptWeakRefConstruct2_Generic), asCALL_GENERIC); assert( r>= 0 );
|
||||
r = engine->RegisterObjectBehaviour("weakref<T>", asBEHAVE_TEMPLATE_CALLBACK, "bool f(int&in, bool&out)", asFUNCTION(ScriptWeakRefTemplateCallback_Generic), asCALL_GENERIC); assert( r >= 0 );
|
||||
r = engine->RegisterObjectBehaviour("weakref<T>", asBEHAVE_DESTRUCT, "void f()", asFUNCTION(ScriptWeakRefDestruct_Generic), asCALL_GENERIC); assert( r >= 0 );
|
||||
|
||||
r = engine->RegisterObjectMethod("weakref<T>", "T@ opImplCast()", asFUNCTION(CScriptWeakRef_Get_Generic), asCALL_GENERIC); assert(r >= 0);
|
||||
r = engine->RegisterObjectMethod("weakref<T>", "T@ get() const", asFUNCTION(CScriptWeakRef_Get_Generic), asCALL_GENERIC); assert( r >= 0 );
|
||||
r = engine->RegisterObjectMethod("weakref<T>", "weakref<T> &opHndlAssign(const weakref<T> &in)", asFUNCTION(CScriptWeakRef_Assign_Generic), asCALL_GENERIC); assert( r >= 0 );
|
||||
r = engine->RegisterObjectMethod("weakref<T>", "weakref<T> &opAssign(const weakref<T> &in)", asFUNCTION(CScriptWeakRef_Assign_Generic), asCALL_GENERIC); assert(r >= 0);
|
||||
r = engine->RegisterObjectMethod("weakref<T>", "bool opEquals(const weakref<T> &in) const", asFUNCTION(CScriptWeakRef_Equals_Generic), asCALL_GENERIC); assert( r >= 0 );
|
||||
r = engine->RegisterObjectMethod("weakref<T>", "weakref<T> &opHndlAssign(T@)", asFUNCTION(CScriptWeakRef_Assign2_Generic), asCALL_GENERIC); assert( r >= 0 );
|
||||
r = engine->RegisterObjectMethod("weakref<T>", "bool opEquals(const T@+) const", asFUNCTION(CScriptWeakRef_Equals2_Generic), asCALL_GENERIC); assert(r >= 0);
|
||||
|
||||
// Register another type for const handles
|
||||
r = engine->RegisterObjectType("const_weakref<class T>", sizeof(CScriptWeakRef), asOBJ_VALUE | asOBJ_ASHANDLE | asOBJ_TEMPLATE | asOBJ_APP_CLASS_DAK); assert( r >= 0 );
|
||||
|
||||
r = engine->RegisterObjectBehaviour("const_weakref<T>", asBEHAVE_CONSTRUCT, "void f(int&in)", asFUNCTION(ScriptWeakRefConstruct_Generic), asCALL_GENERIC); assert( r>= 0 );
|
||||
r = engine->RegisterObjectBehaviour("const_weakref<T>", asBEHAVE_CONSTRUCT, "void f(int&in, const T@+)", asFUNCTION(ScriptWeakRefConstruct2_Generic), asCALL_GENERIC); assert( r>= 0 );
|
||||
r = engine->RegisterObjectBehaviour("const_weakref<T>", asBEHAVE_TEMPLATE_CALLBACK, "bool f(int&in, bool&out)", asFUNCTION(ScriptWeakRefTemplateCallback_Generic), asCALL_GENERIC); assert( r >= 0 );
|
||||
r = engine->RegisterObjectBehaviour("const_weakref<T>", asBEHAVE_DESTRUCT, "void f()", asFUNCTION(ScriptWeakRefDestruct_Generic), asCALL_GENERIC); assert( r >= 0 );
|
||||
|
||||
r = engine->RegisterObjectMethod("const_weakref<T>", "const T@ opImplCast() const", asFUNCTION(CScriptWeakRef_Get_Generic), asCALL_GENERIC); assert(r >= 0);
|
||||
r = engine->RegisterObjectMethod("const_weakref<T>", "const T@ get() const", asFUNCTION(CScriptWeakRef_Get_Generic), asCALL_GENERIC); assert( r >= 0 );
|
||||
r = engine->RegisterObjectMethod("const_weakref<T>", "const_weakref<T> &opHndlAssign(const const_weakref<T> &in)", asFUNCTION(CScriptWeakRef_Assign_Generic), asCALL_GENERIC); assert( r >= 0 );
|
||||
r = engine->RegisterObjectMethod("const_weakref<T>", "const_weakref<T> &opAssign(const const_weakref<T> &in)", asFUNCTION(CScriptWeakRef_Assign_Generic), asCALL_GENERIC); assert(r >= 0);
|
||||
r = engine->RegisterObjectMethod("const_weakref<T>", "bool opEquals(const const_weakref<T> &in) const", asFUNCTION(CScriptWeakRef_Equals_Generic), asCALL_GENERIC); assert( r >= 0 );
|
||||
r = engine->RegisterObjectMethod("const_weakref<T>", "const_weakref<T> &opHndlAssign(const T@)", asFUNCTION(CScriptWeakRef_Assign2_Generic), asCALL_GENERIC); assert( r >= 0 );
|
||||
r = engine->RegisterObjectMethod("const_weakref<T>", "bool opEquals(const T@+) const", asFUNCTION(CScriptWeakRef_Equals2_Generic), asCALL_GENERIC); assert(r >= 0);
|
||||
|
||||
// Allow non-const weak references to be converted to const weak references
|
||||
r = engine->RegisterObjectMethod("const_weakref<T>", "const_weakref<T> &opHndlAssign(const weakref<T> &in)", asFUNCTION(CScriptWeakRef_Assign_Generic), asCALL_GENERIC); assert( r >= 0 );
|
||||
r = engine->RegisterObjectMethod("const_weakref<T>", "bool opEquals(const weakref<T> &in) const", asFUNCTION(CScriptWeakRef_Equals_Generic), asCALL_GENERIC); assert( r >= 0 );
|
||||
}
|
||||
|
||||
void RegisterScriptWeakRef(asIScriptEngine *engine)
|
||||
{
|
||||
if( strstr(asGetLibraryOptions(), "AS_MAX_PORTABILITY") )
|
||||
RegisterScriptWeakRef_Generic(engine);
|
||||
else
|
||||
RegisterScriptWeakRef_Native(engine);
|
||||
}
|
||||
|
||||
|
||||
END_AS_NAMESPACE
|
||||
58
add_on/weakref/weakref.h
Normal file
58
add_on/weakref/weakref.h
Normal file
@@ -0,0 +1,58 @@
|
||||
#ifndef SCRIPTWEAKREF_H
|
||||
#define SCRIPTWEAKREF_H
|
||||
|
||||
// The CScriptWeakRef class was originally implemented by vroad in March 2013
|
||||
|
||||
#ifndef ANGELSCRIPT_H
|
||||
// Avoid having to inform include path if header is already include before
|
||||
#include <angelscript.h>
|
||||
#endif
|
||||
|
||||
|
||||
BEGIN_AS_NAMESPACE
|
||||
|
||||
class CScriptWeakRef
|
||||
{
|
||||
public:
|
||||
// Constructors
|
||||
CScriptWeakRef(asITypeInfo *type);
|
||||
CScriptWeakRef(const CScriptWeakRef &other);
|
||||
CScriptWeakRef(void *ref, asITypeInfo *type);
|
||||
|
||||
~CScriptWeakRef();
|
||||
|
||||
// Copy the stored value from another weakref object
|
||||
CScriptWeakRef &operator=(const CScriptWeakRef &other);
|
||||
|
||||
// Compare equalness
|
||||
bool operator==(const CScriptWeakRef &o) const;
|
||||
bool operator!=(const CScriptWeakRef &o) const;
|
||||
|
||||
// Sets a new reference
|
||||
CScriptWeakRef &Set(void *newRef);
|
||||
|
||||
// Returns the object if it is still alive
|
||||
// This will increment the refCount of the returned object
|
||||
void *Get() const;
|
||||
|
||||
// Returns true if the contained reference is the same
|
||||
bool Equals(void *ref) const;
|
||||
|
||||
// Returns the type of the reference held
|
||||
asITypeInfo *GetRefType() const;
|
||||
|
||||
protected:
|
||||
// These functions need to have access to protected
|
||||
// members in order to call them from the script engine
|
||||
friend void RegisterScriptWeakRef_Native(asIScriptEngine *engine);
|
||||
|
||||
void *m_ref;
|
||||
asITypeInfo *m_type;
|
||||
asILockableSharedBool *m_weakRefFlag;
|
||||
};
|
||||
|
||||
void RegisterScriptWeakRef(asIScriptEngine *engine);
|
||||
|
||||
END_AS_NAMESPACE
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user