1 /*
2 Copyright (c) 2009 Peter "Corsix" Cawley
3 
4 Permission is hereby granted, free of charge, to any person obtaining a copy of
5 this software and associated documentation files (the "Software"), to deal in
6 the Software without restriction, including without limitation the rights to
7 use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
8 of the Software, and to permit persons to whom the Software is furnished to do
9 so, subject to the following conditions:
10 
11 The above copyright notice and this permission notice shall be included in all
12 copies or substantial portions of the Software.
13 
14 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20 SOFTWARE.
21 */
22 
23 #include "config.h"
24 
25 #include <cstdio>
26 #include <cstring>
27 #include <string>
28 
29 #include "iso_fs.h"
30 #include "lua.hpp"
31 #include "lua_rnc.h"
32 #include "lua_sdl.h"
33 #include "persist_lua.h"
34 #include "th_lua.h"
35 
36 // Config file checking
37 #ifndef CORSIX_TH_USE_PACK_PRAGMAS
38 #error "config.h is out of date - please rerun CMake"
39 #endif
40 // End of config file checking
41 
42 extern "C" {
43 int luaopen_random(lua_State* L);
44 }
45 
46 namespace {
47 
preload_lua_package(lua_State * L,const char * name,lua_CFunction fn)48 inline void preload_lua_package(lua_State* L, const char* name,
49                                 lua_CFunction fn) {
50   luaT_execute(
51       L, std::string("package.preload.").append(name).append(" = ...").c_str(),
52       fn);
53 }
54 
55 }  // namespace
56 
lua_main_no_eval(lua_State * L)57 int lua_main_no_eval(lua_State* L) {
58   // assert(_VERSION == LUA_VERSION)
59   size_t iLength;
60   lua_getglobal(L, "_VERSION");
61   const char* sVersion = lua_tolstring(L, -1, &iLength);
62   if (iLength != std::strlen(LUA_VERSION) ||
63       std::strcmp(sVersion, LUA_VERSION) != 0) {
64     lua_pushliteral(
65         L,
66         "Linked against a version of Lua different to the one used "
67         "when compiling.\nPlease recompile CorsixTH against the same "
68         "Lua version it is linked against.");
69     return lua_error(L);
70   }
71   lua_pop(L, 1);
72 
73   // math.random* = Mersenne twister variant
74   luaT_cpcall(L, luaopen_random, nullptr);
75 
76   // Fill in package.preload table so that calls to require("X") from Lua
77   // will call the appropriate luaopen_X function in C.
78   preload_lua_package(L, "rnc", luaopen_rnc);
79   preload_lua_package(L, "TH", luaopen_th);
80   preload_lua_package(L, "persist", luaopen_persist);
81   preload_lua_package(L, "sdl", luaopen_sdl);
82 
83   // require "debug" (Harmless in Lua 5.1, useful in 5.2 for compatibility)
84   luaT_execute(L, "require \"debug\"");
85 
86   // Check for --interpreter and run that instead of CorsixTH.lua
87   bool bGotScriptFile = false;
88   int iNArgs = lua_gettop(L);
89   for (int i = 1; i <= iNArgs; ++i) {
90     if (lua_type(L, i) == LUA_TSTRING) {
91       size_t iLen;
92       const char* sCmd = lua_tolstring(L, i, &iLen);
93       if (iLen > 14 && std::memcmp(sCmd, "--interpreter=", 14) == 0) {
94         lua_getglobal(L, "assert");
95         lua_getglobal(L, "loadfile");
96         lua_pushlstring(L, sCmd + 14, iLen - 14);
97         bGotScriptFile = true;
98         break;
99       }
100     }
101   }
102 
103   if (!bGotScriptFile) {
104     lua_getglobal(L, "assert");
105     lua_getglobal(L, "loadfile");
106     lua_pushstring(L, CORSIX_TH_INTERPRETER_PATH);
107   }
108 
109   lua_call(L, 1, 2);
110   lua_call(L, 2, 1);
111   lua_insert(L, 1);
112   return lua_gettop(L);
113 }
114 
lua_main(lua_State * L)115 int lua_main(lua_State* L) {
116   lua_call(L, lua_main_no_eval(L) - 1, LUA_MULTRET);
117   return lua_gettop(L);
118 }
119 
lua_stacktrace(lua_State * L)120 int lua_stacktrace(lua_State* L) {
121   // err = tostring(err)
122   lua_settop(L, 1);
123   lua_getglobal(L, "tostring");
124   lua_insert(L, 1);
125   lua_call(L, 1, 1);
126 
127   // err = <description> .. err
128   lua_pushliteral(L, "An error has occurred in CorsixTH:\n");
129   lua_insert(L, 1);
130   lua_concat(L, 2);
131 
132   // return debug.traceback(err, 2)
133   lua_getglobal(L, "debug");
134   lua_getfield(L, -1, "traceback");
135   lua_pushvalue(L, 1);
136   lua_pushinteger(L, 2);
137   lua_call(L, 2, 1);
138 
139   return 1;
140 }
141 
lua_panic(lua_State * L)142 int lua_panic(lua_State* L) {
143   std::fprintf(stderr,
144                "A Lua error has occurred in CorsixTH outside of protected "
145                "mode!\n");
146   std::fflush(stderr);
147 
148   if (lua_type(L, -1) == LUA_TSTRING)
149     std::fprintf(stderr, "%s\n", lua_tostring(L, -1));
150   else
151     std::fprintf(stderr, "%p\n", lua_topointer(L, -1));
152   std::fflush(stderr);
153 
154   // A stack trace would be nice, but they cannot be done in a panic.
155 
156   return 0;
157 }
158