From a3dd7506d377b1434f913bd65c019eed52b64b6e Mon Sep 17 00:00:00 2001 From: truebrain Date: Mon, 12 Jan 2009 17:11:45 +0000 Subject: (svn r15027) -Merge: tomatos and bananas left to be, here is NoAI for all to see. NoAI is an API (a framework) to build your own AIs in. See: http://wiki.openttd.org/wiki/index.php/AI:Main_Page With many thanks to: - glx and Rubidium for their syncing, feedback and hard work - Yexo for his feedback, patches, and AIs which tested the system very deep - Morloth for his feedback and patches - TJIP for hosting a challenge which kept NoAI on track - All AI authors for testing our AI API, and all other people who helped in one way or another -Remove: all old AIs and their cheats/hacks --- src/script/squirrel.cpp | 444 ++++++++++++++++++ src/script/squirrel.hpp | 185 ++++++++ src/script/squirrel_class.hpp | 116 +++++ src/script/squirrel_helper.hpp | 870 ++++++++++++++++++++++++++++++++++++ src/script/squirrel_helper_type.hpp | 13 + src/script/squirrel_std.cpp | 114 +++++ src/script/squirrel_std.hpp | 60 +++ 7 files changed, 1802 insertions(+) create mode 100644 src/script/squirrel.cpp create mode 100644 src/script/squirrel.hpp create mode 100644 src/script/squirrel_class.hpp create mode 100644 src/script/squirrel_helper.hpp create mode 100644 src/script/squirrel_helper_type.hpp create mode 100644 src/script/squirrel_std.cpp create mode 100644 src/script/squirrel_std.hpp (limited to 'src/script') diff --git a/src/script/squirrel.cpp b/src/script/squirrel.cpp new file mode 100644 index 000000000..b75833f65 --- /dev/null +++ b/src/script/squirrel.cpp @@ -0,0 +1,444 @@ +/* $Id$ */ + +/** @file squirrel.cpp the implementation of the Squirrel class. It handles all Squirrel-stuff and gives a nice API back to work with. */ + +#include +#include +#include "../stdafx.h" +#include "../debug.h" +#include "squirrel.hpp" +#include "squirrel_std.hpp" +#include "../fileio_func.h" +#include +#include <../squirrel/sqpcheader.h> +#include <../squirrel/sqvm.h> + +void Squirrel::CompileError(HSQUIRRELVM vm, const SQChar *desc, const SQChar *source, SQInteger line, SQInteger column) +{ + SQChar buf[1024]; + +#ifdef _SQ64 + scsnprintf(buf, lengthof(buf), _SC("Error %s:%ld/%ld: %s"), source, line, column, desc); +#else + scsnprintf(buf, lengthof(buf), _SC("Error %s:%d/%d: %s"), source, line, column, desc); +#endif + + /* Check if we have a custom print function */ + SQPrintFunc *func = ((Squirrel *)sq_getforeignptr(vm))->print_func; + if (func == NULL) { + scfprintf(stderr, _SC("%s"), buf); + } else { + (*func)(true, buf); + } +} + +void Squirrel::ErrorPrintFunc(HSQUIRRELVM vm, const SQChar *s, ...) +{ + va_list arglist; + SQChar buf[1024]; + + va_start(arglist, s); + scvsnprintf(buf, lengthof(buf), s, arglist); + va_end(arglist); + + /* Check if we have a custom print function */ + SQPrintFunc *func = ((Squirrel *)sq_getforeignptr(vm))->print_func; + if (func == NULL) { + scfprintf(stderr, _SC("%s"), buf); + } else { + (*func)(true, buf); + } +} + +void Squirrel::RunError(HSQUIRRELVM vm, const SQChar *error) +{ + /* Set the print function to something that prints to stderr */ + SQPRINTFUNCTION pf = sq_getprintfunc(vm); + sq_setprintfunc(vm, &Squirrel::ErrorPrintFunc); + + /* Check if we have a custom print function */ + SQChar buf[1024]; + scsnprintf(buf, lengthof(buf), _SC("Your script made an error: %s\n"), error); + SQPrintFunc *func = ((Squirrel *)sq_getforeignptr(vm))->print_func; + if (func == NULL) { + scfprintf(stderr, _SC("%s"), buf); + } else { + (*func)(true, buf); + } + + /* Print below the error the stack, so the users knows what is happening */ + sqstd_printcallstack(vm); + /* Reset the old print function */ + sq_setprintfunc(vm, pf); +} + +SQInteger Squirrel::_RunError(HSQUIRRELVM vm) +{ + const SQChar *sErr = 0; + + if (sq_gettop(vm) >= 1) { + if (SQ_SUCCEEDED(sq_getstring(vm, -1, &sErr))) { + Squirrel::RunError(vm, sErr); + return 0; + } + } + + Squirrel::RunError(vm, _SC("unknown error")); + return 0; +} + +void Squirrel::PrintFunc(HSQUIRRELVM vm, const SQChar *s, ...) +{ + va_list arglist; + SQChar buf[1024]; + + va_start(arglist, s); + scvsnprintf(buf, lengthof(buf) - 2, s, arglist); + va_end(arglist); + scstrcat(buf, _SC("\n")); + + /* Check if we have a custom print function */ + SQPrintFunc *func = ((Squirrel *)sq_getforeignptr(vm))->print_func; + if (func == NULL) { + scprintf(_SC("%s"), buf); + } else { + (*func)(false, buf); + } +} + +void Squirrel::AddMethod(const char *method_name, SQFUNCTION proc, uint nparam, const char *params, void *userdata, int size) +{ + sq_pushstring(this->vm, OTTD2FS(method_name), -1); + + if (size != 0) { + void *ptr = sq_newuserdata(vm, size); + memcpy(ptr, userdata, size); + } + + sq_newclosure(this->vm, proc, size != 0 ? 1 : 0); + if (nparam != 0) sq_setparamscheck(this->vm, nparam, OTTD2FS(params)); + sq_setnativeclosurename(this->vm, -1, OTTD2FS(method_name)); + sq_newslot(this->vm, -3, SQFalse); +} + +void Squirrel::AddConst(const char *var_name, int value) +{ + sq_pushstring(this->vm, OTTD2FS(var_name), -1); + sq_pushinteger(this->vm, value); + sq_newslot(this->vm, -3, SQTrue); +} + +void Squirrel::AddClassBegin(const char *class_name) +{ + sq_pushroottable(this->vm); + sq_pushstring(this->vm, OTTD2FS(class_name), -1); + sq_newclass(this->vm, SQFalse); +} + +void Squirrel::AddClassBegin(const char *class_name, const char *parent_class) +{ + sq_pushroottable(this->vm); + sq_pushstring(this->vm, OTTD2FS(class_name), -1); + sq_pushstring(this->vm, OTTD2FS(parent_class), -1); + if (SQ_FAILED(sq_get(this->vm, -3))) { + DEBUG(misc, 0, "[squirrel] Failed to initialize class '%s' based on parent class '%s'", class_name, parent_class); + DEBUG(misc, 0, "[squirrel] Make sure that '%s' exists before trying to define '%s'", parent_class, class_name); + return; + } + sq_newclass(this->vm, SQTrue); +} + +void Squirrel::AddClassEnd() +{ + sq_newslot(vm, -3, SQFalse); + sq_pop(vm, 1); +} + +bool Squirrel::MethodExists(HSQOBJECT instance, const char *method_name) +{ + int top = sq_gettop(this->vm); + /* Go to the instance-root */ + sq_pushobject(this->vm, instance); + /* Find the function-name inside the script */ + sq_pushstring(this->vm, OTTD2FS(method_name), -1); + if (SQ_FAILED(sq_get(this->vm, -2))) { + sq_settop(this->vm, top); + return false; + } + sq_settop(this->vm, top); + return true; +} + +bool Squirrel::Resume(int suspend) +{ + sq_resumecatch(this->vm, suspend); + return this->vm->_suspended != 0; +} + +bool Squirrel::CallMethod(HSQOBJECT instance, const char *method_name, HSQOBJECT *ret, int suspend) +{ + /* Store the current top */ + int top = sq_gettop(this->vm); + /* Go to the instance-root */ + sq_pushobject(this->vm, instance); + /* Find the function-name inside the script */ + sq_pushstring(this->vm, OTTD2FS(method_name), -1); + if (SQ_FAILED(sq_get(this->vm, -2))) { + DEBUG(misc, 0, "[squirrel] Could not find '%s' in the class", method_name); + sq_settop(this->vm, top); + return false; + } + /* Call the method */ + sq_pushobject(this->vm, instance); + sq_call(this->vm, 1, ret == NULL ? SQFalse : SQTrue, SQTrue, suspend); + if (ret != NULL) sq_getstackobj(vm, -1, ret); + /* Reset the top */ + sq_settop(this->vm, top); + + return this->vm->_suspended != 0; +} + +/* static */ bool Squirrel::CreateClassInstanceVM(HSQUIRRELVM vm, const char *class_name, void *real_instance, HSQOBJECT *instance, SQRELEASEHOOK release_hook) +{ + int oldtop = sq_gettop(vm); + + /* First, find the class */ + sq_pushroottable(vm); + sq_pushstring(vm, OTTD2FS(class_name), -1); + if (SQ_FAILED(sq_get(vm, -2))) { + DEBUG(misc, 0, "[squirrel] Failed to find class by the name '%s'", class_name); + sq_settop(vm, oldtop); + return false; + } + + /* Create the instance */ + if (SQ_FAILED(sq_createinstance(vm, -1))) { + DEBUG(misc, 0, "[squirrel] Failed to create instance for class '%s'", class_name); + sq_settop(vm, oldtop); + return false; + } + + if (instance != NULL) { + /* Find our instance */ + sq_getstackobj(vm, -1, instance); + /* Add a reference to it, so it survives for ever */ + sq_addref(vm, instance); + } + sq_remove(vm, -2); // Class-name + sq_remove(vm, -2); // Root-table + + /* Store it in the class */ + sq_setinstanceup(vm, -1, real_instance); + if (release_hook != NULL) sq_setreleasehook(vm, -1, release_hook); + + if (instance != NULL) sq_settop(vm, oldtop); + + return true; +} + +bool Squirrel::CreateClassInstance(const char *class_name, void *real_instance, HSQOBJECT *instance) +{ + return Squirrel::CreateClassInstanceVM(this->vm, class_name, real_instance, instance, NULL); +} + +Squirrel::Squirrel() +{ + this->vm = sq_open(1024); + this->print_func = NULL; + this->global_pointer = NULL; + + /* Handle compile-errors ourself, so we can display it nicely */ + sq_setcompilererrorhandler(this->vm, &Squirrel::CompileError); + sq_notifyallexceptions(this->vm, SQTrue); + /* Set a good print-function */ + sq_setprintfunc(this->vm, &Squirrel::PrintFunc); + /* Handle runtime-errors ourself, so we can display it nicely */ + sq_newclosure(this->vm, &Squirrel::_RunError, 0); + sq_seterrorhandler(this->vm); + + /* Set the foreigh pointer, so we can always find this instance from within the VM */ + sq_setforeignptr(this->vm, this); + + sq_pushroottable(this->vm); + squirrel_register_global_std(this); +} + +class SQFile { +private: + FILE *file; + size_t size; + size_t pos; + +public: + SQFile(FILE *file, size_t size) : file(file), size(size), pos(0) {} + + size_t Read(void *buf, size_t elemsize, size_t count) + { + assert(elemsize != 0); + if (this->pos + (elemsize * count) > this->size) { + count = (this->size - this->pos) / elemsize; + } + if (count == 0) return 0; + size_t ret = fread(buf, elemsize, count, this->file); + this->pos += ret * elemsize; + return ret; + } +}; + +static SQInteger _io_file_lexfeed_ASCII(SQUserPointer file) +{ + char c; + if (((SQFile *)file)->Read(&c, sizeof(c), 1) > 0) return c; + return 0; +} + +static SQInteger _io_file_lexfeed_UTF8(SQUserPointer file) +{ + static const SQInteger utf8_lengths[16] = + { + 1, 1, 1, 1, 1, 1, 1, 1, /* 0000 to 0111 : 1 byte (plain ASCII) */ + 0, 0, 0, 0, /* 1000 to 1011 : not valid */ + 2, 2, /* 1100, 1101 : 2 bytes */ + 3, /* 1110 : 3 bytes */ + 4 /* 1111 : 4 bytes */ + }; + static unsigned char byte_masks[5] = {0, 0, 0x1F, 0x0F, 0x07}; + unsigned char inchar; + SQInteger c = 0; + if (((SQFile *)file)->Read(&inchar, sizeof(inchar), 1) != 1) return 0; + c = inchar; + + if (c >= 0x80) { + SQInteger tmp; + SQInteger codelen = utf8_lengths[c >> 4]; + if (codelen == 0) return 0; + + tmp = c & byte_masks[codelen]; + for (SQInteger n = 0; n < codelen - 1; n++) { + tmp <<= 6; + if (((SQFile *)file)->Read(&inchar, sizeof(inchar), 1) != 1) return 0; + tmp |= inchar & 0x3F; + } + c = tmp; + } + return c; +} + +static SQInteger _io_file_lexfeed_UCS2_LE(SQUserPointer file) +{ + wchar_t c; + if (((SQFile *)file)->Read(&c, sizeof(c), 1) > 0) return (SQChar)c; + return 0; +} + +static SQInteger _io_file_lexfeed_UCS2_BE(SQUserPointer file) +{ + unsigned short c; + if (((SQFile *)file)->Read(&c, sizeof(c), 1) > 0) { + c = ((c >> 8) & 0x00FF)| ((c << 8) & 0xFF00); + return (SQChar)c; + } + return 0; +} + +static SQInteger _io_file_read(SQUserPointer file, SQUserPointer buf, SQInteger size) +{ + SQInteger ret = ((SQFile *)file)->Read(buf, 1, size); + if (ret == 0) return -1; + return ret; +} + +/* static */ SQRESULT Squirrel::LoadFile(HSQUIRRELVM vm, const char *filename, SQBool printerror) +{ + size_t size; + FILE *file = FioFOpenFile(filename, "rb", AI_DIR, &size); + SQInteger ret; + unsigned short us; + unsigned char uc; + SQLEXREADFUNC func; + + if (file != NULL) { + SQFile f(file, size); + ret = fread(&us, 1, sizeof(us), file); + /* Most likely an empty file */ + if (ret != 2) us = 0; + + switch (us) { + case SQ_BYTECODE_STREAM_TAG: { // BYTECODE + fseek(file, -2, SEEK_CUR); + if (SQ_SUCCEEDED(sq_readclosure(vm, _io_file_read, &f))) { + FioFCloseFile(file); + return SQ_OK; + } + FioFCloseFile(file); + return sq_throwerror(vm, _SC("Couldn't read bytecode")); + } + case 0xFFFE: func = _io_file_lexfeed_UCS2_BE; break; // UTF-16 little endian + case 0xFEFF: func = _io_file_lexfeed_UCS2_LE; break; // UTF-16 big endian + case 0xBBEF: // UTF-8 + if (fread(&uc, 1, sizeof(uc), file) == 0) { + FioFCloseFile(file); + return sq_throwerror(vm, _SC("I/O error")); + } + if (uc != 0xBF) { + FioFCloseFile(file); + return sq_throwerror(vm, _SC("Unrecognized encoding")); + } + func = _io_file_lexfeed_UTF8; + break; + default: func = _io_file_lexfeed_ASCII; fseek(file, -2, SEEK_CUR); break; // ASCII + } + + if (SQ_SUCCEEDED(sq_compile(vm, func, &f, OTTD2FS(filename), printerror))) { + FioFCloseFile(file); + return SQ_OK; + } + FioFCloseFile(file); + return SQ_ERROR; + } + return sq_throwerror(vm, _SC("cannot open the file")); +} + +/* static */ bool Squirrel::LoadScript(HSQUIRRELVM vm, const char *script, bool in_root) +{ + /* Make sure we are always in the root-table */ + if (in_root) sq_pushroottable(vm); + + /* Load and run the script */ + if (SQ_SUCCEEDED(LoadFile(vm, script, SQTrue))) { + sq_push(vm, -2); + if (SQ_SUCCEEDED(sq_call(vm, 1, SQFalse, SQTrue))) { + sq_pop(vm, 1); + return true; + } + } + + DEBUG(misc, 0, "[squirrel] Failed to compile '%s'", script); + return false; +} + +bool Squirrel::LoadScript(const char *script) +{ + return LoadScript(this->vm, script); +} + +Squirrel::~Squirrel() +{ + /* Clean up the stuff */ + sq_pop(this->vm, 1); + sq_close(this->vm); +} + +void Squirrel::InsertResult(bool result) +{ + sq_pushbool(this->vm, result); + vm->GetAt(vm->_stackbase + vm->_suspended_target) = vm->GetUp(-1); + vm->Pop(); +} + +void Squirrel::InsertResult(int result) +{ + sq_pushinteger(this->vm, result); + vm->GetAt(vm->_stackbase + vm->_suspended_target) = vm->GetUp(-1); + vm->Pop(); +} diff --git a/src/script/squirrel.hpp b/src/script/squirrel.hpp new file mode 100644 index 000000000..bd9a10cfc --- /dev/null +++ b/src/script/squirrel.hpp @@ -0,0 +1,185 @@ +/* $Id$ */ + +/** @file squirrel.hpp defines the Squirrel class */ + +#ifndef SQUIRREL_HPP +#define SQUIRREL_HPP + +class Squirrel { +private: + typedef void (SQPrintFunc)(bool error_msg, const SQChar *message); + + HSQUIRRELVM vm; ///< The VirtualMachine instnace for squirrel + void *global_pointer; ///< Can be set by who ever initializes Squirrel + SQPrintFunc *print_func; ///< Points to either NULL, or a custom print handler + + /** + * The internal RunError handler. It looks up the real error and calls RunError with it. + */ + static SQInteger _RunError(HSQUIRRELVM vm); + + /** + * Get the squirrel VM. Try to avoid using this. + */ + HSQUIRRELVM GetVM() { return this->vm; } + +protected: + /** + * The CompileError handler. + */ + static void CompileError(HSQUIRRELVM vm, const SQChar *desc, const SQChar *source, SQInteger line, SQInteger column); + + /** + * The RunError handler. + */ + static void RunError(HSQUIRRELVM vm, const SQChar *error); + + /** + * If a user runs 'print' inside a script, this function gets the params. + */ + static void PrintFunc(HSQUIRRELVM vm, const SQChar *s, ...); + + /** + * If an error has to be print, this function is called. + */ + static void ErrorPrintFunc(HSQUIRRELVM vm, const SQChar *s, ...); + +public: + friend class AIController; + friend class AIScanner; + friend class AIInstance; + + Squirrel(); + ~Squirrel(); + + /** + * Load a script. + * @param script The full script-name to load. + * @return False if loading failed. + */ + bool LoadScript(const char *script); + static bool LoadScript(HSQUIRRELVM vm, const char *script, bool in_root = true); + + /** + * Load a file to a given VM. + */ + static SQRESULT LoadFile(HSQUIRRELVM vm, const char *filename, SQBool printerror); + + /** + * Adds a function to the stack. Depending on the current state this means + * either a method or a global function. + */ + void AddMethod(const char *method_name, SQFUNCTION proc, uint nparam = 0, const char *params = NULL, void *userdata = NULL, int size = 0); + + /** + * Adds a const to the stack. Depending on the current state this means + * either a const to a class or to the global space. + */ + void AddConst(const char *var_name, int value); + + /** + * Adds a class to the global scope. Make sure to call AddClassEnd when you + * are done adding methods. + */ + void AddClassBegin(const char *class_name); + + /** + * Adds a class to the global scope, extending 'parent_class'. + * Make sure to call AddClassEnd when you are done adding methods. + */ + void AddClassBegin(const char *class_name, const char *parent_class); + + /** + * Finishes adding a class to the global scope. If this isn't called, no + * class is really created. + */ + void AddClassEnd(); + + /** + * Resume a VM when it was suspended via a throw. + */ + bool Resume(int suspend = -1); + + void InsertResult(bool result); + void InsertResult(int result); + + /** + * Call a method of an instance, in various flavors. + */ + bool CallMethod(HSQOBJECT instance, const char *method_name, HSQOBJECT *ret, int suspend = -1); + bool CallMethod(HSQOBJECT instance, const char *method_name, int suspend = -1) { return this->CallMethod(instance, method_name, NULL, suspend); } + const char *CallStringMethodStrdup(HSQOBJECT instance, const char *method_name, int suspend = -1) { HSQOBJECT ret; this->CallMethod(instance, method_name, &ret, suspend); return strdup(ObjectToString(&ret)); } + int CallIntegerMethod(HSQOBJECT instance, const char *method_name, int suspend = -1) { HSQOBJECT ret; this->CallMethod(instance, method_name, &ret, suspend); return ObjectToInteger(&ret); } + + /** + * Check if a method exists in an instance. + */ + bool MethodExists(HSQOBJECT instance, const char *method_name); + + /** + * Creates a class instance. + * @param class_name The name of the class of which we create an instance. + * @param real_instance The instance to the real class, if it represents a real class. + * @param instance Returning value with the pointer to the instance. + * @param release_hook Optional param to give a release hook. + * @return False if creating failed. + */ + static bool CreateClassInstanceVM(HSQUIRRELVM vm, const char *class_name, void *real_instance, HSQOBJECT *instance, SQRELEASEHOOK release_hook); + + /** + * Exactly the same as CreateClassInstanceVM, only callable without instance of Squirrel. + */ + bool CreateClassInstance(const char *class_name, void *real_instance, HSQOBJECT *instance); + + /** + * Get the real-instance pointer. + * @note This will only work just after a function-call from within Squirrel + * to your C++ function. + */ + static bool GetRealInstance(HSQUIRRELVM vm, SQUserPointer *ptr) { return SQ_SUCCEEDED(sq_getinstanceup(vm, 1, ptr, 0)); } + + /** + * Get the Squirrel-instance pointer. + * @note This will only work just after a function-call from within Squirrel + * to your C++ function. + */ + static bool GetInstance(HSQUIRRELVM vm, HSQOBJECT *ptr, int pos = 1) { sq_getclass(vm, pos); sq_getstackobj(vm, pos, ptr); sq_pop(vm, 1); return true; } + + /** + * Convert a Squirrel-object to a string. + */ + static const char *ObjectToString(HSQOBJECT *ptr) { return FS2OTTD(sq_objtostring(ptr)); } + + /** + * Convert a Squirrel-object to an integer. + */ + static int ObjectToInteger(HSQOBJECT *ptr) { return sq_objtointeger(ptr); } + + /** + * Sets a pointer in the VM that is reachable from where ever you are in SQ. + * Useful to keep track of the main instance. + */ + void SetGlobalPointer(void *ptr) { this->global_pointer = ptr; } + + /** + * Get the pointer as set by SetGlobalPointer. + */ + static void *GetGlobalPointer(HSQUIRRELVM vm) { return ((Squirrel *)sq_getforeignptr(vm))->global_pointer; } + + /** + * Set a custom print function, so you can handle outputs from SQ yourself. + */ + void SetPrintFunction(SQPrintFunc *func) { this->print_func = func; } + + /** + * Throw a Squirrel error that will be nicely displayed to the user. + */ + void ThrowError(const char *error) { sq_throwerror(this->vm, OTTD2FS(error)); } + + /** + * Release a SQ object. + */ + void ReleaseObject(HSQOBJECT *ptr) { sq_release(this->vm, ptr); } +}; + +#endif /* SQUIRREL_HPP */ diff --git a/src/script/squirrel_class.hpp b/src/script/squirrel_class.hpp new file mode 100644 index 000000000..20e838089 --- /dev/null +++ b/src/script/squirrel_class.hpp @@ -0,0 +1,116 @@ +/* $Id$ */ + +/** @file squirrel_class.cpp defines templates for converting C++ classes to Squirrel classes */ + +#ifndef SQUIRREL_CLASS_HPP +#define SQUIRREL_CLASS_HPP + +#if (__GNUC__ == 2) +/* GCC 2.95 doesn't like to have SQConvert::DefSQStaticCallback inside a + * template (it gives an internal error 373). Above that, it doesn't listen + * to 'using namespace' inside a function of a template. So for GCC 2.95 we + * do it in the global space to avoid compiler errors. */ +using namespace SQConvert; +#endif /* __GNUC__ == 2 */ + +/** + * The template to define classes in Squirrel. It takes care of the creation + * and calling of such classes, to make the AI Layer cleaner while having a + * powerful script as possible AI language. + */ +template +class DefSQClass { +private: + const char *classname; + +public: + DefSQClass(const char *_classname) : + classname(_classname) + {} + + /** + * This defines a method inside a class for Squirrel. + */ + template + void DefSQMethod(Squirrel *engine, Func function_proc, const char *function_name) + { + using namespace SQConvert; + engine->AddMethod(function_name, DefSQNonStaticCallback, 0, NULL, &function_proc, sizeof(function_proc)); + } + + /** + * This defines a method inside a class for Squirrel, which has access to the 'engine' (experts only!). + */ + template + void DefSQAdvancedMethod(Squirrel *engine, Func function_proc, const char *function_name) + { + using namespace SQConvert; + engine->AddMethod(function_name, DefSQAdvancedNonStaticCallback, 0, NULL, &function_proc, sizeof(function_proc)); + } + + /** + * This defines a method inside a class for Squirrel with defined params. + * @note If you define nparam, make sure that he first param is always 'x', + * which is the 'this' inside the function. This is hidden from the rest + * of the code, but without it calling your function will fail! + */ + template + void DefSQMethod(Squirrel *engine, Func function_proc, const char *function_name, int nparam, const char *params) + { + using namespace SQConvert; + engine->AddMethod(function_name, DefSQNonStaticCallback, nparam, params, &function_proc, sizeof(function_proc)); + } + + /** + * This defines a static method inside a class for Squirrel. + */ + template + void DefSQStaticMethod(Squirrel *engine, Func function_proc, const char *function_name) + { + using namespace SQConvert; + engine->AddMethod(function_name, DefSQStaticCallback, 0, NULL, &function_proc, sizeof(function_proc)); + } + + /** + * This defines a static method inside a class for Squirrel with defined params. + * @note If you define nparam, make sure that he first param is always 'x', + * which is the 'this' inside the function. This is hidden from the rest + * of the code, but without it calling your function will fail! + */ + template + void DefSQStaticMethod(Squirrel *engine, Func function_proc, const char *function_name, int nparam, const char *params) + { + using namespace SQConvert; + engine->AddMethod(function_name, DefSQStaticCallback, nparam, params, &function_proc, sizeof(function_proc)); + } + + template + void DefSQConst(Squirrel *engine, Var value, const char *var_name) + { + engine->AddConst(var_name, value); + } + + void PreRegister(Squirrel *engine) + { + engine->AddClassBegin(this->classname); + } + + void PreRegister(Squirrel *engine, const char *parent_class) + { + engine->AddClassBegin(this->classname, parent_class); + } + + template + void AddConstructor(Squirrel *engine, const char *params) + { + using namespace SQConvert; + engine->AddMethod("constructor", DefSQConstructorCallback, Tnparam, params); + } + + void PostRegister(Squirrel *engine) + { + engine->AddClassEnd(); + } +}; + +#endif /* SQUIRREL_CLASS_HPP */ diff --git a/src/script/squirrel_helper.hpp b/src/script/squirrel_helper.hpp new file mode 100644 index 000000000..67baf7c51 --- /dev/null +++ b/src/script/squirrel_helper.hpp @@ -0,0 +1,870 @@ +/* $Id$ */ + +/** @file squirrel_helper.hpp declarations and parts of the implementation of the class for convert code */ + +#ifndef SQUIRREL_HELPER_HPP +#define SQUIRREL_HELPER_HPP + +#include +#include "../core/math_func.hpp" +#include "../core/smallvec_type.hpp" +#include "../economy_type.h" +#include "squirrel_helper_type.hpp" + +/** + * The Squirrel convert routines + */ +namespace SQConvert { + /** + * Pointers assigned to this class will be free'd when this instance + * comes out of scope. Useful to make sure you can use strdup(), + * without leaking memory. + */ + struct SQAutoFreePointers : SmallVector { + ~SQAutoFreePointers() + { + for (uint i = 0; i < this->items; i++) free(this->data[i]); + } + }; + + template struct YesT { + static const bool Yes = Y; + static const bool No = !Y; + }; + + /** + * Helper class to recognize if the given type is void. Usage: 'IsVoidT::Yes' + */ + template struct IsVoidT : YesT {}; + template <> struct IsVoidT : YesT {}; + + /** + * Helper class to recognize if the function/method return type is void. + */ + template struct HasVoidReturnT; + /* functions */ + template struct HasVoidReturnT : IsVoidT {}; + template struct HasVoidReturnT : IsVoidT {}; + template struct HasVoidReturnT : IsVoidT {}; + template struct HasVoidReturnT : IsVoidT {}; + template struct HasVoidReturnT : IsVoidT {}; + template struct HasVoidReturnT : IsVoidT {}; + template struct HasVoidReturnT : IsVoidT {}; + /* methods */ + template struct HasVoidReturnT : IsVoidT {}; + template struct HasVoidReturnT : IsVoidT {}; + template struct HasVoidReturnT : IsVoidT {}; + template struct HasVoidReturnT : IsVoidT {}; + template struct HasVoidReturnT : IsVoidT {}; + template struct HasVoidReturnT : IsVoidT {}; + template struct HasVoidReturnT : IsVoidT {}; + + + /** + * Special class to make it possible for the compiler to pick the correct GetParam(). + */ + template class ForceType { }; + + /** + * To return a value to squirrel, we call this function. It converts to the right format. + */ + template static int Return(HSQUIRRELVM vm, T t); + + template <> inline int Return (HSQUIRRELVM vm, uint8 res) { sq_pushinteger(vm, (int32)res); return 1; } + template <> inline int Return (HSQUIRRELVM vm, uint16 res) { sq_pushinteger(vm, (int32)res); return 1; } + template <> inline int Return (HSQUIRRELVM vm, uint32 res) { sq_pushinteger(vm, (int32)res); return 1; } + template <> inline int Return (HSQUIRRELVM vm, int8 res) { sq_pushinteger(vm, res); return 1; } + template <> inline int Return (HSQUIRRELVM vm, int16 res) { sq_pushinteger(vm, res); return 1; } + template <> inline int Return (HSQUIRRELVM vm, int32 res) { sq_pushinteger(vm, res); return 1; } + template <> inline int Return (HSQUIRRELVM vm, int64 res) { sq_pushinteger(vm, ClampToI32(res)); return 1; } + template <> inline int Return (HSQUIRRELVM vm, Money res) { sq_pushinteger(vm, ClampToI32(res)); return 1; } + template <> inline int Return (HSQUIRRELVM vm, bool res) { sq_pushbool (vm, res); return 1; } + template <> inline int Return (HSQUIRRELVM vm, char *res) { if (res == NULL) sq_pushnull(vm); else sq_pushstring (vm, OTTD2FS(res), strlen(res)); free(res); return 1; } + template <> inline int Return(HSQUIRRELVM vm, const char *res) { if (res == NULL) sq_pushnull(vm); else sq_pushstring (vm, OTTD2FS(res), strlen(res)); return 1; } + template <> inline int Return (HSQUIRRELVM vm, void *res) { sq_pushuserpointer(vm, res); return 1; } + + /** + * To get a param from squirrel, we call this function. It converts to the right format. + */ + template static T GetParam(ForceType, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr); + + template <> inline uint8 GetParam(ForceType , HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQInteger tmp; sq_getinteger (vm, index, &tmp); return tmp; } + template <> inline uint16 GetParam(ForceType , HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQInteger tmp; sq_getinteger (vm, index, &tmp); return tmp; } + template <> inline uint32 GetParam(ForceType , HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQInteger tmp; sq_getinteger (vm, index, &tmp); return tmp; } + template <> inline int8 GetParam(ForceType , HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQInteger tmp; sq_getinteger (vm, index, &tmp); return tmp; } + template <> inline int16 GetParam(ForceType , HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQInteger tmp; sq_getinteger (vm, index, &tmp); return tmp; } + template <> inline int32 GetParam(ForceType , HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQInteger tmp; sq_getinteger (vm, index, &tmp); return tmp; } + template <> inline bool GetParam(ForceType , HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQBool tmp; sq_getbool (vm, index, &tmp); return tmp != 0; } + template <> inline const char *GetParam(ForceType, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { const SQChar *tmp; sq_getstring (vm, index, &tmp); char *tmp_str = strdup(FS2OTTD(tmp)); *ptr->Append() = (void *)tmp_str; return tmp_str; } + template <> inline void *GetParam(ForceType , HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer tmp; sq_getuserpointer(vm, index, &tmp); return tmp; } + + template <> inline Array *GetParam(ForceType, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) + { + SQObject obj; + sq_getstackobj(vm, index, &obj); + sq_pushobject(vm, obj); + sq_pushnull(vm); + + SmallVector data; + + while (SQ_SUCCEEDED(sq_next(vm, -2))) { + SQInteger tmp; + if (SQ_SUCCEEDED(sq_getinteger(vm, -1, &tmp))) { + *data.Append() = (int32)tmp; + } else { + sq_pop(vm, 4); + throw sq_throwerror(vm, _SC("a member of an array used as parameter to a function is not numeric")); + } + + sq_pop(vm, 2); + } + sq_pop(vm, 2); + + Array *arr = (Array*)MallocT(sizeof(Array) + sizeof(int32) * data.Length()); + arr->size = data.Length(); + memcpy(arr->array, data.Begin(), sizeof(int32) * data.Length()); + + *ptr->Append() = arr; + return arr; + } + + /** + * Helper class to recognize the function type (retval type, args) and use the proper specialization + * for SQ callback. The partial specializations for the second arg (Tis_void_retval) are not possible + * on the function. Therefore the class is used instead. + */ + template ::Yes> struct HelperT; + + /** + * The real C++ caller for function with return value and 0 params. + */ + template + struct HelperT { + static int SQCall(void *instance, Tretval (*func)(), HSQUIRRELVM vm) + { + return Return(vm, (*func)()); + } + }; + + /** + * The real C++ caller for function with no return value and 0 params. + */ + template + struct HelperT { + static int SQCall(void *instance, Tretval (*func)(), HSQUIRRELVM vm) + { + (*func)(); + return 0; + } + }; + + /** + * The real C++ caller for method with return value and 0 params. + */ + template + struct HelperT { + static int SQCall(Tcls *instance, Tretval (Tcls::*func)(), HSQUIRRELVM vm) + { + return Return(vm, (instance->*func)()); + } + }; + + /** + * The real C++ caller for method with no return value and 0 params. + */ + template + struct HelperT { + static int SQCall(Tcls *instance, Tretval (Tcls::*func)(), HSQUIRRELVM vm) + { + (instance->*func)(); + return 0; + } + + static Tcls *SQConstruct(Tcls *instance, Tretval (Tcls::*func)(), HSQUIRRELVM vm) + { + return new Tcls(); + } + }; + + /** + * The real C++ caller for function with return value and 1 param. + */ + template + struct HelperT { + static int SQCall(void *instance, Tretval (*func)(Targ1), HSQUIRRELVM vm) + { + SQAutoFreePointers ptr; + Tretval ret = (*func)( + GetParam(ForceType(), vm, 2, &ptr) + ); + sq_pop(vm, 1); + return Return(vm, ret); + } + }; + + /** + * The real C++ caller for function with no return value and 1 param. + */ + template + struct HelperT { + static int SQCall(void *instance, Tretval (*func)(Targ1), HSQUIRRELVM vm) + { + SQAutoFreePointers ptr; + (*func)( + GetParam(ForceType(), vm, 2, &ptr) + ); + sq_pop(vm, 1); + return 0; + } + }; + + /** + * The real C++ caller for method with return value and 1 param. + */ + template + struct HelperT { + static int SQCall(Tcls *instance, Tretval (Tcls::*func)(Targ1), HSQUIRRELVM vm) + { + SQAutoFreePointers ptr; + Tretval ret = (instance->*func)( + GetParam(ForceType(), vm, 2, &ptr) + ); + sq_pop(vm, 1); + return Return(vm, ret); + } + }; + + /** + * The real C++ caller for method with no return value and 1 param. + */ + template + struct HelperT { + static int SQCall(Tcls *instance, Tretval (Tcls::*func)(Targ1), HSQUIRRELVM vm) + { + SQAutoFreePointers ptr; + (instance->*func)( + GetParam(ForceType(), vm, 2, &ptr) + ); + sq_pop(vm, 1); + return 0; + } + + static Tcls *SQConstruct(Tcls *instance, Tretval (Tcls::*func)(Targ1), HSQUIRRELVM vm) + { + SQAutoFreePointers ptr; + Tcls *inst = new Tcls( + GetParam(ForceType(), vm, 2, &ptr) + ); + + return inst; + } + }; + + /** + * The real C++ caller for function with return value and 2 params. + */ + template + struct HelperT { + static int SQCall(void *instance, Tretval (*func)(Targ1, Targ2), HSQUIRRELVM vm) + { + SQAutoFreePointers ptr; + Tretval ret = (*func)( + GetParam(ForceType(), vm, 2, &ptr), + GetParam(ForceType(), vm, 3, &ptr) + ); + sq_pop(vm, 2); + return Return(vm, ret); + } + }; + + /** + * The real C++ caller for function with no return value and 2 params. + */ + template + struct HelperT { + static int SQCall(void *instance, Tretval (*func)(Targ1, Targ2), HSQUIRRELVM vm) + { + SQAutoFreePointers ptr; + (*func)( + GetParam(ForceType(), vm, 2, &ptr), + GetParam(ForceType(), vm, 3, &ptr) + ); + sq_pop(vm, 2); + return 0; + } + }; + + /** + * The real C++ caller for method with return value and 2 params. + */ + template + struct HelperT { + static int SQCall(Tcls *instance, Tretval (Tcls::*func)(Targ1, Targ2), HSQUIRRELVM vm) + { + SQAutoFreePointers ptr; + Tretval ret = (instance->*func)( + GetParam(ForceType(), vm, 2, &ptr), + GetParam(ForceType(), vm, 3, &ptr) + ); + sq_pop(vm, 2); + return Return(vm, ret); + } + }; + + /** + * The real C++ caller for method with no return value and 2 params. + */ + template + struct HelperT { + static int SQCall(Tcls *instance, Tretval (Tcls::*func)(Targ1, Targ2), HSQUIRRELVM vm) + { + SQAutoFreePointers ptr; + (instance->*func)( + GetParam(ForceType(), vm, 2, &ptr), + GetParam(ForceType(), vm, 3, &ptr) + ); + sq_pop(vm, 2); + return 0; + } + + static Tcls *SQConstruct(Tcls *instance, Tretval (Tcls::*func)(Targ1, Targ2), HSQUIRRELVM vm) + { + SQAutoFreePointers ptr; + Tcls *inst = new Tcls( + GetParam(ForceType(), vm, 2, &ptr), + GetParam(ForceType(), vm, 3, &ptr) + ); + + return inst; + } + }; + + /** + * The real C++ caller for function with return value and 3 params. + */ + template + struct HelperT { + static int SQCall(void *instance, Tretval (*func)(Targ1, Targ2, Targ3), HSQUIRRELVM vm) + { + SQAutoFreePointers ptr; + Tretval ret = (*func)( + GetParam(ForceType(), vm, 2, &ptr), + GetParam(ForceType(), vm, 3, &ptr), + GetParam(ForceType(), vm, 4, &ptr) + ); + sq_pop(vm, 3); + return Return(vm, ret); + } + }; + + /** + * The real C++ caller for function with no return value and 3 params. + */ + template + struct HelperT { + static int SQCall(void *instance, Tretval (*func)(Targ1, Targ2, Targ3), HSQUIRRELVM vm) + { + SQAutoFreePointers ptr; + (*func)( + GetParam(ForceType(), vm, 2, &ptr), + GetParam(ForceType(), vm, 3, &ptr), + GetParam(ForceType(), vm, 4, &ptr) + ); + sq_pop(vm, 3); + return 0; + } + }; + + /** + * The real C++ caller for method with return value and 3 params. + */ + template + struct HelperT { + static int SQCall(Tcls *instance, Tretval (Tcls::*func)(Targ1, Targ2, Targ3), HSQUIRRELVM vm) + { + SQAutoFreePointers ptr; + Tretval ret = (instance->*func)( + GetParam(ForceType(), vm, 2, &ptr), + GetParam(ForceType(), vm, 3, &ptr), + GetParam(ForceType(), vm, 4, &ptr) + ); + sq_pop(vm, 3); + return Return(vm, ret); + } + }; + + /** + * The real C++ caller for method with no return value and 3 params. + */ + template + struct HelperT { + static int SQCall(Tcls *instance, Tretval (Tcls::*func)(Targ1, Targ2, Targ3), HSQUIRRELVM vm) + { + SQAutoFreePointers ptr; + (instance->*func)( + GetParam(ForceType(), vm, 2, &ptr), + GetParam(ForceType(), vm, 3, &ptr), + GetParam(ForceType(), vm, 4, &ptr) + ); + sq_pop(vm, 3); + return 0; + } + + static Tcls *SQConstruct(Tcls *instance, Tretval (Tcls::*func)(Targ1, Targ2, Targ3), HSQUIRRELVM vm) + { + SQAutoFreePointers ptr; + Tcls *inst = new Tcls( + GetParam(ForceType(), vm, 2, &ptr), + GetParam(ForceType(), vm, 3, &ptr), + GetParam(ForceType(), vm, 4, &ptr) + ); + + return inst; + } + }; + + /** + * The real C++ caller for function with return value and 4 params. + */ + template + struct HelperT { + static int SQCall(void *instance, Tretval (*func)(Targ1, Targ2, Targ3, Targ4), HSQUIRRELVM vm) + { + SQAutoFreePointers ptr; + Tretval ret = (*func)( + GetParam(ForceType(), vm, 2, &ptr), + GetParam(ForceType(), vm, 3, &ptr), + GetParam(ForceType(), vm, 4, &ptr), + GetParam(ForceType(), vm, 5, &ptr) + ); + sq_pop(vm, 4); + return Return(vm, ret); + } + }; + + /** + * The real C++ caller for function with no return value and 4 params. + */ + template + struct HelperT { + static int SQCall(void *instance, Tretval (*func)(Targ1, Targ2, Targ3, Targ4), HSQUIRRELVM vm) + { + SQAutoFreePointers ptr; + (*func)( + GetParam(ForceType(), vm, 2, &ptr), + GetParam(ForceType(), vm, 3, &ptr), + GetParam(ForceType(), vm, 4, &ptr), + GetParam(ForceType(), vm, 5, &ptr) + ); + sq_pop(vm, 4); + return 0; + } + }; + + /** + * The real C++ caller for method with return value and 4 params. + */ + template + struct HelperT { + static int SQCall(Tcls *instance, Tretval (Tcls::*func)(Targ1, Targ2, Targ3, Targ4), HSQUIRRELVM vm) + { + SQAutoFreePointers ptr; + Tretval ret = (instance->*func)( + GetParam(ForceType(), vm, 2, &ptr), + GetParam(ForceType(), vm, 3, &ptr), + GetParam(ForceType(), vm, 4, &ptr), + GetParam(ForceType(), vm, 5, &ptr) + ); + sq_pop(vm, 4); + return Return(vm, ret); + } + }; + + /** + * The real C++ caller for method with no return value and 4 params. + */ + template + struct HelperT { + static int SQCall(Tcls *instance, Tretval (Tcls::*func)(Targ1, Targ2, Targ3, Targ4), HSQUIRRELVM vm) + { + SQAutoFreePointers ptr; + (instance->*func)( + GetParam(ForceType(), vm, 2, &ptr), + GetParam(ForceType(), vm, 3, &ptr), + GetParam(ForceType(), vm, 4, &ptr), + GetParam(ForceType(), vm, 5, &ptr) + ); + sq_pop(vm, 4); + return 0; + } + + static Tcls *SQConstruct(Tcls *instance, Tretval (Tcls::*func)(Targ1, Targ2, Targ3, Targ4), HSQUIRRELVM vm) + { + SQAutoFreePointers ptr; + Tcls *inst = new Tcls( + GetParam(ForceType(), vm, 2, &ptr), + GetParam(ForceType(), vm, 3, &ptr), + GetParam(ForceType(), vm, 4, &ptr), + GetParam(ForceType(), vm, 5, &ptr) + ); + + return inst; + } + }; + + /** + * The real C++ caller for function with return value and 5 params. + */ + template + struct HelperT { + static int SQCall(void *instance, Tretval (*func)(Targ1, Targ2, Targ3, Targ4, Targ5), HSQUIRRELVM vm) + { + SQAutoFreePointers ptr; + Tretval ret = (*func)( + GetParam(ForceType(), vm, 2, &ptr), + GetParam(ForceType(), vm, 3, &ptr), + GetParam(ForceType(), vm, 4, &ptr), + GetParam(ForceType(), vm, 5, &ptr), + GetParam(ForceType(), vm, 6, &ptr) + ); + sq_pop(vm, 5); + return Return(vm, ret); + } + }; + + /** + * The real C++ caller for function with no return value and 5 params. + */ + template + struct HelperT { + static int SQCall(void *instance, Tretval (*func)(Targ1, Targ2, Targ3, Targ4, Targ5), HSQUIRRELVM vm) + { + SQAutoFreePointers ptr; + (*func)( + GetParam(ForceType(), vm, 2, &ptr), + GetParam(ForceType(), vm, 3, &ptr), + GetParam(ForceType(), vm, 4, &ptr), + GetParam(ForceType(), vm, 5, &ptr), + GetParam(ForceType(), vm, 6, &ptr) + ); + sq_pop(vm, 5); + return 0; + } + }; + + /** + * The real C++ caller for method with return value and 5 params. + */ + template + struct HelperT { + static int SQCall(Tcls *instance, Tretval (Tcls::*func)(Targ1, Targ2, Targ3, Targ4, Targ5), HSQUIRRELVM vm) + { + SQAutoFreePointers ptr; + Tretval ret = (instance->*func)( + GetParam(ForceType(), vm, 2, &ptr), + GetParam(ForceType(), vm, 3, &ptr), + GetParam(ForceType(), vm, 4, &ptr), + GetParam(ForceType(), vm, 5, &ptr), + GetParam(ForceType(), vm, 6, &ptr) + ); + sq_pop(vm, 5); + return Return(vm, ret); + } + }; + + /** + * The real C++ caller for method with no return value and 5 params. + */ + template + struct HelperT { + static int SQCall(Tcls *instance, Tretval (Tcls::*func)(Targ1, Targ2, Targ3, Targ4, Targ5), HSQUIRRELVM vm) + { + SQAutoFreePointers ptr; + (instance->*func)( + GetParam(ForceType(), vm, 2, &ptr), + GetParam(ForceType(), vm, 3, &ptr), + GetParam(ForceType(), vm, 4, &ptr), + GetParam(ForceType(), vm, 5, &ptr), + GetParam(ForceType(), vm, 6, &ptr) + ); + sq_pop(vm, 5); + return 0; + } + + static Tcls *SQConstruct(Tcls *instance, Tretval (Tcls::*func)(Targ1, Targ2, Targ3, Targ4, Targ5), HSQUIRRELVM vm) + { + SQAutoFreePointers ptr; + Tcls *inst = new Tcls( + GetParam(ForceType(), vm, 2, &ptr), + GetParam(ForceType(), vm, 3, &ptr), + GetParam(ForceType(), vm, 4, &ptr), + GetParam(ForceType(), vm, 5, &ptr), + GetParam(ForceType(), vm, 6, &ptr) + ); + + return inst; + } + }; + + /** + * The real C++ caller for function with return value and 10 params. + */ + template + struct HelperT { + static int SQCall(void *instance, Tretval (*func)(Targ1, Targ2, Targ3, Targ4, Targ5, Targ6, Targ7, Targ8, Targ9, Targ10), HSQUIRRELVM vm) + { + SQAutoFreePointers ptr; + Tretval ret = (*func)( + GetParam(ForceType(), vm, 2, &ptr), + GetParam(ForceType(), vm, 3, &ptr), + GetParam(ForceType(), vm, 4, &ptr), + GetParam(ForceType(), vm, 5, &ptr), + GetParam(ForceType(), vm, 6, &ptr), + GetParam(ForceType(), vm, 7, &ptr), + GetParam(ForceType(), vm, 8, &ptr), + GetParam(ForceType(), vm, 9, &ptr), + GetParam(ForceType(), vm, 10, &ptr), + GetParam(ForceType(), vm, 11, &ptr) + ); + sq_pop(vm, 10); + return Return(vm, ret); + } + }; + + /** + * The real C++ caller for function with no return value and 10 params. + */ + template + struct HelperT { + static int SQCall(void *instance, Tretval (*func)(Targ1, Targ2, Targ3, Targ4, Targ5, Targ6, Targ7, Targ8, Targ9, Targ10), HSQUIRRELVM vm) + { + SQAutoFreePointers ptr; + (*func)( + GetParam(ForceType(), vm, 2, &ptr), + GetParam(ForceType(), vm, 3, &ptr), + GetParam(ForceType(), vm, 4, &ptr), + GetParam(ForceType(), vm, 5, &ptr), + GetParam(ForceType(), vm, 6, &ptr), + GetParam(ForceType(), vm, 7, &ptr), + GetParam(ForceType(), vm, 8, &ptr), + GetParam(ForceType(), vm, 9, &ptr), + GetParam(ForceType(), vm, 10, &ptr), + GetParam(ForceType(), vm, 11, &ptr) + ); + sq_pop(vm, 10); + return 0; + } + }; + + /** + * The real C++ caller for method with return value and 10 params. + */ + template + struct HelperT { + static int SQCall(Tcls *instance, Tretval (Tcls::*func)(Targ1, Targ2, Targ3, Targ4, Targ5, Targ6, Targ7, Targ8, Targ9, Targ10), HSQUIRRELVM vm) + { + SQAutoFreePointers ptr; + Tretval ret = (instance->*func)( + GetParam(ForceType(), vm, 2, &ptr), + GetParam(ForceType(), vm, 3, &ptr), + GetParam(ForceType(), vm, 4, &ptr), + GetParam(ForceType(), vm, 5, &ptr), + GetParam(ForceType(), vm, 6, &ptr), + GetParam(ForceType(), vm, 7, &ptr), + GetParam(ForceType(), vm, 8, &ptr), + GetParam(ForceType(), vm, 9, &ptr), + GetParam(ForceType(), vm, 10, &ptr), + GetParam(ForceType(), vm, 11, &ptr) + ); + sq_pop(vm, 10); + return Return(vm, ret); + } + }; + + /** + * The real C++ caller for method with no return value and 10 params. + */ + template + struct HelperT { + static int SQCall(Tcls *instance, Tretval (Tcls::*func)(Targ1, Targ2, Targ3, Targ4, Targ5, Targ6, Targ7, Targ8, Targ9, Targ10), HSQUIRRELVM vm) + { + SQAutoFreePointers ptr; + (instance->*func)( + GetParam(ForceType(), vm, 2, &ptr), + GetParam(ForceType(), vm, 3, &ptr), + GetParam(ForceType(), vm, 4, &ptr), + GetParam(ForceType(), vm, 5, &ptr), + GetParam(ForceType(), vm, 6, &ptr), + GetParam(ForceType(), vm, 7, &ptr), + GetParam(ForceType(), vm, 8, &ptr), + GetParam(ForceType(), vm, 9, &ptr), + GetParam(ForceType(), vm, 10, &ptr), + GetParam(ForceType(), vm, 11, &ptr) + ); + sq_pop(vm, 10); + return 0; + } + + static Tcls *SQConstruct(Tcls *instance, Tretval (Tcls::*func)(Targ1, Targ2, Targ3, Targ4, Targ5, Targ6, Targ7, Targ8, Targ9, Targ10), HSQUIRRELVM vm) + { + SQAutoFreePointers ptr; + Tcls *inst = new Tcls( + GetParam(ForceType(), vm, 2, &ptr), + GetParam(ForceType(), vm, 3, &ptr), + GetParam(ForceType(), vm, 4, &ptr), + GetParam(ForceType(), vm, 5, &ptr), + GetParam(ForceType(), vm, 6, &ptr), + GetParam(ForceType(), vm, 7, &ptr), + GetParam(ForceType(), vm, 8, &ptr), + GetParam(ForceType(), vm, 9, &ptr), + GetParam(ForceType(), vm, 10, &ptr), + GetParam(ForceType(), vm, 11, &ptr) + ); + + return inst; + } + }; + + + /** + * A general template for all non-static method callbacks from Squirrel. + * In here the function_proc is recovered, and the SQCall is called that + * can handle this exact amount of params. + */ + template + inline SQInteger DefSQNonStaticCallback(HSQUIRRELVM vm) + { + /* Find the amount of params we got */ + int nparam = sq_gettop(vm); + SQUserPointer ptr = NULL; + SQUserPointer real_instance = NULL; + HSQOBJECT instance; + + /* Get the 'SQ' instance of this class */ + Squirrel::GetInstance(vm, &instance); + + /* Protect against calls to a non-static method in a static way */ + sq_pushroottable(vm); + sq_pushstring(vm, OTTD2FS(Tcls::GetClassName()), -1); + sq_get(vm, -2); + sq_pushobject(vm, instance); + if (sq_instanceof(vm) != SQTrue) return sq_throwerror(vm, _SC("class method is non-static")); + sq_pop(vm, 3); + + /* Get the 'real' instance of this class */ + sq_getinstanceup(vm, 1, &real_instance, 0); + /* Get the real function pointer */ + sq_getuserdata(vm, nparam, &ptr, 0); + if (real_instance == NULL) return sq_throwerror(vm, _SC("couldn't detect real instance of class for non-static call")); + /* Remove the userdata from the stack */ + sq_pop(vm, 1); + + try { + /* Delegate it to a template that can handle this specific function */ + return HelperT::SQCall((Tcls *)real_instance, *(Tmethod *)ptr, vm); + } catch (SQInteger e) { + sq_pop(vm, nparam); + return e; + } + } + + /** + * A general template for all non-static advanced method callbacks from Squirrel. + * In here the function_proc is recovered, and the SQCall is called that + * can handle this exact amount of params. + */ + template + inline SQInteger DefSQAdvancedNonStaticCallback(HSQUIRRELVM vm) + { + /* Find the amount of params we got */ + int nparam = sq_gettop(vm); + SQUserPointer ptr = NULL; + SQUserPointer real_instance = NULL; + HSQOBJECT instance; + + /* Get the 'SQ' instance of this class */ + Squirrel::GetInstance(vm, &instance); + + /* Protect against calls to a non-static method in a static way */ + sq_pushroottable(vm); + sq_pushstring(vm, OTTD2FS(Tcls::GetClassName()), -1); + sq_get(vm, -2); + sq_pushobject(vm, instance); + if (sq_instanceof(vm) != SQTrue) return sq_throwerror(vm, _SC("class method is non-static")); + sq_pop(vm, 3); + + /* Get the 'real' instance of this class */ + sq_getinstanceup(vm, 1, &real_instance, 0); + /* Get the real function pointer */ + sq_getuserdata(vm, nparam, &ptr, 0); + if (real_instance == NULL) return sq_throwerror(vm, _SC("couldn't detect real instance of class for non-static call")); + /* Remove the userdata from the stack */ + sq_pop(vm, 1); + + /* Call the function, which its only param is always the VM */ + return (SQInteger)(((Tcls *)real_instance)->*(*(Tmethod *)ptr))(vm); + } + + /** + * A general template for all function/static method callbacks from Squirrel. + * In here the function_proc is recovered, and the SQCall is called that + * can handle this exact amount of params. + */ + template + inline SQInteger DefSQStaticCallback(HSQUIRRELVM vm) + { + /* Find the amount of params we got */ + int nparam = sq_gettop(vm); + SQUserPointer ptr = NULL; + + /* Get the real function pointer */ + sq_getuserdata(vm, nparam, &ptr, 0); + + try { + /* Delegate it to a template that can handle this specific function */ + return HelperT::SQCall((Tcls *)NULL, *(Tmethod *)ptr, vm); + } catch (SQInteger e) { + sq_pop(vm, nparam); + return e; + } + } + + /** + * A general template for the destructor of SQ instances. This is needed + * here as it has to be in the same scope as DefSQConstructorCallback. + */ + template + static SQInteger DefSQDestructorCallback(SQUserPointer p, SQInteger size) + { + /* Remove the real instance too */ + if (p != NULL) ((Tcls *)p)->Release(); + return 0; + } + + /** + * A general template to handle creating of instance with any amount of + * params. It creates the instance in C++, and it sets all the needed + * settings in SQ to register the instance. + */ + template + inline SQInteger DefSQConstructorCallback(HSQUIRRELVM vm) + { + /* Find the amount of params we got */ + int nparam = sq_gettop(vm); + + try { + /* Create the real instance */ + Tcls *instance = HelperT::SQConstruct((Tcls *)NULL, (Tmethod)NULL, vm); + sq_setinstanceup(vm, -Tnparam, instance); + sq_setreleasehook(vm, -Tnparam, DefSQDestructorCallback); + instance->AddRef(); + return 0; + } catch (SQInteger e) { + sq_pop(vm, nparam); + return e; + } + } + +}; // namespace SQConvert + +#endif /* SQUIRREL_HELPER_HPP */ diff --git a/src/script/squirrel_helper_type.hpp b/src/script/squirrel_helper_type.hpp new file mode 100644 index 000000000..c886d6700 --- /dev/null +++ b/src/script/squirrel_helper_type.hpp @@ -0,0 +1,13 @@ +/* $Id$ */ + +/** @file squirrel_helper_type.hpp Helper structs for converting Squirrel data structures to C++. */ + +#ifndef SQUIRREL_HELPER_TYPE_HPP +#define SQUIRREL_HELPER_TYPE_HPP + +struct Array { + int32 size; + int32 array[VARARRAY_SIZE]; +}; + +#endif /* SQUIRREL_HELPER_TYPE_HPP */ diff --git a/src/script/squirrel_std.cpp b/src/script/squirrel_std.cpp new file mode 100644 index 000000000..3850b7b54 --- /dev/null +++ b/src/script/squirrel_std.cpp @@ -0,0 +1,114 @@ + +#include +#include "../stdafx.h" +#include "../debug.h" +#include "squirrel.hpp" +#include "squirrel_std.hpp" +#include "../core/alloc_func.hpp" +#include "../core/math_func.hpp" + +/* abs() is normally defined to myabs(), which we don't want in this file */ +#undef abs + +SQInteger SquirrelStd::abs(HSQUIRRELVM vm) +{ + SQInteger tmp; + + sq_getinteger(vm, 2, &tmp); + sq_pushinteger(vm, ::abs(tmp)); + return 1; +} + +SQInteger SquirrelStd::min(HSQUIRRELVM vm) +{ + SQInteger tmp1, tmp2; + + sq_getinteger(vm, 2, &tmp1); + sq_getinteger(vm, 3, &tmp2); + sq_pushinteger(vm, ::min(tmp1, tmp2)); + return 1; +} + +SQInteger SquirrelStd::max(HSQUIRRELVM vm) +{ + SQInteger tmp1, tmp2; + + sq_getinteger(vm, 2, &tmp1); + sq_getinteger(vm, 3, &tmp2); + sq_pushinteger(vm, ::max(tmp1, tmp2)); + return 1; +} + +SQInteger SquirrelStd::require(HSQUIRRELVM vm) +{ + SQInteger top = sq_gettop(vm); + const SQChar *filename; + SQChar *real_filename; + + sq_getstring(vm, 2, &filename); + + /* Get the script-name of the current file, so we can work relative from it */ + SQStackInfos si; + sq_stackinfos(vm, 1, &si); + if (si.source == NULL) { + DEBUG(misc, 0, "[squirrel] Couldn't detect the script-name of the 'require'-caller; this should never happen!"); + return SQ_ERROR; + } + real_filename = scstrdup(si.source); + /* Keep the dir, remove the rest */ + SQChar *s = scstrrchr(real_filename, PATHSEPCHAR); + if (s != NULL) { + /* Keep the PATHSEPCHAR there, remove the rest */ + *s++; + *s = '\0'; + } + /* And now we concat, so we are relative from the current script + * First, we have to make sure we have enough space for the full path */ + real_filename = ReallocT(real_filename, scstrlen(real_filename) + scstrlen(filename) + 1); + scstrcat(real_filename, filename); + /* Tars dislike opening files with '/' on Windows.. so convert it to '\\' ;) */ + char *filen = strdup(FS2OTTD(real_filename)); +#if (PATHSEPCHAR != '/') + for (char *n = filen; *n != '\0'; n++) if (*n == '/') *n = PATHSEPCHAR; +#endif + + bool ret = Squirrel::LoadScript(vm, filen); + + /* Reset the top, so the stack stays correct */ + sq_settop(vm, top); + free(real_filename); + free(filen); + + return ret ? 0: SQ_ERROR; +} + +SQInteger SquirrelStd::notifyallexceptions(HSQUIRRELVM vm) +{ + SQBool b; + + if (sq_gettop(vm) >= 1) { + if (SQ_SUCCEEDED(sq_getbool(vm, -1, &b))) { + sq_notifyallexceptions(vm, b); + return 0; + } + } + + return SQ_ERROR; +} + +void squirrel_register_global_std(Squirrel *engine) +{ + /* We don't use squirrel_helper here, as we want to register to the global + * scope and not to a class. */ + engine->AddMethod("require", &SquirrelStd::require, 2, "?s"); + engine->AddMethod("notifyallexceptions", &SquirrelStd::notifyallexceptions, 2, "?b"); +} + +void squirrel_register_std(Squirrel *engine) +{ + /* We don't use squirrel_helper here, as we want to register to the global + * scope and not to a class. */ + engine->AddMethod("abs", &SquirrelStd::abs, 2, "?i"); + engine->AddMethod("min", &SquirrelStd::min, 3, "?ii"); + engine->AddMethod("max", &SquirrelStd::max, 3, "?ii"); +} diff --git a/src/script/squirrel_std.hpp b/src/script/squirrel_std.hpp new file mode 100644 index 000000000..93d82995a --- /dev/null +++ b/src/script/squirrel_std.hpp @@ -0,0 +1,60 @@ +/* $Id$ */ + +/** @file squirrel_std.hpp defines the Squirrel Standard Function class */ + +#ifndef SQUIRREL_STD_HPP +#define SQUIRREL_STD_HPP + +#if defined(__APPLE__) +/* Which idiotic system makes 'require' a macro? :s Oh well.... */ +#undef require +#endif /* __APPLE__ */ + +/** + * By default we want to give a set of standard commands to a SQ script. + * Most of them are easy wrappers around internal functions. Of course we + * could just as easy include things like the stdmath of SQ, but of those + * functions we are sure they work on all our supported targets. + */ +class SquirrelStd { +public: + /** + * Make an integer absolute. + */ + static SQInteger abs(HSQUIRRELVM vm); + + /** + * Get the lowest of two integers. + */ + static SQInteger min(HSQUIRRELVM vm); + + /** + * Get the highest of two integers. + */ + static SQInteger max(HSQUIRRELVM vm); + + /** + * Load an other file on runtime. + * @note This is always loaded on the root-level, no matter where you call this. + * @note The filename is always relative from the script it is called from. Absolute calls are NOT allowed! + */ + static SQInteger require(HSQUIRRELVM vm); + + /** + * Enable/disable stack trace showing for handled exceptions. + */ + static SQInteger notifyallexceptions(HSQUIRRELVM vm); +}; + +/** + * Register all standard functions we want to give to a script. + */ +void squirrel_register_std(Squirrel *engine); + +/** + * Register all standard functions that are available on first startup. + * @note this set is very limited, and is only ment to load other scripts and things like that. + */ +void squirrel_register_global_std(Squirrel *engine); + +#endif /* SQUIRREL_STD_HPP */ -- cgit v1.2.3-70-g09d2