13 #include "../stdafx.h"
16 #include "../fileio_func.h"
17 #include "../string_func.h"
19 #include <../squirrel/sqpcheader.h>
20 #include <../squirrel/sqvm.h>
22 #include "../safeguards.h"
24 void Squirrel::CompileError(HSQUIRRELVM vm,
const SQChar *desc,
const SQChar *source, SQInteger line, SQInteger column)
28 seprintf(buf,
lastof(buf),
"Error %s:" OTTD_PRINTF64
"/" OTTD_PRINTF64
": %s", source, line, column, desc);
35 DEBUG(misc, 0,
"[Squirrel] Compile error: %s", buf);
53 fprintf(stderr,
"%s", buf);
62 SQPRINTFUNCTION pf = sq_getprintfunc(vm);
67 seprintf(buf,
lastof(buf),
"Your script made an error: %s\n", error);
71 fprintf(stderr,
"%s", buf);
77 sqstd_printcallstack(vm);
79 sq_setprintfunc(vm, pf);
84 const SQChar *sErr = 0;
86 if (sq_gettop(vm) >= 1) {
87 if (SQ_SUCCEEDED(sq_getstring(vm, -1, &sErr))) {
102 va_start(arglist, s);
116 void Squirrel::AddMethod(
const char *method_name, SQFUNCTION proc, uint nparam,
const char *params,
void *userdata,
int size)
118 sq_pushstring(this->
vm, method_name, -1);
121 void *ptr = sq_newuserdata(
vm, size);
122 memcpy(ptr, userdata, size);
125 sq_newclosure(this->
vm, proc, size != 0 ? 1 : 0);
126 if (nparam != 0) sq_setparamscheck(this->
vm, nparam, params);
127 sq_setnativeclosurename(this->
vm, -1, method_name);
128 sq_newslot(this->
vm, -3, SQFalse);
133 sq_pushstring(this->
vm, var_name, -1);
134 sq_pushinteger(this->
vm, value);
135 sq_newslot(this->
vm, -3, SQTrue);
140 sq_pushstring(this->
vm, var_name, -1);
141 sq_pushbool(this->
vm, value);
142 sq_newslot(this->
vm, -3, SQTrue);
147 sq_pushroottable(this->
vm);
148 sq_pushstring(this->
vm, class_name, -1);
149 sq_newclass(this->
vm, SQFalse);
154 sq_pushroottable(this->
vm);
155 sq_pushstring(this->
vm, class_name, -1);
156 sq_pushstring(this->
vm, parent_class, -1);
157 if (SQ_FAILED(sq_get(this->
vm, -3))) {
158 DEBUG(misc, 0,
"[squirrel] Failed to initialize class '%s' based on parent class '%s'", class_name, parent_class);
159 DEBUG(misc, 0,
"[squirrel] Make sure that '%s' exists before trying to define '%s'", parent_class, class_name);
162 sq_newclass(this->
vm, SQTrue);
167 sq_newslot(
vm, -3, SQFalse);
174 int top = sq_gettop(this->
vm);
176 sq_pushobject(this->
vm, instance);
178 sq_pushstring(this->
vm, method_name, -1);
179 if (SQ_FAILED(sq_get(this->
vm, -2))) {
180 sq_settop(this->
vm, top);
183 sq_settop(this->
vm, top);
201 this->
crashed = !sq_resumecatch(this->
vm, suspend);
203 return this->
vm->_suspended != 0;
209 sq_resumeerror(this->
vm);
214 sq_collectgarbage(this->
vm);
223 SQInteger last_target = this->
vm->_suspended_target;
225 int top = sq_gettop(this->
vm);
227 sq_pushobject(this->
vm, instance);
229 sq_pushstring(this->
vm, method_name, -1);
230 if (SQ_FAILED(sq_get(this->
vm, -2))) {
231 DEBUG(misc, 0,
"[squirrel] Could not find '%s' in the class", method_name);
232 sq_settop(this->
vm, top);
236 sq_pushobject(this->
vm, instance);
237 if (SQ_FAILED(sq_call(this->
vm, 1, ret == NULL ? SQFalse : SQTrue, SQTrue, suspend)))
return false;
238 if (ret != NULL) sq_getstackobj(
vm, -1, ret);
241 if (suspend == -1 || !this->
IsSuspended()) sq_settop(this->
vm, top);
243 this->
vm->_suspended_target = last_target;
248 bool Squirrel::CallStringMethodStrdup(HSQOBJECT instance,
const char *method_name,
const char **res,
int suspend)
251 if (!this->
CallMethod(instance, method_name, &ret, suspend))
return false;
252 if (ret._type != OT_STRING)
return false;
258 bool Squirrel::CallIntegerMethod(HSQOBJECT instance,
const char *method_name,
int *res,
int suspend)
261 if (!this->
CallMethod(instance, method_name, &ret, suspend))
return false;
262 if (ret._type != OT_INTEGER)
return false;
267 bool Squirrel::CallBoolMethod(HSQOBJECT instance,
const char *method_name,
bool *res,
int suspend)
270 if (!this->
CallMethod(instance, method_name, &ret, suspend))
return false;
271 if (ret._type != OT_BOOL)
return false;
276 bool Squirrel::CreateClassInstanceVM(HSQUIRRELVM vm,
const char *class_name,
void *real_instance, HSQOBJECT *instance, SQRELEASEHOOK release_hook,
bool prepend_API_name)
280 int oldtop = sq_gettop(vm);
283 sq_pushroottable(vm);
285 if (prepend_API_name) {
286 size_t len = strlen(class_name) + strlen(engine->
GetAPIName()) + 1;
287 char *class_name2 = (
char *)alloca(len);
288 seprintf(class_name2, class_name2 + len - 1,
"%s%s", engine->
GetAPIName(), class_name);
290 sq_pushstring(vm, class_name2, -1);
292 sq_pushstring(vm, class_name, -1);
295 if (SQ_FAILED(sq_get(vm, -2))) {
296 DEBUG(misc, 0,
"[squirrel] Failed to find class by the name '%s%s'", prepend_API_name ? engine->
GetAPIName() :
"", class_name);
297 sq_settop(vm, oldtop);
302 if (SQ_FAILED(sq_createinstance(vm, -1))) {
303 DEBUG(misc, 0,
"[squirrel] Failed to create instance for class '%s%s'", prepend_API_name ? engine->
GetAPIName() :
"", class_name);
304 sq_settop(vm, oldtop);
308 if (instance != NULL) {
310 sq_getstackobj(vm, -1, instance);
312 sq_addref(vm, instance);
318 sq_setinstanceup(vm, -1, real_instance);
319 if (release_hook != NULL) sq_setreleasehook(vm, -1, release_hook);
321 if (instance != NULL) sq_settop(vm, oldtop);
331 Squirrel::Squirrel(
const char *APIName) :
343 this->
vm = sq_open(1024);
347 sq_notifyallexceptions(this->
vm, SQTrue);
352 sq_seterrorhandler(this->
vm);
355 sq_setforeignptr(this->
vm,
this);
357 sq_pushroottable(this->
vm);
368 SQFile(FILE *file,
size_t size) : file(file), size(size), pos(0) {}
370 size_t Read(
void *buf,
size_t elemsize,
size_t count)
372 assert(elemsize != 0);
373 if (this->pos + (elemsize * count) > this->size) {
374 count = (this->size - this->pos) / elemsize;
376 if (count == 0)
return 0;
377 size_t ret = fread(buf, elemsize, count, this->file);
378 this->pos += ret * elemsize;
383 static WChar _io_file_lexfeed_ASCII(SQUserPointer file)
386 if (((
SQFile *)file)->Read(&c,
sizeof(c), 1) > 0)
return c;
390 static WChar _io_file_lexfeed_UTF8(SQUserPointer file)
395 if (((
SQFile *)file)->Read(buffer,
sizeof(buffer[0]), 1) != 1)
return 0;
397 if (len == 0)
return -1;
400 if (len > 1 && ((
SQFile *)file)->Read(buffer + 1,
sizeof(buffer[0]), len - 1) != len - 1)
return 0;
409 static WChar _io_file_lexfeed_UCS2_no_swap(SQUserPointer file)
412 if (((
SQFile *)file)->Read(&c,
sizeof(c), 1) > 0)
return (
WChar)c;
416 static WChar _io_file_lexfeed_UCS2_swap(SQUserPointer file)
419 if (((
SQFile *)file)->Read(&c,
sizeof(c), 1) > 0) {
420 c = ((c >> 8) & 0x00FF)| ((c << 8) & 0xFF00);
426 static SQInteger _io_file_read(SQUserPointer file, SQUserPointer buf, SQInteger size)
428 SQInteger ret = ((
SQFile *)file)->Read(buf, 1, size);
429 if (ret == 0)
return -1;
442 if (strncmp(this->
GetAPIName(),
"AI", 2) == 0) {
445 }
else if (strncmp(this->
GetAPIName(),
"GS", 2) == 0) {
454 ret = fread(&us, 1,
sizeof(us), file);
456 if (ret != 2) us = 0;
459 case SQ_BYTECODE_STREAM_TAG: {
460 if (fseek(file, -2, SEEK_CUR) < 0) {
462 return sq_throwerror(vm,
"cannot seek the file");
464 if (SQ_SUCCEEDED(sq_readclosure(vm, _io_file_read, &f))) {
469 return sq_throwerror(vm,
"Couldn't read bytecode");
475 func = _io_file_lexfeed_UCS2_swap;
477 case 0xFEFF: func = _io_file_lexfeed_UCS2_no_swap;
break;
480 if (fread(&uc, 1,
sizeof(uc), file) == 0) {
482 return sq_throwerror(vm,
"I/O error");
486 return sq_throwerror(vm,
"Unrecognized encoding");
488 func = _io_file_lexfeed_UTF8;
491 func = _io_file_lexfeed_ASCII;
492 if (fseek(file, -2, SEEK_CUR) < 0) {
494 return sq_throwerror(vm,
"cannot seek the file");
499 if (SQ_SUCCEEDED(sq_compile(vm, func, &f, filename, printerror))) {
506 return sq_throwerror(vm,
"cannot open the file");
512 if (in_root) sq_pushroottable(vm);
514 SQInteger ops_left = vm->_ops_till_suspend;
516 if (SQ_SUCCEEDED(
LoadFile(vm, script, SQTrue))) {
518 if (SQ_SUCCEEDED(sq_call(vm, 1, SQFalse, SQTrue, 100000))) {
521 vm->_ops_till_suspend = ops_left;
526 vm->_ops_till_suspend = ops_left;
527 DEBUG(misc, 0,
"[squirrel] Failed to compile '%s'", script);
536 Squirrel::~Squirrel()
554 void Squirrel::InsertResult(
bool result)
556 sq_pushbool(this->vm, result);
558 vm->GetAt(vm->_stackbase + vm->_suspended_target) = vm->GetUp(-1);
563 void Squirrel::InsertResult(
int result)
565 sq_pushinteger(this->vm, result);
567 vm->GetAt(vm->_stackbase + vm->_suspended_target) = vm->GetUp(-1);
574 vm->DecreaseOps(ops);
579 return this->vm->_suspended != 0;
594 return sq_can_suspend(this->vm);
599 return this->vm->_ops_till_suspend;