/* $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 <squirrel.h>
#include <sqstdio.h>
#include <stdarg.h>
#include "stdafx.h"
#include "debug.h"
#include "squirrel.hpp"
#include "squirrel_std.hpp"
#include <sqstdaux.h>
void Squirrel::CompileError(HSQUIRRELVM vm, const SQChar *desc, const SQChar *source, SQInteger line, SQInteger column)
{
char *src = strdup(FS2OTTD(source));
char *dsc = strdup(FS2OTTD(desc));
#ifdef _SQ64
DEBUG(misc, 0, "Error %s:%ld/%ld: %s", src, line, column, dsc);
#else
DEBUG(misc, 0, "Error %s:%d/%d: %s", src, line, column, dsc);
#endif
free(src);
free(dsc);
}
void Squirrel::ErrorPrintFunc(HSQUIRRELVM vm, const SQChar *s, ...)
{
va_list arglist;
va_start(arglist, s);
scvfprintf(stderr, s, arglist);
va_end(arglist);
}
void Squirrel::RunError(HSQUIRRELVM vm, const char *error)
{
/* Set the print function to something that prints to stderr */
SQPRINTFUNCTION pf = sq_getprintfunc(vm);
sq_setprintfunc(vm, &Squirrel::ErrorPrintFunc);
DEBUG(misc, 0, "Your script made an error: %s", error);
/* 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, FS2OTTD(sErr));
return 0;
}
}
Squirrel::RunError(vm, "unknown error");
return 0;
}
void Squirrel::PrintFunc(HSQUIRRELVM vm, const SQChar *s, ...)
{
va_list arglist;
va_start(arglist, s);
scvprintf(s, arglist);
va_end(arglist);
printf("\n");
}
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;
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);
}
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;
}
void Squirrel::CallMethod(HSQOBJECT instance, const char *method_name, HSQOBJECT *ret)
{
/* 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;
}
/* Call the method */
sq_pushobject(this->vm, instance);
sq_call(this->vm, 1, ret == NULL ? SQFalse : SQTrue, SQFalse);
if (ret != NULL) sq_getstackobj(vm, -1, ret);
/* Reset the top */
sq_settop(this->vm, top);
}
/* 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);
/* 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);
sq_pushroottable(this->vm);
squirrel_register_global_std(this);
}
bool Squirrel::LoadScript(const char *script)
{
/* Make sure we are always in the root-table */
sq_pushroottable(this->vm);
/* Load and run the script */
if (SQ_FAILED(sqstd_dofile(this->vm, OTTD2FS(script), SQFalse, SQTrue))) {
DEBUG(misc, 0, "[squirrel] Failed to compile '%s'", script);
return false;
}
return true;
}
Squirrel::~Squirrel()
{
/* Clean up the stuff */
sq_pop(this->vm, 1);
sq_close(this->vm);
}