src/squirrel.cpp
author rubidium
Sun, 15 Apr 2007 00:17:57 +0000
branchnoai
changeset 9598 b73018a5ffc0
parent 9588 01b2435c977b
child 9673 ee4f133c84ec
permissions -rw-r--r--
(svn r9631) [NoAI] -Codechange: rewrite the loading in such a manner that the NoAI branch gets savegame compatability for both NoAI branch savegames (from this revision on) as well as trunk savegames up to the revision that NoAI is synched with.
Note: this breaks savegame loading of savegames made with the NoAI branch up to this revision.
/* $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);
}

bool Squirrel::CreateClassInstance(const char *class_name, void *real_instance, HSQOBJECT *instance)
{
	int oldtop = sq_gettop(this->vm);

	/* First, find the class */
	sq_pushroottable(this->vm);
	sq_pushstring(this->vm, OTTD2FS(class_name), -1);
	if (SQ_FAILED(sq_get(this->vm, -2))) {
		DEBUG(misc, 0, "[squirrel] Failed to find class by the name '%s'", class_name);
		sq_settop(this->vm, oldtop);
		return false;
	}

	/* Create the instance */
	if (SQ_FAILED(sq_createinstance(this->vm, -1))) {
		DEBUG(misc, 0, "[squirrel] Failed to create instance for class '%s'", class_name);
		sq_settop(this->vm, oldtop);
		return false;
	}

	if (instance != NULL) {
		/* Find our instance */
		sq_getstackobj(this->vm, -1, instance);
		/* Add a reference to it, so it survives for ever */
		sq_addref(this->vm, instance);
	}
	/* Store it in the class */
	sq_setinstanceup(this->vm, -1, real_instance);

	/* Reset the top */
	sq_settop(this->vm, oldtop);

	return true;
}

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);
}