1 // SONIC ROBO BLAST 2
2 //-----------------------------------------------------------------------------
3 // Copyright (C) 2020 by James R.
4 // Copyright (C) 2020 by Sonic Team Junior.
5 //
6 // This program is free software distributed under the
7 // terms of the GNU General Public License, version 2.
8 // See the 'LICENSE' file for more details.
9 //-----------------------------------------------------------------------------
10 /// \file lua_taglib.c
11 /// \brief tag list iterator for Lua scripting
12
13 #include "doomdef.h"
14 #include "taglist.h"
15 #include "r_state.h"
16
17 #include "lua_script.h"
18 #include "lua_libs.h"
19
20 #ifdef MUTABLE_TAGS
21 #include "z_zone.h"
22 #endif
23
tag_iterator(lua_State * L)24 static int tag_iterator(lua_State *L)
25 {
26 INT32 tag = lua_isnil(L, 2) ? -1 : lua_tonumber(L, 2);
27 do
28 {
29 if (++tag >= MAXTAGS)
30 return 0;
31 }
32 while (! in_bit_array(tags_available, tag)) ;
33 lua_pushnumber(L, tag);
34 return 1;
35 }
36
37 enum {
38 #define UPVALUE lua_upvalueindex
39 up_garray = UPVALUE(1),
40 up_max_elements = UPVALUE(2),
41 up_element_array = UPVALUE(3),
42 up_sizeof_element = UPVALUE(4),
43 up_meta = UPVALUE(5),
44 #undef UPVALUE
45 };
46
next_element(lua_State * L,const mtag_t tag,const size_t p)47 static INT32 next_element(lua_State *L, const mtag_t tag, const size_t p)
48 {
49 taggroup_t ** garray = lua_touserdata(L, up_garray);
50 const size_t * max_elements = lua_touserdata(L, up_max_elements);
51 return Taggroup_Iterate(garray, *max_elements, tag, p);
52 }
53
push_element(lua_State * L,void * element)54 static void push_element(lua_State *L, void *element)
55 {
56 if (LUA_RawPushUserdata(L, element) == LPUSHED_NEW)
57 {
58 lua_pushvalue(L, up_meta);
59 lua_setmetatable(L, -2);
60 }
61 }
62
push_next_element(lua_State * L,const INT32 element)63 static void push_next_element(lua_State *L, const INT32 element)
64 {
65 char * element_array = *(char **)lua_touserdata(L, up_element_array);
66 const size_t sizeof_element = lua_tonumber(L, up_sizeof_element);
67 push_element(L, &element_array[element * sizeof_element]);
68 }
69
70 struct element_iterator_state {
71 mtag_t tag;
72 size_t p;
73 };
74
element_iterator(lua_State * L)75 static int element_iterator(lua_State *L)
76 {
77 struct element_iterator_state * state = lua_touserdata(L, 1);
78 if (lua_isnoneornil(L, 3))
79 state->p = 0;
80 lua_pushnumber(L, ++state->p);
81 lua_gettable(L, 1);
82 return 1;
83 }
84
lib_iterateTags(lua_State * L)85 static int lib_iterateTags(lua_State *L)
86 {
87 if (lua_gettop(L) < 2)
88 {
89 lua_pushcfunction(L, tag_iterator);
90 return 1;
91 }
92 else
93 return tag_iterator(L);
94 }
95
lib_numTags(lua_State * L)96 static int lib_numTags(lua_State *L)
97 {
98 lua_pushnumber(L, num_tags);
99 return 1;
100 }
101
lib_getTaggroup(lua_State * L)102 static int lib_getTaggroup(lua_State *L)
103 {
104 struct element_iterator_state *state;
105
106 mtag_t tag;
107
108 if (lua_gettop(L) > 1)
109 return luaL_error(L, "too many arguments");
110
111 if (lua_isnoneornil(L, 1))
112 {
113 tag = MTAG_GLOBAL;
114 }
115 else
116 {
117 tag = lua_tonumber(L, 1);
118 luaL_argcheck(L, tag >= -1, 1, "tag out of range");
119 }
120
121 state = lua_newuserdata(L, sizeof *state);
122 state->tag = tag;
123 state->p = 0;
124
125 lua_pushvalue(L, lua_upvalueindex(1));
126 lua_setmetatable(L, -2);
127
128 return 1;
129 }
130
lib_getTaggroupElement(lua_State * L)131 static int lib_getTaggroupElement(lua_State *L)
132 {
133 const size_t p = luaL_checknumber(L, 2) - 1;
134 const mtag_t tag = *(mtag_t *)lua_touserdata(L, 1);
135 const INT32 element = next_element(L, tag, p);
136
137 if (element == -1)
138 return 0;
139 else
140 {
141 push_next_element(L, element);
142 return 1;
143 }
144 }
145
lib_numTaggroupElements(lua_State * L)146 static int lib_numTaggroupElements(lua_State *L)
147 {
148 const mtag_t tag = *(mtag_t *)lua_touserdata(L, 1);
149 if (tag == MTAG_GLOBAL)
150 lua_pushnumber(L, *(size_t *)lua_touserdata(L, up_max_elements));
151 else
152 {
153 const taggroup_t ** garray = lua_touserdata(L, up_garray);
154 lua_pushnumber(L, Taggroup_Count(garray[tag]));
155 }
156 return 1;
157 }
158
159 #ifdef MUTABLE_TAGS
160 static int meta_ref[2];
161 #endif
162
has_valid_field(lua_State * L)163 static int has_valid_field(lua_State *L)
164 {
165 int equal;
166 lua_rawgeti(L, LUA_ENVIRONINDEX, 1);
167 equal = lua_rawequal(L, 2, -1);
168 lua_pop(L, 1);
169 return equal;
170 }
171
valid_taglist(lua_State * L,int idx,boolean getting)172 static taglist_t * valid_taglist(lua_State *L, int idx, boolean getting)
173 {
174 taglist_t *list = *(taglist_t **)lua_touserdata(L, idx);
175
176 if (list == NULL)
177 {
178 if (getting && has_valid_field(L))
179 lua_pushboolean(L, 0);
180 else
181 LUA_ErrInvalid(L, "taglist");/* doesn't actually return */
182 return NULL;
183 }
184 else
185 return list;
186 }
187
check_taglist(lua_State * L,int idx)188 static taglist_t * check_taglist(lua_State *L, int idx)
189 {
190 if (lua_isuserdata(L, idx) && lua_getmetatable(L, idx))
191 {
192 lua_getref(L, meta_ref[0]);
193 lua_getref(L, meta_ref[1]);
194
195 if (lua_rawequal(L, -3, -2) || lua_rawequal(L, -3, -1))
196 {
197 lua_pop(L, 3);
198 return valid_taglist(L, idx, false);
199 }
200 }
201
202 return luaL_argerror(L, idx, "must be a tag list"), NULL;
203 }
204
taglist_get(lua_State * L)205 static int taglist_get(lua_State *L)
206 {
207 const taglist_t *list = valid_taglist(L, 1, true);
208
209 if (list == NULL)/* valid check */
210 return 1;
211
212 if (lua_isnumber(L, 2))
213 {
214 const size_t i = lua_tonumber(L, 2);
215
216 if (list && i <= list->count)
217 {
218 lua_pushnumber(L, list->tags[i - 1]);
219 return 1;
220 }
221 else
222 return 0;
223 }
224 else if (has_valid_field(L))
225 {
226 lua_pushboolean(L, 1);
227 return 1;
228 }
229 else
230 {
231 lua_getmetatable(L, 1);
232 lua_replace(L, 1);
233 lua_rawget(L, 1);
234 return 1;
235 }
236 }
237
taglist_len(lua_State * L)238 static int taglist_len(lua_State *L)
239 {
240 const taglist_t *list = valid_taglist(L, 1, false);
241 lua_pushnumber(L, list->count);
242 return 1;
243 }
244
taglist_equal(lua_State * L)245 static int taglist_equal(lua_State *L)
246 {
247 const taglist_t *lhs = check_taglist(L, 1);
248 const taglist_t *rhs = check_taglist(L, 2);
249 lua_pushboolean(L, Tag_Compare(lhs, rhs));
250 return 1;
251 }
252
taglist_iterator(lua_State * L)253 static int taglist_iterator(lua_State *L)
254 {
255 const taglist_t *list = valid_taglist(L, 1, false);
256 const size_t i = 1 + lua_tonumber(L, lua_upvalueindex(1));
257 if (i <= list->count)
258 {
259 lua_pushnumber(L, list->tags[i - 1]);
260 /* watch me exploit an upvalue as a control because
261 I want to use the control as the value */
262 lua_pushnumber(L, i);
263 lua_replace(L, lua_upvalueindex(1));
264 return 1;
265 }
266 else
267 return 0;
268 }
269
taglist_iterate(lua_State * L)270 static int taglist_iterate(lua_State *L)
271 {
272 check_taglist(L, 1);
273 lua_pushnumber(L, 0);
274 lua_pushcclosure(L, taglist_iterator, 1);
275 lua_pushvalue(L, 1);
276 return 2;
277 }
278
taglist_find(lua_State * L)279 static int taglist_find(lua_State *L)
280 {
281 const taglist_t *list = check_taglist(L, 1);
282 const mtag_t tag = luaL_checknumber(L, 2);
283 lua_pushboolean(L, Tag_Find(list, tag));
284 return 1;
285 }
286
taglist_shares(lua_State * L)287 static int taglist_shares(lua_State *L)
288 {
289 const taglist_t *lhs = check_taglist(L, 1);
290 const taglist_t *rhs = check_taglist(L, 2);
291 lua_pushboolean(L, Tag_Share(lhs, rhs));
292 return 1;
293 }
294
295 /* only sector tags are mutable... */
296
297 #ifdef MUTABLE_TAGS
sector_of_taglist(taglist_t * list)298 static size_t sector_of_taglist(taglist_t *list)
299 {
300 return (sector_t *)((char *)list - offsetof (sector_t, tags)) - sectors;
301 }
302
this_taglist(lua_State * L)303 static int this_taglist(lua_State *L)
304 {
305 lua_settop(L, 1);
306 return 1;
307 }
308
taglist_add(lua_State * L)309 static int taglist_add(lua_State *L)
310 {
311 taglist_t *list = *(taglist_t **)luaL_checkudata(L, 1, META_SECTORTAGLIST);
312 const mtag_t tag = luaL_checknumber(L, 2);
313
314 if (! Tag_Find(list, tag))
315 {
316 Taggroup_Add(tags_sectors, tag, sector_of_taglist(list));
317 Tag_Add(list, tag);
318 }
319
320 return this_taglist(L);
321 }
322
taglist_remove(lua_State * L)323 static int taglist_remove(lua_State *L)
324 {
325 taglist_t *list = *(taglist_t **)luaL_checkudata(L, 1, META_SECTORTAGLIST);
326 const mtag_t tag = luaL_checknumber(L, 2);
327
328 size_t i;
329
330 for (i = 0; i < list->count; ++i)
331 {
332 if (list->tags[i] == tag)
333 {
334 if (list->count > 1)
335 {
336 memmove(&list->tags[i], &list->tags[i + 1],
337 (list->count - 1 - i) * sizeof (mtag_t));
338 list->tags = Z_Realloc(list->tags,
339 (--list->count) * sizeof (mtag_t), PU_LEVEL, NULL);
340 Taggroup_Remove(tags_sectors, tag, sector_of_taglist(list));
341 }
342 else/* reset to default tag */
343 Tag_SectorFSet(sector_of_taglist(list), 0);
344 break;
345 }
346 }
347
348 return this_taglist(L);
349 }
350 #endif/*MUTABLE_TAGS*/
351
LUA_InsertTaggroupIterator(lua_State * L,taggroup_t * garray[],size_t * max_elements,void * element_array,size_t sizeof_element,const char * meta)352 void LUA_InsertTaggroupIterator
353 ( lua_State *L,
354 taggroup_t *garray[],
355 size_t * max_elements,
356 void * element_array,
357 size_t sizeof_element,
358 const char * meta)
359 {
360 lua_createtable(L, 0, 3);
361 lua_pushlightuserdata(L, garray);
362 lua_pushlightuserdata(L, max_elements);
363
364 lua_pushvalue(L, -2);
365 lua_pushvalue(L, -2);
366 lua_pushlightuserdata(L, element_array);
367 lua_pushnumber(L, sizeof_element);
368 luaL_getmetatable(L, meta);
369 lua_pushcclosure(L, lib_getTaggroupElement, 5);
370 lua_setfield(L, -4, "__index");
371
372 lua_pushcclosure(L, lib_numTaggroupElements, 2);
373 lua_setfield(L, -2, "__len");
374
375 lua_pushcfunction(L, element_iterator);
376 lua_setfield(L, -2, "__call");
377 lua_pushcclosure(L, lib_getTaggroup, 1);
378 lua_setfield(L, -2, "tagged");
379 }
380
381 static luaL_Reg taglist_lib[] = {
382 {"iterate", taglist_iterate},
383 {"find", taglist_find},
384 {"shares", taglist_shares},
385 #ifdef MUTABLE_TAGS
386 {"add", taglist_add},
387 {"remove", taglist_remove},
388 #endif
389 {0}
390 };
391
open_taglist(lua_State * L)392 static void open_taglist(lua_State *L)
393 {
394 luaL_register(L, "taglist", taglist_lib);
395
396 lua_getfield(L, -1, "find");
397 lua_setfield(L, -2, "has");
398 }
399
400 #define new_literal(L, s) \
401 (lua_pushliteral(L, s), luaL_ref(L, -2))
402
403 #ifdef MUTABLE_TAGS
404 static int
405 #else
406 static void
407 #endif
set_taglist_metatable(lua_State * L,const char * meta)408 set_taglist_metatable(lua_State *L, const char *meta)
409 {
410 luaL_newmetatable(L, meta);
411 lua_pushcfunction(L, taglist_get);
412 lua_createtable(L, 0, 1);
413 new_literal(L, "valid");
414 lua_setfenv(L, -2);
415 lua_setfield(L, -2, "__index");
416
417 lua_pushcfunction(L, taglist_len);
418 lua_setfield(L, -2, "__len");
419
420 lua_pushcfunction(L, taglist_equal);
421 lua_setfield(L, -2, "__eq");
422 #ifdef MUTABLE_TAGS
423 return luaL_ref(L, LUA_REGISTRYINDEX);
424 #endif
425 }
426
LUA_TagLib(lua_State * L)427 int LUA_TagLib(lua_State *L)
428 {
429 lua_newuserdata(L, 0);
430 lua_createtable(L, 0, 2);
431 lua_createtable(L, 0, 1);
432 lua_pushcfunction(L, lib_iterateTags);
433 lua_setfield(L, -2, "iterate");
434 lua_setfield(L, -2, "__index");
435
436 lua_pushcfunction(L, lib_numTags);
437 lua_setfield(L, -2, "__len");
438 lua_setmetatable(L, -2);
439 lua_setglobal(L, "tags");
440
441 open_taglist(L);
442
443 #ifdef MUTABLE_TAGS
444 meta_ref[0] = set_taglist_metatable(L, META_TAGLIST);
445 meta_ref[1] = set_taglist_metatable(L, META_SECTORTAGLIST);
446 #else
447 set_taglist_metatable(L, META_TAGLIST);
448 #endif
449
450 return 0;
451 }
452