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