1 /*
2 * Copyright 2009-2021 Peter Kosyh <p.kosyh at gmail.com>
3 *
4 * Permission is hereby granted, free of charge, to any person
5 * obtaining a copy of this software and associated documentation files
6 * (the "Software"), to deal in the Software without restriction,
7 * including without limitation the rights to use, copy, modify, merge,
8 * publish, distribute, sublicense, and/or sell copies of the Software,
9 * and to permit persons to whom the Software is furnished to do so,
10 * subject to the following conditions:
11 *
12 * The above copyright notice and this permission notice shall be
13 * included in all copies or substantial portions of the Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
19 * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
20 * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
21 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22 *
23 */
24
25 #include "system.h"
26 #include "instead.h"
27 #include "util.h"
28 #include "list.h"
29
30 #define DATA_IDF INSTEAD_IDF
31 #ifdef _USE_SDL
32 #ifndef __EMSCRIPTEN__
33 static SDL_mutex *sem;
34 #endif
instead_lock(void)35 void instead_lock(void) {
36 #ifndef __EMSCRIPTEN__
37 SDL_LockMutex(sem);
38 #endif
39 }
instead_unlock(void)40 void instead_unlock(void) {
41 #ifndef __EMSCRIPTEN__
42 SDL_UnlockMutex(sem);
43 #endif
44 }
45 #else
instead_lock(void)46 void instead_lock(void) {
47 }
instead_unlock(void)48 void instead_unlock(void) {
49 }
50 #endif
51 static char instead_cwd_path[PATH_MAX];
52 static char instead_game_path[PATH_MAX];
53
54 static int debug_sw = 0;
55 static int standalone_sw = 0;
56
57 static int busy = 0;
58 static idf_t data_idf = NULL;
59 /* the Lua interpreter */
60
61 char *instead_fromgame(const char *s);
62 char *togame(const char *s);
63 lua_State *L = NULL;
64
65 static char *err_msg = NULL;
66 static char instead_api_path[PATH_MAX + 1];
67 static char instead_base_path[PATH_MAX] = STEAD_PATH;
68
69 static char *API = NULL;
70 static char *MAIN = NULL;
71
72 #define STEAD_API_PATH instead_api_path
73
74 #define ERR_MSG_MAX 512
75
76 static struct list_head extensions = LIST_HEAD_INIT(extensions);
77
78 #define for_each_extension(ext) list_for_each(&extensions, ext, list)
79
80 enum instead_hook {
81 init,
82 done,
83 cmd,
84 err,
85 };
86
87 #define HOOK_INIT 1
88 #define HOOK_DONE 2
89 #define HOOK_CMD 3
90 #define HOOK_ERR 4
91
extensions_hook(enum instead_hook nr)92 static int extensions_hook(enum instead_hook nr)
93 {
94 int rc = 0;
95 struct instead_ext *ext = NULL;
96 for_each_extension(ext) {
97 switch (nr) {
98 case init:
99 if (ext->init)
100 rc = ext->init();
101 break;
102 case done:
103 if (ext->done)
104 rc = ext->done();
105 break;
106 case cmd:
107 if (ext->cmd)
108 rc = ext->cmd();
109 break;
110 case err:
111 if (ext->err)
112 rc = ext->err();
113 break;
114 default:
115 return -1;
116 }
117 if (rc)
118 break;
119 }
120 return rc;
121 }
122
instead_extension(struct instead_ext * ext)123 int instead_extension(struct instead_ext *ext)
124 {
125 struct instead_ext *e = NULL;
126 for_each_extension(e) {
127 if (e == ext)
128 return 0;
129 }
130 list_add(&extensions, &ext->list);
131 return 0;
132 }
133
instead_busy(void)134 int instead_busy(void)
135 {
136 return busy;
137 }
138
instead_err_msg(const char * s)139 void instead_err_msg(const char *s)
140 {
141 if (err_msg)
142 free(err_msg);
143 if (s) {
144 err_msg = strdup(s);
145 if (err_msg && strlen(err_msg) > ERR_MSG_MAX) {
146 err_msg[ERR_MSG_MAX - 4] = 0;
147 strcat(err_msg, "...");
148 }
149 } else
150 err_msg = NULL;
151 }
152
instead_err(void)153 const char *instead_err(void)
154 {
155 return err_msg;
156 }
157
report(lua_State * L,int status)158 static int report (lua_State *L, int status)
159 {
160 if (status && !lua_isnil(L, -1)) {
161 char *p;
162 const char *msg = lua_tostring(L, -1);
163 if (msg == NULL)
164 msg = "(error object is not a string)";
165 fprintf(stderr,"Error: %s\n", msg);
166 p = instead_fromgame(msg);
167 instead_err_msg(p?p:msg);
168 if (p)
169 free(p);
170 lua_pop(L, 1);
171 status = -1;
172 extensions_hook(err);
173 }
174 return status;
175 }
176
177 #if LUA_VERSION_NUM >= 502
traceback(lua_State * L)178 static int traceback (lua_State *L) {
179 const char *msg = lua_tostring(L, 1);
180 if (msg)
181 luaL_traceback(L, L, msg, 1);
182 else if (!lua_isnoneornil(L, 1)) { /* is there an error object? */
183 if (!luaL_callmeta(L, 1, "__tostring")) /* try its 'tostring' metamethod */
184 lua_pushliteral(L, "(no error message)");
185 }
186 return 1;
187 }
188 #else
traceback(lua_State * L)189 static int traceback (lua_State *L)
190 {
191 lua_getfield(L, LUA_GLOBALSINDEX, "debug");
192 if (!lua_istable(L, -1)) {
193 lua_pop(L, 1);
194 return 1;
195 }
196 lua_getfield(L, -1, "traceback");
197 if (!lua_isfunction(L, -1)) {
198 lua_pop(L, 2);
199 return 1;
200 }
201 lua_pushvalue(L, 1); /* pass error message */
202 lua_pushinteger(L, 2); /* skip this function and traceback */
203 lua_call(L, 2, 1); /* call debug.traceback */
204 return 1;
205 }
206 #endif
docall(lua_State * L,int narg)207 static int docall (lua_State *L, int narg)
208 {
209 int status;
210 int base = 0;
211 if (debug_sw) {
212 base = lua_gettop(L) - narg; /* function index */
213 lua_pushcfunction(L, traceback); /* push traceback function */
214 lua_insert(L, base); /* put it under chunk and args */
215 }
216 busy ++;
217 status = lua_pcall(L, narg, LUA_MULTRET, base);
218 busy --;
219 if (debug_sw)
220 lua_remove(L, base); /* remove traceback function */
221 /* force a complete garbage collection in case of errors */
222 if (status != 0)
223 lua_gc(L, LUA_GCCOLLECT, 0);
224 return status;
225 }
226
instead_pcall(lua_State * L,int nargs)227 int instead_pcall(lua_State *L, int nargs)
228 {
229 int status;
230 status = docall(L, nargs);
231 status = report(L, status);
232 return status;
233 }
234
dofile(lua_State * L,const char * name)235 static int dofile (lua_State *L, const char *name) {
236 int status = luaL_loadfile(L, name) || docall(L, 0);
237 return report(L, status);
238 }
239
idf_reader(lua_State * L,void * data,size_t * size)240 static const char *idf_reader(lua_State *L, void *data, size_t *size)
241 {
242 static char buff[4096];
243 int rc;
244 rc = idf_read((idff_t)data, buff, 1, sizeof(buff));
245 *size = rc;
246 if (!rc)
247 return NULL;
248 return buff;
249 }
250
dofile_idf(lua_State * L,idff_t idf,const char * name)251 static int dofile_idf (lua_State *L, idff_t idf, const char *name) {
252 #if LUA_VERSION_NUM >= 502
253 int status = lua_load(L, idf_reader, idf, name, "bt") || docall(L, 0);
254 #else
255 int status = lua_load(L, idf_reader, idf, name) || docall(L, 0);
256 #endif
257 return report(L, status);
258 }
259
dostring(lua_State * L,const char * s)260 static int dostring (lua_State *L, const char *s) {
261 int status = luaL_loadstring(L, s) || docall(L, 0);
262 return report(L, status);
263 }
264
getstring(char * cmd)265 char *getstring(char *cmd)
266 {
267 char *s;
268 int N;
269 if (!L)
270 return NULL;
271 if (dostring(L, cmd))
272 return NULL;
273 N = lua_gettop(L); /* number of arguments */
274 if (-N >=0)
275 return NULL;
276 s = (char*)lua_tostring(L, -N);
277 if (s)
278 s = instead_fromgame(s);
279 return s;
280 }
281
instead_eval(char * s)282 int instead_eval(char *s)
283 {
284 if (!L)
285 return -1;
286 if (dostring(L, s))
287 return -1;
288 return 0;
289 }
290
instead_clear(void)291 int instead_clear(void)
292 {
293 int N;
294 if (!L)
295 return -1;
296 N = lua_gettop(L); /* number of arguments */
297 lua_pop(L, N);
298 return 0;
299 }
300
instead_retval(int n)301 char *instead_retval(int n)
302 {
303 char *s;
304 int N;
305 if (!L)
306 return NULL;
307 N = lua_gettop(L); /* number of arguments */
308 /* fprintf(stderr,"%d\n", N); */
309 if (n - N >= 0)
310 return NULL;
311 s = (char*)lua_tostring(L, n - N);
312 if (s)
313 s = instead_fromgame(s);
314 return s;
315 }
316
instead_bretval(int n)317 int instead_bretval(int n)
318 {
319 int N;
320 if (!L)
321 return 0;
322 N = lua_gettop(L); /* number of arguments */
323 if (n - N >= 0)
324 return 1;
325 return lua_toboolean(L, n - N);
326 }
327
instead_iretval(int n)328 int instead_iretval(int n)
329 {
330 int N;
331 if (!L)
332 return 0;
333 N = lua_gettop(L); /* number of arguments */
334 if (n - N >= 0)
335 return 0;
336 return lua_tonumber(L, n - N);
337 }
338
instead_file_cmd(char * s,int * rc)339 char *instead_file_cmd(char *s, int *rc)
340 {
341 struct instead_args args[] = {
342 { .val = NULL, .type = INSTEAD_STR },
343 { .val = NULL, },
344 };
345 if (!s)
346 return NULL;
347 args[0].val = s;
348 instead_function("iface:cmd", args);
349 s = instead_retval(0);
350 if (rc)
351 *rc = !instead_bretval(1);
352 instead_clear();
353 extensions_hook(cmd);
354 return s;
355 }
356
instead_cmd(char * s,int * rc)357 char *instead_cmd(char *s, int *rc)
358 {
359 struct instead_args args[] = {
360 { .val = NULL, .type = INSTEAD_STR },
361 { .val = NULL, },
362 };
363 if (!s)
364 return NULL;
365 s = togame(s);
366 if (!s)
367 return NULL;
368 args[0].val = s;
369 instead_function("iface:cmd", args);
370 free(s);
371 s = instead_retval(0);
372 if (rc)
373 *rc = !instead_bretval(1);
374 instead_clear();
375 extensions_hook(cmd);
376 return s;
377 }
378
instead_function(char * s,struct instead_args * args)379 int instead_function(char *s, struct instead_args *args)
380 {
381 int base = 0;
382 int status = 0;
383 int n = 0;
384 char *p;
385 char f[64];
386 int method = 0;
387 if (!L)
388 return -1;
389 strcpy(f, s);
390 p = strchr(f, '.');
391 if (!p)
392 p = strchr(f, ':');
393 if (p) {
394 if (*p == ':')
395 method = 1;
396 *p = 0;
397 p ++;
398 lua_getglobal(L, f);
399 lua_getfield(L, -1, p);
400 lua_remove(L, -2);
401 if (method)
402 lua_getglobal(L, f);
403 } else
404 lua_getglobal(L, s);
405 if (args) {
406 while (args->val) {
407 switch(args->type) {
408 case INSTEAD_NIL:
409 lua_pushnil(L);
410 break;
411 case INSTEAD_NUM:
412 lua_pushnumber(L, atoi(args->val));
413 break;
414 case INSTEAD_BOOL:
415 if (!strcmp(args->val, "true"))
416 lua_pushboolean(L, 1);
417 else
418 lua_pushboolean(L, 0);
419 break;
420 default:
421 case INSTEAD_STR:
422 lua_pushstring(L, args->val);
423 }
424 args ++;
425 n ++;
426 }
427 }
428 if (debug_sw) {
429 base = lua_gettop(L) - (method + n); /* function index */
430 lua_pushcfunction(L, traceback); /* push traceback function */
431 lua_insert(L, base); /* put it under chunk and args */
432 }
433 busy ++;
434 status = lua_pcall(L, method + n, LUA_MULTRET, base);
435 busy --;
436 if (debug_sw)
437 lua_remove(L, base); /* remove traceback function */
438 if (status) {
439 fprintf(stderr, "Error calling:%s\n", s);
440 lua_gc(L, LUA_GCCOLLECT, 0);
441 }
442 return report(L, status);
443 }
444
445 #ifdef _HAVE_ICONV
446 static char *curcp = NULL;
447 static char *fromcp = NULL;
448 #endif
449
450 #ifdef _HAVE_ICONV
instead_set_encoding(const char * cp)451 void instead_set_encoding(const char *cp)
452 {
453 if (curcp)
454 free(curcp);
455 if (cp)
456 curcp = strdup(cp);
457 else
458 curcp = NULL;
459 return;
460 }
instead_fromgame(const char * s)461 char *instead_fromgame(const char *s)
462 {
463 iconv_t han;
464 char *str;
465 if (!s)
466 return NULL;
467 if (!fromcp)
468 goto out0;
469 han = iconv_open(curcp, fromcp);
470 if (han == (iconv_t)-1)
471 goto out0;
472 if (!(str = decode(han, s)))
473 goto out1;
474 iconv_close(han);
475 return str;
476 out1:
477 iconv_close(han);
478 out0:
479 return strdup(s);
480 }
481
togame(const char * s)482 char *togame(const char *s)
483 {
484 iconv_t han;
485 char *str;
486 if (!s)
487 return NULL;
488 if (!fromcp)
489 goto out0;
490 han = iconv_open(fromcp, curcp);
491 if (han == (iconv_t)-1)
492 goto out0;
493 if (!(str = decode(han, s)))
494 goto out1;
495 iconv_close(han);
496 return str;
497 out1:
498 iconv_close(han);
499 out0:
500 return strdup(s);
501 }
502 #else
instead_fromgame(const char * s)503 char *instead_fromgame(const char *s)
504 {
505 if (!s)
506 return NULL;
507 return strdup(s);
508 }
togame(const char * s)509 char *togame(const char *s)
510 {
511 if (!s)
512 return NULL;
513 return strdup(s);
514 }
instead_set_encoding(const char * cp)515 void instead_set_encoding(const char *cp)
516 {
517 }
518 #endif
519
instead_getargs(char ** argv,int n)520 static int instead_getargs (char **argv, int n)
521 {
522 int i;
523
524 for (i = 0; i < n; i++) {
525 lua_pushstring(L, argv[i]);
526 }
527
528 lua_createtable(L, n, 0);
529 for (i = 0; i < n; i++) {
530 lua_pushstring(L, argv[i]);
531 lua_rawseti(L, -2, i + 1);
532 }
533 lua_setglobal(L, "arg");
534 return 0;
535 }
536
instead_load(char ** info)537 int instead_load(char **info)
538 {
539 int rc;
540 idff_t idf;
541
542 rc = instead_function("stead:init", NULL); instead_clear();
543 if (rc)
544 goto err;
545
546 idf = idf_open(data_idf, MAIN);
547
548 if (idf) {
549 int rc = dofile_idf(L, idf, MAIN);
550 idf_close(idf);
551 if (rc)
552 goto err;
553 } else if (dofile(L, dirpath(MAIN))) {
554 goto err;
555 }
556 instead_clear();
557 #ifdef _HAVE_ICONV
558 if (fromcp)
559 free(fromcp);
560 fromcp = getstring("return game.codepage;");
561 instead_clear();
562 #endif
563 rc = instead_function("game:ini", NULL);
564 if (rc)
565 goto err2;
566 if (info) {
567 *info = instead_retval(0);
568 *info = instead_fromgame(*info);
569 }
570 instead_clear();
571 return rc;
572 err2:
573 instead_clear();
574 err:
575 return -1;
576 }
577
instead_loadfile(char * name)578 int instead_loadfile(char *name)
579 {
580 return instead_loadscript(name, -1, NULL, 1);
581 }
582
instead_loadscript(char * name,int argc,char ** argv,int exec)583 int instead_loadscript(char *name, int argc, char **argv, int exec)
584 {
585 int status;
586 if (exec && argc >= 0)
587 instead_getargs(argv, argc);
588 status = luaL_loadfile(L, name);
589 if (!status) {
590 if (exec) {
591 if (argc >= 0)
592 lua_insert(L, -(argc + 1));
593 else
594 argc = 0;
595 status |= docall(L, argc);
596 }
597 }
598 status = report(L, status);
599 instead_clear();
600 return status;
601 }
602
603 typedef struct LoadF {
604 int extraline;
605 unsigned char byte;
606 FILE *f;
607 idff_t idff;
608 int enc;
609 unsigned char buff[4096];
610 } LoadF;
611
getF(lua_State * L,void * ud,size_t * size)612 static const char *getF (lua_State *L, void *ud, size_t *size) {
613 unsigned int i = 0;
614 LoadF *lf = (LoadF *)ud;
615 (void)L;
616 if (lf->extraline) {
617 lf->extraline = 0;
618 *size = 1;
619 return "\n";
620 }
621
622 if (lf->f && feof(lf->f))
623 return NULL;
624 if (lf->idff && idf_eof(lf->idff))
625 return NULL;
626
627 if (lf->idff)
628 *size = idf_read(lf->idff, lf->buff, 1, sizeof(lf->buff));
629 else
630 *size = fread(lf->buff, 1, sizeof(lf->buff), lf->f);
631
632 if (lf->enc) {
633 for (i = 0; i < *size; i ++) {
634 unsigned char b = lf->buff[i];
635 lf->buff[i] ^= lf->byte;
636 lf->buff[i] = (lf->buff[i] >> 3) | (lf->buff[i] << 5);
637 lf->byte = b;
638 }
639 }
640 return (*size > 0) ? (char*)lf->buff : NULL;
641 }
642
errfile(lua_State * L,const char * what,int fnameindex)643 static int errfile (lua_State *L, const char *what, int fnameindex) {
644 const char *serr = strerror(errno);
645 const char *filename = lua_tostring(L, fnameindex) + 1;
646 lua_pushfstring(L, "cannot %s %s: %s", what, filename, serr);
647 lua_remove(L, fnameindex);
648 return LUA_ERRFILE;
649 }
650
loadfile(lua_State * L,const char * filename,int enc)651 static int loadfile (lua_State *L, const char *filename, int enc) {
652 LoadF lf;
653 int status, readstatus;
654 int fnameindex = lua_gettop(L) + 1; /* index of filename on the stack */
655 lf.extraline = 0;
656 lua_pushfstring(L, "@%s", filename);
657 lf.idff = idf_open(data_idf, filename);
658 if (!lf.idff)
659 lf.f = fopen(dirpath(filename), "rb");
660 else
661 lf.f = NULL;
662 lf.byte = 0xcc;
663 lf.enc = enc;
664 if (lf.f == NULL && lf.idff == NULL) return errfile(L, "open", fnameindex);
665
666 #if LUA_VERSION_NUM >= 502
667 status = lua_load(L, getF, &lf, lua_tostring(L, -1), "bt");
668 #else
669 status = lua_load(L, getF, &lf, lua_tostring(L, -1));
670 #endif
671 if (lf.f)
672 readstatus = ferror(lf.f);
673 else
674 readstatus = idf_error(lf.idff);
675
676 if (lf.f)
677 fclose(lf.f); /* close file (even in case of errors) */
678 else
679 idf_close(lf.idff);
680
681 if (readstatus) {
682 lua_settop(L, fnameindex); /* ignore results from `lua_load' */
683 return errfile(L, "read", fnameindex);
684 }
685 lua_remove(L, fnameindex);
686 return status;
687 }
688
689
luaB_doencfile(lua_State * L)690 static int luaB_doencfile (lua_State *L) {
691 const char *fname = luaL_optstring(L, 1, NULL);
692 int n = lua_gettop(L);
693 if (loadfile(L, fname, 1) != 0) lua_error(L);
694 lua_call(L, 0, LUA_MULTRET);
695 return lua_gettop(L) - n;
696 }
697
luaB_dofile(lua_State * L)698 static int luaB_dofile (lua_State *L) {
699 const char *fname = luaL_optstring(L, 1, NULL);
700 int n = lua_gettop(L);
701 if (loadfile(L, fname, 0) != 0) lua_error(L);
702 lua_call(L, 0, LUA_MULTRET);
703 return lua_gettop(L) - n;
704 }
705
706 #if LUA_VERSION_NUM <= 503
707 /* is this hack still needed? */
luaB_print(lua_State * L)708 static int luaB_print (lua_State *L) {
709 int n = lua_gettop(L); /* number of arguments */
710 int i;
711 lua_getglobal(L, "tostring");
712 for (i=1; i<=n; i++) {
713 const char *s;
714 lua_pushvalue(L, -1); /* function to be called */
715 lua_pushvalue(L, i); /* value to print */
716 lua_call(L, 1, 1);
717 s = lua_tostring(L, -1); /* get result */
718 if (s == NULL)
719 return luaL_error(L, LUA_QL("tostring") " must return a string to "LUA_QL("print"));
720 if (i>1) fputs("\t", stdout);
721 fputs(s, stdout);
722 lua_pop(L, 1); /* pop result */
723 }
724 fputs("\n", stdout);
725 return 0;
726 }
727 #else
luaB_print(lua_State * L)728 static int luaB_print (lua_State *L) {
729 int n = lua_gettop(L); /* number of arguments */
730 int i;
731 for (i = 1; i <= n; i++) { /* for each argument */
732 size_t l;
733 const char *s = luaL_tolstring(L, i, &l); /* convert it to string */
734 if (i > 1) /* not the first element? */
735 lua_writestring("\t", 1); /* add a tab before it */
736 lua_writestring(s, l); /* print it */
737 lua_pop(L, 1); /* pop result */
738 }
739 lua_writeline();
740 return 0;
741 }
742 #endif
743
luaB_maxn(lua_State * L)744 static int luaB_maxn (lua_State *L) {
745 lua_Integer max = 0;
746 luaL_checktype(L, 1, LUA_TTABLE);
747 lua_pushnil(L); /* first key */
748 while (lua_next(L, 1)) {
749 lua_pop(L, 1); /* remove value */
750 if (lua_type(L, -1) == LUA_TNUMBER) {
751 lua_Number v = lua_tonumber(L, -1);
752 if (v > max) max = v;
753 }
754 }
755 lua_pushinteger(L, max);
756 return 1;
757 }
758
luaB_srandom(lua_State * L)759 static int luaB_srandom(lua_State *L) {
760 mt_random_seed(luaL_optnumber(L, 1, time(NULL)));
761 return 0;
762 }
763
luaB_random(lua_State * L)764 static int luaB_random(lua_State *L) {
765 lua_Number rt;
766 unsigned long r = 0;
767 long a = luaL_optnumber(L, 1, -1);
768 long b = luaL_optnumber(L, 2, -1);
769 r = mt_random();
770 if (a >=0 && b > a) {
771 r = a + (r % (b - a + 1));
772 lua_pushinteger(L, r);
773 } else if (a > 0 && b == -1) {
774 r = (r % a) + 1;
775 lua_pushinteger(L, r);
776 } else {
777 rt = mt_random_double();
778 lua_pushnumber(L, rt);
779 }
780 return 1;
781 }
782
luaB_get_realpath(lua_State * L)783 static int luaB_get_realpath(lua_State *L) {
784 char realpath[PATH_MAX];
785 char outpath[PATH_MAX];
786 const char *path = luaL_optstring(L, 1, NULL);
787 if (!path)
788 return 0;
789 strncpy(realpath, path, sizeof(realpath));
790 realpath[sizeof(realpath) - 1] = 0;
791 unix_path(realpath);
792 path = getrealpath(realpath, outpath);
793 if (!path)
794 return 0;
795 lua_pushstring(L, outpath);
796 return 1;
797 }
798
luaB_get_gamepath(lua_State * L)799 static int luaB_get_gamepath(lua_State *L) {
800 char path[PATH_MAX * 2];
801 char *p = getdir(path, sizeof(path));
802 if (!p)
803 return 0;
804 unix_path(p);
805
806 if (idf_only(instead_idf(), -1) == 1) { /* no gamepath */
807 strcpy(path, instead_game_path);
808 }
809
810 lua_pushstring(L, p);
811 return 1;
812 }
813
luaB_get_steadpath(lua_State * L)814 static int luaB_get_steadpath(lua_State *L) {
815 char stead_path[PATH_MAX];
816
817 if (STEAD_API_PATH[0] != '/') {
818 strcpy(stead_path, instead_cwd());
819 strcat(stead_path, "/");
820 } else
821 stead_path[0] = 0;
822 strcat(stead_path, STEAD_API_PATH);
823 unix_path(stead_path);
824 lua_pushstring(L, stead_path);
825 return 1;
826 }
827
828 #define utf_cont(p) ((*(p) & 0xc0) == 0x80)
829
utf_ff(const char * s,const char * e)830 static int utf_ff(const char *s, const char *e)
831 {
832 int l = 0;
833 if (!s || !e)
834 return 0;
835 if (s > e)
836 return 0;
837 if ((*s & 0x80) == 0) /* ascii */
838 return 1;
839 l = 1;
840 while (s < e && utf_cont(s + 1)) {
841 s ++;
842 l ++;
843 }
844 return l;
845 }
846
utf_bb(const char * s,const char * e)847 static int utf_bb(const char *s, const char *e)
848 {
849 int l = 0;
850 if (!s || !e)
851 return 0;
852 if (s > e)
853 return 0;
854 if ((*e & 0x80) == 0) /* ascii */
855 return 1;
856 l = 1;
857 while (s < e && utf_cont(e)) {
858 e --;
859 l ++;
860 }
861 return l;
862 }
863
luaB_utf_next(lua_State * L)864 static int luaB_utf_next(lua_State *L) {
865 int l = 0;
866 const char *s = luaL_optstring(L, 1, NULL);
867 int idx = luaL_optnumber(L, 2, 1) - 1;
868 if (s && idx >= 0) {
869 int len = strlen(s);
870 if (idx < len)
871 l = utf_ff(s + idx, s + len - 1);
872 }
873 lua_pushnumber(L, l);
874 return 1;
875 }
876
luaB_utf_prev(lua_State * L)877 static int luaB_utf_prev(lua_State *L) {
878 int l = 0;
879 const char *s = luaL_optstring(L, 1, NULL);
880 int idx = luaL_optnumber(L, 2, 0) - 1;
881 int len = 0;
882 if (s) {
883 len = strlen(s);
884 if (idx < 0)
885 idx += len;
886 if (idx >= 0) {
887 if (idx < len)
888 l = utf_bb(s, s + idx);
889 }
890 }
891 lua_pushnumber(L, l);
892 return 1;
893 }
894
luaB_utf_char(lua_State * L)895 static int luaB_utf_char(lua_State *L) {
896 int len, l = 0;
897 char *rs;
898 const char *s = luaL_optstring(L, 1, NULL);
899 int idx = luaL_optnumber(L, 2, 1) - 1;
900 if (!s || idx < 0)
901 return 0;
902 len = strlen(s) - 1;
903 while (idx >= 0 && len >= 0) {
904 s += l;
905 l = utf_ff(s, s + len);
906 if (l <= 0)
907 return 0;
908 idx --;
909 len -= l;
910 }
911 rs = malloc(l + 1);
912 if (!rs)
913 return 0;
914 if (l)
915 memcpy(rs, s, l);
916 rs[l] = 0;
917 lua_pushstring(L, rs);
918 free(rs);
919 return 1;
920 }
921
luaB_utf_len(lua_State * L)922 static int luaB_utf_len(lua_State *L) {
923 int l = 0;
924 int sym = 0;
925 const char *s = luaL_optstring(L, 1, NULL);
926 if (s) {
927 int len = strlen(s) - 1;
928 while (len >= 0) {
929 l = utf_ff(s, s + len);
930 if (!l)
931 break;
932 s += l;
933 len -= l;
934 sym ++;
935 }
936 }
937 lua_pushnumber(L, sym);
938 return 1;
939 }
940
941 extern int dir_iter_factory (lua_State *L);
942 extern int luaopen_lfs (lua_State *L);
943
944 static const luaL_Reg base_funcs[] = {
945 {"print", luaB_print}, /* for some mystic, it is needed in win version (with -debug) */
946
947 {"doencfile", luaB_doencfile},
948 {"dofile", luaB_dofile},
949
950 {"table_get_maxn", luaB_maxn},
951
952 {"instead_random", luaB_random},
953 {"instead_srandom", luaB_srandom},
954
955 {"instead_realpath", luaB_get_realpath},
956 {"instead_gamepath", luaB_get_gamepath},
957 {"instead_steadpath", luaB_get_steadpath},
958
959 {"instead_readdir", dir_iter_factory},
960
961 {"utf8_next", luaB_utf_next},
962 {"utf8_prev", luaB_utf_prev},
963 {"utf8_char", luaB_utf_char},
964 {"utf8_len", luaB_utf_len},
965 { NULL, NULL }
966 };
967
968
instead_platform(void)969 static int instead_platform(void)
970 {
971 char plat[64];
972 if (!L)
973 return 0;
974
975 #if defined(IOS)
976 snprintf(plat, sizeof(plat) - 1, "PLATFORM='IOS'");
977 #elif defined(__APPLE__)
978 snprintf(plat, sizeof(plat) - 1, "PLATFORM='MACOSX'");
979 #elif defined(_WIN32_WCE)
980 snprintf(plat, sizeof(plat) - 1, "PLATFORM='WINCE'");
981 #elif defined(WINRT)
982 snprintf(plat, sizeof(plat) - 1, "PLATFORM='WINRT'");
983 #elif defined(S60)
984 snprintf(plat, sizeof(plat) - 1, "PLATFORM='S60'");
985 #elif defined(ANDROID)
986 snprintf(plat, sizeof(plat) - 1, "PLATFORM='ANDROID'");
987 #elif defined(_WIN32)
988 snprintf(plat, sizeof(plat) - 1, "PLATFORM='WIN32'");
989 #elif defined(MAEMO)
990 snprintf(plat, sizeof(plat) - 1, "PLATFORM='MAEMO'");
991 #elif defined(SAILFISHOS)
992 snprintf(plat, sizeof(plat) - 1, "PLATFORM='SFOS'");
993 #else
994 snprintf(plat, sizeof(plat) - 1, "PLATFORM='UNIX'");
995 #endif
996
997 plat[sizeof(plat) - 1] = 0;
998 instead_eval(plat); instead_clear();
999 return 0;
1000 }
1001
instead_package(const char * path)1002 static int instead_package(const char *path)
1003 {
1004 char *stead_path;
1005 stead_path = malloc(PATH_MAX * 5); /* instead_cwd + STEAD_API_PATH and so on... */
1006 if (!stead_path)
1007 return -1;
1008 strcpy(stead_path, "package.path=\"");
1009 #if defined(_WIN32_WCE) || defined(WINRT)
1010 if (path) {
1011 strcat(stead_path, path); /* wince have not cwd :) */
1012 strcat(stead_path, "/?.lua;");
1013 }
1014 #else
1015 if (path)
1016 strcat(stead_path, "./?.lua;");
1017 #endif
1018
1019 #ifdef INSTEAD_LEGACY
1020 p = instead_local_stead_path(wd);
1021 if (p) {
1022 strcat(stead_path, p);
1023 strcat(stead_path, "/?.lua;");
1024 }
1025 #endif
1026
1027 if (!is_absolute_path(STEAD_API_PATH)) {
1028 strcat(stead_path, instead_cwd());
1029 strcat(stead_path, "/");
1030 strcat(stead_path, STEAD_API_PATH);
1031 } else {
1032 strcat(stead_path, STEAD_API_PATH);
1033 }
1034 strcat(stead_path, "/?.lua");
1035 strcat(stead_path, "\"");
1036
1037 if (standalone_sw) {
1038 strcat(stead_path, "..';'..(package.path or '')");
1039 }
1040 instead_eval(stead_path); instead_clear();
1041 free(stead_path);
1042 /* putenv(stead_path); */
1043 return 0;
1044 }
1045
instead_get_api(void)1046 const char *instead_get_api(void)
1047 {
1048 return API;
1049 }
1050
instead_lua_path(const char * path)1051 const char *instead_lua_path(const char *path)
1052 {
1053 if (!path)
1054 return instead_base_path;
1055 if (!*path) {
1056 strncpy(instead_base_path, STEAD_PATH, sizeof(instead_base_path) - 1);
1057 return instead_base_path;
1058 }
1059 strncpy(instead_base_path, path, sizeof(instead_base_path) - 1);
1060 return instead_base_path;
1061 }
1062
instead_set_api(const char * api)1063 static int instead_set_api(const char *api)
1064 {
1065 int i, c = 0;
1066 ssize_t s;
1067 char *oa;
1068 if (!api || !*api) {
1069 FREE(API);
1070 snprintf(instead_api_path, sizeof(instead_api_path), "%s", instead_lua_path(NULL));
1071 } else {
1072 s = strlen(api);
1073 for (i = 0; i < s; i ++) {
1074 if (api[i] == '.') {
1075 if (c > 0) {
1076 instead_err_msg("Wrong API.");
1077 fprintf(stderr, "Wrong API.\n");
1078 return -1;
1079 }
1080 c ++;
1081 } else
1082 c = 0;
1083 }
1084 oa = API;
1085 API = strdup(api);
1086 FREE(oa);
1087 snprintf(instead_api_path, sizeof(instead_api_path), "%s/%s", instead_lua_path(NULL), API);
1088 }
1089 return 0;
1090 }
1091
1092
instead_detect_api(const char * path)1093 static int instead_detect_api(const char *path)
1094 {
1095 int api = 0;
1096 char *p;
1097 if (data_idf && idf_only(data_idf, -1) == 1) {
1098 if (!idf_access(data_idf, INSTEAD_MAIN3))
1099 api = 3;
1100 else if (!idf_access(data_idf, INSTEAD_MAIN))
1101 api = 2;
1102 } else {
1103 p = getfilepath(path, INSTEAD_MAIN3);
1104 if (!p)
1105 return -1;
1106 if (!access(dirpath(p), R_OK))
1107 api = 3;
1108 free(p);
1109 if (api)
1110 goto out;
1111 p = getfilepath(path, INSTEAD_MAIN);
1112 if (!access(dirpath(p), R_OK))
1113 api = 2;
1114 free(p);
1115 }
1116 out:
1117 switch (api){
1118 case 2:
1119 if (instead_set_api("stead2") < 0)
1120 return -1;
1121 MAIN = INSTEAD_MAIN;
1122 break;
1123 case 3:
1124 if (instead_set_api("stead3") < 0)
1125 return -1;
1126 MAIN = INSTEAD_MAIN3;
1127 break;
1128 default:
1129 return -1;
1130 }
1131 return api;
1132 }
1133
instead_init_lua(const char * path,int detect)1134 int instead_init_lua(const char *path, int detect)
1135 {
1136 int api = 0;
1137 busy = 0;
1138 setlocale(LC_ALL, "");
1139 setlocale(LC_NUMERIC, "C"); /* to avoid . -> , in numbers */
1140 setlocale(LC_CTYPE, "C"); /* to avoid lower/upper problems */
1141 #ifdef LC_MESSAGES
1142 setlocale(LC_MESSAGES, "C");
1143 #endif
1144 #ifdef LC_COLLATE
1145 setlocale(LC_COLLATE, "C");
1146 #endif
1147 /* strcpy(curcp, "UTF-8"); */
1148 instead_set_encoding("UTF-8");
1149 getdir(instead_cwd_path, sizeof(instead_cwd_path));
1150 unix_path(instead_cwd_path);
1151 instead_cwd_path[sizeof(instead_cwd_path) - 1] = 0;
1152 strncpy(instead_game_path, path, sizeof(instead_game_path) - 1);
1153 instead_cwd_path[sizeof(instead_game_path) - 1] = 0;
1154
1155 if (detect && (api = instead_detect_api(path)) < 0) {
1156 fprintf(stderr, "Can not detect game format: %s\n", path);
1157 instead_err_msg("Can not detect game format.");
1158 return -1;
1159 }
1160
1161 /* initialize Lua */
1162 #if LUA_VERSION_NUM >= 502
1163 L = luaL_newstate();
1164 #else
1165 L = lua_open();
1166 #endif
1167 if (!L)
1168 return -1;
1169
1170 luaL_openlibs(L);
1171 #if LUA_VERSION_NUM >= 502
1172 lua_pushglobaltable(L);
1173 lua_pushglobaltable(L);
1174 lua_setfield(L, -2, "_G");
1175 /* open lib into global table */
1176 luaL_setfuncs(L, base_funcs, 0);
1177 #else
1178 luaL_register(L, "_G", base_funcs);
1179 #endif
1180 instead_package(path);
1181 instead_platform();
1182 /* instead_set_lang(opt_lang); */
1183 if (api == 3)
1184 instead_eval("API='stead3'");
1185 else if (api == 2)
1186 instead_eval("API='stead2'");
1187 if (debug_sw)
1188 instead_eval("DEBUG=true");
1189 else
1190 instead_eval("DEBUG=false");
1191 instead_clear();
1192 if (standalone_sw)
1193 instead_eval("STANDALONE=true");
1194 else
1195 instead_eval("STANDALONE=false");
1196 instead_clear();
1197 srand(time(NULL));
1198 mt_random_init();
1199 luaopen_lfs(L);
1200 return 0;
1201 }
1202
instead_init(const char * path)1203 int instead_init(const char *path)
1204 {
1205 char stead_path[PATH_MAX];
1206 int idf = 0;
1207
1208 if (data_idf)
1209 idf_done(data_idf);
1210
1211 data_idf = idf_init(path);
1212
1213 if (data_idf) {
1214 idf_only(data_idf, 1);
1215 idf = 1;
1216 }
1217
1218 if (instead_init_lua(path, 1))
1219 goto err;
1220
1221 if (snprintf(stead_path, sizeof(stead_path), "%s/stead.lua", STEAD_API_PATH) >=
1222 (int)sizeof(stead_path))
1223 fprintf(stderr, "Path is too long.\n");
1224
1225 if (dofile(L, dirpath(stead_path)))
1226 goto err;
1227
1228 if (extensions_hook(init) < 0) {
1229 fprintf(stderr, "Can't init instead engine.\n");
1230 goto err;
1231 }
1232 #ifdef _USE_SDL
1233 #ifndef __EMSCRIPTEN__
1234 sem = SDL_CreateMutex();
1235 if (!sem)
1236 goto err;
1237 #endif
1238 #endif
1239
1240 if ((!idf && setdir(path))) {
1241 instead_clear();
1242 goto err;
1243 }
1244
1245 if (!idf)
1246 data_idf = idf_init(DATA_IDF);
1247
1248 /* cleanup Lua */
1249 instead_clear();
1250 instead_err_msg(NULL);
1251 return 0;
1252 err:
1253 if (data_idf) {
1254 idf_done(data_idf);
1255 data_idf = NULL;
1256 }
1257 if (L)
1258 lua_close(L);
1259 L = NULL;
1260 return -1;
1261 }
1262
instead_api_register(const luaL_Reg * api)1263 int instead_api_register(const luaL_Reg *api)
1264 {
1265 if (!L)
1266 return -1;
1267 #if LUA_VERSION_NUM >= 502
1268 lua_pushglobaltable(L);
1269 luaL_setfuncs(L, api, 0);
1270 #else
1271 lua_getfield(L, LUA_GLOBALSINDEX, "_G");
1272 luaL_register(L, NULL, api);
1273 #endif
1274 lua_pop(L, 1);
1275 return 0;
1276 }
1277
instead_done(void)1278 void instead_done(void)
1279 {
1280 int wasL = !!L;
1281 if (wasL)
1282 extensions_hook(done);
1283 #ifdef _USE_SDL
1284 #ifndef __EMSCRIPTEN__
1285 if (sem)
1286 SDL_DestroyMutex(sem);
1287 sem = NULL;
1288 #endif
1289 #endif
1290 #ifdef _HAVE_ICONV
1291 FREE(fromcp);
1292 FREE(curcp);
1293 #endif
1294 if (L)
1295 lua_close(L);
1296 L = NULL;
1297 if (data_idf)
1298 idf_done(data_idf);
1299 data_idf = NULL;
1300 if (wasL)
1301 setdir(instead_cwd_path);
1302 FREE(API);
1303 }
1304
instead_encode(const char * s,const char * d)1305 int instead_encode(const char *s, const char *d)
1306 {
1307 FILE *src;
1308 FILE *dst;
1309 size_t size;
1310 unsigned int i = 0;
1311 unsigned char byte = 0xcc;
1312 unsigned char buff[4096];
1313
1314 src = fopen(s, "rb");
1315 if (!src) {
1316 fprintf(stderr,"Can't open on read: '%s'.\n", s);
1317 return -1;
1318 }
1319 dst = fopen(d, "wb");
1320 if (!dst) {
1321 fprintf(stderr,"Can't open on write: '%s'.\n", s);
1322 fclose(src);
1323 return -1;
1324 }
1325 while ((size = fread(buff, 1, sizeof(buff), src))) {
1326 for (i = 0; i < size; i++) {
1327 buff[i] = (buff[i] << 3) | (buff[i] >> 5);
1328 buff[i] ^= byte;
1329 byte = buff[i];
1330 }
1331 if (fwrite(buff, 1, size, dst) != size) {
1332 fprintf(stderr, "Error while writing file: '%s'.\n", d);
1333 fclose(src);
1334 fclose(dst);
1335 return -1;
1336 }
1337 }
1338 fclose(src);
1339 fclose(dst);
1340 return 0;
1341 }
1342
instead_idf(void)1343 idf_t instead_idf(void)
1344 {
1345 return data_idf;
1346 }
1347
instead_stead_path(void)1348 char *instead_stead_path(void)
1349 {
1350 return instead_api_path;
1351 }
1352
instead_path(void)1353 char *instead_path(void)
1354 {
1355 return instead_game_path;
1356 }
1357
instead_cwd(void)1358 char *instead_cwd(void)
1359 {
1360 return instead_cwd_path;
1361 }
1362
instead_set_debug(int sw)1363 int instead_set_debug(int sw)
1364 {
1365 int ov = debug_sw;
1366 debug_sw = sw;
1367 if (L) {
1368 if (sw)
1369 instead_eval("DEBUG=true");
1370 else
1371 instead_eval("DEBUG=false");
1372 instead_clear();
1373 }
1374 return ov;
1375 }
1376
instead_set_standalone(int sw)1377 int instead_set_standalone(int sw)
1378 {
1379 int ov = standalone_sw;
1380 standalone_sw = sw;
1381 if (L) {
1382 if (sw)
1383 instead_eval("STANDALONE=true");
1384 else
1385 instead_eval("STANDALONE=false");
1386 instead_clear();
1387 }
1388 return ov;
1389 }
1390
instead_set_lang(const char * opt_lang)1391 int instead_set_lang(const char *opt_lang)
1392 {
1393 char lang[64];
1394 if (!L)
1395 return 0;
1396 if (opt_lang && *opt_lang)
1397 snprintf(lang, sizeof(lang) - 1, "LANG='%s'", opt_lang);
1398 else
1399 snprintf(lang, sizeof(lang) - 1, "LANG='en'");
1400 instead_eval(lang); instead_clear();
1401 return 0;
1402 }
1403
instead_lua(void)1404 lua_State *instead_lua(void)
1405 {
1406 return L;
1407 }
1408