1 /*
2 Copyright (c) 2010 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 #ifndef CORSIX_TH_TH_LUA_INTERNAL_H_
24 #define CORSIX_TH_TH_LUA_INTERNAL_H_
25 #include "config.h"
26 
27 #include <string>
28 
29 #include "th_lua.h"
30 
31 enum class lua_metatable {
32   map,
33   palette,
34   sheet,
35   font,
36   bitmap_font,
37 #ifdef CORSIX_TH_USE_FREETYPE2
38   freetype_font,
39 #endif
40   layers,
41   anims,
42   anim,
43   pathfinder,
44   surface,
45   bitmap,
46   cursor,
47   lfs_ext,
48   sound_archive,
49   sound_fx,
50   movie,
51   string,
52   window_base,
53   sprite_list,
54   string_proxy,
55   line,
56   iso_fs,
57 
58   count
59 };
60 
61 struct lua_register_state {
62   lua_State* L;
63   int metatables[static_cast<size_t>(lua_metatable::count)];
64   int main_table;
65   int top;
66 };
67 
68 void luaT_setclosure(const lua_register_state* pState, lua_CFunction fn,
69                      int iUps);
70 
71 template <typename... Args>
luaT_setclosure(const lua_register_state * pState,lua_CFunction fn,int iUps,lua_metatable eMetatable1,Args...args)72 void luaT_setclosure(const lua_register_state* pState, lua_CFunction fn,
73                      int iUps, lua_metatable eMetatable1, Args... args) {
74   lua_pushvalue(pState->L,
75                 pState->metatables[static_cast<size_t>(eMetatable1)]);
76   luaT_setclosure(pState, fn, iUps + 1, args...);
77 }
78 
79 template <typename... Args>
luaT_setclosure(const lua_register_state * pState,lua_CFunction fn,int iUps,const char * str,Args...args)80 void luaT_setclosure(const lua_register_state* pState, lua_CFunction fn,
81                      int iUps, const char* str, Args... args) {
82   lua_pushstring(pState->L, str);
83   luaT_setclosure(pState, fn, iUps + 1, args...);
84 }
85 
86 /**
87  * Add a c++ function to the lua state.
88  *
89  * @param pState Lua state for the game
90  * @param fn The C/C++ function to register
91  * @param name The name to use for the function in lua
92  * @param args The upvalues to associate with the function in lua
93  */
94 template <typename... Args>
add_lua_function(const lua_register_state * pState,lua_CFunction fn,const char * name,Args...args)95 void add_lua_function(const lua_register_state* pState, lua_CFunction fn,
96                       const char* name, Args... args) {
97   luaT_setclosure(pState, fn, 0, args...);
98   lua_setfield(pState->L, -2, name);
99 }
100 
101 /**
102  * Create a lua 'class' bound to a C++ class.
103  *
104  * This class should be immediately destructed after adding all of it's
105  * functions, metamethods and constants to complete the creation of the bind.
106  */
107 template <typename T>
108 class lua_class_binding final {
109  public:
110   lua_class_binding() = delete;
111   lua_class_binding(const lua_class_binding&) = delete;
112   lua_class_binding(lua_class_binding&&) = delete;
113   lua_class_binding& operator=(const lua_class_binding&) = delete;
114   lua_class_binding operator=(lua_class_binding&&) = delete;
115 
116   /**
117    * Initiate class bindings for lua.
118    *
119    * @param pState The lua environment to bind to.
120    * @param name The name to give this lua 'class'.
121    * @param new_fn The function to call when a new class is created.
122    * @param mt The metatable id for the class
123    */
lua_class_binding(const lua_register_state * pState,const char * name,lua_CFunction new_fn,lua_metatable mt)124   lua_class_binding(const lua_register_state* pState, const char* name,
125                     lua_CFunction new_fn, lua_metatable mt)
126       : pState(pState),
127         class_name(name),
128         class_metatable(pState->metatables[static_cast<size_t>(mt)]) {
129     lua_settop(pState->L, pState->top);
130     /* Make metatable the environment for registered functions */
131     lua_pushvalue(pState->L, class_metatable);
132     lua_replace(pState->L, luaT_environindex);
133     /* Set the __gc metamethod to C++ destructor */
134     luaT_pushcclosure(pState->L, luaT_stdgc<T, luaT_environindex>, 0);
135     lua_setfield(pState->L, class_metatable, "__gc");
136     /* Set the depersist size */
137     lua_pushinteger(pState->L, sizeof(T));
138     lua_setfield(pState->L, class_metatable, "__depersist_size");
139     /* Create the methods table; call it -> new instance */
140     luaT_pushcclosuretable(pState->L, new_fn, 0);
141     /* Set __class_name on the methods metatable */
142     lua_getmetatable(pState->L, -1);
143     lua_pushstring(pState->L, class_name);
144     lua_setfield(pState->L, -2, "__class_name");
145     lua_pop(pState->L, 1);
146     /* Set __index to the methods table */
147     lua_pushvalue(pState->L, -1);
148     lua_setfield(pState->L, class_metatable, "__index");
149   }
150 
151   /**
152    * Set another class as the superclass of this class.
153    *
154    * @param super_mt The metatable id of the super class.
155    */
set_superclass(lua_metatable super_mt)156   void set_superclass(lua_metatable super_mt) {
157     lua_getmetatable(pState->L, -1);
158     lua_getfield(pState->L, pState->metatables[static_cast<size_t>(super_mt)],
159                  "__index");
160     lua_setfield(pState->L, -2, "__index");
161     lua_pop(pState->L, 1);
162     /* Set metatable[1] to super_mt */
163     lua_pushvalue(pState->L, pState->metatables[static_cast<size_t>(super_mt)]);
164     lua_rawseti(pState->L, class_metatable, 1);
165   }
166 
167   /**
168    * Add a named constant to the lua interface.
169    *
170    * @param name (string literal) Name of the constant.
171    * @param value (tested with int) Value of the constant.
172    */
173   template <typename V>
add_constant(const char * name,V value)174   void add_constant(const char* name, V value) {
175     luaT_push(pState->L, value);
176     lua_setfield(pState->L, -2, name);
177   }
178 
179   /**
180    * Add a C++ metamethod to the lua class.
181    *
182    * @param fn The C++ function to call.
183    * @param name The name of the metamethod (without the __ prefix).
184    * @param args The upvalues for the function.
185    */
186   template <typename... Args>
add_metamethod(lua_CFunction fn,const char * name,Args...args)187   void add_metamethod(lua_CFunction fn, const char* name, Args... args) {
188     luaT_setclosure(pState, fn, 0, args...);
189     lua_setfield(pState->L, class_metatable,
190                  std::string("__").append(name).c_str());
191   }
192 
193   /**
194    * Add a C++ function to the lua class.
195    *
196    * @param fn The C++ function.
197    * @param name The name of the function in lua.
198    * @param args The upvalues for the function
199    */
200   template <typename... Args>
add_function(lua_CFunction fn,const char * name,Args...args)201   void add_function(lua_CFunction fn, const char* name, Args... args) {
202     add_lua_function(pState, fn, name, args...);
203   }
204 
205   /**
206    * Destructor which finalizes the lua binding
207    */
~lua_class_binding()208   ~lua_class_binding() {
209     lua_setfield(pState->L, pState->main_table, class_name);
210   }
211 
212  private:
213   const lua_register_state* pState;
214   const char* class_name;
215   int class_metatable;
216 };
217 
218 #endif
219