00001
00002
00003
00004
00005
00006
00007
00008
00009
00012 #include <squirrel.h>
00013 #include <stdarg.h>
00014 #include "../stdafx.h"
00015 #include "../debug.h"
00016 #include "squirrel.hpp"
00017 #include "squirrel_std.hpp"
00018 #include "../fileio_func.h"
00019 #include <sqstdaux.h>
00020 #include <../squirrel/sqpcheader.h>
00021 #include <../squirrel/sqvm.h>
00022
00023 void Squirrel::CompileError(HSQUIRRELVM vm, const SQChar *desc, const SQChar *source, SQInteger line, SQInteger column)
00024 {
00025 SQChar buf[1024];
00026
00027 #ifdef _SQ64
00028 scsnprintf(buf, lengthof(buf), _SC("Error %s:%ld/%ld: %s"), source, line, column, desc);
00029 #else
00030 scsnprintf(buf, lengthof(buf), _SC("Error %s:%d/%d: %s"), source, line, column, desc);
00031 #endif
00032
00033
00034 Squirrel *engine = (Squirrel *)sq_getforeignptr(vm);
00035 engine->crashed = true;
00036 SQPrintFunc *func = engine->print_func;
00037 if (func == NULL) {
00038 scfprintf(stderr, _SC("%s"), buf);
00039 } else {
00040 (*func)(true, buf);
00041 }
00042 }
00043
00044 void Squirrel::ErrorPrintFunc(HSQUIRRELVM vm, const SQChar *s, ...)
00045 {
00046 va_list arglist;
00047 SQChar buf[1024];
00048
00049 va_start(arglist, s);
00050 scvsnprintf(buf, lengthof(buf), s, arglist);
00051 va_end(arglist);
00052
00053
00054 SQPrintFunc *func = ((Squirrel *)sq_getforeignptr(vm))->print_func;
00055 if (func == NULL) {
00056 scfprintf(stderr, _SC("%s"), buf);
00057 } else {
00058 (*func)(true, buf);
00059 }
00060 }
00061
00062 void Squirrel::RunError(HSQUIRRELVM vm, const SQChar *error)
00063 {
00064
00065 SQPRINTFUNCTION pf = sq_getprintfunc(vm);
00066 sq_setprintfunc(vm, &Squirrel::ErrorPrintFunc);
00067
00068
00069 SQChar buf[1024];
00070 scsnprintf(buf, lengthof(buf), _SC("Your script made an error: %s\n"), error);
00071 Squirrel *engine = (Squirrel *)sq_getforeignptr(vm);
00072 SQPrintFunc *func = engine->print_func;
00073 if (func == NULL) {
00074 scfprintf(stderr, _SC("%s"), buf);
00075 } else {
00076 (*func)(true, buf);
00077 }
00078
00079
00080 sqstd_printcallstack(vm);
00081
00082 sq_setprintfunc(vm, pf);
00083 }
00084
00085 SQInteger Squirrel::_RunError(HSQUIRRELVM vm)
00086 {
00087 const SQChar *sErr = 0;
00088
00089 if (sq_gettop(vm) >= 1) {
00090 if (SQ_SUCCEEDED(sq_getstring(vm, -1, &sErr))) {
00091 Squirrel::RunError(vm, sErr);
00092 return 0;
00093 }
00094 }
00095
00096 Squirrel::RunError(vm, _SC("unknown error"));
00097 return 0;
00098 }
00099
00100 void Squirrel::PrintFunc(HSQUIRRELVM vm, const SQChar *s, ...)
00101 {
00102 va_list arglist;
00103 SQChar buf[1024];
00104
00105 va_start(arglist, s);
00106 scvsnprintf(buf, lengthof(buf) - 2, s, arglist);
00107 va_end(arglist);
00108 scstrcat(buf, _SC("\n"));
00109
00110
00111 SQPrintFunc *func = ((Squirrel *)sq_getforeignptr(vm))->print_func;
00112 if (func == NULL) {
00113 scprintf(_SC("%s"), buf);
00114 } else {
00115 (*func)(false, buf);
00116 }
00117 }
00118
00119 void Squirrel::AddMethod(const char *method_name, SQFUNCTION proc, uint nparam, const char *params, void *userdata, int size)
00120 {
00121 sq_pushstring(this->vm, OTTD2FS(method_name), -1);
00122
00123 if (size != 0) {
00124 void *ptr = sq_newuserdata(vm, size);
00125 memcpy(ptr, userdata, size);
00126 }
00127
00128 sq_newclosure(this->vm, proc, size != 0 ? 1 : 0);
00129 if (nparam != 0) sq_setparamscheck(this->vm, nparam, OTTD2FS(params));
00130 sq_setnativeclosurename(this->vm, -1, OTTD2FS(method_name));
00131 sq_newslot(this->vm, -3, SQFalse);
00132 }
00133
00134 void Squirrel::AddConst(const char *var_name, int value)
00135 {
00136 sq_pushstring(this->vm, OTTD2FS(var_name), -1);
00137 sq_pushinteger(this->vm, value);
00138 sq_newslot(this->vm, -3, SQTrue);
00139 }
00140
00141 void Squirrel::AddConst(const char *var_name, bool value)
00142 {
00143 sq_pushstring(this->vm, OTTD2FS(var_name), -1);
00144 sq_pushbool(this->vm, value);
00145 sq_newslot(this->vm, -3, SQTrue);
00146 }
00147
00148 void Squirrel::AddClassBegin(const char *class_name)
00149 {
00150 sq_pushroottable(this->vm);
00151 sq_pushstring(this->vm, OTTD2FS(class_name), -1);
00152 sq_newclass(this->vm, SQFalse);
00153 }
00154
00155 void Squirrel::AddClassBegin(const char *class_name, const char *parent_class)
00156 {
00157 sq_pushroottable(this->vm);
00158 sq_pushstring(this->vm, OTTD2FS(class_name), -1);
00159 sq_pushstring(this->vm, OTTD2FS(parent_class), -1);
00160 if (SQ_FAILED(sq_get(this->vm, -3))) {
00161 DEBUG(misc, 0, "[squirrel] Failed to initialize class '%s' based on parent class '%s'", class_name, parent_class);
00162 DEBUG(misc, 0, "[squirrel] Make sure that '%s' exists before trying to define '%s'", parent_class, class_name);
00163 return;
00164 }
00165 sq_newclass(this->vm, SQTrue);
00166 }
00167
00168 void Squirrel::AddClassEnd()
00169 {
00170 sq_newslot(vm, -3, SQFalse);
00171 sq_pop(vm, 1);
00172 }
00173
00174 bool Squirrel::MethodExists(HSQOBJECT instance, const char *method_name)
00175 {
00176 assert(!this->crashed);
00177 int top = sq_gettop(this->vm);
00178
00179 sq_pushobject(this->vm, instance);
00180
00181 sq_pushstring(this->vm, OTTD2FS(method_name), -1);
00182 if (SQ_FAILED(sq_get(this->vm, -2))) {
00183 sq_settop(this->vm, top);
00184 return false;
00185 }
00186 sq_settop(this->vm, top);
00187 return true;
00188 }
00189
00190 bool Squirrel::Resume(int suspend)
00191 {
00192 assert(!this->crashed);
00193 this->crashed = !sq_resumecatch(this->vm, suspend);
00194 return this->vm->_suspended != 0;
00195 }
00196
00197 void Squirrel::ResumeError()
00198 {
00199 assert(!this->crashed);
00200 sq_resumeerror(this->vm);
00201 }
00202
00203 void Squirrel::CollectGarbage()
00204 {
00205 sq_collectgarbage(this->vm);
00206 }
00207
00208 bool Squirrel::CallMethod(HSQOBJECT instance, const char *method_name, HSQOBJECT *ret, int suspend)
00209 {
00210 assert(!this->crashed);
00211
00212
00213
00214 SQInteger last_target = this->vm->_suspended_target;
00215
00216 int top = sq_gettop(this->vm);
00217
00218 sq_pushobject(this->vm, instance);
00219
00220 sq_pushstring(this->vm, OTTD2FS(method_name), -1);
00221 if (SQ_FAILED(sq_get(this->vm, -2))) {
00222 DEBUG(misc, 0, "[squirrel] Could not find '%s' in the class", method_name);
00223 sq_settop(this->vm, top);
00224 return false;
00225 }
00226
00227 sq_pushobject(this->vm, instance);
00228 if (SQ_FAILED(sq_call(this->vm, 1, ret == NULL ? SQFalse : SQTrue, SQTrue, suspend))) return false;
00229 if (ret != NULL) sq_getstackobj(vm, -1, ret);
00230
00231
00232 if (suspend == -1 || !this->IsSuspended()) sq_settop(this->vm, top);
00233
00234 this->vm->_suspended_target = last_target;
00235
00236 return true;
00237 }
00238
00239 bool Squirrel::CallStringMethodStrdup(HSQOBJECT instance, const char *method_name, const char **res, int suspend)
00240 {
00241 HSQOBJECT ret;
00242 if (!this->CallMethod(instance, method_name, &ret, suspend)) return false;
00243 if (ret._type != OT_STRING) return false;
00244 *res = strdup(ObjectToString(&ret));
00245 return true;
00246 }
00247
00248 bool Squirrel::CallIntegerMethod(HSQOBJECT instance, const char *method_name, int *res, int suspend)
00249 {
00250 HSQOBJECT ret;
00251 if (!this->CallMethod(instance, method_name, &ret, suspend)) return false;
00252 if (ret._type != OT_INTEGER) return false;
00253 *res = ObjectToInteger(&ret);
00254 return true;
00255 }
00256
00257 bool Squirrel::CallBoolMethod(HSQOBJECT instance, const char *method_name, bool *res, int suspend)
00258 {
00259 HSQOBJECT ret;
00260 if (!this->CallMethod(instance, method_name, &ret, suspend)) return false;
00261 if (ret._type != OT_BOOL) return false;
00262 *res = ObjectToBool(&ret);
00263 return true;
00264 }
00265
00266 bool Squirrel::CreateClassInstanceVM(HSQUIRRELVM vm, const char *class_name, void *real_instance, HSQOBJECT *instance, SQRELEASEHOOK release_hook)
00267 {
00268 int oldtop = sq_gettop(vm);
00269
00270
00271 sq_pushroottable(vm);
00272 sq_pushstring(vm, OTTD2FS(class_name), -1);
00273 if (SQ_FAILED(sq_get(vm, -2))) {
00274 DEBUG(misc, 0, "[squirrel] Failed to find class by the name '%s'", class_name);
00275 sq_settop(vm, oldtop);
00276 return false;
00277 }
00278
00279
00280 if (SQ_FAILED(sq_createinstance(vm, -1))) {
00281 DEBUG(misc, 0, "[squirrel] Failed to create instance for class '%s'", class_name);
00282 sq_settop(vm, oldtop);
00283 return false;
00284 }
00285
00286 if (instance != NULL) {
00287
00288 sq_getstackobj(vm, -1, instance);
00289
00290 sq_addref(vm, instance);
00291 }
00292 sq_remove(vm, -2);
00293 sq_remove(vm, -2);
00294
00295
00296 sq_setinstanceup(vm, -1, real_instance);
00297 if (release_hook != NULL) sq_setreleasehook(vm, -1, release_hook);
00298
00299 if (instance != NULL) sq_settop(vm, oldtop);
00300
00301 return true;
00302 }
00303
00304 bool Squirrel::CreateClassInstance(const char *class_name, void *real_instance, HSQOBJECT *instance)
00305 {
00306 return Squirrel::CreateClassInstanceVM(this->vm, class_name, real_instance, instance, NULL);
00307 }
00308
00309 Squirrel::Squirrel()
00310 {
00311 this->vm = sq_open(1024);
00312 this->print_func = NULL;
00313 this->global_pointer = NULL;
00314 this->crashed = false;
00315
00316
00317 sq_setcompilererrorhandler(this->vm, &Squirrel::CompileError);
00318 sq_notifyallexceptions(this->vm, SQTrue);
00319
00320 sq_setprintfunc(this->vm, &Squirrel::PrintFunc);
00321
00322 sq_newclosure(this->vm, &Squirrel::_RunError, 0);
00323 sq_seterrorhandler(this->vm);
00324
00325
00326 sq_setforeignptr(this->vm, this);
00327
00328 sq_pushroottable(this->vm);
00329 squirrel_register_global_std(this);
00330 }
00331
00332 class SQFile {
00333 private:
00334 FILE *file;
00335 size_t size;
00336 size_t pos;
00337
00338 public:
00339 SQFile(FILE *file, size_t size) : file(file), size(size), pos(0) {}
00340
00341 size_t Read(void *buf, size_t elemsize, size_t count)
00342 {
00343 assert(elemsize != 0);
00344 if (this->pos + (elemsize * count) > this->size) {
00345 count = (this->size - this->pos) / elemsize;
00346 }
00347 if (count == 0) return 0;
00348 size_t ret = fread(buf, elemsize, count, this->file);
00349 this->pos += ret * elemsize;
00350 return ret;
00351 }
00352 };
00353
00354 static SQInteger _io_file_lexfeed_ASCII(SQUserPointer file)
00355 {
00356 char c;
00357 if (((SQFile *)file)->Read(&c, sizeof(c), 1) > 0) return c;
00358 return 0;
00359 }
00360
00361 static SQInteger _io_file_lexfeed_UTF8(SQUserPointer file)
00362 {
00363 static const SQInteger utf8_lengths[16] =
00364 {
00365 1, 1, 1, 1, 1, 1, 1, 1,
00366 0, 0, 0, 0,
00367 2, 2,
00368 3,
00369 4
00370 };
00371 static unsigned char byte_masks[5] = {0, 0, 0x1F, 0x0F, 0x07};
00372 unsigned char inchar;
00373 SQInteger c = 0;
00374 if (((SQFile *)file)->Read(&inchar, sizeof(inchar), 1) != 1) return 0;
00375 c = inchar;
00376
00377 if (c >= 0x80) {
00378 SQInteger tmp;
00379 SQInteger codelen = utf8_lengths[c >> 4];
00380 if (codelen == 0) return 0;
00381
00382 tmp = c & byte_masks[codelen];
00383 for (SQInteger n = 0; n < codelen - 1; n++) {
00384 tmp <<= 6;
00385 if (((SQFile *)file)->Read(&inchar, sizeof(inchar), 1) != 1) return 0;
00386 tmp |= inchar & 0x3F;
00387 }
00388 c = tmp;
00389 }
00390 return c;
00391 }
00392
00393 static SQInteger _io_file_lexfeed_UCS2_LE(SQUserPointer file)
00394 {
00395 wchar_t c;
00396 if (((SQFile *)file)->Read(&c, sizeof(c), 1) > 0) return (SQChar)c;
00397 return 0;
00398 }
00399
00400 static SQInteger _io_file_lexfeed_UCS2_BE(SQUserPointer file)
00401 {
00402 unsigned short c;
00403 if (((SQFile *)file)->Read(&c, sizeof(c), 1) > 0) {
00404 c = ((c >> 8) & 0x00FF)| ((c << 8) & 0xFF00);
00405 return (SQChar)c;
00406 }
00407 return 0;
00408 }
00409
00410 static SQInteger _io_file_read(SQUserPointer file, SQUserPointer buf, SQInteger size)
00411 {
00412 SQInteger ret = ((SQFile *)file)->Read(buf, 1, size);
00413 if (ret == 0) return -1;
00414 return ret;
00415 }
00416
00417 SQRESULT Squirrel::LoadFile(HSQUIRRELVM vm, const char *filename, SQBool printerror)
00418 {
00419 size_t size;
00420 FILE *file = FioFOpenFile(filename, "rb", AI_DIR, &size);
00421 SQInteger ret;
00422 unsigned short us;
00423 unsigned char uc;
00424 SQLEXREADFUNC func;
00425
00426 if (file != NULL) {
00427 SQFile f(file, size);
00428 ret = fread(&us, 1, sizeof(us), file);
00429
00430 if (ret != 2) us = 0;
00431
00432 switch (us) {
00433 case SQ_BYTECODE_STREAM_TAG: {
00434 fseek(file, -2, SEEK_CUR);
00435 if (SQ_SUCCEEDED(sq_readclosure(vm, _io_file_read, &f))) {
00436 FioFCloseFile(file);
00437 return SQ_OK;
00438 }
00439 FioFCloseFile(file);
00440 return sq_throwerror(vm, _SC("Couldn't read bytecode"));
00441 }
00442 case 0xFFFE: func = _io_file_lexfeed_UCS2_BE; break;
00443 case 0xFEFF: func = _io_file_lexfeed_UCS2_LE; break;
00444 case 0xBBEF:
00445 if (fread(&uc, 1, sizeof(uc), file) == 0) {
00446 FioFCloseFile(file);
00447 return sq_throwerror(vm, _SC("I/O error"));
00448 }
00449 if (uc != 0xBF) {
00450 FioFCloseFile(file);
00451 return sq_throwerror(vm, _SC("Unrecognized encoding"));
00452 }
00453 func = _io_file_lexfeed_UTF8;
00454 break;
00455 default: func = _io_file_lexfeed_ASCII; fseek(file, -2, SEEK_CUR); break;
00456 }
00457
00458 if (SQ_SUCCEEDED(sq_compile(vm, func, &f, OTTD2FS(filename), printerror))) {
00459 FioFCloseFile(file);
00460 return SQ_OK;
00461 }
00462 FioFCloseFile(file);
00463 return SQ_ERROR;
00464 }
00465 return sq_throwerror(vm, _SC("cannot open the file"));
00466 }
00467
00468 bool Squirrel::LoadScript(HSQUIRRELVM vm, const char *script, bool in_root)
00469 {
00470
00471 if (in_root) sq_pushroottable(vm);
00472
00473
00474 if (SQ_SUCCEEDED(LoadFile(vm, script, SQTrue))) {
00475 sq_push(vm, -2);
00476 if (SQ_SUCCEEDED(sq_call(vm, 1, SQFalse, SQTrue, 100000))) {
00477 sq_pop(vm, 1);
00478 return true;
00479 }
00480 }
00481
00482 DEBUG(misc, 0, "[squirrel] Failed to compile '%s'", script);
00483 return false;
00484 }
00485
00486 bool Squirrel::LoadScript(const char *script)
00487 {
00488 return LoadScript(this->vm, script);
00489 }
00490
00491 Squirrel::~Squirrel()
00492 {
00493
00494 sq_pop(this->vm, 1);
00495 sq_close(this->vm);
00496 }
00497
00498 void Squirrel::InsertResult(bool result)
00499 {
00500 sq_pushbool(this->vm, result);
00501 vm->GetAt(vm->_stackbase + vm->_suspended_target) = vm->GetUp(-1);
00502 vm->Pop();
00503 }
00504
00505 void Squirrel::InsertResult(int result)
00506 {
00507 sq_pushinteger(this->vm, result);
00508 vm->GetAt(vm->_stackbase + vm->_suspended_target) = vm->GetUp(-1);
00509 vm->Pop();
00510 }
00511
00512 void Squirrel::DecreaseOps(HSQUIRRELVM vm, int ops)
00513 {
00514 vm->DecreaseOps(ops);
00515 }
00516
00517 bool Squirrel::IsSuspended()
00518 {
00519 return this->vm->_suspended != 0;
00520 }
00521
00522 bool Squirrel::HasScriptCrashed()
00523 {
00524 return this->crashed;
00525 }
00526
00527 void Squirrel::ResetCrashed()
00528 {
00529 this->crashed = false;
00530 }
00531
00532 void Squirrel::CrashOccurred()
00533 {
00534 this->crashed = true;
00535 }
00536
00537 bool Squirrel::CanSuspend()
00538 {
00539 return sq_can_suspend(this->vm);
00540 }