1 // license:BSD-3-Clause
2 // copyright-holders:Miodrag Milanovic,Luca Bruno
3 /***************************************************************************
4 
5     luaengine.cpp
6 
7     Controls execution of the core MAME system.
8 
9 ***************************************************************************/
10 
11 #include <lua.hpp>
12 #include "emu.h"
13 #include "mame.h"
14 #include "debugger.h"
15 #include "debug/debugcon.h"
16 #include "debug/debugcpu.h"
17 #include "debug/points.h"
18 #include "debug/textbuf.h"
19 #include "drivenum.h"
20 #include "emuopts.h"
21 #include "ui/ui.h"
22 #include "ui/pluginopt.h"
23 #include "luaengine.h"
24 #include "natkeyboard.h"
25 #include "uiinput.h"
26 #include "pluginopts.h"
27 #include "softlist.h"
28 #include "inputdev.h"
29 
30 #include <cstring>
31 #include <thread>
32 
33 
34 #ifdef __clang__
35 #pragma clang diagnostic ignored "-Wshift-count-overflow"
36 #endif
37 #if defined(_MSC_VER)
38 #pragma warning(disable:4503)
39 #endif
40 
41 //**************************************************************************
42 //  LUA ENGINE
43 //**************************************************************************
44 
45 extern "C" {
46 	int luaopen_zlib(lua_State *L);
47 	int luaopen_lfs(lua_State *L);
48 	int luaopen_linenoise(lua_State *L);
49 	int luaopen_lsqlite3(lua_State *L);
50 }
51 
52 namespace sol
53 {
54 	class buffer
55 	{
56 	public:
57 		// sol does lua_settop(0), save userdata buffer in registry if necessary
buffer(int size,lua_State * L)58 		buffer(int size, lua_State *L)
59 		{
60 			ptr = luaL_buffinitsize(L, &buff, size);
61 			len = size;
62 			if(buff.b != buff.initb)
63 			{
64 				lua_pushvalue(L, -1);
65 				lua_setfield(L, LUA_REGISTRYINDEX, "sol::buffer_temp");
66 			}
67 		}
~buffer()68 		~buffer()
69 		{
70 			lua_State *L = buff.L;
71 			if(lua_getfield(L, LUA_REGISTRYINDEX, "sol::buffer_temp") != LUA_TNIL)
72 			{
73 				lua_pushnil(L);
74 				lua_setfield(L, LUA_REGISTRYINDEX, "sol::buffer_temp");
75 			}
76 			else
77 				lua_pop(L, -1);
78 
79 			luaL_pushresultsize(&buff, len);
80 		}
set_len(int size)81 		void set_len(int size) { len = size; }
get_len()82 		int get_len() { return len; }
get_ptr()83 		char *get_ptr() { return ptr; }
84 	private:
85 		luaL_Buffer buff;
86 		int len;
87 		char *ptr;
88 	};
89 	template<>
90 	struct is_container<core_options> : std::false_type {}; // don't convert core_optons to a table directly
91 	namespace stack
92 	{
93 		template <>
94 		struct pusher<osd_file::error>
95 		{
pushsol::stack::pusher96 			static int push(lua_State *L, osd_file::error error)
97 			{
98 				const char *strerror;
99 				switch(error)
100 				{
101 					case osd_file::error::NONE:
102 						return stack::push(L, sol::nil);
103 					case osd_file::error::FAILURE:
104 						strerror = "failure";
105 						break;
106 					case osd_file::error::OUT_OF_MEMORY:
107 						strerror = "out_of_memory";
108 						break;
109 					case osd_file::error::NOT_FOUND:
110 						strerror = "not_found";
111 						break;
112 					case osd_file::error::ACCESS_DENIED:
113 						strerror = "access_denied";
114 						break;
115 					case osd_file::error::ALREADY_OPEN:
116 						strerror = "already_open";
117 						break;
118 					case osd_file::error::TOO_MANY_FILES:
119 						strerror = "too_many_files";
120 						break;
121 					case osd_file::error::INVALID_DATA:
122 						strerror = "invalid_data";
123 						break;
124 					case osd_file::error::INVALID_ACCESS:
125 						strerror = "invalid_access";
126 						break;
127 					default:
128 						strerror = "unknown_error";
129 						break;
130 				}
131 				return stack::push(L, strerror);
132 			}
133 		};
134 		template <>
135 		struct checker<sol::buffer *>
136 		{
137 			template <typename Handler>
checksol::stack::checker138 			static bool check (lua_State* L, int index, Handler&& handler, record& tracking)
139 			{
140 				return stack::check<int>(L, index, handler);
141 			}
142 		};
143 		template <>
144 		struct getter<sol::buffer *>
145 		{
getsol::stack::getter146 			static sol::buffer *get(lua_State* L, int index, record& tracking)
147 			{
148 				return new sol::buffer(stack::get<int>(L, index), L);
149 			}
150 		};
151 		template <>
152 		struct pusher<sol::buffer *>
153 		{
pushsol::stack::pusher154 			static int push(lua_State* L, sol::buffer *buff)
155 			{
156 				delete buff;
157 				return 1;
158 			}
159 		};
160 		template <>
161 		struct pusher<map_handler_type>
162 		{
pushsol::stack::pusher163 			static int push(lua_State *L, map_handler_type type)
164 			{
165 				const char *typestr;
166 				switch(type)
167 				{
168 					case AMH_NONE:
169 						typestr = "none";
170 						break;
171 					case AMH_RAM:
172 						typestr = "ram";
173 						break;
174 					case AMH_ROM:
175 						typestr = "rom";
176 						break;
177 					case AMH_NOP:
178 						typestr = "nop";
179 						break;
180 					case AMH_UNMAP:
181 						typestr = "unmap";
182 						break;
183 					case AMH_DEVICE_DELEGATE:
184 					case AMH_DEVICE_DELEGATE_M:
185 					case AMH_DEVICE_DELEGATE_S:
186 					case AMH_DEVICE_DELEGATE_SM:
187 					case AMH_DEVICE_DELEGATE_MO:
188 					case AMH_DEVICE_DELEGATE_SMO:
189 						typestr = "delegate";
190 						break;
191 					case AMH_PORT:
192 						typestr = "port";
193 						break;
194 					case AMH_BANK:
195 						typestr = "bank";
196 						break;
197 					case AMH_DEVICE_SUBMAP:
198 						typestr = "submap";
199 						break;
200 					default:
201 						typestr = "unknown";
202 						break;
203 				}
204 				return stack::push(L, typestr);
205 			}
206 		};
207 	}
208 }
209 
210 
211 namespace
212 {
213 	// ======================> enum_parser
214 	template<typename T, size_t SIZE>
215 	class enum_parser
216 	{
217 	public:
enum_parser(std::initializer_list<std::pair<const char *,T>> values)218 		constexpr enum_parser(std::initializer_list<std::pair<const char *, T>> values)
219 		{
220 			if (values.size() != SIZE)
221 				throw false && "size template argument incorrectly specified";
222 			std::copy(values.begin(), values.end(), m_map.begin());
223 		}
224 
operator ()(const char * text) const225 		T operator()(const char *text) const
226 		{
227 			auto iter = std::find_if(
228 				m_map.begin() + 1,
229 				m_map.end(),
230 				[text](const auto &x) { return !strcmp(text, x.first); });
231 			if (iter == m_map.end())
232 				iter = m_map.begin();
233 			return iter->second;
234 		}
235 
operator ()(const std::string & text) const236 		T operator()(const std::string &text) const
237 		{
238 			return (*this)(text.c_str());
239 		}
240 
241 	private:
242 		std::array<std::pair<const char *, T>, SIZE>    m_map;
243 	};
244 };
245 
246 
247 static const enum_parser<input_seq_type, 3> s_seq_type_parser =
248 {
249 	{ "standard", SEQ_TYPE_STANDARD },
250 	{ "increment", SEQ_TYPE_INCREMENT },
251 	{ "decrement", SEQ_TYPE_DECREMENT },
252 };
253 
254 
255 static const enum_parser<movie_recording::format, 2> s_movie_recording_format_parser =
256 {
257 	{ "avi", movie_recording::format::AVI },
258 	{ "mng", movie_recording::format::MNG }
259 };
260 
261 
262 static const enum_parser<read_or_write, 4> s_read_or_write_parser =
263 {
264 	{ "r", read_or_write::READ },
265 	{ "w", read_or_write::WRITE },
266 	{ "rw", read_or_write::READWRITE },
267 	{ "wr", read_or_write::READWRITE }
268 };
269 
270 
271 static const enum_parser<ui::text_layout::text_justify, 3> s_text_justify_parser =
272 {
273 	{ "left", ui::text_layout::LEFT },
274 	{ "right", ui::text_layout::RIGHT },
275 	{ "center", ui::text_layout::CENTER },
276 };
277 
278 
279 //-------------------------------------------------
280 //  process_snapshot_filename - processes a snapshot
281 //  filename
282 //-------------------------------------------------
283 
process_snapshot_filename(running_machine & machine,const char * s)284 static std::string process_snapshot_filename(running_machine &machine, const char *s)
285 {
286 	std::string result(s);
287 	if (!osd_is_absolute_path(s))
288 	{
289 		strreplace(result, "/", PATH_SEPARATOR);
290 		strreplace(result, "%g", machine.basename());
291 	}
292 	return result;
293 }
294 
295 
296 //-------------------------------------------------
297 //  mem_read - templated memory readers for <sign>,<size>
298 //  -> manager:machine().devices[":maincpu"].spaces["program"]:read_i8(0xC000)
299 //-------------------------------------------------
300 
301 template <typename T>
mem_read(offs_t address)302 T lua_engine::addr_space::mem_read(offs_t address)
303 {
304 	T mem_content = 0;
305 	switch(sizeof(mem_content) * 8) {
306 		case 8:
307 			mem_content = space.read_byte(address);
308 			break;
309 		case 16:
310 			if (WORD_ALIGNED(address)) {
311 				mem_content = space.read_word(address);
312 			} else {
313 				mem_content = space.read_word_unaligned(address);
314 			}
315 			break;
316 		case 32:
317 			if (DWORD_ALIGNED(address)) {
318 				mem_content = space.read_dword(address);
319 			} else {
320 				mem_content = space.read_dword_unaligned(address);
321 			}
322 			break;
323 		case 64:
324 			if (QWORD_ALIGNED(address)) {
325 				mem_content = space.read_qword(address);
326 			} else {
327 				mem_content = space.read_qword_unaligned(address);
328 			}
329 			break;
330 		default:
331 			break;
332 	}
333 
334 	return mem_content;
335 }
336 
337 //-------------------------------------------------
338 //  mem_write - templated memory writer for <sign>,<size>
339 //  -> manager:machine().devices[":maincpu"].spaces["program"]:write_u16(0xC000, 0xF00D)
340 //-------------------------------------------------
341 
342 template <typename T>
mem_write(offs_t address,T val)343 void lua_engine::addr_space::mem_write(offs_t address, T val)
344 {
345 	switch(sizeof(val) * 8) {
346 		case 8:
347 			space.write_byte(address, val);
348 			break;
349 		case 16:
350 			if (WORD_ALIGNED(address)) {
351 				space.write_word(address, val);
352 			} else {
353 				space.write_word_unaligned(address, val);
354 			}
355 			break;
356 		case 32:
357 			if (DWORD_ALIGNED(address)) {
358 				space.write_dword(address, val);
359 			} else {
360 				space.write_dword_unaligned(address, val);
361 			}
362 			break;
363 		case 64:
364 			if (QWORD_ALIGNED(address)) {
365 				space.write_qword(address, val);
366 			} else {
367 				space.write_qword_unaligned(address, val);
368 			}
369 			break;
370 		default:
371 			break;
372 	}
373 }
374 
375 //-------------------------------------------------
376 //  log_mem_read - templated logical memory readers for <sign>,<size>
377 //  -> manager:machine().devices[":maincpu"].spaces["program"]:read_log_i8(0xC000)
378 //-------------------------------------------------
379 
380 template <typename T>
log_mem_read(offs_t address)381 T lua_engine::addr_space::log_mem_read(offs_t address)
382 {
383 	T mem_content = 0;
384 	if(!dev.translate(space.spacenum(), TRANSLATE_READ_DEBUG, address))
385 		return 0;
386 
387 	switch(sizeof(mem_content) * 8) {
388 		case 8:
389 			mem_content = space.read_byte(address);
390 			break;
391 		case 16:
392 			if (WORD_ALIGNED(address)) {
393 				mem_content = space.read_word(address);
394 			} else {
395 				mem_content = space.read_word_unaligned(address);
396 			}
397 			break;
398 		case 32:
399 			if (DWORD_ALIGNED(address)) {
400 				mem_content = space.read_dword(address);
401 			} else {
402 				mem_content = space.read_dword_unaligned(address);
403 			}
404 			break;
405 		case 64:
406 			if (QWORD_ALIGNED(address)) {
407 				mem_content = space.read_qword(address);
408 			} else {
409 				mem_content = space.read_qword_unaligned(address);
410 			}
411 			break;
412 		default:
413 			break;
414 	}
415 
416 	return mem_content;
417 }
418 
419 //-------------------------------------------------
420 //  log_mem_write - templated logical memory writer for <sign>,<size>
421 //  -> manager:machine().devices[":maincpu"].spaces["program"]:write_log_u16(0xC000, 0xF00D)
422 //-------------------------------------------------
423 
424 template <typename T>
log_mem_write(offs_t address,T val)425 void lua_engine::addr_space::log_mem_write(offs_t address, T val)
426 {
427 	if(!dev.translate(space.spacenum(), TRANSLATE_WRITE_DEBUG, address))
428 		return;
429 
430 	switch(sizeof(val) * 8) {
431 		case 8:
432 			space.write_byte(address, val);
433 			break;
434 		case 16:
435 			if (WORD_ALIGNED(address)) {
436 				space.write_word(address, val);
437 			} else {
438 				space.write_word_unaligned(address, val);
439 			}
440 			break;
441 		case 32:
442 			if (DWORD_ALIGNED(address)) {
443 				space.write_dword(address, val);
444 			} else {
445 				space.write_dword_unaligned(address, val);
446 			}
447 			break;
448 		case 64:
449 			if (QWORD_ALIGNED(address)) {
450 				space.write_qword(address, val);
451 			} else {
452 				space.write_qword_unaligned(address, val);
453 			}
454 			break;
455 		default:
456 			break;
457 	}
458 }
459 
460 //-------------------------------------------------
461 //  mem_direct_read - templated direct memory readers for <sign>,<size>
462 //  -> manager:machine().devices[":maincpu"].spaces["program"]:read_direct_i8(0xC000)
463 //-------------------------------------------------
464 
465 template <typename T>
direct_mem_read(offs_t address)466 T lua_engine::addr_space::direct_mem_read(offs_t address)
467 {
468 	T mem_content = 0;
469 	offs_t lowmask = space.data_width() / 8 - 1;
470 	for(int i = 0; i < sizeof(T); i++)
471 	{
472 		int addr = space.endianness() == ENDIANNESS_LITTLE ? address + sizeof(T) - 1 - i : address + i;
473 		uint8_t *base = (uint8_t *)space.get_read_ptr(addr & ~lowmask);
474 		if(!base)
475 			continue;
476 		mem_content <<= 8;
477 		if(space.endianness() == ENDIANNESS_BIG)
478 			mem_content |= base[BYTE8_XOR_BE(addr) & lowmask];
479 		else
480 			mem_content |= base[BYTE8_XOR_LE(addr) & lowmask];
481 	}
482 
483 	return mem_content;
484 }
485 
486 //-------------------------------------------------
487 //  mem_direct_write - templated memory writer for <sign>,<size>
488 //  -> manager:machine().devices[":maincpu"].spaces["program"]:write_direct_u16(0xC000, 0xF00D)
489 //-------------------------------------------------
490 
491 template <typename T>
direct_mem_write(offs_t address,T val)492 void lua_engine::addr_space::direct_mem_write(offs_t address, T val)
493 {
494 	offs_t lowmask = space.data_width() / 8 - 1;
495 	for(int i = 0; i < sizeof(T); i++)
496 	{
497 		int addr = space.endianness() == ENDIANNESS_BIG ? address + sizeof(T) - 1 - i : address + i;
498 		uint8_t *base = (uint8_t *)space.get_read_ptr(addr & ~lowmask);
499 		if(!base)
500 			continue;
501 		if(space.endianness() == ENDIANNESS_BIG)
502 			base[BYTE8_XOR_BE(addr) & lowmask] = val & 0xff;
503 		else
504 			base[BYTE8_XOR_LE(addr) & lowmask] = val & 0xff;
505 		val >>= 8;
506 	}
507 }
508 
509 //-------------------------------------------------
510 //  region_read - templated region readers for <sign>,<size>
511 //  -> manager:machine():memory().regions[":maincpu"]:read_i8(0xC000)
512 //-------------------------------------------------
513 
514 template <typename T>
region_read(memory_region & region,offs_t address)515 T lua_engine::region_read(memory_region &region, offs_t address)
516 {
517 	T mem_content = 0;
518 	offs_t lowmask = region.bytewidth() - 1;
519 	for(int i = 0; i < sizeof(T); i++)
520 	{
521 		int addr = region.endianness() == ENDIANNESS_LITTLE ? address + sizeof(T) - 1 - i : address + i;
522 		if(addr >= region.bytes())
523 			continue;
524 		mem_content <<= 8;
525 		if(region.endianness() == ENDIANNESS_BIG)
526 			mem_content |= region.as_u8((BYTE8_XOR_BE(addr) & lowmask) | (addr & ~lowmask));
527 		else
528 			mem_content |= region.as_u8((BYTE8_XOR_LE(addr) & lowmask) | (addr & ~lowmask));
529 	}
530 
531 	return mem_content;
532 }
533 
534 //-------------------------------------------------
535 //  region_write - templated region writer for <sign>,<size>
536 //  -> manager:machine():memory().regions[":maincpu"]:write_u16(0xC000, 0xF00D)
537 //-------------------------------------------------
538 
539 template <typename T>
region_write(memory_region & region,offs_t address,T val)540 void lua_engine::region_write(memory_region &region, offs_t address, T val)
541 {
542 	offs_t lowmask = region.bytewidth() - 1;
543 	for(int i = 0; i < sizeof(T); i++)
544 	{
545 		int addr = region.endianness() == ENDIANNESS_BIG ? address + sizeof(T) - 1 - i : address + i;
546 		if(addr >= region.bytes())
547 			continue;
548 		if(region.endianness() == ENDIANNESS_BIG)
549 			region.base()[(BYTE8_XOR_BE(addr) & lowmask) | (addr & ~lowmask)] = val & 0xff;
550 		else
551 			region.base()[(BYTE8_XOR_LE(addr) & lowmask) | (addr & ~lowmask)] = val & 0xff;
552 		val >>= 8;
553 	}
554 }
555 
556 //-------------------------------------------------
557 //  share_read - templated share readers for <sign>,<size>
558 //  -> manager:machine():memory().shares[":maincpu"]:read_i8(0xC000)
559 //-------------------------------------------------
560 
561 template <typename T>
share_read(memory_share & share,offs_t address)562 T lua_engine::share_read(memory_share &share, offs_t address)
563 {
564 	T mem_content = 0;
565 	offs_t lowmask = share.bytewidth() - 1;
566 	uint8_t* ptr = (uint8_t*)share.ptr();
567 	for(int i = 0; i < sizeof(T); i++)
568 	{
569 		int addr = share.endianness() == ENDIANNESS_LITTLE ? address + sizeof(T) - 1 - i : address + i;
570 		if(addr >= share.bytes())
571 			continue;
572 		mem_content <<= 8;
573 		if(share.endianness() == ENDIANNESS_BIG)
574 			mem_content |= ptr[(BYTE8_XOR_BE(addr) & lowmask) | (addr & ~lowmask)];
575 		else
576 			mem_content |= ptr[(BYTE8_XOR_LE(addr) & lowmask) | (addr & ~lowmask)];
577 	}
578 
579 	return mem_content;
580 }
581 
582 //-------------------------------------------------
583 //  share_write - templated share writer for <sign>,<size>
584 //  -> manager:machine():memory().shares[":maincpu"]:write_u16(0xC000, 0xF00D)
585 //-------------------------------------------------
586 
587 template <typename T>
share_write(memory_share & share,offs_t address,T val)588 void lua_engine::share_write(memory_share &share, offs_t address, T val)
589 {
590 	offs_t lowmask = share.bytewidth() - 1;
591 	uint8_t* ptr = (uint8_t*)share.ptr();
592 	for(int i = 0; i < sizeof(T); i++)
593 	{
594 		int addr = share.endianness() == ENDIANNESS_BIG ? address + sizeof(T) - 1 - i : address + i;
595 		if(addr >= share.bytes())
596 			continue;
597 		if(share.endianness() == ENDIANNESS_BIG)
598 			ptr[(BYTE8_XOR_BE(addr) & lowmask) | (addr & ~lowmask)] = val & 0xff;
599 		else
600 			ptr[(BYTE8_XOR_LE(addr) & lowmask) | (addr & ~lowmask)] = val & 0xff;
601 		val >>= 8;
602 	}
603 }
604 
605 //-------------------------------------------------
606 //  lua_engine - constructor
607 //-------------------------------------------------
608 
lua_engine()609 lua_engine::lua_engine()
610 {
611 	m_machine = nullptr;
612 	m_lua_state = luaL_newstate();  /* create state */
613 	m_sol_state = std::make_unique<sol::state_view>(m_lua_state); // create sol view
614 
615 	luaL_checkversion(m_lua_state);
616 	lua_gc(m_lua_state, LUA_GCSTOP, 0);  /* stop collector during initialization */
617 	sol().open_libraries();
618 
619 	// Get package.preload so we can store builtins in it.
620 	sol()["package"]["preload"]["zlib"] = &luaopen_zlib;
621 	sol()["package"]["preload"]["lfs"] = &luaopen_lfs;
622 	sol()["package"]["preload"]["linenoise"] = &luaopen_linenoise;
623 	sol()["package"]["preload"]["lsqlite3"] = &luaopen_lsqlite3;
624 
625 	lua_gc(m_lua_state, LUA_GCRESTART, 0);
626 }
627 
628 //-------------------------------------------------
629 //  ~lua_engine - destructor
630 //-------------------------------------------------
631 
~lua_engine()632 lua_engine::~lua_engine()
633 {
634 	close();
635 }
636 
call_plugin(const std::string & name,sol::object in)637 sol::object lua_engine::call_plugin(const std::string &name, sol::object in)
638 {
639 	std::string field = "cb_" + name;
640 	sol::object obj = sol().registry()[field];
641 	if(obj.is<sol::protected_function>())
642 	{
643 		auto res = invoke(obj.as<sol::protected_function>(), in);
644 		if(!res.valid())
645 		{
646 			sol::error err = res;
647 			osd_printf_error("[LUA ERROR] in call_plugin: %s\n", err.what());
648 		}
649 		else
650 			return res.get<sol::object>();
651 	}
652 	return sol::make_object(sol(), sol::nil);
653 }
654 
menu_populate(const std::string & menu,std::vector<std::tuple<std::string,std::string,std::string>> & menu_list)655 void lua_engine::menu_populate(const std::string &menu, std::vector<std::tuple<std::string, std::string, std::string>> &menu_list)
656 {
657 	std::string field = "menu_pop_" + menu;
658 	sol::object obj = sol().registry()[field];
659 	if(obj.is<sol::protected_function>())
660 	{
661 		auto res = invoke(obj.as<sol::protected_function>());
662 		if(!res.valid())
663 		{
664 			sol::error err = res;
665 			osd_printf_error("[LUA ERROR] in menu_populate: %s\n", err.what());
666 		}
667 		else
668 		{
669 			sol::table table = res;
670 			for(auto &entry : table)
671 			{
672 				if(entry.second.is<sol::table>())
673 				{
674 					sol::table enttable = entry.second.as<sol::table>();
675 					menu_list.emplace_back(enttable.get<std::string, std::string, std::string>(1, 2, 3));
676 				}
677 			}
678 		}
679 	}
680 }
681 
menu_callback(const std::string & menu,int index,const std::string & event)682 bool lua_engine::menu_callback(const std::string &menu, int index, const std::string &event)
683 {
684 	std::string field = "menu_cb_" + menu;
685 	bool ret = false;
686 	sol::object obj = sol().registry()[field];
687 	if(obj.is<sol::protected_function>())
688 	{
689 		auto res = invoke(obj.as<sol::protected_function>(), index, event);
690 		if(!res.valid())
691 		{
692 			sol::error err = res;
693 			osd_printf_error("[LUA ERROR] in menu_callback: %s\n", err.what());
694 		}
695 		else
696 			ret = res;
697 	}
698 	return ret;
699 }
700 
set_machine(running_machine * machine)701 void lua_engine::set_machine(running_machine *machine)
702 {
703 	if (!machine || (machine != m_machine))
704 		m_seq_poll.reset();
705 	m_machine = machine;
706 }
707 
enumerate_functions(const char * id,std::function<bool (const sol::protected_function & func)> && callback)708 int lua_engine::enumerate_functions(const char *id, std::function<bool(const sol::protected_function &func)> &&callback)
709 {
710 	int count = 0;
711 	sol::object functable = sol().registry()[id];
712 	if (functable.is<sol::table>())
713 	{
714 		for (auto &func : functable.as<sol::table>())
715 		{
716 			if (func.second.is<sol::protected_function>())
717 			{
718 				bool cont = callback(func.second.as<sol::protected_function>());
719 				count++;
720 				if (!cont)
721 					break;
722 			}
723 		}
724 		return true;
725 	}
726 	return count;
727 }
728 
execute_function(const char * id)729 bool lua_engine::execute_function(const char *id)
730 {
731 	int count = enumerate_functions(id, [this](const sol::protected_function &func)
732 	{
733 		auto ret = invoke(func);
734 		if(!ret.valid())
735 		{
736 			sol::error err = ret;
737 			osd_printf_error("[LUA ERROR] in execute_function: %s\n", err.what());
738 		}
739 		return true;
740 	});
741 	return count > 0;
742 }
743 
register_function(sol::function func,const char * id)744 void lua_engine::register_function(sol::function func, const char *id)
745 {
746 	sol::object functable = sol().registry()[id];
747 	if(functable.is<sol::table>())
748 		functable.as<sol::table>().add(func);
749 	else
750 		sol().registry().create_named(id, 1, func);
751 }
752 
on_machine_prestart()753 void lua_engine::on_machine_prestart()
754 {
755 	execute_function("LUA_ON_PRESTART");
756 }
757 
on_machine_start()758 void lua_engine::on_machine_start()
759 {
760 	execute_function("LUA_ON_START");
761 }
762 
on_machine_stop()763 void lua_engine::on_machine_stop()
764 {
765 	execute_function("LUA_ON_STOP");
766 }
767 
on_machine_before_load_settings()768 void lua_engine::on_machine_before_load_settings()
769 {
770 	execute_function("LUA_ON_BEFORE_LOAD_SETTINGS");
771 }
772 
on_machine_pause()773 void lua_engine::on_machine_pause()
774 {
775 	execute_function("LUA_ON_PAUSE");
776 }
777 
on_machine_resume()778 void lua_engine::on_machine_resume()
779 {
780 	execute_function("LUA_ON_RESUME");
781 }
782 
on_machine_frame()783 void lua_engine::on_machine_frame()
784 {
785 	execute_function("LUA_ON_FRAME");
786 }
787 
on_frame_done()788 void lua_engine::on_frame_done()
789 {
790 	execute_function("LUA_ON_FRAME_DONE");
791 }
792 
on_sound_update()793 void lua_engine::on_sound_update()
794 {
795 	execute_function("LUA_ON_SOUND_UPDATE");
796 }
797 
on_periodic()798 void lua_engine::on_periodic()
799 {
800 	execute_function("LUA_ON_PERIODIC");
801 }
802 
on_missing_mandatory_image(const std::string & instance_name)803 bool lua_engine::on_missing_mandatory_image(const std::string &instance_name)
804 {
805 	bool handled = false;
806 	enumerate_functions("LUA_ON_MANDATORY_FILE_MANAGER_OVERRIDE", [this, &instance_name, &handled](const sol::protected_function &func)
807 	{
808 		auto ret = invoke(func, instance_name);
809 
810 		if(!ret.valid())
811 		{
812 			sol::error err = ret;
813 			osd_printf_error("[LUA ERROR] in on_missing_mandatory_image: %s\n", err.what());
814 		}
815 		else if (ret.get<bool>())
816 		{
817 			handled = true;
818 		}
819 		return !handled;
820 	});
821 	return handled;
822 }
823 
attach_notifiers()824 void lua_engine::attach_notifiers()
825 {
826 	machine().add_notifier(MACHINE_NOTIFY_RESET, machine_notify_delegate(&lua_engine::on_machine_prestart, this), true);
827 	machine().add_notifier(MACHINE_NOTIFY_RESET, machine_notify_delegate(&lua_engine::on_machine_start, this));
828 	machine().add_notifier(MACHINE_NOTIFY_EXIT, machine_notify_delegate(&lua_engine::on_machine_stop, this));
829 	machine().add_notifier(MACHINE_NOTIFY_PAUSE, machine_notify_delegate(&lua_engine::on_machine_pause, this));
830 	machine().add_notifier(MACHINE_NOTIFY_RESUME, machine_notify_delegate(&lua_engine::on_machine_resume, this));
831 	machine().add_notifier(MACHINE_NOTIFY_FRAME, machine_notify_delegate(&lua_engine::on_machine_frame, this));
832 }
833 
834 //-------------------------------------------------
835 //  initialize - initialize lua hookup to emu engine
836 //-------------------------------------------------
837 
initialize()838 void lua_engine::initialize()
839 {
840 
841 /*  emu library
842  *
843  * emu.app_name() - return application name
844  * emu.app_version() - return application version
845  * emu.gamename() - return game full name
846  * emu.romname() - return game ROM name
847  * emu.softname() - return softlist name
848  * emu.time() - return emulation time
849  * emu.pid() - return frontend process ID
850  *
851  * emu.driver_find(driver_name) - find and return game_driver for driver_name
852  * emu.start(driver_name) - start given driver_name
853  * emu.pause() - pause emulation
854  * emu.unpause() - unpause emulation
855  * emu.step() - advance one frame
856  * emu.keypost(keys) - post keys to natural keyboard
857  * emu.wait(len) - wait for len within coroutine
858  * emu.lang_translate(str) - get translation for str if available
859  * emu.subst_env(str) - substitute environment variables with values for str
860  *
861  * emu.register_prestart(callback) - register callback before reset
862  * emu.register_start(callback) - register callback after reset
863  * emu.register_stop(callback) - register callback after stopping
864  * emu.register_pause(callback) - register callback at pause
865  * emu.register_resume(callback) - register callback at resume
866  * emu.register_frame(callback) - register callback at end of frame
867  * emu.register_frame_done(callback) - register callback after frame is drawn to screen (for overlays)
868  * emu.register_sound_update(callback) - register callback after sound update has generated new samples
869  * emu.register_periodic(callback) - register periodic callback while program is running
870  * emu.register_callback(callback, name) - register callback to be used by MAME via lua_engine::call_plugin()
871  * emu.register_menu(event_callback, populate_callback, name) - register callbacks for plugin menu
872  * emu.register_mandatory_file_manager_override(callback) - register callback invoked to override mandatory file manager
873  * emu.register_before_load_settings(callback) - register callback to be run before settings are loaded
874  * emu.show_menu(menu_name) - show menu by name and pause the machine
875  *
876  * emu.print_verbose(str) - output to stderr at verbose level
877  * emu.print_error(str) - output to stderr at error level
878  * emu.print_info(str) - output to stderr at info level
879  * emu.print_debug(str) - output to stderr at debug level
880  */
881 
882 	sol::table emu = sol().create_named_table("emu");
883 	emu["app_name"] = &emulator_info::get_appname_lower;
884 	emu["app_version"] = &emulator_info::get_bare_build_version;
885 	emu["gamename"] = [this](){ return machine().system().type.fullname(); };
886 	emu["romname"] = [this](){ return machine().basename(); };
887 	emu["softname"] = [this]() { return machine().options().software_name(); };
888 	emu["keypost"] = [this](const char *keys){ machine().ioport().natkeyboard().post_utf8(keys); };
889 	emu["time"] = [this](){ return machine().time().as_double(); };
890 	emu["start"] = [this](const char *driver) {
891 			int i = driver_list::find(driver);
892 			if (i != -1)
893 			{
894 				mame_machine_manager::instance()->schedule_new_driver(driver_list::driver(i));
895 				machine().schedule_hard_reset();
896 			}
897 			return 1;
898 		};
899 	emu["pause"] = [this](){ return machine().pause(); };
900 	emu["unpause"] = [this](){ return machine().resume(); };
901 	emu["step"] = [this]() {
902 			mame_machine_manager::instance()->ui().set_single_step(true);
903 			machine().resume();
904 		};
905 	emu["register_prestart"] = [this](sol::function func){ register_function(func, "LUA_ON_PRESTART"); };
906 	emu["register_start"] = [this](sol::function func){ register_function(func, "LUA_ON_START"); };
907 	emu["register_stop"] = [this](sol::function func){ register_function(func, "LUA_ON_STOP"); };
908 	emu["register_pause"] = [this](sol::function func){ register_function(func, "LUA_ON_PAUSE"); };
909 	emu["register_resume"] = [this](sol::function func){ register_function(func, "LUA_ON_RESUME"); };
910 	emu["register_frame"] = [this](sol::function func){ register_function(func, "LUA_ON_FRAME"); };
911 	emu["register_frame_done"] = [this](sol::function func){ register_function(func, "LUA_ON_FRAME_DONE"); };
912 	emu["register_sound_update"] = [this](sol::function func){ register_function(func, "LUA_ON_SOUND_UPDATE"); };
913 	emu["register_periodic"] = [this](sol::function func){ register_function(func, "LUA_ON_PERIODIC"); };
914 	emu["register_mandatory_file_manager_override"] = [this](sol::function func) { register_function(func, "LUA_ON_MANDATORY_FILE_MANAGER_OVERRIDE"); };
915 	emu["register_before_load_settings"] = [this](sol::function func) { register_function(func, "LUA_ON_BEFORE_LOAD_SETTINGS"); };
916 	emu["register_menu"] = [this](sol::function cb, sol::function pop, const std::string &name) {
917 			std::string cbfield = "menu_cb_" + name;
918 			std::string popfield = "menu_pop_" + name;
919 			sol().registry()[cbfield] = cb;
920 			sol().registry()[popfield] = pop;
921 			m_menu.push_back(name);
922 		};
923 	emu["show_menu"] = [this](const char *name) {
924 			mame_ui_manager &mui = mame_machine_manager::instance()->ui();
925 			render_container &container = machine().render().ui_container();
926 			ui::menu_plugin::show_menu(mui, container, (char *)name);
927 		};
928 	emu["register_callback"] = [this](sol::function cb, const std::string &name) {
929 			std::string field = "cb_" + name;
930 			sol().registry()[field] = cb;
931 		};
932 	emu["print_verbose"] = [](const char *str) { osd_printf_verbose("%s\n", str); };
933 	emu["print_error"] = [](const char *str) { osd_printf_error("%s\n", str); };
934 	emu["print_info"] = [](const char *str) { osd_printf_info("%s\n", str); };
935 	emu["print_debug"] = [](const char *str) { osd_printf_debug("%s\n", str); };
936 	emu["driver_find"] = [this](const char *driver) -> sol::object {
937 			int i = driver_list::find(driver);
938 			if(i == -1)
939 				return sol::make_object(sol(), sol::nil);
940 			return sol::make_object(sol(), driver_list::driver(i));
941 		};
942 	emu["wait"] = lua_CFunction([](lua_State *L) {
943 			lua_engine *engine = mame_machine_manager::instance()->lua();
944 			luaL_argcheck(L, lua_isnumber(L, 1), 1, "waiting duration expected");
945 			int ret = lua_pushthread(L);
946 			if(ret == 1)
947 				return luaL_error(L, "cannot wait from outside coroutine");
948 			int ref = luaL_ref(L, LUA_REGISTRYINDEX);
949 			engine->machine().scheduler().timer_set(attotime::from_double(lua_tonumber(L, 1)), timer_expired_delegate(FUNC(lua_engine::resume), engine), ref, nullptr);
950 			return lua_yield(L, 0);
951 		});
952 	emu["lang_translate"] = &lang_translate;
953 	emu["pid"] = &osd_getpid;
954 	emu["subst_env"] = [](const std::string &str) {
955 			std::string result;
956 			osd_subst_env(result, str);
957 			return result;
958 		};
959 
960 
961 /*  emu_file library
962  *
963  * emu.file([opt] searchpath, flags) - flags can be as in osdcore "OPEN_FLAG_*" or lua style
964  *                                     with 'rwc' with addtional c for create *and truncate*
965  *                                     (be careful) support zipped files on the searchpath
966  *
967  * file:open(name) - open first file matching name in searchpath. supports read
968  *                   and write sockets as "socket.127.0.0.1:1234"
969  * file:open_next() - open next file matching name in searchpath
970  * file:read(len) - only reads len bytes, doesn't do lua style formats
971  * file:write(data) - write data to file
972  * file:seek(offset, whence) - whence is as C "SEEK_*" int
973  * file:seek([opt] whence, [opt] offset) - lua style "set"|"cur"|"end", returns cur offset
974  * file:size() - file size in bytes
975  * file:filename() - name of current file, container name if file is in zip
976  * file:fullpath() -
977 */
978 
979 	auto file_type = emu.create_simple_usertype<emu_file>(sol::call_constructor, sol::initializers(
980 				[](emu_file &file, u32 flags) { new (&file) emu_file(flags); },
981 				[](emu_file &file, const char *path, u32 flags) { new (&file) emu_file(path, flags); },
982 				[](emu_file &file, const char *mode) {
983 					int flags = 0;
984 					for(int i = 0; i < 3 && mode[i]; i++) // limit to three chars
985 					{
986 						switch(mode[i])
987 						{
988 							case 'r':
989 								flags |= OPEN_FLAG_READ;
990 								break;
991 							case 'w':
992 								flags |= OPEN_FLAG_WRITE;
993 								break;
994 							case 'c':
995 								flags |= OPEN_FLAG_CREATE;
996 								break;
997 						}
998 					}
999 					new (&file) emu_file(flags);
1000 				},
1001 				[](emu_file &file, const char *path, const char* mode) {
1002 					int flags = 0;
1003 					for(int i = 0; i < 3 && mode[i]; i++) // limit to three chars
1004 					{
1005 						switch(mode[i])
1006 						{
1007 							case 'r':
1008 								flags |= OPEN_FLAG_READ;
1009 								break;
1010 							case 'w':
1011 								flags |= OPEN_FLAG_WRITE;
1012 								break;
1013 							case 'c':
1014 								flags |= OPEN_FLAG_CREATE;
1015 								break;
1016 						}
1017 					}
1018 					new (&file) emu_file(path, flags);
1019 				}));
1020 	file_type.set("read", [](emu_file &file, sol::buffer *buff) { buff->set_len(file.read(buff->get_ptr(), buff->get_len())); return buff; });
1021 	file_type.set("write", [](emu_file &file, const std::string &data) { return file.write(data.data(), data.size()); });
1022 	file_type.set("open", static_cast<osd_file::error (emu_file::*)(const std::string &)>(&emu_file::open));
1023 	file_type.set("open_next", &emu_file::open_next);
1024 	file_type.set("seek", sol::overload(
1025 			[](emu_file &file) { return file.tell(); },
1026 			[this](emu_file &file, s64 offset, int whence) -> sol::object {
1027 				if(file.seek(offset, whence))
1028 					return sol::make_object(sol(), sol::nil);
1029 				else
1030 					return sol::make_object(sol(), file.tell());
1031 			},
1032 			[this](emu_file &file, const char* whence) -> sol::object {
1033 				int wval = -1;
1034 				const char *seekdirs[] = {"set", "cur", "end"};
1035 				for(int i = 0; i < 3; i++)
1036 				{
1037 					if(!strncmp(whence, seekdirs[i], 3))
1038 					{
1039 						wval = i;
1040 						break;
1041 					}
1042 				}
1043 				if(wval < 0 || wval >= 3)
1044 					return sol::make_object(sol(), sol::nil);
1045 				if(file.seek(0, wval))
1046 					return sol::make_object(sol(), sol::nil);
1047 				return sol::make_object(sol(), file.tell());
1048 			},
1049 			[this](emu_file &file, const char* whence, s64 offset) -> sol::object {
1050 				int wval = -1;
1051 				const char *seekdirs[] = {"set", "cur", "end"};
1052 				for(int i = 0; i < 3; i++)
1053 				{
1054 					if(!strncmp(whence, seekdirs[i], 3))
1055 					{
1056 						wval = i;
1057 						break;
1058 					}
1059 				}
1060 				if(wval < 0 || wval >= 3)
1061 					return sol::make_object(sol(), sol::nil);
1062 				if(file.seek(offset, wval))
1063 					return sol::make_object(sol(), sol::nil);
1064 				return sol::make_object(sol(), file.tell());
1065 			}));
1066 	file_type.set("size", &emu_file::size);
1067 	file_type.set("filename", &emu_file::filename);
1068 	file_type.set("fullpath", &emu_file::fullpath);
1069 	emu.set_usertype("file", file_type);
1070 
1071 
1072 /*  thread library
1073  *
1074  * emu.thread()
1075  *
1076  * thread:start(scr) - run scr (lua code as string) in a separate thread
1077  *                     in a new empty (other than modules) lua context.
1078  *                     thread runs until yield() and/or terminates on return.
1079  * thread:continue(val) - resume thread that has yielded and pass val to it
1080  *
1081  * thread.result - get result of a terminated thread as string
1082  * thread.busy - check if thread is running
1083  * thread.yield - check if thread is yielded
1084  */
1085 
1086 	auto thread_type = emu.create_simple_usertype<context>(sol::call_constructor, sol::constructors<sol::types<>>());
1087 	thread_type.set("start", [](context &ctx, const char *scr) {
1088 			std::string script(scr);
1089 			if(ctx.busy)
1090 				return false;
1091 			std::thread th([&ctx, script]() {
1092 					sol::state thstate;
1093 					thstate.open_libraries();
1094 					thstate["package"]["preload"]["zlib"] = &luaopen_zlib;
1095 					thstate["package"]["preload"]["lfs"] = &luaopen_lfs;
1096 					thstate["package"]["preload"]["linenoise"] = &luaopen_linenoise;
1097 					sol::load_result res = thstate.load(script);
1098 					if(res.valid())
1099 					{
1100 						sol::protected_function func = res.get<sol::protected_function>();
1101 						thstate["yield"] = [&ctx, &thstate]() {
1102 								std::mutex m;
1103 								std::unique_lock<std::mutex> lock(m);
1104 								ctx.result = thstate["status"];
1105 								ctx.yield = true;
1106 								ctx.sync.wait(lock);
1107 								ctx.yield = false;
1108 								thstate["status"] = ctx.result;
1109 							};
1110 						auto ret = func();
1111 						if (ret.valid()) {
1112 							const char *tmp = ret.get<const char *>();
1113 							if (tmp != nullptr)
1114 								ctx.result = tmp;
1115 							else
1116 								exit(0);
1117 						}
1118 					}
1119 					ctx.busy = false;
1120 				});
1121 			ctx.busy = true;
1122 			ctx.yield = false;
1123 			th.detach();
1124 			return true;
1125 		});
1126 	thread_type.set("continue", [](context &ctx, const char *val) {
1127 			if(!ctx.yield)
1128 				return;
1129 			ctx.result = val;
1130 			ctx.sync.notify_all();
1131 		});
1132 	thread_type.set("result", sol::property([](context &ctx) -> std::string {
1133 			if(ctx.busy && !ctx.yield)
1134 				return "";
1135 			return ctx.result;
1136 		}));
1137 	thread_type.set("busy", sol::readonly(&context::busy));
1138 	thread_type.set("yield", sol::readonly(&context::yield));
1139 	emu.set_usertype("thread", thread_type);
1140 
1141 
1142 /*  save_item library
1143  *
1144  * emu.item(item_index)
1145  *
1146  * item.size - size of the raw data type
1147  * item.count - number of entries
1148  *
1149  * item:read(offset) - read entry value by index
1150  * item:read_block(offset, count) - read a block of entry values as a string (byte addressing)
1151  * item:write(offset, value) - write entry value by index
1152  */
1153 
1154 	auto item_type = emu.create_simple_usertype<save_item>(sol::call_constructor, sol::initializers([this](save_item &item, int index) {
1155 					if(machine().save().indexed_item(index, item.base, item.size, item.valcount, item.blockcount, item.stride))
1156 					{
1157 						item.count = item.valcount * item.blockcount;
1158 					}
1159 					else
1160 					{
1161 						item.base = nullptr;
1162 						item.size = 0;
1163 						item.count = 0;
1164 						item.valcount = 0;
1165 						item.blockcount = 0;
1166 						item.stride = 0;
1167 					}
1168 				}));
1169 	item_type.set("size", sol::readonly(&save_item::size));
1170 	item_type.set("count", sol::readonly(&save_item::count));
1171 	item_type.set("read", [this](save_item &item, int offset) -> sol::object {
1172 			if(!item.base || (offset >= item.count))
1173 				return sol::make_object(sol(), sol::nil);
1174 			const void *const data = reinterpret_cast<const uint8_t *>(item.base) + (item.stride * (offset / item.valcount));
1175 			uint64_t ret = 0;
1176 			switch(item.size)
1177 			{
1178 				case 1:
1179 				default:
1180 					ret = reinterpret_cast<const uint8_t *>(data)[offset % item.valcount];
1181 					break;
1182 				case 2:
1183 					ret = reinterpret_cast<const uint16_t *>(data)[offset % item.valcount];
1184 					break;
1185 				case 4:
1186 					ret = reinterpret_cast<const uint32_t *>(data)[offset % item.valcount];
1187 					break;
1188 				case 8:
1189 					ret = reinterpret_cast<const uint64_t *>(data)[offset % item.valcount];
1190 					break;
1191 			}
1192 			return sol::make_object(sol(), ret);
1193 		});
1194 	item_type.set("read_block", [](save_item &item, int offset, sol::buffer *buff) {
1195 			if(!item.base || ((offset + buff->get_len()) > (item.size * item.count)))
1196 			{
1197 				buff->set_len(0);
1198 			}
1199 			else
1200 			{
1201 				const uint32_t blocksize = item.size * item.valcount;
1202 				uint32_t remaining = buff->get_len();
1203 				uint8_t *dest = reinterpret_cast<uint8_t *>(buff->get_ptr());
1204 				while(remaining)
1205 				{
1206 					const uint32_t blockno = offset / blocksize;
1207 					const uint32_t available = blocksize - (offset % blocksize);
1208 					const uint32_t chunk = (available < remaining) ? available : remaining;
1209 					const void *const source = reinterpret_cast<const uint8_t *>(item.base) + (blockno * item.stride) + (offset % blocksize);
1210 					std::memcpy(dest, source, chunk);
1211 					offset += chunk;
1212 					remaining -= chunk;
1213 					dest += chunk;
1214 				}
1215 			}
1216 			return buff;
1217 		});
1218 	item_type.set("write", [](save_item &item, int offset, uint64_t value) {
1219 			if(!item.base || (offset >= item.count))
1220 				return;
1221 			void *const data = reinterpret_cast<uint8_t *>(item.base) + (item.stride * (offset / item.valcount));
1222 			switch(item.size)
1223 			{
1224 				case 1:
1225 				default:
1226 					reinterpret_cast<uint8_t *>(data)[offset % item.valcount] = uint8_t(value);
1227 					break;
1228 				case 2:
1229 					reinterpret_cast<uint16_t *>(data)[offset % item.valcount] = uint16_t(value);
1230 					break;
1231 				case 4:
1232 					reinterpret_cast<uint32_t *>(data)[offset % item.valcount] = uint32_t(value);
1233 					break;
1234 				case 8:
1235 					reinterpret_cast<uint64_t *>(data)[offset % item.valcount] = uint64_t(value);
1236 					break;
1237 			}
1238 		});
1239 	emu.set_usertype("item", item_type);
1240 
1241 
1242 /*  core_options library
1243  *
1244  * manager:options()
1245  * manager:machine():options()
1246  * manager:ui():options()
1247  * manager:plugins()
1248  *
1249  * options:help() - get help for options
1250  * options:command(command) - return output for command
1251  *
1252  * options.entries[] - get table of option entries (k=name, v=core_options::entry)
1253  */
1254 
1255 	auto core_options_type = sol().registry().create_simple_usertype<core_options>("new", sol::no_constructor);
1256 	core_options_type.set("help", &core_options::output_help);
1257 	core_options_type.set("command", &core_options::command);
1258 	core_options_type.set("entries", sol::property([this](core_options &options) {
1259 			sol::table table = sol().create_table();
1260 			int unadorned_index = 0;
1261 			for (auto &curentry : options.entries())
1262 			{
1263 				const char *name = curentry->names().size() > 0
1264 					? curentry->name().c_str()
1265 					: nullptr;
1266 				bool is_unadorned = false;
1267 				// check if it's unadorned
1268 				if (name && strlen(name) && !strcmp(name, options.unadorned(unadorned_index)))
1269 				{
1270 					unadorned_index++;
1271 					is_unadorned = true;
1272 				}
1273 				if (curentry->type() != core_options::option_type::HEADER && curentry->type() != core_options::option_type::COMMAND && !is_unadorned)
1274 					table[name] = &*curentry;
1275 			}
1276 			return table;
1277 		}));
1278 	sol().registry().set_usertype("core_options", core_options_type);
1279 
1280 
1281 /*  core_options::entry library
1282  *
1283  * options.entries[entry_name]
1284  *
1285  * entry:value() - get value of entry
1286  * entry:value(val) - set entry to val
1287  * entry:description() - get info about entry
1288  * entry:default_value() - get default for entry
1289  * entry:minimum() - get min value for entry
1290  * entry:maximum() - get max value for entry
1291  * entry:has_range() - are min and max valid for entry
1292  */
1293 
1294 	auto core_options_entry_type = sol().registry().create_simple_usertype<core_options::entry>("new", sol::no_constructor);
1295 	core_options_entry_type.set("value", sol::overload(
1296 		[this](core_options::entry &e, bool val) {
1297 			if(e.type() != OPTION_BOOLEAN)
1298 				luaL_error(m_lua_state, "Cannot set option to wrong type");
1299 			else
1300 				e.set_value(val ? "1" : "0", OPTION_PRIORITY_CMDLINE);
1301 		},
1302 		[this](core_options::entry &e, float val) {
1303 			if(e.type() != OPTION_FLOAT)
1304 				luaL_error(m_lua_state, "Cannot set option to wrong type");
1305 			else
1306 				e.set_value(string_format("%f", val), OPTION_PRIORITY_CMDLINE);
1307 		},
1308 		[this](core_options::entry &e, int val) {
1309 			if(e.type() != OPTION_INTEGER)
1310 				luaL_error(m_lua_state, "Cannot set option to wrong type");
1311 			else
1312 				e.set_value(string_format("%d", val), OPTION_PRIORITY_CMDLINE);
1313 		},
1314 		[this](core_options::entry &e, const char *val) {
1315 			if(e.type() != OPTION_STRING)
1316 				luaL_error(m_lua_state, "Cannot set option to wrong type");
1317 			else
1318 				e.set_value(val, OPTION_PRIORITY_CMDLINE);
1319 		},
1320 		[this](core_options::entry &e) -> sol::object {
1321 			if (e.type() == core_options::option_type::INVALID)
1322 				return sol::make_object(sol(), sol::nil);
1323 			switch(e.type())
1324 			{
1325 				case core_options::option_type::BOOLEAN:
1326 					return sol::make_object(sol(), atoi(e.value()) != 0);
1327 				case core_options::option_type::INTEGER:
1328 					return sol::make_object(sol(), atoi(e.value()));
1329 				case core_options::option_type::FLOAT:
1330 					return sol::make_object(sol(), atof(e.value()));
1331 				default:
1332 					return sol::make_object(sol(), e.value());
1333 			}
1334 		}));
1335 	core_options_entry_type.set("description", &core_options::entry::description);
1336 	core_options_entry_type.set("default_value", &core_options::entry::default_value);
1337 	core_options_entry_type.set("minimum", &core_options::entry::minimum);
1338 	core_options_entry_type.set("maximum", &core_options::entry::maximum);
1339 	core_options_entry_type.set("has_range", &core_options::entry::has_range);
1340 	sol().registry().set_usertype("core_options_entry", core_options_entry_type);
1341 
1342 
1343 /*  running_machine library
1344  *
1345  * manager:machine()
1346  *
1347  * machine:exit() - close program
1348  * machine:hard_reset() - hard reset emulation
1349  * machine:soft_reset() - soft reset emulation
1350  * machine:save(filename) - save state to filename
1351  * machine:load(filename) - load state from filename
1352  * machine:buffer_save() - return save state buffer as binary string
1353  * machine:buffer_load(str) - load state from binary string buffer. returns true on success, otherwise nil
1354  * machine:popmessage(str) - print str as popup
1355  * machine:popmessage() - clear displayed popup message
1356  * machine:logerror(str) - print str to log
1357  * machine:system() - get game_driver for running driver
1358  * machine:video() - get video_manager
1359  * machine:sound() - get sound_manager
1360  * machine:render() - get render_manager
1361  * machine:ioport() - get ioport_manager
1362  * machine:parameters() - get parameter_manager
1363  * machine:memory() - get memory_manager
1364  * machine:options() - get machine core_options
1365  * machine:outputs() - get output_manager
1366  * machine:input() - get input_manager
1367  * machine:uiinput() - get ui_input_manager
1368  * machine:debugger() - get debugger_manager
1369  *
1370  * machine.paused - get paused state
1371  * machine.samplerate - get audio sample rate
1372  * machine.exit_pending
1373  * machine.hard_reset_pending
1374  *
1375  * machine.devices[] - get device table (k=tag, v=device_t)
1376  * machine.screens[] - get screens table (k=tag, v=screen_device)
1377  * machine.images[] - get available image devices table (k=type, v=device_image_interface)
1378  */
1379 
1380 	auto machine_type = sol().registry().create_simple_usertype<running_machine>("new", sol::no_constructor);
1381 	machine_type.set("exit", &running_machine::schedule_exit);
1382 	machine_type.set("hard_reset", &running_machine::schedule_hard_reset);
1383 	machine_type.set("soft_reset", &running_machine::schedule_soft_reset);
1384 	machine_type.set("save", &running_machine::schedule_save);
1385 	machine_type.set("load", &running_machine::schedule_load);
1386 	machine_type.set("buffer_save", [](running_machine &m, sol::this_state s) {
1387 			lua_State *L = s;
1388 			luaL_Buffer buff;
1389 			int size = ram_state::get_size(m.save());
1390 			u8 *ptr = (u8 *)luaL_buffinitsize(L, &buff, size);
1391 			save_error error = m.save().write_buffer(ptr, size);
1392 			if (error == STATERR_NONE)
1393 			{
1394 				luaL_pushresultsize(&buff, size);
1395 				return sol::make_reference(L, sol::stack_reference(L, -1));
1396 			}
1397 			luaL_error(L, "State save error.");
1398 			return sol::make_reference(L, nullptr);
1399 		});
1400 	machine_type.set("buffer_load", [](running_machine &m, sol::this_state s, std::string str) {
1401 			lua_State *L = s;
1402 			save_error error = m.save().read_buffer((u8 *)str.data(), str.size());
1403 			if (error == STATERR_NONE)
1404 				return true;
1405 			else
1406 			{
1407 				luaL_error(L,"State load error.");
1408 				return false;
1409 			}
1410 		});
1411 	machine_type.set("system", &running_machine::system);
1412 	machine_type.set("video", &running_machine::video);
1413 	machine_type.set("sound", &running_machine::sound);
1414 	machine_type.set("render", &running_machine::render);
1415 	machine_type.set("ioport", &running_machine::ioport);
1416 	machine_type.set("parameters", &running_machine::parameters);
1417 	machine_type.set("memory", &running_machine::memory);
1418 	machine_type.set("options", [](running_machine &m) { return static_cast<core_options *>(&m.options()); });
1419 	machine_type.set("outputs", &running_machine::output);
1420 	machine_type.set("input", &running_machine::input);
1421 	machine_type.set("uiinput", &running_machine::ui_input);
1422 	machine_type.set("debugger", [this](running_machine &m) -> sol::object {
1423 			if(!(m.debug_flags & DEBUG_FLAG_ENABLED))
1424 				return sol::make_object(sol(), sol::nil);
1425 			return sol::make_object(sol(), &m.debugger());
1426 		});
1427 	machine_type.set("paused", sol::property(&running_machine::paused));
1428 	machine_type.set("samplerate", sol::property(&running_machine::sample_rate));
1429 	machine_type.set("exit_pending", sol::property(&running_machine::exit_pending));
1430 	machine_type.set("hard_reset_pending", sol::property(&running_machine::hard_reset_pending));
1431 	machine_type.set("devices", sol::property([this](running_machine &m) {
1432 			std::function<void(device_t &, sol::table)> tree;
1433 			sol::table table = sol().create_table();
1434 			tree = [&tree](device_t &root, sol::table table) {
1435 				for(device_t &dev : root.subdevices())
1436 				{
1437 					if(dev.configured() && dev.started())
1438 					{
1439 						table[dev.tag()] = &dev;
1440 						tree(dev, table);
1441 					}
1442 				}
1443 			};
1444 			tree(m.root_device(), table);
1445 			return table;
1446 		}));
1447 	machine_type.set("screens", sol::property([this](running_machine &r) {
1448 			sol::table table = sol().create_table();
1449 			for (screen_device &sc : screen_device_iterator(r.root_device()))
1450 			{
1451 				if (sc.configured() && sc.started() && sc.type())
1452 					table[sc.tag()] = &sc;
1453 			}
1454 			return table;
1455 		}));
1456 	machine_type.set("images", sol::property([this](running_machine &r) {
1457 			sol::table image_table = sol().create_table();
1458 			for(device_image_interface &image : image_interface_iterator(r.root_device()))
1459 			{
1460 				image_table[image.brief_instance_name()] = &image;
1461 				image_table[image.instance_name()] = &image;
1462 			}
1463 			return image_table;
1464 		}));
1465 	machine_type.set("popmessage", sol::overload(
1466 			[](running_machine &m, const char *str) { m.popmessage("%s", str); },
1467 			[](running_machine &m) { m.popmessage(); }));
1468 	machine_type.set("logerror", [](running_machine &m, const char *str) { m.logerror("[luaengine] %s\n", str); } );
1469 	sol().registry().set_usertype("machine", machine_type);
1470 
1471 
1472 /*  game_driver library
1473  *
1474  * emu.driver_find(driver_name)
1475  *
1476  * driver.source_file - relative path to the source file
1477  * driver.parent
1478  * driver.name
1479  * driver.description
1480  * driver.year
1481  * driver.manufacturer
1482  * driver.compatible_with
1483  * driver.default_layout
1484  * driver.orientation - screen rotation degree (rot0/90/180/270)
1485  * driver.type - machine type (arcade/console/computer/other)
1486  * driver.not_working - not considered working
1487  * driver.supports_save - supports save states
1488  * driver.no_cocktail - screen flip support is missing
1489  * driver.is_bios_root - this driver entry is a BIOS root
1490  * driver.requires_artwork - requires external artwork for key game elements
1491  * driver.clickable_artwork - artwork is clickable and requires mouse cursor
1492  * driver.unofficial - unofficial hardware modification
1493  * driver.no_sound_hw - system has no sound output
1494  * driver.mechanical - contains mechanical parts (pinball, redemption games, ...)
1495  * driver.is_incomplete - official system with blatantly incomplete hardware/software
1496  */
1497 
1498 	auto game_driver_type = sol().registry().create_simple_usertype<game_driver>("new", sol::no_constructor);
1499 	game_driver_type.set("source_file", sol::property([] (game_driver const &driver) { return &driver.type.source()[0]; }));
1500 	game_driver_type.set("parent", sol::readonly(&game_driver::parent));
1501 	game_driver_type.set("name", sol::property([] (game_driver const &driver) { return &driver.name[0]; }));
1502 	game_driver_type.set("description", sol::property([] (game_driver const &driver) { return &driver.type.fullname()[0]; }));
1503 	game_driver_type.set("year", sol::readonly(&game_driver::year));
1504 	game_driver_type.set("manufacturer", sol::readonly(&game_driver::manufacturer));
1505 	game_driver_type.set("compatible_with", sol::readonly(&game_driver::compatible_with));
1506 	game_driver_type.set("default_layout", sol::readonly(&game_driver::default_layout));
1507 	game_driver_type.set("orientation", sol::property([](game_driver const &driver) {
1508 			std::string rot;
1509 			switch (driver.flags & machine_flags::MASK_ORIENTATION)
1510 			{
1511 			case machine_flags::ROT0:
1512 				rot = "rot0";
1513 				break;
1514 			case machine_flags::ROT90:
1515 				rot = "rot90";
1516 				break;
1517 			case machine_flags::ROT180:
1518 				rot = "rot180";
1519 				break;
1520 			case machine_flags::ROT270:
1521 				rot = "rot270";
1522 				break;
1523 			default:
1524 				rot = "undefined";
1525 				break;
1526 			}
1527 			return rot;
1528 		}));
1529 	game_driver_type.set("type", sol::property([](game_driver const &driver) {
1530 			std::string type;
1531 			switch (driver.flags & machine_flags::MASK_TYPE)
1532 			{
1533 			case machine_flags::TYPE_ARCADE:
1534 				type = "arcade";
1535 				break;
1536 			case machine_flags::TYPE_CONSOLE:
1537 				type = "console";
1538 				break;
1539 			case machine_flags::TYPE_COMPUTER:
1540 				type = "computer";
1541 				break;
1542 			default:
1543 				type = "other";
1544 				break;
1545 			}
1546 			return type;
1547 		}));
1548 	game_driver_type.set("not_working", sol::property([](game_driver const &driver) { return (driver.flags & machine_flags::NOT_WORKING) > 0; }));
1549 	game_driver_type.set("supports_save", sol::property([](game_driver const &driver) { return (driver.flags & machine_flags::SUPPORTS_SAVE) > 0; }));
1550 	game_driver_type.set("no_cocktail", sol::property([](game_driver const &driver) { return (driver.flags & machine_flags::NO_COCKTAIL) > 0; }));
1551 	game_driver_type.set("is_bios_root", sol::property([](game_driver const &driver) { return (driver.flags & machine_flags::IS_BIOS_ROOT) > 0; }));
1552 	game_driver_type.set("requires_artwork", sol::property([](game_driver const &driver) { return (driver.flags & machine_flags::REQUIRES_ARTWORK) > 0; }));
1553 	game_driver_type.set("clickable_artwork", sol::property([](game_driver const &driver) { return (driver.flags & machine_flags::CLICKABLE_ARTWORK) > 0; }));
1554 	game_driver_type.set("unofficial", sol::property([](game_driver const &driver) { return (driver.flags & machine_flags::UNOFFICIAL) > 0; }));
1555 	game_driver_type.set("no_sound_hw", sol::property([](game_driver const &driver) { return (driver.flags & machine_flags::NO_SOUND_HW) > 0; }));
1556 	game_driver_type.set("mechanical", sol::property([](game_driver const &driver) { return (driver.flags & machine_flags::MECHANICAL) > 0; }));
1557 	game_driver_type.set("is_incomplete", sol::property([](game_driver const &driver) { return (driver.flags & machine_flags::IS_INCOMPLETE) > 0; } ));
1558 	sol().registry().set_usertype("game_driver", game_driver_type);
1559 
1560 
1561 /*  debugger_manager library (requires debugger to be active)
1562  *
1563  * manager:machine():debugger()
1564  *
1565  * debugger:command(command_string) - run command_string in debugger console
1566  *
1567  * debugger.consolelog[] - get consolelog text buffer (wrap_textbuf)
1568  * debugger.errorlog[] - get errorlog text buffer (wrap_textbuf)
1569  * debugger.visible_cpu - accessor for debugger active cpu for commands, affects debug views
1570  * debugger.execution_state - accessor for active cpu run state
1571  */
1572 
1573 	struct wrap_textbuf { wrap_textbuf(const text_buffer &buf) : textbuf(buf) { } std::reference_wrapper<const text_buffer> textbuf; };
1574 
1575 	auto debugger_type = sol().registry().create_simple_usertype<debugger_manager>("new", sol::no_constructor);
1576 	debugger_type.set("command", [](debugger_manager &debug, const std::string &cmd) { debug.console().execute_command(cmd, false); });
1577 	debugger_type.set("consolelog", sol::property([](debugger_manager &debug) { return wrap_textbuf(debug.console().get_console_textbuf()); }));
1578 	debugger_type.set("errorlog", sol::property([](debugger_manager &debug) { return wrap_textbuf(debug.console().get_errorlog_textbuf()); }));
1579 	debugger_type.set("visible_cpu", sol::property(
1580 		[](debugger_manager &debug) { debug.console().get_visible_cpu(); },
1581 		[](debugger_manager &debug, device_t &dev) { debug.console().set_visible_cpu(&dev); }));
1582 	debugger_type.set("execution_state", sol::property(
1583 		[](debugger_manager &debug) {
1584 			return debug.cpu().is_stopped() ? "stop" : "run";
1585 		},
1586 		[](debugger_manager &debug, const std::string &state) {
1587 			if(state == "stop")
1588 				debug.cpu().set_execution_stopped();
1589 			else
1590 				debug.cpu().set_execution_running();
1591 		}));
1592 	sol().registry().set_usertype("debugger", debugger_type);
1593 
1594 
1595 /*  wrap_textbuf library (requires debugger to be active)
1596  *
1597  * manager:machine():debugger().consolelog
1598  * manager:machine():debugger().errorlog
1599  *
1600  * log[index] - get log entry
1601  * #log - entry count
1602  */
1603 
1604 	sol().registry().new_usertype<wrap_textbuf>("text_buffer", "new", sol::no_constructor,
1605 			"__metatable", [](){},
1606 			"__newindex", [](){},
1607 			"__index", [](wrap_textbuf &buf, int index) { return text_buffer_get_seqnum_line(buf.textbuf, index - 1); },
1608 			"__len", [](wrap_textbuf &buf) { return text_buffer_num_lines(buf.textbuf) + text_buffer_line_index_to_seqnum(buf.textbuf, 0) - 1; });
1609 
1610 /*  device_debug library (requires debugger to be active)
1611  *
1612  * manager:machine().devices[device_tag]:debug()
1613  *
1614  * debug:step([opt] steps) - run cpu steps, default 1
1615  * debug:go() - run cpu
1616  * debug:bpset(addr, [opt] cond, [opt] act) - set breakpoint on addr, cond and act are debugger
1617  *                                            expressions. returns breakpoint index
1618  * debug:bpclr(idx) - clear break
1619  * debug:bplist()[] - table of breakpoints (k=index, v=debug_breakpoint)
1620  * debug:wpset(space, type, addr, len, [opt] cond, [opt] act) - set watchpoint, cond and act
1621  *                                                              are debugger expressions.
1622  *                                                              returns watchpoint index
1623  * debug:wpclr(idx) - clear watch
1624  * debug:wplist(space)[] - table of watchpoints (k=index, v=watchpoint)
1625  */
1626 
1627 	auto device_debug_type = sol().registry().create_simple_usertype<device_debug>("new", sol::no_constructor);
1628 	device_debug_type.set("step", [](device_debug &dev, sol::object num) {
1629 			int steps = 1;
1630 			if(num.is<int>())
1631 				steps = num.as<int>();
1632 			dev.single_step(steps);
1633 		});
1634 	device_debug_type.set("go", &device_debug::go);
1635 	device_debug_type.set("bpset", [](device_debug &dev, offs_t addr, const char *cond, const char *act) { return dev.breakpoint_set(addr, cond, act); });
1636 	device_debug_type.set("bpclr", &device_debug::breakpoint_clear);
1637 	device_debug_type.set("bplist", [this](device_debug &dev) {
1638 			sol::table table = sol().create_table();
1639 			for(const auto &bpp : dev.breakpoint_list())
1640 			{
1641 				const debug_breakpoint &bpt = *bpp.second;
1642 				sol::table bp = sol().create_table();
1643 				bp["enabled"] = bpt.enabled();
1644 				bp["address"] = bpt.address();
1645 				bp["condition"] = bpt.condition();
1646 				bp["action"] = bpt.action();
1647 				table[bpt.index()] = bp;
1648 			}
1649 			return table;
1650 		});
1651 	device_debug_type.set("wpset", [](device_debug &dev, addr_space &sp, const std::string &type, offs_t addr, offs_t len, const char *cond, const char *act) {
1652 			read_or_write wptype = s_read_or_write_parser(type);
1653 			return dev.watchpoint_set(sp.space, wptype, addr, len, cond, act);
1654 		});
1655 	device_debug_type.set("wpclr", &device_debug::watchpoint_clear);
1656 	device_debug_type.set("wplist", [this](device_debug &dev, addr_space &sp) {
1657 			sol::table table = sol().create_table();
1658 			for(auto &wpp : dev.watchpoint_vector(sp.space.spacenum()))
1659 			{
1660 				sol::table wp = sol().create_table();
1661 				wp["enabled"] = wpp->enabled();
1662 				wp["address"] = wpp->address();
1663 				wp["length"] = wpp->length();
1664 				switch(wpp->type())
1665 				{
1666 					case read_or_write::READ:
1667 						wp["type"] = "r";
1668 						break;
1669 					case read_or_write::WRITE:
1670 						wp["type"] = "w";
1671 						break;
1672 					case read_or_write::READWRITE:
1673 						wp["type"] = "rw";
1674 						break;
1675 					default: // huh?
1676 						wp["type"] = "";
1677 						break;
1678 				}
1679 				wp["condition"] = wpp->condition();
1680 				wp["action"] = wpp->action();
1681 				table[wpp->index()] = wp;
1682 			}
1683 			return table;
1684 		});
1685 	sol().registry().set_usertype("device_debug", device_debug_type);
1686 
1687 
1688 /*  device_t library
1689  *
1690  * manager:machine().devices[device_tag]
1691  *
1692  * device:name() - device long name
1693  * device:shortname() - device short name
1694  * device:tag() - device tree tag
1695  * device:owner() - device parent tag
1696  * device:debug() - debug interface, cpus only
1697  *
1698  * device.spaces[] - device address spaces table (k=name, v=addr_space)
1699  * device.state[] - device state entries table (k=name, v=device_state_entry)
1700  * device.items[] - device save state items table (k=name, v=index)
1701  * device.roms[] - device rom entry table (k=name, v=rom_entry)
1702  */
1703 
1704 	auto device_type = sol().registry().create_simple_usertype<device_t>("new", sol::no_constructor);
1705 	device_type.set("name", &device_t::name);
1706 	device_type.set("shortname", &device_t::shortname);
1707 	device_type.set("tag", &device_t::tag);
1708 	device_type.set("owner", &device_t::owner);
1709 	device_type.set("debug", [this](device_t &dev) -> sol::object {
1710 			if(!(dev.machine().debug_flags & DEBUG_FLAG_ENABLED) || !dynamic_cast<cpu_device *>(&dev)) // debugger not enabled or not cpu
1711 				return sol::make_object(sol(), sol::nil);
1712 			return sol::make_object(sol(), dev.debug());
1713 		});
1714 	device_type.set("spaces", sol::property([this](device_t &dev) {
1715 			device_memory_interface *memdev = dynamic_cast<device_memory_interface *>(&dev);
1716 			sol::table sp_table = sol().create_table();
1717 			if(!memdev)
1718 				return sp_table;
1719 			for(int sp = 0; sp < memdev->max_space_count(); ++sp)
1720 			{
1721 				if(memdev->has_space(sp))
1722 					sp_table[memdev->space(sp).name()] = addr_space(memdev->space(sp), *memdev);
1723 			}
1724 			return sp_table;
1725 		}));
1726 	device_type.set("state", sol::property([this](device_t &dev) {
1727 			sol::table st_table = sol().create_table();
1728 			if(!dynamic_cast<device_state_interface *>(&dev))
1729 				return st_table;
1730 			// XXX: refrain from exporting non-visible entries?
1731 			for(auto &s : dev.state().state_entries())
1732 				st_table[s->symbol()] = s.get();
1733 			return st_table;
1734 		}));
1735 	device_type.set("items", sol::property([this](device_t &dev) {
1736 			sol::table table = sol().create_table();
1737 			std::string tag = dev.tag();
1738 			// 10000 is enough?
1739 			for(int i = 0; i < 10000; i++)
1740 			{
1741 				std::string name;
1742 				const char *item;
1743 				void *base;
1744 				uint32_t size, valcount, blockcount, stride;
1745 				item = dev.machine().save().indexed_item(i, base, size, valcount, blockcount, stride);
1746 				if(!item)
1747 					break;
1748 				name = &(strchr(item, '/')[1]);
1749 				if(name.substr(0, name.find('/')) == tag)
1750 				{
1751 					name = name.substr(name.find('/') + 1, std::string::npos);
1752 					table[name] = i;
1753 				}
1754 			}
1755 			return table;
1756 		}));
1757 	device_type.set("roms", sol::property([this](device_t &dev) {
1758 			sol::table table = sol().create_table();
1759 			for(auto rom : dev.rom_region_vector())
1760 				if(!rom.name().empty())
1761 					table[rom.name()] = rom;
1762 			return table;
1763 		}));
1764 	sol().registry().set_usertype("device", device_type);
1765 
1766 
1767 /*  addr_space library
1768  *
1769  * manager:machine().devices[device_tag].spaces[space]
1770  *
1771  * read/write by signedness u/i and bit-width 8/16/32/64:
1772  * space:read_*(addr)
1773  * space:write_*(addr, val)
1774  * space:read_log_*(addr)
1775  * space:write_log_*(addr, val)
1776  * space:read_direct_*(addr)
1777  * space:write_direct_*(addr, val)
1778  * space:read_range(first_addr, last_addr, width, [opt] step) - read range of addresses and
1779  *                                                              return as a binary string
1780  *
1781  * space.name - address space name
1782  * space.shift - address bus shift, bitshift required for a bytewise address
1783  *               to map onto this space's address resolution (addressing granularity).
1784  *               positive value means leftshift, negative means rightshift.
1785  * space.index
1786  * space.address_mask
1787  * space.data_width
1788  * space.endianness
1789  *
1790  * space.map[] - table of address map entries (k=index, v=address_map_entry)
1791  */
1792 
1793 	auto addr_space_type = sol().registry().create_simple_usertype<addr_space>(sol::call_constructor, sol::constructors<sol::types<address_space &, device_memory_interface &>>());
1794 	addr_space_type.set("read_i8", &addr_space::mem_read<int8_t>);
1795 	addr_space_type.set("read_u8", &addr_space::mem_read<uint8_t>);
1796 	addr_space_type.set("read_i16", &addr_space::mem_read<int16_t>);
1797 	addr_space_type.set("read_u16", &addr_space::mem_read<uint16_t>);
1798 	addr_space_type.set("read_i32", &addr_space::mem_read<int32_t>);
1799 	addr_space_type.set("read_u32", &addr_space::mem_read<uint32_t>);
1800 	addr_space_type.set("read_i64", &addr_space::mem_read<int64_t>);
1801 	addr_space_type.set("read_u64", &addr_space::mem_read<uint64_t>);
1802 	addr_space_type.set("write_i8", &addr_space::mem_write<int8_t>);
1803 	addr_space_type.set("write_u8", &addr_space::mem_write<uint8_t>);
1804 	addr_space_type.set("write_i16", &addr_space::mem_write<int16_t>);
1805 	addr_space_type.set("write_u16", &addr_space::mem_write<uint16_t>);
1806 	addr_space_type.set("write_i32", &addr_space::mem_write<int32_t>);
1807 	addr_space_type.set("write_u32", &addr_space::mem_write<uint32_t>);
1808 	addr_space_type.set("write_i64", &addr_space::mem_write<int64_t>);
1809 	addr_space_type.set("write_u64", &addr_space::mem_write<uint64_t>);
1810 	addr_space_type.set("read_log_i8", &addr_space::log_mem_read<int8_t>);
1811 	addr_space_type.set("read_log_u8", &addr_space::log_mem_read<uint8_t>);
1812 	addr_space_type.set("read_log_i16", &addr_space::log_mem_read<int16_t>);
1813 	addr_space_type.set("read_log_u16", &addr_space::log_mem_read<uint16_t>);
1814 	addr_space_type.set("read_log_i32", &addr_space::log_mem_read<int32_t>);
1815 	addr_space_type.set("read_log_u32", &addr_space::log_mem_read<uint32_t>);
1816 	addr_space_type.set("read_log_i64", &addr_space::log_mem_read<int64_t>);
1817 	addr_space_type.set("read_log_u64", &addr_space::log_mem_read<uint64_t>);
1818 	addr_space_type.set("write_log_i8", &addr_space::log_mem_write<int8_t>);
1819 	addr_space_type.set("write_log_u8", &addr_space::log_mem_write<uint8_t>);
1820 	addr_space_type.set("write_log_i16", &addr_space::log_mem_write<int16_t>);
1821 	addr_space_type.set("write_log_u16", &addr_space::log_mem_write<uint16_t>);
1822 	addr_space_type.set("write_log_i32", &addr_space::log_mem_write<int32_t>);
1823 	addr_space_type.set("write_log_u32", &addr_space::log_mem_write<uint32_t>);
1824 	addr_space_type.set("write_log_i64", &addr_space::log_mem_write<int64_t>);
1825 	addr_space_type.set("write_log_u64", &addr_space::log_mem_write<uint64_t>);
1826 	addr_space_type.set("read_direct_i8", &addr_space::direct_mem_read<int8_t>);
1827 	addr_space_type.set("read_direct_u8", &addr_space::direct_mem_read<uint8_t>);
1828 	addr_space_type.set("read_direct_i16", &addr_space::direct_mem_read<int16_t>);
1829 	addr_space_type.set("read_direct_u16", &addr_space::direct_mem_read<uint16_t>);
1830 	addr_space_type.set("read_direct_i32", &addr_space::direct_mem_read<int32_t>);
1831 	addr_space_type.set("read_direct_u32", &addr_space::direct_mem_read<uint32_t>);
1832 	addr_space_type.set("read_direct_i64", &addr_space::direct_mem_read<int64_t>);
1833 	addr_space_type.set("read_direct_u64", &addr_space::direct_mem_read<uint64_t>);
1834 	addr_space_type.set("write_direct_i8", &addr_space::direct_mem_write<int8_t>);
1835 	addr_space_type.set("write_direct_u8", &addr_space::direct_mem_write<uint8_t>);
1836 	addr_space_type.set("write_direct_i16", &addr_space::direct_mem_write<int16_t>);
1837 	addr_space_type.set("write_direct_u16", &addr_space::direct_mem_write<uint16_t>);
1838 	addr_space_type.set("write_direct_i32", &addr_space::direct_mem_write<int32_t>);
1839 	addr_space_type.set("write_direct_u32", &addr_space::direct_mem_write<uint32_t>);
1840 	addr_space_type.set("write_direct_i64", &addr_space::direct_mem_write<int64_t>);
1841 	addr_space_type.set("write_direct_u64", &addr_space::direct_mem_write<uint64_t>);
1842 	addr_space_type.set("read_range", [](addr_space &sp, sol::this_state s, u64 first, u64 last, int width, sol::object opt_step) {
1843 			lua_State *L = s;
1844 			luaL_Buffer buff;
1845 			offs_t space_size = sp.space.addrmask();
1846 			u64 step = 1;
1847 			if (opt_step.is<u64>())
1848 			{
1849 				step = opt_step.as<u64>();
1850 				if (step < 1 || step > last - first)
1851 				{
1852 					luaL_error(L, "Invalid step");
1853 					return sol::make_reference(L, nullptr);
1854 				}
1855 			}
1856 			if (first > space_size || last > space_size || last < first)
1857 			{
1858 				luaL_error(L, "Invalid offset");
1859 				return sol::make_reference(L, nullptr);
1860 			}
1861 			int byte_count = width / 8 * (last - first + 1) / step;
1862 			switch (width)
1863 			{
1864 			case 8:
1865 			{
1866 				u8 *dest = (u8 *)luaL_buffinitsize(L, &buff, byte_count);
1867 				for(; first <= last; first += step)
1868 					*dest++ = sp.mem_read<u8>(first);
1869 				break;
1870 			}
1871 			case 16:
1872 			{
1873 				u16 *dest = (u16 *)luaL_buffinitsize(L, &buff, byte_count);
1874 				for(; first <= last; first += step)
1875 					*dest++ = sp.mem_read<u16>(first);
1876 				break;
1877 			}
1878 			case 32:
1879 			{
1880 				u32 *dest = (u32 *)luaL_buffinitsize(L, &buff, byte_count);
1881 				for(; first <= last; first += step)
1882 					*dest++ = sp.mem_read<u32>(first);
1883 				break;
1884 			}
1885 			case 64:
1886 			{
1887 				u64 *dest = (u64 *)luaL_buffinitsize(L, &buff, byte_count);
1888 				for(; first <= last; first += step)
1889 					*dest++ = sp.mem_read<u64>(first);
1890 				break;
1891 			}
1892 			default:
1893 				luaL_error(L, "Invalid width. Must be 8/16/32/64");
1894 				return sol::make_reference(L, nullptr);
1895 			}
1896 			luaL_pushresultsize(&buff, byte_count);
1897 			return sol::make_reference(L, sol::stack_reference(L, -1));
1898 		});
1899 	addr_space_type.set("name", sol::property([](addr_space &sp) { return sp.space.name(); }));
1900 	addr_space_type.set("shift", sol::property([](addr_space &sp) { return sp.space.addr_shift(); }));
1901 	addr_space_type.set("index", sol::property([](addr_space &sp) { return sp.space.spacenum(); }));
1902 	addr_space_type.set("address_mask", sol::property([](addr_space &sp) { return sp.space.addrmask(); }));
1903 	addr_space_type.set("data_width", sol::property([](addr_space &sp) { return sp.space.data_width(); }));
1904 	addr_space_type.set("endianness", sol::property([](addr_space &sp) {
1905 			std::string endianness;
1906 			switch (sp.space.endianness())
1907 			{
1908 				case endianness_t::ENDIANNESS_BIG:
1909 					endianness = "big";
1910 					break;
1911 				case endianness_t::ENDIANNESS_LITTLE:
1912 					endianness = "little";
1913 					break;
1914 			}
1915 			return endianness;
1916 		}));
1917 
1918 
1919 /* address_map_entry library
1920  *
1921  * manager:machine().devices[device_tag].spaces[space].map[entry_index]
1922  *
1923  * mapentry.offset - address start
1924  * mapentry.endoff - address end
1925  * mapentry.readtype
1926  * mapentry.writetype
1927  */
1928 	addr_space_type.set("map", sol::property([this](addr_space &sp) {
1929 			address_space &space = sp.space;
1930 			sol::table map = sol().create_table();
1931 			for (address_map_entry &entry : space.map()->m_entrylist)
1932 			{
1933 				sol::table mapentry = sol().create_table();
1934 				mapentry["offset"] = entry.m_addrstart & space.addrmask();
1935 				mapentry["endoff"] = entry.m_addrend & space.addrmask();
1936 				mapentry["readtype"] = entry.m_read.m_type;
1937 				mapentry["writetype"] = entry.m_write.m_type;
1938 				map.add(mapentry);
1939 			}
1940 			return map;
1941 		}));
1942 	sol().registry().set_usertype("addr_space", addr_space_type);
1943 
1944 
1945 /*  ioport_manager library
1946  *
1947  * manager:machine():ioport()
1948  *
1949  * ioport:count_players() - get count of player controllers
1950  * ioport:type_group(type, player)
1951  * ioport:type_seq(type, player, seqtype) - get input sequence for ioport type/player
1952  *
1953  * ioport.ports[] - ioports table (k=tag, v=ioport_port)
1954  */
1955 
1956 	auto ioport_manager_type = sol().registry().create_simple_usertype<ioport_manager>("new", sol::no_constructor);
1957 	ioport_manager_type.set("count_players", &ioport_manager::count_players);
1958 	ioport_manager_type.set("natkeyboard", &ioport_manager::natkeyboard);
1959 	ioport_manager_type.set("type_group", [](ioport_manager &im, ioport_type type, int player) {
1960 			return im.type_group(type, player);
1961 		});
1962 	ioport_manager_type.set("ports", sol::property([this](ioport_manager &im) {
1963 			sol::table port_table = sol().create_table();
1964 			for (auto &port : im.ports())
1965 				port_table[port.second->tag()] = port.second.get();
1966 			return port_table;
1967 		}));
1968 	ioport_manager_type.set("type_seq", [](ioport_manager &m, ioport_type type, int player, input_seq_type seqtype) {
1969 			return sol::make_user(m.type_seq(type, player, seqtype));
1970 		});
1971 	sol().registry().set_usertype("ioport", ioport_manager_type);
1972 
1973 
1974 /*  natural_keyboard library
1975  *
1976  * manager:machine():ioport():natkeyboard()
1977  *
1978  * natkeyboard:paste() - paste clipboard data
1979  * natkeyboard:post() - post data to natural keyboard
1980  * natkeyboard:post_coded() - post data to natural keyboard
1981  *
1982  * natkeyboard.empty - is the natural keyboard buffer empty?
1983  * natkeyboard.in_use - is the natural keyboard in use?
1984  */
1985 
1986 	auto natkeyboard_type = sol().registry().create_simple_usertype<natural_keyboard>("new", sol::no_constructor);
1987 	natkeyboard_type.set("empty", sol::property(&natural_keyboard::empty));
1988 	natkeyboard_type.set("in_use", sol::property(&natural_keyboard::in_use, &natural_keyboard::set_in_use));
1989 	natkeyboard_type.set("paste", &natural_keyboard::paste);
1990 	natkeyboard_type.set("post", [](natural_keyboard &nat, const std::string &text)          { nat.post_utf8(text); });
1991 	natkeyboard_type.set("post_coded", [](natural_keyboard &nat, const std::string &text)    { nat.post_coded(text); });
1992 	sol().registry().set_usertype("natkeyboard", natkeyboard_type);
1993 
1994 
1995 /*  ioport_port library
1996  *
1997  * manager:machine():ioport().ports[port_tag]
1998  *
1999  * port:tag() - get port tag
2000  * port:active() - get port status
2001  * port:live() - get port ioport_port_live (TODO: not usable from lua as of now)
2002  * port:read() - get port value
2003  * port:write(val, mask) - set port to value & mask (output fields only, for other fields use field:set_value(val))
2004  * port:field(mask) - get ioport_field for port and mask
2005  *
2006  * port.fields[] - get ioport_field table (k=name, v=ioport_field)
2007  */
2008 
2009 	auto ioport_port_type = sol().registry().create_simple_usertype<ioport_port>("new", sol::no_constructor);
2010 	ioport_port_type.set("tag", &ioport_port::tag);
2011 	ioport_port_type.set("active", &ioport_port::active);
2012 	ioport_port_type.set("live", &ioport_port::live);
2013 	ioport_port_type.set("read", &ioport_port::read);
2014 	ioport_port_type.set("write", &ioport_port::write);
2015 	ioport_port_type.set("field", &ioport_port::field);
2016 	ioport_port_type.set("fields", sol::property([this](ioport_port &p){
2017 			sol::table f_table = sol().create_table();
2018 			// parse twice for custom and default names, default has priority
2019 			for(ioport_field &field : p.fields())
2020 			{
2021 				if (field.type_class() != INPUT_CLASS_INTERNAL)
2022 					f_table[field.name()] = &field;
2023 			}
2024 			for(ioport_field &field : p.fields())
2025 			{
2026 				if (field.type_class() != INPUT_CLASS_INTERNAL)
2027 				{
2028 					if(field.specific_name())
2029 						f_table[field.specific_name()] = &field;
2030 					else
2031 						f_table[field.manager().type_name(field.type(), field.player())] = &field;
2032 				}
2033 			}
2034 			return f_table;
2035 		}));
2036 	sol().registry().set_usertype("ioport_port", ioport_port_type);
2037 
2038 
2039 /*  ioport_field library
2040  *
2041  * manager:machine():ioport().ports[port_tag].fields[field_name]
2042  *
2043  * field:set_value(value)
2044  * field:set_input_seq(seq_type, seq)
2045  * field:input_seq(seq_type)
2046  * field:set_default_input_seq(seq_type, seq)
2047  * field:default_input_seq(seq_type)
2048  * field:keyboard_codes(which)
2049  *
2050  * field.device - get associated device_t
2051  * field.port - get associated ioport_port
2052  * field.live - get ioport_field_live
2053  * field.name
2054  * field.default_name
2055  * field.player
2056  * field.mask
2057  * field.defvalue
2058  * field.sensitivity
2059  * field.way - amount of available directions
2060  * field.type_class
2061  * field.is_analog
2062  * field.is_digital_joystick
2063  * field.enabled
2064  * field.optional
2065  * field.cocktail
2066  * field.toggle - whether field is a toggle
2067  * field.rotated
2068  * field.analog_reverse
2069  * field.analog_reset
2070  * field.analog_wraps
2071  * field.analog_invert
2072  * field.impulse
2073  * field.type
2074  * field.crosshair_scale
2075  * field.crosshair_offset
2076  * field.user_value
2077  *
2078  * field.settings[] - ioport_setting table (k=value, v=name)
2079  */
2080 
2081 	auto ioport_field_type = sol().registry().create_simple_usertype<ioport_field>("new", sol::no_constructor);
2082 	ioport_field_type.set("set_value", &ioport_field::set_value);
2083 	ioport_field_type.set("set_input_seq", [](ioport_field &f, const std::string &seq_type_string, sol::user<input_seq> seq) {
2084 			input_seq_type seq_type = s_seq_type_parser(seq_type_string);
2085 			ioport_field::user_settings settings;
2086 			f.get_user_settings(settings);
2087 			settings.seq[seq_type] = seq;
2088 			f.set_user_settings(settings);
2089 		});
2090 	ioport_field_type.set("input_seq", [](ioport_field &f, const std::string &seq_type_string) {
2091 			input_seq_type seq_type = s_seq_type_parser(seq_type_string);
2092 			return sol::make_user(f.seq(seq_type));
2093 		});
2094 	ioport_field_type.set("set_default_input_seq", [](ioport_field &f, const std::string &seq_type_string, sol::user<input_seq> seq) {
2095 			input_seq_type seq_type = s_seq_type_parser(seq_type_string);
2096 			f.set_defseq(seq_type, seq);
2097 		});
2098 	ioport_field_type.set("default_input_seq", [](ioport_field &f, const std::string &seq_type_string) {
2099 			input_seq_type seq_type = s_seq_type_parser(seq_type_string);
2100 			return sol::make_user(f.defseq(seq_type));
2101 		});
2102 	ioport_field_type.set("keyboard_codes", [this](ioport_field &f, int which) {
2103 			sol::table result = sol().create_table();
2104 			int index = 1;
2105 			for (char32_t code : f.keyboard_codes(which))
2106 				result[index++] = code;
2107 			return result;
2108 		});
2109 	ioport_field_type.set("device", sol::property(&ioport_field::device));
2110 	ioport_field_type.set("port", sol::property(&ioport_field::port));
2111 	ioport_field_type.set("name", sol::property(&ioport_field::name));
2112 	ioport_field_type.set("default_name", sol::property([](ioport_field &f) {
2113 			return f.specific_name() ? f.specific_name() : f.manager().type_name(f.type(), f.player());
2114 		}));
2115 	ioport_field_type.set("player", sol::property(&ioport_field::player, &ioport_field::set_player));
2116 	ioport_field_type.set("mask", sol::property(&ioport_field::mask));
2117 	ioport_field_type.set("defvalue", sol::property(&ioport_field::defvalue));
2118 	ioport_field_type.set("sensitivity", sol::property(&ioport_field::sensitivity));
2119 	ioport_field_type.set("way", sol::property(&ioport_field::way));
2120 	ioport_field_type.set("type_class", sol::property([](ioport_field &f) {
2121 			switch (f.type_class())
2122 			{
2123 			case INPUT_CLASS_KEYBOARD:      return "keyboard";
2124 			case INPUT_CLASS_CONTROLLER:    return "controller";
2125 			case INPUT_CLASS_CONFIG:        return "config";
2126 			case INPUT_CLASS_DIPSWITCH:     return "dipswitch";
2127 			case INPUT_CLASS_MISC:          return "misc";
2128 			default:                        break;
2129 			}
2130 			throw false;
2131 		}));
2132 	ioport_field_type.set("is_analog", sol::property(&ioport_field::is_analog));
2133 	ioport_field_type.set("is_digital_joystick", sol::property(&ioport_field::is_digital_joystick));
2134 	ioport_field_type.set("enabled", sol::property(&ioport_field::enabled));
2135 	ioport_field_type.set("optional", sol::property(&ioport_field::optional));
2136 	ioport_field_type.set("cocktail", sol::property(&ioport_field::cocktail));
2137 	ioport_field_type.set("toggle", sol::property(&ioport_field::toggle));
2138 	ioport_field_type.set("rotated", sol::property(&ioport_field::rotated));
2139 	ioport_field_type.set("analog_reverse", sol::property(&ioport_field::analog_reverse));
2140 	ioport_field_type.set("analog_reset", sol::property(&ioport_field::analog_reset));
2141 	ioport_field_type.set("analog_wraps", sol::property(&ioport_field::analog_wraps));
2142 	ioport_field_type.set("analog_invert", sol::property(&ioport_field::analog_invert));
2143 	ioport_field_type.set("impulse", sol::property(&ioport_field::impulse));
2144 	ioport_field_type.set("type", sol::property(&ioport_field::type));
2145 	ioport_field_type.set("live", sol::property(&ioport_field::live));
2146 	ioport_field_type.set("crosshair_scale", sol::property(&ioport_field::crosshair_scale, &ioport_field::set_crosshair_scale));
2147 	ioport_field_type.set("crosshair_offset", sol::property(&ioport_field::crosshair_offset, &ioport_field::set_crosshair_offset));
2148 	ioport_field_type.set("user_value", sol::property(
2149 		[](ioport_field &f) {
2150 			ioport_field::user_settings settings;
2151 			f.get_user_settings(settings);
2152 			return settings.value;
2153 		},
2154 		[](ioport_field &f, ioport_value val) {
2155 			ioport_field::user_settings settings;
2156 			f.get_user_settings(settings);
2157 			settings.value = val;
2158 			f.set_user_settings(settings);
2159 		}));
2160 	ioport_field_type.set("settings", sol::property([this](ioport_field &f) {
2161 			sol::table result = sol().create_table();
2162 			for (ioport_setting &setting : f.settings())
2163 				if (setting.enabled())
2164 					result[setting.value()] = setting.name();
2165 			return result;
2166 		}));
2167 	sol().registry().set_usertype("ioport_field", ioport_field_type);
2168 
2169 
2170 /*  ioport_field_live library
2171  *
2172  * manager:machine():ioport().ports[port_tag].fields[field_name].live
2173  *
2174  * live.name
2175  */
2176 
2177 	sol().registry().new_usertype<ioport_field_live>("ioport_field_live", "new", sol::no_constructor,
2178 			"name", &ioport_field_live::name);
2179 
2180 
2181 /*  parameters_manager library
2182  *
2183  * manager:machine():parameters()
2184  *
2185  * parameters:add(tag, val) - add tag = val parameter
2186  * parameters:lookup(tag) - get val for tag
2187  */
2188 
2189 	sol().registry().new_usertype<parameters_manager>("parameters", "new", sol::no_constructor,
2190 			"add", &parameters_manager::add,
2191 			"lookup", &parameters_manager::lookup);
2192 
2193 
2194 /*  video_manager library
2195  *
2196  * manager:machine():video()
2197  *
2198  * video:begin_recording([opt] filename, [opt] format) - start AVI recording to filename if given or default
2199  * video:end_recording() - stop AVI recording
2200  * video:is_recording() - get recording status
2201  * video:snapshot() - save shot of all screens
2202  * video:skip_this_frame() - is current frame going to be skipped
2203  * video:speed_factor() - get speed factor
2204  * video:speed_percent() - get percent from realtime
2205  * video:frame_update() - render a frame
2206  * video:size() - get width and height of snapshot bitmap in pixels
2207  * video:pixels() - get binary bitmap of all screens as string
2208  *
2209  * video.frameskip - current frameskip
2210  * video.throttled - throttle state
2211  * video.throttle_rate - throttle rate
2212  */
2213 
2214 	auto video_type = sol().registry().create_simple_usertype<video_manager>("new", sol::no_constructor);
2215 	video_type.set("begin_recording", sol::overload(
2216 		[this](video_manager &vm, const char *filename, const char *format_string) {
2217 			std::string fn = process_snapshot_filename(machine(), filename);
2218 			movie_recording::format format = s_movie_recording_format_parser(format_string);
2219 			vm.begin_recording(fn.c_str(), format);
2220 		},
2221 		[this](video_manager &vm, const char *filename) {
2222 			std::string fn = process_snapshot_filename(machine(), filename);
2223 			vm.begin_recording(fn.c_str(), movie_recording::format::AVI);
2224 		},
2225 		[](video_manager &vm) {
2226 			vm.begin_recording(nullptr, movie_recording::format::AVI);
2227 		}));
2228 	video_type.set("end_recording", [this](video_manager &vm) {
2229 			if(!vm.is_recording())
2230 			{
2231 				machine().logerror("[luaengine] No active recording to stop\n");
2232 				return;
2233 			}
2234 			vm.end_recording();
2235 		});
2236 	video_type.set("snapshot", &video_manager::save_active_screen_snapshots);
2237 	video_type.set("is_recording", &video_manager::is_recording);
2238 	video_type.set("skip_this_frame", &video_manager::skip_this_frame);
2239 	video_type.set("speed_factor", &video_manager::speed_factor);
2240 	video_type.set("speed_percent", &video_manager::speed_percent);
2241 	video_type.set("effective_frameskip", &video_manager::effective_frameskip);
2242 	video_type.set("frame_update", &video_manager::frame_update);
2243 	video_type.set("size", [](video_manager &vm) {
2244 			s32 width, height;
2245 			vm.compute_snapshot_size(width, height);
2246 			return std::tuple<s32, s32>(width, height);
2247 		});
2248 	video_type.set("pixels", [](video_manager &vm, sol::this_state s) {
2249 			lua_State *L = s;
2250 			luaL_Buffer buff;
2251 			s32 width, height;
2252 			vm.compute_snapshot_size(width, height);
2253 			int size = width * height * 4;
2254 			u32 *ptr = (u32 *)luaL_buffinitsize(L, &buff, size);
2255 			vm.pixels(ptr);
2256 			luaL_pushresultsize(&buff, size);
2257 			return sol::make_reference(L, sol::stack_reference(L, -1));
2258 		});
2259 	video_type.set("frameskip", sol::property(&video_manager::frameskip, &video_manager::set_frameskip));
2260 	video_type.set("throttled", sol::property(&video_manager::throttled, &video_manager::set_throttled));
2261 	video_type.set("throttle_rate", sol::property(&video_manager::throttle_rate, &video_manager::set_throttle_rate));
2262 	sol().registry().set_usertype("video", video_type);
2263 
2264 
2265 /*  sound_manager library
2266  *
2267  * manager:machine():sound()
2268  *
2269  * sound:start_recording() - begin audio recording
2270  * sound:stop_recording() - end audio recording
2271  * sound:ui_mute(turn_off) - turns on/off UI sound
2272  * sound:system_mute() - turns on/off system sound
2273  * sound:samples() - get current audio buffer contents in binary form as string (updates 50 times per second)
2274  *
2275  * sound.attenuation - sound attenuation
2276  */
2277 
2278 	auto sound_type = sol().registry().create_simple_usertype<sound_manager>("new", sol::no_constructor);
2279 	sound_type.set("start_recording", &sound_manager::start_recording);
2280 	sound_type.set("stop_recording", &sound_manager::stop_recording);
2281 	sound_type.set("ui_mute", &sound_manager::ui_mute);
2282 	sound_type.set("debugger_mute", &sound_manager::debugger_mute);
2283 	sound_type.set("system_mute", &sound_manager::system_mute);
2284 	sound_type.set("samples", [](sound_manager &sm, sol::this_state s) {
2285 			lua_State *L = s;
2286 			luaL_Buffer buff;
2287 			s32 count = sm.sample_count() * 2 * 2; // 2 channels, 2 bytes per sample
2288 			s16 *ptr = (s16 *)luaL_buffinitsize(L, &buff, count);
2289 			sm.samples(ptr);
2290 			luaL_pushresultsize(&buff, count);
2291 			return sol::make_reference(L, sol::stack_reference(L, -1));
2292 		});
2293 	sound_type.set("attenuation", sol::property(&sound_manager::attenuation, &sound_manager::set_attenuation));
2294 	sol().registry().set_usertype("sound", sound_type);
2295 
2296 
2297 /*  input_manager library
2298  *
2299  * manager:machine():input()
2300  *
2301  * input:code_from_token(token) - get input_code for KEYCODE_* string token
2302  * input:code_pressed(code) - get pressed state for input_code
2303  * input:code_to_token(code) - get KEYCODE_* string token for code
2304  * input:code_name(code) - get code friendly name
2305  * input:seq_from_tokens(tokens) - get input_seq for multiple space separated KEYCODE_* string tokens
2306  * input:seq_pressed(seq) - get pressed state for input_seq
2307  * input:seq_to_tokens(seq) - get KEYCODE_* string tokens for seq
2308  * input:seq_name(seq) - get seq friendly name
2309  * input:seq_clean(seq) - clean the seq and remove invalid elements
2310  * input:seq_poll_start(class, [opt] start_seq) - start polling for input_item_class passed as string
2311  *                                                (switch/abs[olute]/rel[ative]/max[imum])
2312  * input:seq_poll() - poll once, returns true if input was fetched
2313  * input:seq_poll_final() - get final input_seq
2314  * input.device_classes - returns device classes
2315  */
2316 
2317 	auto input_type = sol().registry().create_simple_usertype<input_manager>("new", sol::no_constructor);
2318 	input_type.set("code_from_token", [](input_manager &input, const char *token) { return sol::make_user(input.code_from_token(token)); });
2319 	input_type.set("code_pressed", [](input_manager &input, sol::user<input_code> code) { return input.code_pressed(code); });
2320 	input_type.set("code_to_token", [](input_manager &input, sol::user<input_code> code) { return input.code_to_token(code); });
2321 	input_type.set("code_name", [](input_manager &input, sol::user<input_code> code) { return input.code_name(code); });
2322 	input_type.set("seq_from_tokens", [](input_manager &input, const char *tokens) { input_seq seq; input.seq_from_tokens(seq, tokens); return sol::make_user(seq); });
2323 	input_type.set("seq_pressed", [](input_manager &input, sol::user<input_seq> seq) { return input.seq_pressed(seq); });
2324 	input_type.set("seq_to_tokens", [](input_manager &input, sol::user<input_seq> seq) { return input.seq_to_tokens(seq); });
2325 	input_type.set("seq_name", [](input_manager &input, sol::user<input_seq> seq) { return input.seq_name(seq); });
2326 	input_type.set("seq_clean", [](input_manager &input, sol::user<input_seq> seq) { input_seq cleaned_seq = input.seq_clean(seq); return sol::make_user(cleaned_seq); });
2327 	input_type.set("seq_poll_start", [this](input_manager &input, const char *cls_string, sol::object seq) {
2328 			if (!m_seq_poll)
2329 				m_seq_poll.reset(new input_sequence_poller(input));
2330 
2331 			input_item_class cls;
2332 			if (!strcmp(cls_string, "switch"))
2333 				cls = ITEM_CLASS_SWITCH;
2334 			else if (!strcmp(cls_string, "absolute") || !strcmp(cls_string, "abs"))
2335 				cls = ITEM_CLASS_ABSOLUTE;
2336 			else if (!strcmp(cls_string, "relative") || !strcmp(cls_string, "rel"))
2337 				cls = ITEM_CLASS_RELATIVE;
2338 			else if (!strcmp(cls_string, "maximum") || !strcmp(cls_string, "max"))
2339 				cls = ITEM_CLASS_MAXIMUM;
2340 			else
2341 				cls = ITEM_CLASS_INVALID;
2342 
2343 			if (seq.is<sol::user<input_seq>>())
2344 				m_seq_poll->start(cls, seq.as<sol::user<input_seq>>());
2345 			else
2346 				m_seq_poll->start(cls);
2347 		});
2348 	input_type.set("seq_poll", [this](input_manager &input) -> sol::object {
2349 			if (!m_seq_poll)
2350 				return sol::make_object(sol(), sol::nil);
2351 			return sol::make_object(sol(), m_seq_poll->poll());
2352 		});
2353 	input_type.set("seq_poll_final", [this](input_manager &input) -> sol::object {
2354 			if (!m_seq_poll)
2355 				return sol::make_object(sol(), sol::nil);
2356 			return sol::make_object(sol(), sol::make_user(m_seq_poll->valid() ? m_seq_poll->sequence() : input_seq()));
2357 		});
2358 	input_type.set("seq_poll_modified", [this](input_manager &input) -> sol::object {
2359 			if (!m_seq_poll)
2360 				return sol::make_object(sol(), sol::nil);
2361 			return sol::make_object(sol(), m_seq_poll->modified());
2362 		});
2363 	input_type.set("seq_poll_valid", [this](input_manager &input) -> sol::object {
2364 			if (!m_seq_poll)
2365 				return sol::make_object(sol(), sol::nil);
2366 			return sol::make_object(sol(), m_seq_poll->valid());
2367 		});
2368 	input_type.set("seq_poll_sequence", [this](input_manager &input) -> sol::object {
2369 			if (!m_seq_poll)
2370 				return sol::make_object(sol(), sol::nil);
2371 			return sol::make_object(sol(), sol::make_user(m_seq_poll->sequence()));
2372 	});
2373 	input_type.set("device_classes", sol::property([this](input_manager &input) {
2374 			sol::table result = sol().create_table();
2375 			for (input_device_class devclass_id = DEVICE_CLASS_FIRST_VALID; devclass_id <= DEVICE_CLASS_LAST_VALID; devclass_id++)
2376 			{
2377 				input_class &devclass = input.device_class(devclass_id);
2378 				result[devclass.name()] = &devclass;
2379 			}
2380 			return result;
2381 		}));
2382 	sol().registry().set_usertype("input", input_type);
2383 
2384 
2385 /*  input_class library
2386  *
2387  * manager:machine():input().device_classes[devclass]
2388  *
2389  * devclass.name
2390  * devclass.enabled
2391  * devclass.multi
2392  * devclass.devices[]
2393  */
2394 
2395 	auto input_class_type = sol().registry().create_simple_usertype<input_class>("new", sol::no_constructor);
2396 	input_class_type.set("name", sol::property(&input_class::name));
2397 	input_class_type.set("enabled", sol::property(&input_class::enabled, &input_class::enable));
2398 	input_class_type.set("multi", sol::property(&input_class::multi, &input_class::set_multi));
2399 	input_class_type.set("devices", sol::property([this](input_class &devclass) {
2400 			sol::table result = sol().create_table();
2401 			int index = 1;
2402 			for (int devindex = 0; devindex <= devclass.maxindex(); devindex++)
2403 			{
2404 				input_device *dev = devclass.device(devindex);
2405 				if (dev)
2406 					result[index++] = dev;
2407 			}
2408 			return result;
2409 		}));
2410 	sol().registry().set_usertype("input_class", input_class_type);
2411 
2412 
2413 /*  input_device library
2414  *
2415  * manager:machine():input().device_classes[devclass].devices[index]
2416  * device.name
2417  * device.id
2418  * device.devindex
2419  * device.items[]
2420  */
2421 
2422 	auto input_device_type = sol().registry().create_simple_usertype<input_device>("new", sol::no_constructor);
2423 	input_device_type.set("name", sol::property(&input_device::name));
2424 	input_device_type.set("id", sol::property(&input_device::id));
2425 	input_device_type.set("devindex", sol::property(&input_device::devindex));
2426 	input_device_type.set("items", sol::property([this](input_device &dev) {
2427 			sol::table result = sol().create_table();
2428 			for (input_item_id id = ITEM_ID_FIRST_VALID; id < dev.maxitem(); id++)
2429 			{
2430 				input_device_item *item = dev.item(id);
2431 				if (item)
2432 					result[id] = dev.item(id);
2433 			}
2434 			return result;
2435 		}));
2436 	sol().registry().set_usertype("input_device", input_device_type);
2437 
2438 
2439 /*  input_device_item library
2440  *
2441  * manager:machine():input().device_classes[devclass].devices[index].items[item_id]
2442  * item.name
2443  * item.token
2444  * item:code()
2445  */
2446 
2447 	auto input_device_item_type = sol().registry().create_simple_usertype<input_device_item>("new", sol::no_constructor);
2448 	input_device_item_type.set("name", sol::property(&input_device_item::name));
2449 	input_device_item_type.set("token", sol::property(&input_device_item::token));
2450 	input_device_item_type.set("code", [](input_device_item &item) {
2451 			input_code code(item.device().devclass(), item.device().devindex(), item.itemclass(), ITEM_MODIFIER_NONE, item.itemid());
2452 			return sol::make_user(code);
2453 		});
2454 	sol().registry().set_usertype("input_device_item", input_device_item_type);
2455 
2456 
2457 /*  ui_input_manager library
2458  *
2459  * manager:machine():uiinput()
2460  *
2461  * uiinput:find_mouse() - return x, y, button state, ui render target
2462  * uiinput:pressed(key) - get pressed state for ui key
2463  * uiinput.presses_enabled - enable/disable ui key presses
2464  */
2465 
2466 	auto uiinput_type = sol().registry().create_simple_usertype<ui_input_manager>("new", sol::no_constructor);
2467 	uiinput_type.set("find_mouse", [](ui_input_manager &ui) {
2468 			int32_t x, y;
2469 			bool button;
2470 			render_target *rt = ui.find_mouse(&x, &y, &button);
2471 			return std::tuple<int32_t, int32_t, bool, render_target *>(x, y, button, rt);
2472 		});
2473 	uiinput_type.set("pressed", &ui_input_manager::pressed);
2474 	uiinput_type.set("presses_enabled", sol::property(&ui_input_manager::presses_enabled, &ui_input_manager::set_presses_enabled));
2475 	sol().registry().set_usertype("uiinput", uiinput_type);
2476 
2477 
2478 /*  render_target library
2479  *
2480  * manager:machine():render().targets[target_index]
2481  * manager:machine():render():ui_target()
2482  *
2483  * target:view_bounds() - get x0, x1, y0, y1 bounds for target
2484  * target:width() - get target width
2485  * target:height() - get target height
2486  * target:pixel_aspect() - get target aspect
2487  * target:hidden() - is target hidden
2488  * target:is_ui_target() - is ui render target
2489  * target:index() - target index
2490  * target:view_name([opt] index) - current target layout view name
2491  *
2492  * target.max_update_rate -
2493  * target.view - current target layout view
2494  * target.orientation - current target orientation
2495  * target.screen_overlay - enable overlays
2496  * target.zoom - enable zoom
2497  */
2498 
2499 	auto target_type = sol().registry().create_simple_usertype<render_target>("new", sol::no_constructor);
2500 	target_type.set("view_bounds", [](render_target &rt) {
2501 			const render_bounds b = rt.current_view().bounds();
2502 			return std::tuple<float, float, float, float>(b.x0, b.x1, b.y0, b.y1);
2503 		});
2504 	target_type.set("width", &render_target::width);
2505 	target_type.set("height", &render_target::height);
2506 	target_type.set("pixel_aspect", &render_target::pixel_aspect);
2507 	target_type.set("hidden", &render_target::hidden);
2508 	target_type.set("is_ui_target", &render_target::is_ui_target);
2509 	target_type.set("index", &render_target::index);
2510 	target_type.set("view_name", &render_target::view_name);
2511 	target_type.set("max_update_rate", sol::property(&render_target::max_update_rate, &render_target::set_max_update_rate));
2512 	target_type.set("view", sol::property(&render_target::view, &render_target::set_view));
2513 	target_type.set("orientation", sol::property(&render_target::orientation, &render_target::set_orientation));
2514 	target_type.set("screen_overlay", sol::property(&render_target::screen_overlay_enabled, &render_target::set_screen_overlay_enabled));
2515 	target_type.set("zoom", sol::property(&render_target::zoom_to_screen, &render_target::set_zoom_to_screen));
2516 	sol().registry().set_usertype("target", target_type);
2517 
2518 
2519 /*  render_container library
2520  *
2521  * manager:machine():render():ui_container()
2522  *
2523  * container:orientation()
2524  * container:xscale()
2525  * container:yscale()
2526  * container:xoffset()
2527  * container:yoffset()
2528  * container:is_empty()
2529  */
2530 
2531 	auto render_container_type = sol().registry().create_simple_usertype<render_container>("new", sol::no_constructor);
2532 	render_container_type.set("orientation", &render_container::orientation);
2533 	render_container_type.set("xscale", &render_container::xscale);
2534 	render_container_type.set("yscale", &render_container::yscale);
2535 	render_container_type.set("xoffset", &render_container::xoffset);
2536 	render_container_type.set("yoffset", &render_container::yoffset);
2537 	render_container_type.set("is_empty", &render_container::is_empty);
2538 	sol().registry().set_usertype("render_container", render_container_type);
2539 
2540 
2541 /*  render_manager library
2542  *
2543  * manager:machine():render()
2544  *
2545  * render:max_update_rate() -
2546  * render:ui_target() - render_target for ui drawing
2547  * render:ui_container() - render_container for ui drawing
2548  *
2549  * render.targets[] - render_target table
2550  */
2551 
2552 	auto render_type = sol().registry().create_simple_usertype<render_manager>("new", sol::no_constructor);
2553 	render_type.set("max_update_rate", &render_manager::max_update_rate);
2554 	render_type.set("ui_target", &render_manager::ui_target);
2555 	render_type.set("ui_container", &render_manager::ui_container);
2556 	render_type.set("targets", sol::property([this](render_manager &r) {
2557 			sol::table target_table = sol().create_table();
2558 			int tc = 0;
2559 			for(render_target &curr_rt : r.targets())
2560 				target_table[tc++] = &curr_rt;
2561 			return target_table;
2562 		}));
2563 	sol().registry().set_usertype("render", render_type);
2564 
2565 
2566 /*  screen_device library
2567  *
2568  * manager:machine().screens[screen_tag]
2569  *
2570  * screen:draw_box(x1, y1, x2, y2, fillcol, linecol) - draw box from (x1, y1)-(x2, y2) colored linecol
2571  *                                                     filled with fillcol, color is 32bit argb
2572  * screen:draw_line(x1, y1, x2, y2, linecol) - draw line from (x1, y1)-(x2, y2) colored linecol
2573  * screen:draw_text(x || justify, y, message, [opt] fgcolor, [opt] bgcolor) - draw message at (x, y) or at line y
2574  *                                                                            with left/right/center justification
2575  * screen:height() - screen height
2576  * screen:width() - screen width
2577  * screen:orientation() - screen angle, flipx, flipy
2578  * screen:refresh() - screen refresh rate in Hz
2579  * screen:refresh_attoseconds() - screen refresh rate in attoseconds
2580  * screen:snapshot([opt] filename) - save snap shot
2581  * screen:type() - screen drawing type
2582  * screen:frame_number() - screen frame count
2583  * screen:name() - screen device full name
2584  * screen:shortname() - screen device short name
2585  * screen:tag() - screen device tag
2586  * screen:xscale() - screen x scale factor
2587  * screen:yscale() - screen y scale factor
2588  * screen:pixel(x, y) - get pixel at (x, y) as packed RGB in a u32
2589  * screen:pixels() - get whole screen binary bitmap as string
2590  * screen:time_until_pos(vpos, hpos) - get the time until this screen pos is reached
2591  */
2592 
2593 	auto screen_dev_type = sol().registry().create_simple_usertype<screen_device>("new", sol::no_constructor);
2594 	screen_dev_type.set("draw_box", [](screen_device &sdev, float x1, float y1, float x2, float y2, uint32_t bgcolor, uint32_t fgcolor) {
2595 			int sc_width = sdev.visible_area().width();
2596 			int sc_height = sdev.visible_area().height();
2597 			x1 = std::min(std::max(0.0f, x1), float(sc_width-1)) / float(sc_width);
2598 			y1 = std::min(std::max(0.0f, y1), float(sc_height-1)) / float(sc_height);
2599 			x2 = std::min(std::max(0.0f, x2), float(sc_width-1)) / float(sc_width);
2600 			y2 = std::min(std::max(0.0f, y2), float(sc_height-1)) / float(sc_height);
2601 			mame_machine_manager::instance()->ui().draw_outlined_box(sdev.container(), x1, y1, x2, y2, fgcolor, bgcolor);
2602 		});
2603 	screen_dev_type.set("draw_line", [](screen_device &sdev, float x1, float y1, float x2, float y2, uint32_t color) {
2604 			int sc_width = sdev.visible_area().width();
2605 			int sc_height = sdev.visible_area().height();
2606 			x1 = std::min(std::max(0.0f, x1), float(sc_width-1)) / float(sc_width);
2607 			y1 = std::min(std::max(0.0f, y1), float(sc_height-1)) / float(sc_height);
2608 			x2 = std::min(std::max(0.0f, x2), float(sc_width-1)) / float(sc_width);
2609 			y2 = std::min(std::max(0.0f, y2), float(sc_height-1)) / float(sc_height);
2610 			sdev.container().add_line(x1, y1, x2, y2, UI_LINE_WIDTH, rgb_t(color), PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA));
2611 		});
2612 	screen_dev_type.set("draw_text", [this](screen_device &sdev, sol::object xobj, float y, const char *msg, sol::object color, sol::object bcolor) {
2613 			int sc_width = sdev.visible_area().width();
2614 			int sc_height = sdev.visible_area().height();
2615 			auto justify = ui::text_layout::LEFT;
2616 			float x = 0;
2617 			if(xobj.is<float>())
2618 			{
2619 				x = std::min(std::max(0.0f, xobj.as<float>()), float(sc_width-1)) / float(sc_width);
2620 				y = std::min(std::max(0.0f, y), float(sc_height-1)) / float(sc_height);
2621 			}
2622 			else if(xobj.is<const char *>())
2623 			{
2624 				justify = s_text_justify_parser(xobj.as<const char *>());
2625 			}
2626 			else
2627 			{
2628 				luaL_error(m_lua_state, "Error in param 1 to draw_text");
2629 				return;
2630 			}
2631 			rgb_t textcolor = mame_machine_manager::instance()->ui().colors().text_color();
2632 			rgb_t bgcolor = 0;
2633 			if(color.is<uint32_t>())
2634 				textcolor = rgb_t(color.as<uint32_t>());
2635 			if(bcolor.is<uint32_t>())
2636 				bgcolor = rgb_t(bcolor.as<uint32_t>());
2637 			mame_machine_manager::instance()->ui().draw_text_full(sdev.container(), msg, x, y, (1.0f - x),
2638 								justify, ui::text_layout::WORD, mame_ui_manager::OPAQUE_, textcolor, bgcolor);
2639 		});
2640 	screen_dev_type.set("height", [](screen_device &sdev) { return sdev.visible_area().height(); });
2641 	screen_dev_type.set("width", [](screen_device &sdev) { return sdev.visible_area().width(); });
2642 	screen_dev_type.set("orientation", [](screen_device &sdev) {
2643 			uint32_t flags = sdev.orientation();
2644 			int rotation_angle = 0;
2645 			switch (flags)
2646 			{
2647 				case ORIENTATION_FLIP_X:
2648 					rotation_angle = 0;
2649 					break;
2650 				case ORIENTATION_SWAP_XY:
2651 				case ORIENTATION_SWAP_XY|ORIENTATION_FLIP_X:
2652 					rotation_angle = 90;
2653 					break;
2654 				case ORIENTATION_FLIP_Y:
2655 				case ORIENTATION_FLIP_X|ORIENTATION_FLIP_Y:
2656 					rotation_angle = 180;
2657 					break;
2658 				case ORIENTATION_SWAP_XY|ORIENTATION_FLIP_Y:
2659 				case ORIENTATION_SWAP_XY|ORIENTATION_FLIP_X|ORIENTATION_FLIP_Y:
2660 					rotation_angle = 270;
2661 					break;
2662 			}
2663 			return std::tuple<int, bool, bool>(rotation_angle, flags & ORIENTATION_FLIP_X, flags & ORIENTATION_FLIP_Y);
2664 		});
2665 	screen_dev_type.set("refresh", [](screen_device &sdev) { return ATTOSECONDS_TO_HZ(sdev.refresh_attoseconds()); });
2666 	screen_dev_type.set("refresh_attoseconds", [](screen_device &sdev) { return sdev.refresh_attoseconds(); });
2667 	screen_dev_type.set("snapshot", [this](screen_device &sdev, sol::object filename) -> sol::object {
2668 			std::string snapstr;
2669 			bool is_absolute_path = false;
2670 			if (filename.is<const char *>())
2671 			{
2672 				// a filename was specified; if it isn't absolute postprocess it
2673 				snapstr = process_snapshot_filename(machine(), filename.as<const char *>());
2674 				is_absolute_path = osd_is_absolute_path(snapstr);
2675 			}
2676 
2677 			// open the file
2678 			emu_file file(is_absolute_path ? "" : machine().options().snapshot_directory(), OPEN_FLAG_WRITE | OPEN_FLAG_CREATE | OPEN_FLAG_CREATE_PATHS);
2679 			osd_file::error filerr;
2680 			if (!snapstr.empty())
2681 				filerr = file.open(snapstr);
2682 			else
2683 				filerr = machine().video().open_next(file, "png");
2684 			if (filerr != osd_file::error::NONE)
2685 				return sol::make_object(sol(), filerr);
2686 
2687 			// and save the snapshot
2688 			machine().video().save_snapshot(&sdev, file);
2689 			return sol::make_object(sol(), sol::nil);
2690 		});
2691 	screen_dev_type.set("type", [](screen_device &sdev) {
2692 			switch (sdev.screen_type())
2693 			{
2694 				case SCREEN_TYPE_RASTER:  return "raster"; break;
2695 				case SCREEN_TYPE_VECTOR:  return "vector"; break;
2696 				case SCREEN_TYPE_LCD:     return "lcd"; break;
2697 				case SCREEN_TYPE_SVG:     return "svg"; break;
2698 				default: break;
2699 			}
2700 			return "unknown";
2701 		});
2702 	screen_dev_type.set("frame_number", &screen_device::frame_number);
2703 	screen_dev_type.set("name", &screen_device::name);
2704 	screen_dev_type.set("shortname", &screen_device::shortname);
2705 	screen_dev_type.set("tag", &screen_device::tag);
2706 	screen_dev_type.set("xscale", &screen_device::xscale);
2707 	screen_dev_type.set("yscale", &screen_device::yscale);
2708 	screen_dev_type.set("pixel", [](screen_device &sdev, float x, float y) { return sdev.pixel((s32)x, (s32)y); });
2709 	screen_dev_type.set("pixels", [](screen_device &sdev, sol::this_state s) {
2710 			lua_State *L = s;
2711 			const rectangle &visarea = sdev.visible_area();
2712 			luaL_Buffer buff;
2713 			int size = visarea.height() * visarea.width() * 4;
2714 			u32 *ptr = (u32 *)luaL_buffinitsize(L, &buff, size);
2715 			sdev.pixels(ptr);
2716 			luaL_pushresultsize(&buff, size);
2717 			return sol::make_reference(L, sol::stack_reference(L, -1));
2718 		});
2719 	screen_dev_type.set("time_until_pos", [](screen_device &sdev, int vpos, int hpos) { return sdev.time_until_pos(vpos, hpos).as_double(); });
2720 	sol().registry().set_usertype("screen_dev", screen_dev_type);
2721 
2722 
2723 /*  mame_ui_manager library
2724  *
2725  * manager:ui()
2726  *
2727  * ui:is_menu_active() - ui menu state
2728  * ui:options() - ui core_options
2729  * ui:get_line_height() - current ui font height
2730  * ui:get_string_width(str, scale) - get str width with ui font at scale factor of current font size
2731  * ui:get_char_width(char) - get width of utf8 glyph char with ui font
2732  * ui:set_aggressive_input_focus(bool)
2733  *
2734  * ui.single_step
2735  * ui.show_fps - fps display enabled
2736  * ui.show_profiler - profiler display enabled
2737  */
2738 
2739 	auto ui_type = sol().registry().create_simple_usertype<mame_ui_manager>("new", sol::no_constructor);
2740 	ui_type.set("is_menu_active", &mame_ui_manager::is_menu_active);
2741 	ui_type.set("options", [](mame_ui_manager &m) { return static_cast<core_options *>(&m.options()); });
2742 	ui_type.set("show_fps", sol::property(&mame_ui_manager::show_fps, &mame_ui_manager::set_show_fps));
2743 	ui_type.set("show_profiler", sol::property(&mame_ui_manager::show_profiler, &mame_ui_manager::set_show_profiler));
2744 	ui_type.set("single_step", sol::property(&mame_ui_manager::single_step, &mame_ui_manager::set_single_step));
2745 	ui_type.set("get_line_height", &mame_ui_manager::get_line_height);
2746 	ui_type.set("get_string_width", &mame_ui_manager::get_string_width);
2747 	// sol converts char32_t to a string
2748 	ui_type.set("get_char_width", [](mame_ui_manager &m, uint32_t utf8char) { return m.get_char_width(utf8char); });
2749 	ui_type.set("set_aggressive_input_focus", [](mame_ui_manager &m, bool aggressive_focus) { osd_set_aggressive_input_focus(aggressive_focus); });
2750 	sol().registry().set_usertype("ui", ui_type);
2751 
2752 
2753 /*  device_state_entry library
2754  *
2755  * manager:machine().devices[device_tag].state[state_name]
2756  *
2757  * state:name() - get device state name
2758  * state:is_visible() - is state visible in debugger
2759  * state:is_divider() - is state a divider
2760  *
2761  * state.value - get device state value
2762  */
2763 
2764 	auto dev_space_type = sol().registry().create_simple_usertype<device_state_entry>("new", sol::no_constructor);
2765 	dev_space_type.set("name", &device_state_entry::symbol);
2766 	dev_space_type.set("value", sol::property(
2767 		[this](device_state_entry &entry) -> uint64_t {
2768 			device_state_interface *state = entry.parent_state();
2769 			if(state)
2770 			{
2771 				machine().save().dispatch_presave();
2772 				return state->state_int(entry.index());
2773 			}
2774 			return 0;
2775 		},
2776 		[this](device_state_entry &entry, uint64_t val) {
2777 			device_state_interface *state = entry.parent_state();
2778 			if(state)
2779 			{
2780 				state->set_state_int(entry.index(), val);
2781 				machine().save().dispatch_presave();
2782 			}
2783 		}));
2784 	dev_space_type.set("is_visible", &device_state_entry::visible);
2785 	dev_space_type.set("is_divider", &device_state_entry::divider);
2786 	sol().registry().set_usertype("dev_space", dev_space_type);
2787 
2788 
2789 /*  rom_entry library
2790  *
2791  * manager:machine().devices[device_tag].roms[rom]
2792  *
2793  * rom:name()
2794  * rom:hashdata() - see hash.h
2795  * rom:offset()
2796  * rom:length()
2797  * rom:flags() - see romentry.h
2798  */
2799 
2800 	auto rom_entry_type = sol().registry().create_simple_usertype<rom_entry>("new", sol::no_constructor);
2801 	rom_entry_type.set("name", &rom_entry::name);
2802 	rom_entry_type.set("hashdata", &rom_entry::hashdata);
2803 	rom_entry_type.set("offset", &rom_entry::get_offset);
2804 	rom_entry_type.set("length", &rom_entry::get_length);
2805 	rom_entry_type.set("flags", &rom_entry::get_flags);
2806 	sol().registry().set_usertype("rom_entry", rom_entry_type);
2807 
2808 
2809 /*  memory_manager library
2810  *
2811  * manager:machine():memory()
2812  *
2813  * memory.banks[] - table of memory banks (k=tag, v=memory_bank)
2814  * memory.regions[] - table of memory regions (k=tag, v=memory_region)
2815  * memory.shares[] - table of memory shares (k=tag, v=memory_share)
2816  */
2817 
2818 	auto memory_type = sol().registry().create_simple_usertype<memory_manager>("new", sol::no_constructor);
2819 	memory_type.set("banks", sol::property([this](memory_manager &mm) {
2820 			sol::table table = sol().create_table();
2821 			for (auto &bank : mm.banks())
2822 				table[bank.second->tag()] = bank.second.get();
2823 			return table;
2824 		}));
2825 	memory_type.set("regions", sol::property([this](memory_manager &mm) {
2826 			sol::table table = sol().create_table();
2827 			for (auto &region : mm.regions())
2828 				table[region.second->name()] = region.second.get();
2829 			return table;
2830 		}));
2831 	memory_type.set("shares", sol::property([this](memory_manager &mm) {
2832 			sol::table table = sol().create_table();
2833 			for (auto &share : mm.shares())
2834 				table[share.first] = share.second.get();
2835 			return table;
2836 		}));
2837 	sol().registry().set_usertype("memory", memory_type);
2838 
2839 
2840 /*  memory_region library
2841  *
2842  * manager:machine():memory().regions[region_tag]
2843  *
2844  * read/write by signedness u/i and bit-width 8/16/32/64:
2845  * region:read_*(addr)
2846  * region:write_*(addr, val)
2847  *
2848  * region.size
2849  */
2850 
2851 	auto region_type = sol().registry().create_simple_usertype<memory_region>("new", sol::no_constructor);
2852 	region_type.set("read_i8", &region_read<int8_t>);
2853 	region_type.set("read_u8", &region_read<uint8_t>);
2854 	region_type.set("read_i16", &region_read<int16_t>);
2855 	region_type.set("read_u16", &region_read<uint16_t>);
2856 	region_type.set("read_i32", &region_read<int32_t>);
2857 	region_type.set("read_u32", &region_read<uint32_t>);
2858 	region_type.set("read_i64", &region_read<int64_t>);
2859 	region_type.set("read_u64", &region_read<uint64_t>);
2860 	region_type.set("write_i8", &region_write<int8_t>);
2861 	region_type.set("write_u8", &region_write<uint8_t>);
2862 	region_type.set("write_i16", &region_write<int16_t>);
2863 	region_type.set("write_u16", &region_write<uint16_t>);
2864 	region_type.set("write_i32", &region_write<int32_t>);
2865 	region_type.set("write_u32", &region_write<uint32_t>);
2866 	region_type.set("write_i64", &region_write<int64_t>);
2867 	region_type.set("write_u64", &region_write<uint64_t>);
2868 	region_type.set("size", sol::property(&memory_region::bytes));
2869 	sol().registry().set_usertype("region", region_type);
2870 
2871 
2872 /*  memory_share library
2873  *
2874  * manager:machine():memory().shares[share_tag]
2875  *
2876  * read/write by signedness u/i and bit-width 8/16/32/64:
2877  * share:read_*(addr)
2878  * share:write_*(addr, val)
2879  *
2880  * region.size
2881 */
2882 
2883 	auto share_type = sol().registry().create_simple_usertype<memory_share>("new", sol::no_constructor);
2884 	share_type.set("read_i8", &share_read<int8_t>);
2885 	share_type.set("read_u8", &share_read<uint8_t>);
2886 	share_type.set("read_i16", &share_read<int16_t>);
2887 	share_type.set("read_u16", &share_read<uint16_t>);
2888 	share_type.set("read_i32", &share_read<int32_t>);
2889 	share_type.set("read_u32", &share_read<uint32_t>);
2890 	share_type.set("read_i64", &share_read<int64_t>);
2891 	share_type.set("read_u64", &share_read<uint64_t>);
2892 	share_type.set("write_i8", &share_write<int8_t>);
2893 	share_type.set("write_u8", &share_write<uint8_t>);
2894 	share_type.set("write_i16", &share_write<int16_t>);
2895 	share_type.set("write_u16", &share_write<uint16_t>);
2896 	share_type.set("write_i32", &share_write<int32_t>);
2897 	share_type.set("write_u32", &share_write<uint32_t>);
2898 	share_type.set("write_i64", &share_write<int64_t>);
2899 	share_type.set("write_u64", &share_write<uint64_t>);
2900 	share_type.set("size", sol::property(&memory_share::bytes));
2901 	sol().registry().set_usertype("share", share_type);
2902 
2903 
2904 /*  output_manager library
2905  *
2906  * manager:machine():outputs()
2907  *
2908  * outputs:set_value(name, val) - set output name to val
2909  * outputs:set_indexed_value(index, val) - set output index to val
2910  * outputs:get_value(name) - get output name value
2911  * outputs:get_indexed_value(index) - get output index value
2912  * outputs:name_to_id(name) - get index for name
2913  * outputs:id_to_name(index) - get name for index
2914  */
2915 
2916 	auto output_type = sol().registry().create_simple_usertype<output_manager>("new", sol::no_constructor);
2917 	output_type.set("set_value", &output_manager::set_value);
2918 	output_type.set("set_indexed_value", [](output_manager &o, char const *basename, int index, int value) {
2919 			o.set_value(util::string_format("%s%d", basename, index).c_str(), value);
2920 		});
2921 	output_type.set("get_value", &output_manager::get_value);
2922 	output_type.set("get_indexed_value", [](output_manager &o, char const *basename, int index) {
2923 			return o.get_value(util::string_format("%s%d", basename, index).c_str());
2924 		});
2925 	output_type.set("name_to_id", &output_manager::name_to_id);
2926 	output_type.set("id_to_name", &output_manager::id_to_name);
2927 	sol().registry().set_usertype("output", output_type);
2928 
2929 
2930 /*  device_image_interface library
2931  *
2932  * manager:machine().images[image_type]
2933  *
2934  * image:exists()
2935  * image:filename() - full path to the image file
2936  * image:longname()
2937  * image:manufacturer()
2938  * image:year()
2939  * image:software_list_name()
2940  * image:image_type_name() - floppy/cart/cdrom/tape/hdd etc
2941  * image:load(filename)
2942  * image:load_software(softlist_name)
2943  * image:unload()
2944  * image:create()
2945  * image:crc()
2946  * image:display()
2947  *
2948  * image.device - get associated device_t
2949  * image.instance_name
2950  * image.brief_instance_name
2951  * image.software_parent
2952  * image.is_readable
2953  * image.is_writeable
2954  * image.is_creatable
2955  * image.is_reset_on_load
2956  * image.must_be_loaded
2957  */
2958 
2959 	auto image_type = sol().registry().create_simple_usertype<device_image_interface>("new", sol::no_constructor);
2960 	image_type.set("exists", &device_image_interface::exists);
2961 	image_type.set("filename", &device_image_interface::filename);
2962 	image_type.set("longname", &device_image_interface::longname);
2963 	image_type.set("manufacturer", &device_image_interface::manufacturer);
2964 	image_type.set("year", &device_image_interface::year);
2965 	image_type.set("software_list_name", &device_image_interface::software_list_name);
2966 	image_type.set("software_parent", sol::property([](device_image_interface &di) {
2967 			const software_info *si = di.software_entry();
2968 			return si ? si->parentname() : "";
2969 		}));
2970 	image_type.set("image_type_name", &device_image_interface::image_type_name);
2971 	image_type.set("load", &device_image_interface::load);
2972 	image_type.set("load_software", static_cast<image_init_result (device_image_interface::*)(const std::string &)>(&device_image_interface::load_software));
2973 	image_type.set("unload", &device_image_interface::unload);
2974 	image_type.set("create", [](device_image_interface &di, const std::string &filename) { return di.create(filename); });
2975 	image_type.set("crc", &device_image_interface::crc);
2976 	image_type.set("display", [](device_image_interface &di) { return di.call_display(); });
2977 	image_type.set("device", sol::property(static_cast<const device_t &(device_image_interface::*)() const>(&device_image_interface::device)));
2978 	image_type.set("instance_name", sol::property(&device_image_interface::instance_name));
2979 	image_type.set("brief_instance_name", sol::property(&device_image_interface::brief_instance_name));
2980 	image_type.set("is_readable", sol::property(&device_image_interface::is_readable));
2981 	image_type.set("is_writeable", sol::property(&device_image_interface::is_writeable));
2982 	image_type.set("is_creatable", sol::property(&device_image_interface::is_creatable));
2983 	image_type.set("is_reset_on_load", sol::property(&device_image_interface::is_reset_on_load));
2984 	image_type.set("must_be_loaded", sol::property(&device_image_interface::must_be_loaded));
2985 	sol().registry().set_usertype("image", image_type);
2986 
2987 
2988 /*  mame_machine_manager library
2989  *
2990  * manager
2991  * mame_manager - alias of manager
2992  *
2993  * manager:machine() - running machine
2994  * manager:options() - core options
2995  * manager:plugins() - plugin options
2996  * manager:ui() - mame ui manager
2997  */
2998 
2999 	sol().registry().new_usertype<mame_machine_manager>("manager", "new", sol::no_constructor,
3000 			"machine", &machine_manager::machine,
3001 			"options", [](mame_machine_manager &m) { return static_cast<core_options *>(&m.options()); },
3002 			"plugins", [this](mame_machine_manager &m) {
3003 				sol::table table = sol().create_table();
3004 				for (auto &curentry : m.plugins().plugins())
3005 				{
3006 					sol::table plugin_table = sol().create_table();
3007 					plugin_table["name"] = curentry.m_name;
3008 					plugin_table["description"] = curentry.m_description;
3009 					plugin_table["type"] = curentry.m_type;
3010 					plugin_table["directory"] = curentry.m_directory;
3011 					plugin_table["start"] = curentry.m_start;
3012 					table[curentry.m_name] = plugin_table;
3013 				}
3014 				return table;
3015 			},
3016 			"ui", &mame_machine_manager::ui);
3017 	sol()["manager"] = std::ref(*mame_machine_manager::instance());
3018 	sol()["mame_manager"] = std::ref(*mame_machine_manager::instance());
3019 }
3020 
3021 //-------------------------------------------------
3022 //  frame_hook - called at each frame refresh, used to draw a HUD
3023 //-------------------------------------------------
frame_hook()3024 bool lua_engine::frame_hook()
3025 {
3026 	return execute_function("LUA_ON_FRAME_DONE");
3027 }
3028 
3029 //-------------------------------------------------
3030 //  close - close and cleanup of lua engine
3031 //-------------------------------------------------
3032 
close()3033 void lua_engine::close()
3034 {
3035 	m_sol_state.reset();
3036 	if (m_lua_state)
3037 	{
3038 		lua_settop(m_lua_state, 0);  /* clear stack */
3039 		lua_close(m_lua_state);
3040 		m_lua_state = nullptr;
3041 	}
3042 }
3043 
resume(void * ptr,int nparam)3044 void lua_engine::resume(void *ptr, int nparam)
3045 {
3046 	lua_rawgeti(m_lua_state, LUA_REGISTRYINDEX, nparam);
3047 	lua_State *L = lua_tothread(m_lua_state, -1);
3048 	lua_pop(m_lua_state, 1);
3049 	int stat = lua_resume(L, nullptr, 0);
3050 	if((stat != LUA_OK) && (stat != LUA_YIELD))
3051 	{
3052 		osd_printf_error("[LUA ERROR] in resume: %s\n", lua_tostring(L, -1));
3053 		lua_pop(L, 1);
3054 	}
3055 	luaL_unref(m_lua_state, LUA_REGISTRYINDEX, nparam);
3056 }
3057 
run(sol::load_result res)3058 void lua_engine::run(sol::load_result res)
3059 {
3060 	if(res.valid())
3061 	{
3062 		auto ret = invoke(res.get<sol::protected_function>());
3063 		if(!ret.valid())
3064 		{
3065 			sol::error err = ret;
3066 			osd_printf_error("[LUA ERROR] in run: %s\n", err.what());
3067 		}
3068 	}
3069 	else
3070 		osd_printf_error("[LUA ERROR] %d loading Lua script\n", (int)res.status());
3071 }
3072 
3073 //-------------------------------------------------
3074 //  execute - load and execute script
3075 //-------------------------------------------------
3076 
load_script(const char * filename)3077 void lua_engine::load_script(const char *filename)
3078 {
3079 	run(sol().load_file(filename));
3080 }
3081 
3082 //-------------------------------------------------
3083 //  execute_string - execute script from string
3084 //-------------------------------------------------
3085 
load_string(const char * value)3086 void lua_engine::load_string(const char *value)
3087 {
3088 	run(sol().load(value));
3089 }
3090 
3091 //-------------------------------------------------
3092 //  invoke - invokes a function, wrapping profiler
3093 //-------------------------------------------------
3094 
3095 template<typename TFunc, typename... TArgs>
invoke(TFunc && func,TArgs &&...args)3096 sol::protected_function_result lua_engine::invoke(TFunc &&func, TArgs&&... args)
3097 {
3098 	g_profiler.start(PROFILER_LUA);
3099 	sol::protected_function_result result = func(std::forward<TArgs>(args)...);
3100 	g_profiler.stop();
3101 	return result;
3102 }
3103