1 /*
2 Minetest
3 Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@gmail.com>
4 
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU Lesser General Public License as published by
7 the Free Software Foundation; either version 2.1 of the License, or
8 (at your option) any later version.
9 
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 GNU Lesser General Public License for more details.
14 
15 You should have received a copy of the GNU Lesser General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 */
19 
20 extern "C" {
21 #include "lua.h"
22 #include "lauxlib.h"
23 }
24 
25 #include "util/numeric.h"
26 #include "util/serialize.h"
27 #include "util/string.h"
28 #include "common/c_converter.h"
29 #include "common/c_internal.h"
30 #include "constants.h"
31 #include <set>
32 
33 
34 #define CHECK_TYPE(index, name, type) { \
35 		int t = lua_type(L, (index)); \
36 		if (t != (type)) { \
37 			throw LuaError(std::string("Invalid ") + (name) + \
38 				" (expected " + lua_typename(L, (type)) + \
39 				" got " + lua_typename(L, t) + ")."); \
40 		} \
41 	}
42 #define CHECK_POS_COORD(name) CHECK_TYPE(-1, "position coordinate '" name "'", LUA_TNUMBER)
43 #define CHECK_FLOAT_RANGE(value, name) \
44 if (value < F1000_MIN || value > F1000_MAX) { \
45 	std::ostringstream error_text; \
46 	error_text << "Invalid float vector dimension range '" name "' " << \
47 	"(expected " << F1000_MIN << " < " name " < " << F1000_MAX << \
48 	" got " << value << ")." << std::endl; \
49 	throw LuaError(error_text.str()); \
50 }
51 #define CHECK_POS_TAB(index) CHECK_TYPE(index, "position", LUA_TTABLE)
52 
53 
push_float_string(lua_State * L,float value)54 void push_float_string(lua_State *L, float value)
55 {
56 	std::stringstream ss;
57 	std::string str;
58 	ss << value;
59 	str = ss.str();
60 	lua_pushstring(L, str.c_str());
61 }
62 
push_v3f(lua_State * L,v3f p)63 void push_v3f(lua_State *L, v3f p)
64 {
65 	lua_createtable(L, 0, 3);
66 	lua_pushnumber(L, p.X);
67 	lua_setfield(L, -2, "x");
68 	lua_pushnumber(L, p.Y);
69 	lua_setfield(L, -2, "y");
70 	lua_pushnumber(L, p.Z);
71 	lua_setfield(L, -2, "z");
72 }
73 
push_v2f(lua_State * L,v2f p)74 void push_v2f(lua_State *L, v2f p)
75 {
76 	lua_createtable(L, 0, 2);
77 	lua_pushnumber(L, p.X);
78 	lua_setfield(L, -2, "x");
79 	lua_pushnumber(L, p.Y);
80 	lua_setfield(L, -2, "y");
81 }
82 
push_v3_float_string(lua_State * L,v3f p)83 void push_v3_float_string(lua_State *L, v3f p)
84 {
85 	lua_createtable(L, 0, 3);
86 	push_float_string(L, p.X);
87 	lua_setfield(L, -2, "x");
88 	push_float_string(L, p.Y);
89 	lua_setfield(L, -2, "y");
90 	push_float_string(L, p.Z);
91 	lua_setfield(L, -2, "z");
92 }
93 
push_v2_float_string(lua_State * L,v2f p)94 void push_v2_float_string(lua_State *L, v2f p)
95 {
96 	lua_createtable(L, 0, 2);
97 	push_float_string(L, p.X);
98 	lua_setfield(L, -2, "x");
99 	push_float_string(L, p.Y);
100 	lua_setfield(L, -2, "y");
101 }
102 
read_v2s16(lua_State * L,int index)103 v2s16 read_v2s16(lua_State *L, int index)
104 {
105 	v2s16 p;
106 	CHECK_POS_TAB(index);
107 	lua_getfield(L, index, "x");
108 	p.X = lua_tonumber(L, -1);
109 	lua_pop(L, 1);
110 	lua_getfield(L, index, "y");
111 	p.Y = lua_tonumber(L, -1);
112 	lua_pop(L, 1);
113 	return p;
114 }
115 
push_v2s16(lua_State * L,v2s16 p)116 void push_v2s16(lua_State *L, v2s16 p)
117 {
118 	lua_createtable(L, 0, 2);
119 	lua_pushinteger(L, p.X);
120 	lua_setfield(L, -2, "x");
121 	lua_pushinteger(L, p.Y);
122 	lua_setfield(L, -2, "y");
123 }
124 
push_v2s32(lua_State * L,v2s32 p)125 void push_v2s32(lua_State *L, v2s32 p)
126 {
127 	lua_createtable(L, 0, 2);
128 	lua_pushinteger(L, p.X);
129 	lua_setfield(L, -2, "x");
130 	lua_pushinteger(L, p.Y);
131 	lua_setfield(L, -2, "y");
132 }
133 
read_v2s32(lua_State * L,int index)134 v2s32 read_v2s32(lua_State *L, int index)
135 {
136 	v2s32 p;
137 	CHECK_POS_TAB(index);
138 	lua_getfield(L, index, "x");
139 	p.X = lua_tonumber(L, -1);
140 	lua_pop(L, 1);
141 	lua_getfield(L, index, "y");
142 	p.Y = lua_tonumber(L, -1);
143 	lua_pop(L, 1);
144 	return p;
145 }
146 
read_v2f(lua_State * L,int index)147 v2f read_v2f(lua_State *L, int index)
148 {
149 	v2f p;
150 	CHECK_POS_TAB(index);
151 	lua_getfield(L, index, "x");
152 	p.X = lua_tonumber(L, -1);
153 	lua_pop(L, 1);
154 	lua_getfield(L, index, "y");
155 	p.Y = lua_tonumber(L, -1);
156 	lua_pop(L, 1);
157 	return p;
158 }
159 
check_v2f(lua_State * L,int index)160 v2f check_v2f(lua_State *L, int index)
161 {
162 	v2f p;
163 	CHECK_POS_TAB(index);
164 	lua_getfield(L, index, "x");
165 	CHECK_POS_COORD("x");
166 	p.X = lua_tonumber(L, -1);
167 	lua_pop(L, 1);
168 	lua_getfield(L, index, "y");
169 	CHECK_POS_COORD("y");
170 	p.Y = lua_tonumber(L, -1);
171 	lua_pop(L, 1);
172 	return p;
173 }
174 
read_v3f(lua_State * L,int index)175 v3f read_v3f(lua_State *L, int index)
176 {
177 	v3f pos;
178 	CHECK_POS_TAB(index);
179 	lua_getfield(L, index, "x");
180 	pos.X = lua_tonumber(L, -1);
181 	lua_pop(L, 1);
182 	lua_getfield(L, index, "y");
183 	pos.Y = lua_tonumber(L, -1);
184 	lua_pop(L, 1);
185 	lua_getfield(L, index, "z");
186 	pos.Z = lua_tonumber(L, -1);
187 	lua_pop(L, 1);
188 	return pos;
189 }
190 
check_v3f(lua_State * L,int index)191 v3f check_v3f(lua_State *L, int index)
192 {
193 	v3f pos;
194 	CHECK_POS_TAB(index);
195 	lua_getfield(L, index, "x");
196 	CHECK_POS_COORD("x");
197 	pos.X = lua_tonumber(L, -1);
198 	CHECK_FLOAT_RANGE(pos.X, "x")
199 	lua_pop(L, 1);
200 	lua_getfield(L, index, "y");
201 	CHECK_POS_COORD("y");
202 	pos.Y = lua_tonumber(L, -1);
203 	CHECK_FLOAT_RANGE(pos.Y, "y")
204 	lua_pop(L, 1);
205 	lua_getfield(L, index, "z");
206 	CHECK_POS_COORD("z");
207 	pos.Z = lua_tonumber(L, -1);
208 	CHECK_FLOAT_RANGE(pos.Z, "z")
209 	lua_pop(L, 1);
210 	return pos;
211 }
212 
read_v3d(lua_State * L,int index)213 v3d read_v3d(lua_State *L, int index)
214 {
215 	v3d pos;
216 	CHECK_POS_TAB(index);
217 	lua_getfield(L, index, "x");
218 	pos.X = lua_tonumber(L, -1);
219 	lua_pop(L, 1);
220 	lua_getfield(L, index, "y");
221 	pos.Y = lua_tonumber(L, -1);
222 	lua_pop(L, 1);
223 	lua_getfield(L, index, "z");
224 	pos.Z = lua_tonumber(L, -1);
225 	lua_pop(L, 1);
226 	return pos;
227 }
228 
check_v3d(lua_State * L,int index)229 v3d check_v3d(lua_State *L, int index)
230 {
231 	v3d pos;
232 	CHECK_POS_TAB(index);
233 	lua_getfield(L, index, "x");
234 	CHECK_POS_COORD("x");
235 	pos.X = lua_tonumber(L, -1);
236 	CHECK_FLOAT_RANGE(pos.X, "x")
237 	lua_pop(L, 1);
238 	lua_getfield(L, index, "y");
239 	CHECK_POS_COORD("y");
240 	pos.Y = lua_tonumber(L, -1);
241 	CHECK_FLOAT_RANGE(pos.Y, "y")
242 	lua_pop(L, 1);
243 	lua_getfield(L, index, "z");
244 	CHECK_POS_COORD("z");
245 	pos.Z = lua_tonumber(L, -1);
246 	CHECK_FLOAT_RANGE(pos.Z, "z")
247 	lua_pop(L, 1);
248 	return pos;
249 }
250 
push_ARGB8(lua_State * L,video::SColor color)251 void push_ARGB8(lua_State *L, video::SColor color)
252 {
253 	lua_createtable(L, 0, 4);
254 	lua_pushinteger(L, color.getAlpha());
255 	lua_setfield(L, -2, "a");
256 	lua_pushinteger(L, color.getRed());
257 	lua_setfield(L, -2, "r");
258 	lua_pushinteger(L, color.getGreen());
259 	lua_setfield(L, -2, "g");
260 	lua_pushinteger(L, color.getBlue());
261 	lua_setfield(L, -2, "b");
262 }
263 
pushFloatPos(lua_State * L,v3f p)264 void pushFloatPos(lua_State *L, v3f p)
265 {
266 	p /= BS;
267 	push_v3f(L, p);
268 }
269 
checkFloatPos(lua_State * L,int index)270 v3f checkFloatPos(lua_State *L, int index)
271 {
272 	return check_v3f(L, index) * BS;
273 }
274 
push_v3s16(lua_State * L,v3s16 p)275 void push_v3s16(lua_State *L, v3s16 p)
276 {
277 	lua_createtable(L, 0, 3);
278 	lua_pushinteger(L, p.X);
279 	lua_setfield(L, -2, "x");
280 	lua_pushinteger(L, p.Y);
281 	lua_setfield(L, -2, "y");
282 	lua_pushinteger(L, p.Z);
283 	lua_setfield(L, -2, "z");
284 }
285 
read_v3s16(lua_State * L,int index)286 v3s16 read_v3s16(lua_State *L, int index)
287 {
288 	// Correct rounding at <0
289 	v3d pf = read_v3d(L, index);
290 	return doubleToInt(pf, 1.0);
291 }
292 
check_v3s16(lua_State * L,int index)293 v3s16 check_v3s16(lua_State *L, int index)
294 {
295 	// Correct rounding at <0
296 	v3d pf = check_v3d(L, index);
297 	return doubleToInt(pf, 1.0);
298 }
299 
read_color(lua_State * L,int index,video::SColor * color)300 bool read_color(lua_State *L, int index, video::SColor *color)
301 {
302 	if (lua_istable(L, index)) {
303 		*color = read_ARGB8(L, index);
304 	} else if (lua_isnumber(L, index)) {
305 		color->set(lua_tonumber(L, index));
306 	} else if (lua_isstring(L, index)) {
307 		video::SColor parsed_color;
308 		if (!parseColorString(lua_tostring(L, index), parsed_color, true))
309 			return false;
310 
311 		*color = parsed_color;
312 	} else {
313 		return false;
314 	}
315 
316 	return true;
317 }
318 
read_ARGB8(lua_State * L,int index)319 video::SColor read_ARGB8(lua_State *L, int index)
320 {
321 	video::SColor color(0);
322 	CHECK_TYPE(index, "ARGB color", LUA_TTABLE);
323 	lua_getfield(L, index, "a");
324 	color.setAlpha(lua_isnumber(L, -1) ? lua_tonumber(L, -1) : 0xFF);
325 	lua_pop(L, 1);
326 	lua_getfield(L, index, "r");
327 	color.setRed(lua_tonumber(L, -1));
328 	lua_pop(L, 1);
329 	lua_getfield(L, index, "g");
330 	color.setGreen(lua_tonumber(L, -1));
331 	lua_pop(L, 1);
332 	lua_getfield(L, index, "b");
333 	color.setBlue(lua_tonumber(L, -1));
334 	lua_pop(L, 1);
335 	return color;
336 }
337 
is_color_table(lua_State * L,int index)338 bool is_color_table(lua_State *L, int index)
339 {
340 	// Check whole table in case of missing ColorSpec keys:
341 	// This check does not remove the checked value from the stack.
342 	// Only update the value if we know we have a valid ColorSpec key pair.
343 	if (!lua_istable(L, index))
344 		return false;
345 
346 	bool is_color_table = false;
347 	lua_getfield(L, index, "r");
348 	if (!is_color_table)
349 		is_color_table = lua_isnumber(L, -1);
350 	lua_getfield(L, index, "g");
351 	if (!is_color_table)
352 		is_color_table = lua_isnumber(L, -1);
353 	lua_getfield(L, index, "b");
354 	if (!is_color_table)
355 		is_color_table = lua_isnumber(L, -1);
356 	lua_pop(L, 3); // b, g, r values
357 	return is_color_table;
358 }
359 
read_aabb3f(lua_State * L,int index,f32 scale)360 aabb3f read_aabb3f(lua_State *L, int index, f32 scale)
361 {
362 	aabb3f box;
363 	if(lua_istable(L, index)){
364 		lua_rawgeti(L, index, 1);
365 		box.MinEdge.X = lua_tonumber(L, -1) * scale;
366 		lua_pop(L, 1);
367 		lua_rawgeti(L, index, 2);
368 		box.MinEdge.Y = lua_tonumber(L, -1) * scale;
369 		lua_pop(L, 1);
370 		lua_rawgeti(L, index, 3);
371 		box.MinEdge.Z = lua_tonumber(L, -1) * scale;
372 		lua_pop(L, 1);
373 		lua_rawgeti(L, index, 4);
374 		box.MaxEdge.X = lua_tonumber(L, -1) * scale;
375 		lua_pop(L, 1);
376 		lua_rawgeti(L, index, 5);
377 		box.MaxEdge.Y = lua_tonumber(L, -1) * scale;
378 		lua_pop(L, 1);
379 		lua_rawgeti(L, index, 6);
380 		box.MaxEdge.Z = lua_tonumber(L, -1) * scale;
381 		lua_pop(L, 1);
382 	}
383 	box.repair();
384 	return box;
385 }
386 
push_aabb3f(lua_State * L,aabb3f box)387 void push_aabb3f(lua_State *L, aabb3f box)
388 {
389 	lua_createtable(L, 6, 0);
390 	lua_pushnumber(L, box.MinEdge.X);
391 	lua_rawseti(L, -2, 1);
392 	lua_pushnumber(L, box.MinEdge.Y);
393 	lua_rawseti(L, -2, 2);
394 	lua_pushnumber(L, box.MinEdge.Z);
395 	lua_rawseti(L, -2, 3);
396 	lua_pushnumber(L, box.MaxEdge.X);
397 	lua_rawseti(L, -2, 4);
398 	lua_pushnumber(L, box.MaxEdge.Y);
399 	lua_rawseti(L, -2, 5);
400 	lua_pushnumber(L, box.MaxEdge.Z);
401 	lua_rawseti(L, -2, 6);
402 }
403 
read_aabb3f_vector(lua_State * L,int index,f32 scale)404 std::vector<aabb3f> read_aabb3f_vector(lua_State *L, int index, f32 scale)
405 {
406 	std::vector<aabb3f> boxes;
407 	if(lua_istable(L, index)){
408 		int n = lua_objlen(L, index);
409 		// Check if it's a single box or a list of boxes
410 		bool possibly_single_box = (n == 6);
411 		for(int i = 1; i <= n && possibly_single_box; i++){
412 			lua_rawgeti(L, index, i);
413 			if(!lua_isnumber(L, -1))
414 				possibly_single_box = false;
415 			lua_pop(L, 1);
416 		}
417 		if(possibly_single_box){
418 			// Read a single box
419 			boxes.push_back(read_aabb3f(L, index, scale));
420 		} else {
421 			// Read a list of boxes
422 			for(int i = 1; i <= n; i++){
423 				lua_rawgeti(L, index, i);
424 				boxes.push_back(read_aabb3f(L, -1, scale));
425 				lua_pop(L, 1);
426 			}
427 		}
428 	}
429 	return boxes;
430 }
431 
read_stringlist(lua_State * L,int index,std::vector<std::string> * result)432 size_t read_stringlist(lua_State *L, int index, std::vector<std::string> *result)
433 {
434 	if (index < 0)
435 		index = lua_gettop(L) + 1 + index;
436 
437 	size_t num_strings = 0;
438 
439 	if (lua_istable(L, index)) {
440 		lua_pushnil(L);
441 		while (lua_next(L, index)) {
442 			if (lua_isstring(L, -1)) {
443 				result->push_back(lua_tostring(L, -1));
444 				num_strings++;
445 			}
446 			lua_pop(L, 1);
447 		}
448 	} else if (lua_isstring(L, index)) {
449 		result->push_back(lua_tostring(L, index));
450 		num_strings++;
451 	}
452 
453 	return num_strings;
454 }
455 
456 /*
457 	Table field getters
458 */
459 
460 #if defined(__MINGW32__) && !defined(__MINGW64__)
461 /* MinGW 32-bit somehow crashes in the std::set destructor when this
462  * variable is thread-local, so just don't do that. */
463 static std::set<u64> warned_msgs;
464 #endif
465 
check_field_or_nil(lua_State * L,int index,int type,const char * fieldname)466 bool check_field_or_nil(lua_State *L, int index, int type, const char *fieldname)
467 {
468 #if !defined(__MINGW32__) || defined(__MINGW64__)
469 	thread_local std::set<u64> warned_msgs;
470 #endif
471 
472 	int t = lua_type(L, index);
473 	if (t == LUA_TNIL)
474 		return false;
475 
476 	if (t == type)
477 		return true;
478 
479 	// Check coercion types
480 	if (type == LUA_TNUMBER) {
481 		if (lua_isnumber(L, index))
482 			return true;
483 	} else if (type == LUA_TSTRING) {
484 		if (lua_isstring(L, index))
485 			return true;
486 	}
487 
488 	// Types mismatch. Log unique line.
489 	std::string backtrace = std::string("Invalid field ") + fieldname +
490 		" (expected " + lua_typename(L, type) +
491 		" got " + lua_typename(L, t) + ").\n" + script_get_backtrace(L);
492 
493 	u64 hash = murmur_hash_64_ua(backtrace.data(), backtrace.length(), 0xBADBABE);
494 	if (warned_msgs.find(hash) == warned_msgs.end()) {
495 		errorstream << backtrace << std::endl;
496 		warned_msgs.insert(hash);
497 	}
498 
499 	return false;
500 }
501 
getstringfield(lua_State * L,int table,const char * fieldname,std::string & result)502 bool getstringfield(lua_State *L, int table,
503 		const char *fieldname, std::string &result)
504 {
505 	lua_getfield(L, table, fieldname);
506 	bool got = false;
507 
508 	if (check_field_or_nil(L, -1, LUA_TSTRING, fieldname)) {
509 		size_t len = 0;
510 		const char *ptr = lua_tolstring(L, -1, &len);
511 		if (ptr) {
512 			result.assign(ptr, len);
513 			got = true;
514 		}
515 	}
516 	lua_pop(L, 1);
517 	return got;
518 }
519 
getfloatfield(lua_State * L,int table,const char * fieldname,float & result)520 bool getfloatfield(lua_State *L, int table,
521 		const char *fieldname, float &result)
522 {
523 	lua_getfield(L, table, fieldname);
524 	bool got = false;
525 
526 	if (check_field_or_nil(L, -1, LUA_TNUMBER, fieldname)) {
527 		result = lua_tonumber(L, -1);
528 		got = true;
529 	}
530 	lua_pop(L, 1);
531 	return got;
532 }
533 
getboolfield(lua_State * L,int table,const char * fieldname,bool & result)534 bool getboolfield(lua_State *L, int table,
535 		const char *fieldname, bool &result)
536 {
537 	lua_getfield(L, table, fieldname);
538 	bool got = false;
539 
540 	if (check_field_or_nil(L, -1, LUA_TBOOLEAN, fieldname)){
541 		result = lua_toboolean(L, -1);
542 		got = true;
543 	}
544 	lua_pop(L, 1);
545 	return got;
546 }
547 
getstringlistfield(lua_State * L,int table,const char * fieldname,std::vector<std::string> * result)548 size_t getstringlistfield(lua_State *L, int table, const char *fieldname,
549 		std::vector<std::string> *result)
550 {
551 	lua_getfield(L, table, fieldname);
552 
553 	size_t num_strings_read = read_stringlist(L, -1, result);
554 
555 	lua_pop(L, 1);
556 	return num_strings_read;
557 }
558 
getstringfield_default(lua_State * L,int table,const char * fieldname,const std::string & default_)559 std::string getstringfield_default(lua_State *L, int table,
560 		const char *fieldname, const std::string &default_)
561 {
562 	std::string result = default_;
563 	getstringfield(L, table, fieldname, result);
564 	return result;
565 }
566 
getintfield_default(lua_State * L,int table,const char * fieldname,int default_)567 int getintfield_default(lua_State *L, int table,
568 		const char *fieldname, int default_)
569 {
570 	int result = default_;
571 	getintfield(L, table, fieldname, result);
572 	return result;
573 }
574 
getfloatfield_default(lua_State * L,int table,const char * fieldname,float default_)575 float getfloatfield_default(lua_State *L, int table,
576 		const char *fieldname, float default_)
577 {
578 	float result = default_;
579 	getfloatfield(L, table, fieldname, result);
580 	return result;
581 }
582 
getboolfield_default(lua_State * L,int table,const char * fieldname,bool default_)583 bool getboolfield_default(lua_State *L, int table,
584 		const char *fieldname, bool default_)
585 {
586 	bool result = default_;
587 	getboolfield(L, table, fieldname, result);
588 	return result;
589 }
590 
getv3s16field_default(lua_State * L,int table,const char * fieldname,v3s16 default_)591 v3s16 getv3s16field_default(lua_State *L, int table,
592 		const char *fieldname, v3s16 default_)
593 {
594 	getv3intfield(L, table, fieldname, default_);
595 	return default_;
596 }
597 
setstringfield(lua_State * L,int table,const char * fieldname,const std::string & value)598 void setstringfield(lua_State *L, int table,
599 		const char *fieldname, const std::string &value)
600 {
601 	lua_pushlstring(L, value.c_str(), value.length());
602 	if(table < 0)
603 		table -= 1;
604 	lua_setfield(L, table, fieldname);
605 }
606 
setintfield(lua_State * L,int table,const char * fieldname,int value)607 void setintfield(lua_State *L, int table,
608 		const char *fieldname, int value)
609 {
610 	lua_pushinteger(L, value);
611 	if(table < 0)
612 		table -= 1;
613 	lua_setfield(L, table, fieldname);
614 }
615 
setfloatfield(lua_State * L,int table,const char * fieldname,float value)616 void setfloatfield(lua_State *L, int table,
617 		const char *fieldname, float value)
618 {
619 	lua_pushnumber(L, value);
620 	if(table < 0)
621 		table -= 1;
622 	lua_setfield(L, table, fieldname);
623 }
624 
setboolfield(lua_State * L,int table,const char * fieldname,bool value)625 void setboolfield(lua_State *L, int table,
626 		const char *fieldname, bool value)
627 {
628 	lua_pushboolean(L, value);
629 	if(table < 0)
630 		table -= 1;
631 	lua_setfield(L, table, fieldname);
632 }
633 
634 
635 ////
636 //// Array table slices
637 ////
638 
write_array_slice_float(lua_State * L,int table_index,float * data,v3u16 data_size,v3u16 slice_offset,v3u16 slice_size)639 size_t write_array_slice_float(
640 	lua_State *L,
641 	int table_index,
642 	float *data,
643 	v3u16 data_size,
644 	v3u16 slice_offset,
645 	v3u16 slice_size)
646 {
647 	v3u16 pmin, pmax(data_size);
648 
649 	if (slice_offset.X > 0) {
650 		slice_offset.X--;
651 		pmin.X = slice_offset.X;
652 		pmax.X = MYMIN(slice_offset.X + slice_size.X, data_size.X);
653 	}
654 
655 	if (slice_offset.Y > 0) {
656 		slice_offset.Y--;
657 		pmin.Y = slice_offset.Y;
658 		pmax.Y = MYMIN(slice_offset.Y + slice_size.Y, data_size.Y);
659 	}
660 
661 	if (slice_offset.Z > 0) {
662 		slice_offset.Z--;
663 		pmin.Z = slice_offset.Z;
664 		pmax.Z = MYMIN(slice_offset.Z + slice_size.Z, data_size.Z);
665 	}
666 
667 	const u32 ystride = data_size.X;
668 	const u32 zstride = data_size.X * data_size.Y;
669 
670 	u32 elem_index = 1;
671 	for (u32 z = pmin.Z; z != pmax.Z; z++)
672 	for (u32 y = pmin.Y; y != pmax.Y; y++)
673 	for (u32 x = pmin.X; x != pmax.X; x++) {
674 		u32 i = z * zstride + y * ystride + x;
675 		lua_pushnumber(L, data[i]);
676 		lua_rawseti(L, table_index, elem_index);
677 		elem_index++;
678 	}
679 
680 	return elem_index - 1;
681 }
682