/
lua_glue.c
384 lines (315 loc) · 11.3 KB
1
#include "universal.h"
2
#include "lua_glue.h"
3
#include "platform.h"
4
5
6
7
#include "fileio.h"
#include "lua.h"
#include "lauxlib.h"
#include "lualib.h"
8
#include "gui.h"
9
10
11
12
13
#define MOJOSETUP_NAMESPACE "MojoSetup"
static lua_State *luaState = NULL;
14
15
16
17
18
19
20
21
22
23
24
25
// Allocator interface for internal Lua use.
static void *MojoLua_alloc(void *ud, void *ptr, size_t osize, size_t nsize)
{
if (nsize == 0)
{
free(ptr);
return NULL;
} // if
return xrealloc(ptr, nsize);
} // MojoLua_alloc
26
27
28
29
// Read data from a MojoInput when loading Lua code.
static const char *MojoLua_reader(lua_State *L, void *data, size_t *size)
{
MojoInput *in = (MojoInput *) data;
30
char *retval = (char *) scratchbuf_128k;
31
32
33
34
35
36
37
38
39
40
41
42
int64 br = in->read(in, scratchbuf_128k, sizeof (scratchbuf_128k));
if (br <= 0) // eof or error? (lua doesn't care which?!)
{
br = 0;
retval = NULL;
} // if
*size = (size_t) br;
return retval;
} // MojoLua_reader
43
44
boolean MojoLua_runFile(const char *basefname)
{
45
46
MojoArchive *ar = GBaseArchive; // in case we want to generalize later.
const MojoArchiveEntryInfo *entinfo;
47
boolean retval = false;
48
49
char clua[128]; // compiled filename.
char ulua[128]; // uncompiled filename.
50
51
52
int rc = 0;
MojoInput *io = NULL;
53
54
if (snprintf(clua, sizeof (clua), "%s.luac", basefname) >= sizeof (clua))
return false;
55
56
57
58
59
if (snprintf(ulua, sizeof (ulua), "%s.lua", basefname) >= sizeof (ulua))
return false;
if (ar->enumerate(ar, "lua"))
60
{
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
while ((entinfo = ar->enumNext(ar)) != NULL)
{
boolean match = (strcmp(entinfo->filename, clua) == 0);
#if !DISABLE_LUA_PARSER
if (!match)
match = (strcmp(entinfo->filename, ulua) == 0);
#endif
if (match)
{
if (entinfo->type == MOJOARCHIVE_ENTRY_FILE)
io = ar->openCurrentEntry(ar);
break;
} // if
} // while
76
77
78
79
} // if
if (io != NULL)
{
80
rc = lua_load(luaState, MojoLua_reader, io, entinfo->filename);
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
io->close(io);
if (rc != 0)
lua_error(luaState);
else
{
// !!! FIXME: use pcall instead so we can get error backtraces and localize.
// Call new chunk on top of the stack (lua_call will pop it off).
lua_call(luaState, 0, 0); // return values are dumped.
retval = true; // if this didn't panic, we succeeded.
} // if
} // if
return retval;
} // MojoLua_runFile
98
99
100
void MojoLua_collectGarbage(void)
{
lua_State *L = luaState;
101
uint32 ticks = 0;
102
103
104
105
106
107
int pre = 0;
int post = 0;
STUBBED("logging!");
pre = (lua_gc(L, LUA_GCCOUNT, 0) * 1024) + lua_gc(L, LUA_GCCOUNTB, 0);
printf("Collecting garbage (currently using %d bytes).\n", pre);
108
ticks = MojoPlatform_ticks();
109
lua_gc (L, LUA_GCCOLLECT, 0);
110
111
ticks = MojoPlatform_ticks() - ticks;
printf("Collection finished (took %d milliseconds).\n", (int) ticks);
112
113
114
115
116
post = (lua_gc(L, LUA_GCCOUNT, 0) * 1024) + lua_gc(L, LUA_GCCOUNTB, 0);
printf("Now using %d bytes (%d bytes savings).\n", post, pre - post);
} // MojoLua_collectGarbage
117
118
119
120
121
// Since localization is kept in Lua tables, I stuck this in the Lua glue.
const char *translate(const char *str)
{
const char *retval = str;
122
if (luaState != NULL) // No translations before Lua is initialized.
123
{
124
if (lua_checkstack(luaState, 3))
125
{
126
127
128
int popcount = 0;
lua_getglobal(luaState, MOJOSETUP_NAMESPACE); popcount++;
if (lua_istable(luaState, -1)) // namespace is sane?
129
{
130
131
lua_getfield(luaState, -1, "translations"); popcount++;
if (lua_istable(luaState, -1)) // translation table is sane?
132
{
133
134
135
136
137
const char *tr = NULL;
lua_getfield(luaState, -1, str); popcount++;
tr = lua_tostring(luaState, -1);
if (tr != NULL) // translated for this locale?
{
138
139
140
char *dst = (char *) scratchbuf_128k;
xstrncpy(dst, tr, sizeof(scratchbuf_128k));
retval = dst;
141
} // if
142
} // if
143
} // if
144
lua_pop(luaState, popcount); // remove our stack salsa.
145
146
147
148
149
150
151
152
153
154
} // if
} // if
return retval;
} // translate
// Hook into our error handling in case Lua throws a runtime error.
static int MojoLua_panic(lua_State *L)
{
155
156
const char *errstr = lua_tostring(L, 1);
if (errstr == NULL)
157
errstr = _("Unknown error");
158
159
160
161
return fatal(errstr); // doesn't actually return.
} // MojoLua_panic
162
163
164
165
166
167
168
169
170
171
172
173
174
175
// Use this instead of Lua's error() function if you don't have a
// programatic error, so you don't get stack callback stuff:
// MojoSetup.fatal("You need the base game to install this expansion pack.")
// Doesn't actually return.
static int luahook_fatal(lua_State *L)
{
const char *err = luaL_checkstring(L, 1);
fatal(err);
return 0;
} // luahook_runfile
176
177
178
179
180
181
182
183
184
185
// Lua interface to MojoLua_runFile(). This is needed instead of Lua's
// require(), since it can access scripts inside an archive.
static int luahook_runfile(lua_State *L)
{
const char *fname = luaL_checkstring(L, 1);
lua_pushboolean(L, MojoLua_runFile(fname));
return 1;
} // luahook_runfile
186
187
188
189
190
191
192
193
194
// Lua interface to translate().
static int luahook_translate(lua_State *L)
{
const char *str = luaL_checkstring(L, 1);
lua_pushstring(L, translate(str));
return 1;
} // luahook_translate
195
196
197
198
199
200
201
static int luahook_ticks(lua_State *L)
{
lua_pushnumber(L, MojoPlatform_ticks());
return 1;
} // luahook_ticks
202
203
204
205
206
207
static int luahook_msgbox(lua_State *L)
{
if (GGui != NULL)
{
const char *title = luaL_checkstring(L, 1);
const char *text = luaL_checkstring(L, 2);
208
GGui->msgbox(title, text);
209
210
211
212
213
214
215
216
217
218
219
220
} // if
return 0;
} // luahook_msgbox
static int luahook_promptyn(lua_State *L)
{
boolean rc = false;
if (GGui != NULL)
{
const char *title = luaL_checkstring(L, 1);
const char *text = luaL_checkstring(L, 2);
221
rc = GGui->promptyn(title, text);
222
223
224
225
226
227
228
} // if
lua_pushboolean(L, rc);
return 1;
} // luahook_msgbox
229
230
231
232
233
234
235
236
// Sets t[sym]=f, where t is on the top of the Lua stack.
static inline void set_cfunc(lua_State *L, lua_CFunction f, const char *sym)
{
lua_pushcfunction(luaState, f);
lua_setfield(luaState, -2, sym);
} // set_cfunc
237
238
239
240
241
242
243
// Sets t[sym]=f, where t is on the top of the Lua stack.
static inline void set_string(lua_State *L, const char *str, const char *sym)
{
lua_pushstring(luaState, str);
lua_setfield(luaState, -2, sym);
} // set_string
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
static inline void set_string_array(lua_State *L, int argc, const char **argv,
const char *sym)
{
int i;
lua_newtable(luaState);
for (i = 0; i < argc; i++)
{
lua_pushinteger(luaState, i+1); // lua is option base 1!
lua_pushstring(luaState, argv[i]);
lua_settable(luaState, -3);
} // for
lua_setfield(luaState, -2, sym);
} // set_string_array
void MojoLua_setString(const char *str, const char *sym)
{
lua_getglobal(luaState, MOJOSETUP_NAMESPACE);
set_string(luaState, str, sym);
lua_pop(luaState, 1);
} // MojoLua_setString
void MojoLua_setStringArray(int argc, const char **argv, const char *sym)
{
lua_getglobal(luaState, MOJOSETUP_NAMESPACE);
set_string_array(luaState, argc, argv, sym);
lua_pop(luaState, 1);
} // MojoLua_setStringArray
274
275
276
boolean MojoLua_initLua(void)
{
277
char locale[16];
278
279
char ostype[64];
char osversion[64];
280
281
282
if (!MojoPlatform_locale(locale, sizeof (locale)))
xstrncpy(locale, "???", sizeof (locale));
283
284
285
286
287
288
if (!MojoPlatform_osType(ostype, sizeof (ostype)))
xstrncpy(ostype, "???", sizeof (ostype));
if (!MojoPlatform_osVersion(osversion, sizeof (osversion)))
xstrncpy(osversion, "???", sizeof (osversion));
assert(luaState == NULL);
289
290
luaState = lua_newstate(MojoLua_alloc, NULL);
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
if (luaState == NULL)
return false;
lua_atpanic(luaState, MojoLua_panic);
if (!lua_checkstack(luaState, 20)) // Just in case.
{
lua_close(luaState);
luaState = NULL;
return false;
} // if
luaL_openlibs(luaState);
// Build MojoSetup namespace for Lua to access and fill in C bridges...
lua_newtable(luaState);
307
// Set up initial C functions, etc we want to expose to Lua code...
308
set_cfunc(luaState, luahook_runfile, "runfile");
309
set_cfunc(luaState, luahook_translate, "translate");
310
set_cfunc(luaState, luahook_ticks, "ticks");
311
312
313
set_cfunc(luaState, luahook_fatal, "fatal");
set_cfunc(luaState, luahook_msgbox, "msgbox");
set_cfunc(luaState, luahook_promptyn, "promptyn");
314
set_string(luaState, locale, "locale");
315
316
317
318
set_string(luaState, PLATFORM_NAME, "platform");
set_string(luaState, PLATFORM_ARCH, "arch");
set_string(luaState, ostype, "ostype");
set_string(luaState, osversion, "osversion");
319
set_string(luaState, GBuildVer, "buildver");
320
321
set_string(luaState, GLuaLicense, "lualicense");
set_string_array(luaState, GArgc, GArgv, "commandLine");
322
lua_setglobal(luaState, MOJOSETUP_NAMESPACE);
323
324
// Set up localization table, if possible.
325
MojoLua_runFile("localization");
326
327
328
329
// Transfer control to Lua to setup some APIs and state...
if (!MojoLua_runFile("mojosetup_init"))
return false;
330
331
// ...and run the installer-specific config file.
332
333
334
if (!MojoLua_runFile("config"))
return false;
335
336
337
338
339
340
341
342
343
344
345
346
// We don't need the MojoSetup.schema namespace anymore. Make it
// eligible for garbage collection.
lua_getglobal(luaState, MOJOSETUP_NAMESPACE);
if (lua_istable(luaState, -1))
{
lua_pushnil(luaState);
lua_setfield(luaState, -2, "schema");
} // if
lua_pop(luaState, 1);
MojoLua_collectGarbage(); // get rid of old init crap we don't need.
347
348
349
350
351
352
353
354
355
356
357
358
359
return true;
} // MojoLua_initLua
void MojoLua_deinitLua(void)
{
if (luaState != NULL)
{
lua_close(luaState);
luaState = NULL;
} // if
} // MojoLua_deinitLua
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
const char *GLuaLicense =
"Copyright (C) 1994-2006 Lua.org, PUC-Rio.\n"
"\n"
"Permission is hereby granted, free of charge, to any person obtaining a copy\n"
"of this software and associated documentation files (the \"Software\"), to deal\n"
"in the Software without restriction, including without limitation the rights\n"
"to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n"
"copies of the Software, and to permit persons to whom the Software is\n"
"furnished to do so, subject to the following conditions:\n"
"\n"
"The above copyright notice and this permission notice shall be included in\n"
"all copies or substantial portions of the Software.\n"
"\n"
"THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n"
"IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n"
"FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n"
"AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n"
"LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n"
"OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n"
"THE SOFTWARE.\n"
"\n";
383
// end of lua_glue.c ...