1 /*
2 script/lua_api/l_mainmenu.cpp
3 Copyright (C) 2013 sapier
4 */
5 
6 /*
7 This file is part of Freeminer.
8 
9 Freeminer is free software: you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation, either version 3 of the License, or
12 (at your option) any later version.
13 
14 Freeminer  is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17 GNU General Public License for more details.
18 
19 You should have received a copy of the GNU General Public License
20 along with Freeminer.  If not, see <http://www.gnu.org/licenses/>.
21 */
22 
23 #include "lua_api/l_mainmenu.h"
24 #include "lua_api/l_internal.h"
25 #include "common/c_content.h"
26 #include "cpp_api/s_async.h"
27 #include "guiEngine.h"
28 #include "guiMainMenu.h"
29 #include "guiKeyChangeMenu.h"
30 #include "guiFileSelectMenu.h"
31 #include "subgame.h"
32 #include "version.h"
33 #include "porting.h"
34 #include "filesys.h"
35 #include "convert_json.h"
36 #include "serverlist.h"
37 #include "sound.h"
38 #include "settings.h"
39 #include "main.h" // for g_settings
40 #include "EDriverTypes.h"
41 
42 #include <IFileArchive.h>
43 #include <IFileSystem.h>
44 
45 /******************************************************************************/
getTextData(lua_State * L,std::string name)46 std::string ModApiMainMenu::getTextData(lua_State *L, std::string name)
47 {
48 	lua_getglobal(L, "gamedata");
49 
50 	lua_getfield(L, -1, name.c_str());
51 
52 	if(lua_isnil(L, -1))
53 		return "";
54 
55 	return luaL_checkstring(L, -1);
56 }
57 
58 /******************************************************************************/
getIntegerData(lua_State * L,std::string name,bool & valid)59 int ModApiMainMenu::getIntegerData(lua_State *L, std::string name,bool& valid)
60 {
61 	lua_getglobal(L, "gamedata");
62 
63 	lua_getfield(L, -1, name.c_str());
64 
65 	if(lua_isnil(L, -1)) {
66 		valid = false;
67 		return -1;
68 		}
69 
70 	valid = true;
71 	return luaL_checkinteger(L, -1);
72 }
73 
74 /******************************************************************************/
getBoolData(lua_State * L,std::string name,bool & valid)75 int ModApiMainMenu::getBoolData(lua_State *L, std::string name,bool& valid)
76 {
77 	lua_getglobal(L, "gamedata");
78 
79 	lua_getfield(L, -1, name.c_str());
80 
81 	if(lua_isnil(L, -1)) {
82 		valid = false;
83 		return false;
84 		}
85 
86 	valid = true;
87 	return lua_toboolean(L, -1);
88 }
89 
90 /******************************************************************************/
l_update_formspec(lua_State * L)91 int ModApiMainMenu::l_update_formspec(lua_State *L)
92 {
93 	GUIEngine* engine = getGuiEngine(L);
94 	assert(engine != 0);
95 
96 	if (engine->m_startgame)
97 		return 0;
98 
99 	//read formspec
100 	std::string formspec(luaL_checkstring(L, 1));
101 
102 	if (engine->m_formspecgui != 0) {
103 		engine->m_formspecgui->setForm(formspec);
104 	}
105 
106 	return 0;
107 }
108 
109 /******************************************************************************/
l_start(lua_State * L)110 int ModApiMainMenu::l_start(lua_State *L)
111 {
112 	GUIEngine* engine = getGuiEngine(L);
113 	assert(engine != 0);
114 
115 	//update c++ gamedata from lua table
116 
117 	bool valid = false;
118 
119 
120 	engine->m_data->selected_world		= getIntegerData(L, "selected_world",valid) -1;
121 	engine->m_data->simple_singleplayer_mode = getBoolData(L,"singleplayer",valid);
122 	engine->m_data->name				= getTextData(L,"playername");
123 	engine->m_data->password			= getTextData(L,"password");
124 	engine->m_data->address				= getTextData(L,"address");
125 	engine->m_data->port				= getTextData(L,"port");
126 	engine->m_data->serverdescription	= getTextData(L,"serverdescription");
127 	engine->m_data->servername			= getTextData(L,"servername");
128 
129 	//close menu next time
130 	engine->m_startgame = true;
131 	return 0;
132 }
133 
134 /******************************************************************************/
l_close(lua_State * L)135 int ModApiMainMenu::l_close(lua_State *L)
136 {
137 	GUIEngine* engine = getGuiEngine(L);
138 	assert(engine != 0);
139 
140 	engine->m_kill = true;
141 	return 0;
142 }
143 
144 /******************************************************************************/
l_set_background(lua_State * L)145 int ModApiMainMenu::l_set_background(lua_State *L)
146 {
147 	GUIEngine* engine = getGuiEngine(L);
148 	assert(engine != 0);
149 
150 	std::string backgroundlevel(luaL_checkstring(L, 1));
151 	std::string texturename(luaL_checkstring(L, 2));
152 
153 	bool tile_image = false;
154 	bool retval     = false;
155 	unsigned int minsize = 16;
156 
157 	if (!lua_isnone(L, 3)) {
158 		tile_image = lua_toboolean(L, 3);
159 	}
160 
161 	if (!lua_isnone(L, 4)) {
162 		minsize = lua_tonumber(L, 4);
163 	}
164 
165 	if (backgroundlevel == "background") {
166 		retval |= engine->setTexture(TEX_LAYER_BACKGROUND, texturename,
167 				tile_image, minsize);
168 	}
169 
170 	if (backgroundlevel == "overlay") {
171 		retval |= engine->setTexture(TEX_LAYER_OVERLAY, texturename,
172 				tile_image, minsize);
173 	}
174 
175 	if (backgroundlevel == "header") {
176 		retval |= engine->setTexture(TEX_LAYER_HEADER,  texturename,
177 				tile_image, minsize);
178 	}
179 
180 	if (backgroundlevel == "footer") {
181 		retval |= engine->setTexture(TEX_LAYER_FOOTER, texturename,
182 				tile_image, minsize);
183 	}
184 
185 	lua_pushboolean(L,retval);
186 	return 1;
187 }
188 
189 /******************************************************************************/
l_set_clouds(lua_State * L)190 int ModApiMainMenu::l_set_clouds(lua_State *L)
191 {
192 	GUIEngine* engine = getGuiEngine(L);
193 	assert(engine != 0);
194 
195 	bool value = lua_toboolean(L,1);
196 
197 	engine->m_clouds_enabled = value;
198 
199 	return 0;
200 }
201 
202 /******************************************************************************/
l_get_textlist_index(lua_State * L)203 int ModApiMainMenu::l_get_textlist_index(lua_State *L)
204 {
205 	// get_table_index accepts both tables and textlists
206 	return l_get_table_index(L);
207 }
208 
209 /******************************************************************************/
l_get_table_index(lua_State * L)210 int ModApiMainMenu::l_get_table_index(lua_State *L)
211 {
212 	GUIEngine* engine = getGuiEngine(L);
213 	assert(engine != 0);
214 
215 	std::wstring tablename(narrow_to_wide(luaL_checkstring(L, 1)));
216 	GUITable *table = engine->m_menu->getTable(tablename);
217 	s32 selection = table ? table->getSelected() : 0;
218 
219 	if (selection >= 1)
220 		lua_pushinteger(L, selection);
221 	else
222 		lua_pushnil(L);
223 	return 1;
224 }
225 
226 /******************************************************************************/
l_get_worlds(lua_State * L)227 int ModApiMainMenu::l_get_worlds(lua_State *L)
228 {
229 	std::vector<WorldSpec> worlds = getAvailableWorlds();
230 
231 	lua_newtable(L);
232 	int top = lua_gettop(L);
233 	unsigned int index = 1;
234 
235 	for (unsigned int i = 0; i < worlds.size(); i++)
236 	{
237 		lua_pushnumber(L,index);
238 
239 		lua_newtable(L);
240 		int top_lvl2 = lua_gettop(L);
241 
242 		lua_pushstring(L,"path");
243 		lua_pushstring(L,worlds[i].path.c_str());
244 		lua_settable(L, top_lvl2);
245 
246 		lua_pushstring(L,"name");
247 		lua_pushstring(L,worlds[i].name.c_str());
248 		lua_settable(L, top_lvl2);
249 
250 		lua_pushstring(L,"gameid");
251 		lua_pushstring(L,worlds[i].gameid.c_str());
252 		lua_settable(L, top_lvl2);
253 
254 		lua_settable(L, top);
255 		index++;
256 	}
257 	return 1;
258 }
259 
260 /******************************************************************************/
l_get_games(lua_State * L)261 int ModApiMainMenu::l_get_games(lua_State *L)
262 {
263 	std::vector<SubgameSpec> games = getAvailableGames();
264 
265 	lua_newtable(L);
266 	int top = lua_gettop(L);
267 	unsigned int index = 1;
268 
269 	for (unsigned int i = 0; i < games.size(); i++)
270 	{
271 		lua_pushnumber(L,index);
272 		lua_newtable(L);
273 		int top_lvl2 = lua_gettop(L);
274 
275 		lua_pushstring(L,"id");
276 		lua_pushstring(L,games[i].id.c_str());
277 		lua_settable(L, top_lvl2);
278 
279 		lua_pushstring(L,"path");
280 		lua_pushstring(L,games[i].path.c_str());
281 		lua_settable(L, top_lvl2);
282 
283 		lua_pushstring(L,"gamemods_path");
284 		lua_pushstring(L,games[i].gamemods_path.c_str());
285 		lua_settable(L, top_lvl2);
286 
287 		lua_pushstring(L,"name");
288 		lua_pushstring(L,games[i].name.c_str());
289 		lua_settable(L, top_lvl2);
290 
291 		lua_pushstring(L,"menuicon_path");
292 		lua_pushstring(L,games[i].menuicon_path.c_str());
293 		lua_settable(L, top_lvl2);
294 
295 		lua_pushstring(L,"addon_mods_paths");
296 		lua_newtable(L);
297 		int table2 = lua_gettop(L);
298 		int internal_index=1;
299 		for (std::set<std::string>::iterator iter = games[i].addon_mods_paths.begin();
300 				iter != games[i].addon_mods_paths.end(); iter++) {
301 			lua_pushnumber(L,internal_index);
302 			lua_pushstring(L,(*iter).c_str());
303 			lua_settable(L, table2);
304 			internal_index++;
305 		}
306 		lua_settable(L, top_lvl2);
307 		lua_settable(L, top);
308 		index++;
309 	}
310 	return 1;
311 }
312 /******************************************************************************/
l_get_modstore_details(lua_State * L)313 int ModApiMainMenu::l_get_modstore_details(lua_State *L)
314 {
315 	const char *modid	= luaL_checkstring(L, 1);
316 
317 	if (modid != 0) {
318 		Json::Value details;
319 		std::string url = "";
320 		try{
321 			url = g_settings->get("modstore_details_url");
322 		}
323 		catch(SettingNotFoundException &e) {
324 			lua_pushnil(L);
325 			return 1;
326 		}
327 
328 		size_t idpos = url.find("*");
329 		url.erase(idpos,1);
330 		url.insert(idpos,modid);
331 
332 		details = getModstoreUrl(url);
333 
334 		ModStoreModDetails current_mod = readModStoreModDetails(details);
335 
336 		if ( current_mod.valid) {
337 			lua_newtable(L);
338 			int top = lua_gettop(L);
339 
340 			lua_pushstring(L,"id");
341 			lua_pushnumber(L,current_mod.id);
342 			lua_settable(L, top);
343 
344 			lua_pushstring(L,"title");
345 			lua_pushstring(L,current_mod.title.c_str());
346 			lua_settable(L, top);
347 
348 			lua_pushstring(L,"basename");
349 			lua_pushstring(L,current_mod.basename.c_str());
350 			lua_settable(L, top);
351 
352 			lua_pushstring(L,"description");
353 			lua_pushstring(L,current_mod.description.c_str());
354 			lua_settable(L, top);
355 
356 			lua_pushstring(L,"author");
357 			lua_pushstring(L,current_mod.author.username.c_str());
358 			lua_settable(L, top);
359 
360 			lua_pushstring(L,"download_url");
361 			lua_pushstring(L,current_mod.versions[0].file.c_str());
362 			lua_settable(L, top);
363 
364 			lua_pushstring(L,"versions");
365 			lua_newtable(L);
366 			int versionstop = lua_gettop(L);
367 			for (unsigned int i=0;i < current_mod.versions.size(); i++) {
368 				lua_pushnumber(L,i+1);
369 				lua_newtable(L);
370 				int current_element = lua_gettop(L);
371 
372 				lua_pushstring(L,"date");
373 				lua_pushstring(L,current_mod.versions[i].date.c_str());
374 				lua_settable(L,current_element);
375 
376 				lua_pushstring(L,"download_url");
377 				lua_pushstring(L,current_mod.versions[i].file.c_str());
378 				lua_settable(L,current_element);
379 
380 				lua_settable(L,versionstop);
381 			}
382 			lua_settable(L, top);
383 
384 			lua_pushstring(L,"screenshot_url");
385 			lua_pushstring(L,current_mod.titlepic.file.c_str());
386 			lua_settable(L, top);
387 
388 			lua_pushstring(L,"license");
389 			lua_pushstring(L,current_mod.license.shortinfo.c_str());
390 			lua_settable(L, top);
391 
392 			lua_pushstring(L,"rating");
393 			lua_pushnumber(L,current_mod.rating);
394 			lua_settable(L, top);
395 
396 			//TODO depends
397 
398 			//TODO softdepends
399 			return 1;
400 		}
401 	}
402 	return 0;
403 }
404 
405 /******************************************************************************/
l_get_modstore_list(lua_State * L)406 int ModApiMainMenu::l_get_modstore_list(lua_State *L)
407 {
408 	Json::Value mods;
409 	std::string url = "";
410 	try{
411 		url = g_settings->get("modstore_listmods_url");
412 	}
413 	catch(SettingNotFoundException &e) {
414 		lua_pushnil(L);
415 		return 1;
416 	}
417 
418 	mods = getModstoreUrl(url);
419 
420 	std::vector<ModStoreMod> moddata = readModStoreList(mods);
421 
422 	lua_newtable(L);
423 	int top = lua_gettop(L);
424 	unsigned int index = 1;
425 
426 	for (unsigned int i = 0; i < moddata.size(); i++)
427 	{
428 		if (moddata[i].valid) {
429 			lua_pushnumber(L,index);
430 			lua_newtable(L);
431 
432 			int top_lvl2 = lua_gettop(L);
433 
434 			lua_pushstring(L,"id");
435 			lua_pushnumber(L,moddata[i].id);
436 			lua_settable(L, top_lvl2);
437 
438 			lua_pushstring(L,"title");
439 			lua_pushstring(L,moddata[i].title.c_str());
440 			lua_settable(L, top_lvl2);
441 
442 			lua_pushstring(L,"basename");
443 			lua_pushstring(L,moddata[i].basename.c_str());
444 			lua_settable(L, top_lvl2);
445 
446 			lua_settable(L, top);
447 			index++;
448 		}
449 	}
450 	return 1;
451 }
452 
453 /******************************************************************************/
l_get_favorites(lua_State * L)454 int ModApiMainMenu::l_get_favorites(lua_State *L)
455 {
456 	std::string listtype = "local";
457 
458 	if (!lua_isnone(L,1)) {
459 		listtype = luaL_checkstring(L,1);
460 	}
461 
462 	std::vector<ServerListSpec> servers;
463 
464 	if(listtype == "online") {
465 		servers = ServerList::getOnline();
466 	} else {
467 		servers = ServerList::getLocal();
468 	}
469 
470 	Json::Value root(Json::arrayValue);
471 	for (unsigned int i = 0; i < servers.size(); i++)
472 	{
473 		root[i] = servers[i];
474 	}
475 	lua_pushnil(L);
476 	int nullindex = lua_gettop(L);
477 	if(!push_json_value(L, root, nullindex)) {
478 	}
479 
480 	return 1;
481 }
482 
483 /******************************************************************************/
l_delete_favorite(lua_State * L)484 int ModApiMainMenu::l_delete_favorite(lua_State *L)
485 {
486 	std::vector<ServerListSpec> servers;
487 
488 	std::string listtype = "local";
489 
490 	if (!lua_isnone(L,2)) {
491 		listtype = luaL_checkstring(L,2);
492 	}
493 
494 	if ((listtype != "local") &&
495 		(listtype != "online"))
496 		return 0;
497 
498 
499 	if(listtype == "online") {
500 		servers = ServerList::getOnline();
501 	} else {
502 		servers = ServerList::getLocal();
503 	}
504 
505 	int fav_idx	= luaL_checkinteger(L,1) -1;
506 
507 	if ((fav_idx >= 0) &&
508 			(fav_idx < (int) servers.size())) {
509 
510 		ServerList::deleteEntry(servers[fav_idx]);
511 	}
512 
513 	return 0;
514 }
515 
516 /******************************************************************************/
l_show_keys_menu(lua_State * L)517 int ModApiMainMenu::l_show_keys_menu(lua_State *L)
518 {
519 	GUIEngine* engine = getGuiEngine(L);
520 	assert(engine != 0);
521 
522 	GUIKeyChangeMenu *kmenu
523 		= new GUIKeyChangeMenu(	engine->m_device->getGUIEnvironment(),
524 								engine->m_parent,
525 								-1,
526 								engine->m_menumanager);
527 	kmenu->drop();
528 	return 0;
529 }
530 
531 /******************************************************************************/
l_create_world(lua_State * L)532 int ModApiMainMenu::l_create_world(lua_State *L)
533 {
534 	const char *name	= luaL_checkstring(L, 1);
535 	int gameidx			= luaL_checkinteger(L,2) -1;
536 
537 	std::string path = porting::path_user + DIR_DELIM
538 			"worlds" + DIR_DELIM
539 			+ name;
540 
541 	std::vector<SubgameSpec> games = getAvailableGames();
542 
543 	if ((gameidx >= 0) &&
544 			(gameidx < (int) games.size())) {
545 
546 		// Create world if it doesn't exist
547 		if(!initializeWorld(path, games[gameidx].id)){
548 			lua_pushstring(L, "Failed to initialize world");
549 
550 		}
551 		else {
552 		lua_pushnil(L);
553 		}
554 	}
555 	else {
556 		lua_pushstring(L, "Invalid game index");
557 	}
558 	return 1;
559 }
560 
561 /******************************************************************************/
l_delete_world(lua_State * L)562 int ModApiMainMenu::l_delete_world(lua_State *L)
563 {
564 	int worldidx	= luaL_checkinteger(L,1) -1;
565 
566 	std::vector<WorldSpec> worlds = getAvailableWorlds();
567 
568 	if ((worldidx >= 0) &&
569 		(worldidx < (int) worlds.size())) {
570 
571 		WorldSpec spec = worlds[worldidx];
572 
573 		std::vector<std::string> paths;
574 		paths.push_back(spec.path);
575 		fs::GetRecursiveSubPaths(spec.path, paths);
576 
577 		// Delete files
578 		if (!fs::DeletePaths(paths)) {
579 			lua_pushstring(L, "Failed to delete world");
580 		}
581 		else {
582 			lua_pushnil(L);
583 		}
584 	}
585 	else {
586 		lua_pushstring(L, "Invalid world index");
587 	}
588 	return 1;
589 }
590 
591 /******************************************************************************/
l_set_topleft_text(lua_State * L)592 int ModApiMainMenu::l_set_topleft_text(lua_State *L)
593 {
594 	GUIEngine* engine = getGuiEngine(L);
595 	assert(engine != 0);
596 
597 	std::string text = "";
598 
599 	if (!lua_isnone(L,1) &&	!lua_isnil(L,1))
600 		text = luaL_checkstring(L, 1);
601 
602 	engine->setTopleftText(text);
603 	return 0;
604 }
605 
606 /******************************************************************************/
l_get_modpath(lua_State * L)607 int ModApiMainMenu::l_get_modpath(lua_State *L)
608 {
609 	std::string modpath
610 			= fs::RemoveRelativePathComponents(porting::path_user + DIR_DELIM + "mods" + DIR_DELIM);
611 	lua_pushstring(L, modpath.c_str());
612 	return 1;
613 }
614 
615 /******************************************************************************/
l_get_gamepath(lua_State * L)616 int ModApiMainMenu::l_get_gamepath(lua_State *L)
617 {
618 	std::string gamepath
619 			= fs::RemoveRelativePathComponents(porting::path_user + DIR_DELIM + "games" + DIR_DELIM);
620 	lua_pushstring(L, gamepath.c_str());
621 	return 1;
622 }
623 
624 /******************************************************************************/
l_get_texturepath(lua_State * L)625 int ModApiMainMenu::l_get_texturepath(lua_State *L)
626 {
627 	std::string gamepath
628 			= fs::RemoveRelativePathComponents(porting::path_user + DIR_DELIM + "textures");
629 	lua_pushstring(L, gamepath.c_str());
630 	return 1;
631 }
632 
l_get_texturepath_share(lua_State * L)633 int ModApiMainMenu::l_get_texturepath_share(lua_State *L)
634 {
635 	std::string gamepath
636 			= fs::RemoveRelativePathComponents(porting::path_share + DIR_DELIM + "textures");
637 	lua_pushstring(L, gamepath.c_str());
638 	return 1;
639 }
640 
641 /******************************************************************************/
l_get_dirlist(lua_State * L)642 int ModApiMainMenu::l_get_dirlist(lua_State *L)
643 {
644 	const char *path	= luaL_checkstring(L, 1);
645 	bool dironly		= lua_toboolean(L, 2);
646 
647 	std::vector<fs::DirListNode> dirlist = fs::GetDirListing(path);
648 
649 	unsigned int index = 1;
650 	lua_newtable(L);
651 	int table = lua_gettop(L);
652 
653 	for (unsigned int i=0;i< dirlist.size(); i++) {
654 		if ((dirlist[i].dir) || (dironly == false)) {
655 			lua_pushnumber(L,index);
656 			lua_pushstring(L,dirlist[i].name.c_str());
657 			lua_settable(L, table);
658 			index++;
659 		}
660 	}
661 
662 	return 1;
663 }
664 
665 /******************************************************************************/
l_create_dir(lua_State * L)666 int ModApiMainMenu::l_create_dir(lua_State *L) {
667 	const char *path	= luaL_checkstring(L, 1);
668 
669 	if (ModApiMainMenu::isMinetestPath(path)) {
670 		lua_pushboolean(L,fs::CreateAllDirs(path));
671 		return 1;
672 	}
673 	lua_pushboolean(L,false);
674 	return 1;
675 }
676 
677 /******************************************************************************/
l_delete_dir(lua_State * L)678 int ModApiMainMenu::l_delete_dir(lua_State *L)
679 {
680 	const char *path	= luaL_checkstring(L, 1);
681 
682 	std::string absolute_path = fs::RemoveRelativePathComponents(path);
683 
684 	if (ModApiMainMenu::isMinetestPath(absolute_path)) {
685 		lua_pushboolean(L,fs::RecursiveDelete(absolute_path));
686 		return 1;
687 	}
688 	lua_pushboolean(L,false);
689 	return 1;
690 }
691 
692 /******************************************************************************/
l_copy_dir(lua_State * L)693 int ModApiMainMenu::l_copy_dir(lua_State *L)
694 {
695 	const char *source	= luaL_checkstring(L, 1);
696 	const char *destination	= luaL_checkstring(L, 2);
697 
698 	bool keep_source = true;
699 
700 	if ((!lua_isnone(L,3)) &&
701 			(!lua_isnil(L,3))) {
702 		keep_source = lua_toboolean(L,3);
703 	}
704 
705 	std::string absolute_destination = fs::RemoveRelativePathComponents(destination);
706 	std::string absolute_source = fs::RemoveRelativePathComponents(source);
707 
708 	if ((ModApiMainMenu::isMinetestPath(absolute_source)) &&
709 			(ModApiMainMenu::isMinetestPath(absolute_destination))) {
710 		bool retval = fs::CopyDir(absolute_source,absolute_destination);
711 
712 		if (retval && (!keep_source)) {
713 
714 			retval &= fs::RecursiveDelete(absolute_source);
715 		}
716 		lua_pushboolean(L,retval);
717 		return 1;
718 	}
719 	lua_pushboolean(L,false);
720 	return 1;
721 }
722 
723 /******************************************************************************/
l_extract_zip(lua_State * L)724 int ModApiMainMenu::l_extract_zip(lua_State *L)
725 {
726 	GUIEngine* engine = getGuiEngine(L);
727 	assert(engine != 0);
728 
729 	const char *zipfile	= luaL_checkstring(L, 1);
730 	const char *destination	= luaL_checkstring(L, 2);
731 
732 	std::string absolute_destination = fs::RemoveRelativePathComponents(destination);
733 
734 	if (ModApiMainMenu::isMinetestPath(absolute_destination)) {
735 		fs::CreateAllDirs(absolute_destination);
736 
737 		io::IFileSystem* fs = engine->m_device->getFileSystem();
738 
739 		if (!fs->addFileArchive(zipfile,true,false,io::EFAT_ZIP)) {
740 			lua_pushboolean(L,false);
741 			return 1;
742 		}
743 
744 		assert(fs->getFileArchiveCount() > 0);
745 
746 		/**********************************************************************/
747 		/* WARNING this is not threadsafe!!                                   */
748 		/**********************************************************************/
749 		io::IFileArchive* opened_zip =
750 			fs->getFileArchive(fs->getFileArchiveCount()-1);
751 
752 		const io::IFileList* files_in_zip = opened_zip->getFileList();
753 
754 		unsigned int number_of_files = files_in_zip->getFileCount();
755 
756 		for (unsigned int i=0; i < number_of_files;  i++) {
757 			std::string fullpath = destination;
758 			fullpath += DIR_DELIM;
759 			fullpath += files_in_zip->getFullFileName(i).c_str();
760 
761 			if (files_in_zip->isDirectory(i)) {
762 				if (! fs::CreateAllDirs(fullpath) ) {
763 					fs->removeFileArchive(fs->getFileArchiveCount()-1);
764 					lua_pushboolean(L,false);
765 					return 1;
766 				}
767 			}
768 			else {
769 				io::IReadFile* toread = opened_zip->createAndOpenFile(i);
770 
771 				FILE *targetfile = fopen(fullpath.c_str(),"wb");
772 
773 				if (targetfile == NULL) {
774 					fs->removeFileArchive(fs->getFileArchiveCount()-1);
775 					lua_pushboolean(L,false);
776 					return 1;
777 				}
778 
779 				char read_buffer[1024];
780 				unsigned int total_read = 0;
781 
782 				while (total_read < toread->getSize()) {
783 
784 					unsigned int bytes_read =
785 							toread->read(read_buffer,sizeof(read_buffer));
786 					if ((bytes_read == 0 ) ||
787 						(fwrite(read_buffer, 1, bytes_read, targetfile) != bytes_read))
788 					{
789 						fclose(targetfile);
790 						fs->removeFileArchive(fs->getFileArchiveCount()-1);
791 						lua_pushboolean(L,false);
792 						return 1;
793 					}
794 					total_read += bytes_read;
795 				}
796 
797 				fclose(targetfile);
798 			}
799 
800 		}
801 
802 		fs->removeFileArchive(fs->getFileArchiveCount()-1);
803 		lua_pushboolean(L,true);
804 		return 1;
805 	}
806 
807 	lua_pushboolean(L,false);
808 	return 1;
809 }
810 
811 /******************************************************************************/
l_get_mainmenu_path(lua_State * L)812 int ModApiMainMenu::l_get_mainmenu_path(lua_State *L)
813 {
814 	GUIEngine* engine = getGuiEngine(L);
815 	assert(engine != 0);
816 
817 	lua_pushstring(L,engine->getScriptDir().c_str());
818 	return 1;
819 }
820 
821 /******************************************************************************/
isMinetestPath(std::string path)822 bool ModApiMainMenu::isMinetestPath(std::string path)
823 {
824 	if (fs::PathStartsWith(path,fs::TempPath()))
825 		return true;
826 
827 	/* games */
828 	if (fs::PathStartsWith(path,fs::RemoveRelativePathComponents(porting::path_share + DIR_DELIM + "games")))
829 		return true;
830 
831 	/* mods */
832 	if (fs::PathStartsWith(path,fs::RemoveRelativePathComponents(porting::path_user + DIR_DELIM + "mods")))
833 		return true;
834 
835 	/* worlds */
836 	if (fs::PathStartsWith(path,fs::RemoveRelativePathComponents(porting::path_user + DIR_DELIM + "worlds")))
837 		return true;
838 
839 
840 	return false;
841 }
842 
843 /******************************************************************************/
l_show_file_open_dialog(lua_State * L)844 int ModApiMainMenu::l_show_file_open_dialog(lua_State *L)
845 {
846 	GUIEngine* engine = getGuiEngine(L);
847 	assert(engine != 0);
848 
849 	const char *formname= luaL_checkstring(L, 1);
850 	const char *title	= luaL_checkstring(L, 2);
851 
852 	GUIFileSelectMenu* fileOpenMenu =
853 		new GUIFileSelectMenu(engine->m_device->getGUIEnvironment(),
854 								engine->m_parent,
855 								-1,
856 								engine->m_menumanager,
857 								title,
858 								formname);
859 	fileOpenMenu->setTextDest(engine->m_buttonhandler);
860 	fileOpenMenu->drop();
861 	return 0;
862 }
863 
864 /******************************************************************************/
l_get_version(lua_State * L)865 int ModApiMainMenu::l_get_version(lua_State *L)
866 {
867 	lua_pushstring(L, minetest_version_simple);
868 	return 1;
869 }
870 
871 /******************************************************************************/
l_sound_play(lua_State * L)872 int ModApiMainMenu::l_sound_play(lua_State *L)
873 {
874 	GUIEngine* engine = getGuiEngine(L);
875 
876 	SimpleSoundSpec spec;
877 	read_soundspec(L, 1, spec);
878 	bool looped = lua_toboolean(L, 2);
879 
880 	u32 handle = engine->playSound(spec, looped);
881 
882 	lua_pushinteger(L, handle);
883 
884 	return 1;
885 }
886 
887 /******************************************************************************/
l_sound_stop(lua_State * L)888 int ModApiMainMenu::l_sound_stop(lua_State *L)
889 {
890 	GUIEngine* engine = getGuiEngine(L);
891 
892 	u32 handle = luaL_checkinteger(L, 1);
893 	engine->stopSound(handle);
894 
895 	return 1;
896 }
897 
898 /******************************************************************************/
l_download_file(lua_State * L)899 int ModApiMainMenu::l_download_file(lua_State *L)
900 {
901 	const char *url    = luaL_checkstring(L, 1);
902 	const char *target = luaL_checkstring(L, 2);
903 
904 	//check path
905 	std::string absolute_destination = fs::RemoveRelativePathComponents(target);
906 
907 	if (ModApiMainMenu::isMinetestPath(absolute_destination)) {
908 		if (GUIEngine::downloadFile(url,absolute_destination)) {
909 			lua_pushboolean(L,true);
910 			return 1;
911 		}
912 	} else {
913 		errorstream << "DOWNLOAD denied: " << absolute_destination
914 				<< " isn't a allowed path" << std::endl;
915 	}
916 	lua_pushboolean(L,false);
917 	return 1;
918 }
919 
920 /******************************************************************************/
l_get_video_drivers(lua_State * L)921 int ModApiMainMenu::l_get_video_drivers(lua_State *L)
922 {
923 	static const char* drivernames[] = {
924 		"NULL Driver",
925 		"Software",
926 		"Burningsvideo",
927 		"Direct3D 8",
928 		"Direct3D 9",
929 		"OpenGL",
930 		"OGLES1",
931 		"OGLES2"
932 	};
933 	unsigned int index = 1;
934 	lua_newtable(L);
935 	int top = lua_gettop(L);
936 
937 	for (unsigned int i = irr::video::EDT_SOFTWARE;
938 			i < MYMIN(irr::video::EDT_COUNT, (sizeof(drivernames)/sizeof(drivernames[0])));
939 			i++) {
940 		if (irr::IrrlichtDevice::isDriverSupported((irr::video::E_DRIVER_TYPE) i)) {
941 			lua_pushnumber(L,index++);
942 			lua_pushstring(L,drivernames[i]);
943 			lua_settable(L, top);
944 		}
945 	}
946 
947 	return 1;
948 }
949 
950 /******************************************************************************/
l_gettext(lua_State * L)951 int ModApiMainMenu::l_gettext(lua_State *L)
952 {
953 	std::wstring wtext = wstrgettext((std::string) luaL_checkstring(L, 1));
954 	lua_pushstring(L, wide_to_narrow(wtext).c_str());
955 
956 	return 1;
957 }
958 
959 /******************************************************************************/
l_get_screen_info(lua_State * L)960 int ModApiMainMenu::l_get_screen_info(lua_State *L)
961 {
962 	lua_newtable(L);
963 	int top = lua_gettop(L);
964 	lua_pushstring(L,"density");
965 	lua_pushnumber(L,porting::getDisplayDensity());
966 	lua_settable(L, top);
967 
968 	lua_pushstring(L,"display_width");
969 	lua_pushnumber(L,porting::getDisplaySize().X);
970 	lua_settable(L, top);
971 
972 	lua_pushstring(L,"display_height");
973 	lua_pushnumber(L,porting::getDisplaySize().Y);
974 	lua_settable(L, top);
975 
976 	lua_pushstring(L,"window_width");
977 	lua_pushnumber(L,porting::getWindowSize().X);
978 	lua_settable(L, top);
979 
980 	lua_pushstring(L,"window_height");
981 	lua_pushnumber(L,porting::getWindowSize().Y);
982 	lua_settable(L, top);
983 	return 1;
984 }
985 
986 /******************************************************************************/
l_do_async_callback(lua_State * L)987 int ModApiMainMenu::l_do_async_callback(lua_State *L)
988 {
989 	GUIEngine* engine = getGuiEngine(L);
990 
991 	size_t func_length, param_length;
992 	const char* serialized_func_raw = luaL_checklstring(L, 1, &func_length);
993 
994 	const char* serialized_param_raw = luaL_checklstring(L, 2, &param_length);
995 
996 	assert(serialized_func_raw != NULL);
997 	assert(serialized_param_raw != NULL);
998 
999 	std::string serialized_func = std::string(serialized_func_raw, func_length);
1000 	std::string serialized_param = std::string(serialized_param_raw, param_length);
1001 
1002 	lua_pushinteger(L, engine->queueAsync(serialized_func, serialized_param));
1003 
1004 	return 1;
1005 }
1006 
1007 /******************************************************************************/
Initialize(lua_State * L,int top)1008 void ModApiMainMenu::Initialize(lua_State *L, int top)
1009 {
1010 	API_FCT(update_formspec);
1011 	API_FCT(set_clouds);
1012 	API_FCT(get_textlist_index);
1013 	API_FCT(get_table_index);
1014 	API_FCT(get_worlds);
1015 	API_FCT(get_games);
1016 	API_FCT(start);
1017 	API_FCT(close);
1018 	API_FCT(get_favorites);
1019 	API_FCT(show_keys_menu);
1020 	API_FCT(create_world);
1021 	API_FCT(delete_world);
1022 	API_FCT(delete_favorite);
1023 	API_FCT(set_background);
1024 	API_FCT(set_topleft_text);
1025 	API_FCT(get_modpath);
1026 	API_FCT(get_gamepath);
1027 	API_FCT(get_texturepath);
1028 	API_FCT(get_texturepath_share);
1029 	API_FCT(get_dirlist);
1030 	API_FCT(create_dir);
1031 	API_FCT(delete_dir);
1032 	API_FCT(copy_dir);
1033 	API_FCT(extract_zip);
1034 	API_FCT(get_mainmenu_path);
1035 	API_FCT(show_file_open_dialog);
1036 	API_FCT(get_version);
1037 	API_FCT(download_file);
1038 	API_FCT(get_modstore_details);
1039 	API_FCT(get_modstore_list);
1040 	API_FCT(sound_play);
1041 	API_FCT(sound_stop);
1042 	API_FCT(gettext);
1043 	API_FCT(get_video_drivers);
1044 	API_FCT(get_screen_info);
1045 	API_FCT(do_async_callback);
1046 }
1047 
1048 /******************************************************************************/
InitializeAsync(AsyncEngine & engine)1049 void ModApiMainMenu::InitializeAsync(AsyncEngine& engine)
1050 {
1051 
1052 	ASYNC_API_FCT(get_worlds);
1053 	ASYNC_API_FCT(get_games);
1054 	ASYNC_API_FCT(get_favorites);
1055 	ASYNC_API_FCT(get_modpath);
1056 	ASYNC_API_FCT(get_gamepath);
1057 	ASYNC_API_FCT(get_texturepath);
1058 	ASYNC_API_FCT(get_texturepath_share);
1059 	ASYNC_API_FCT(get_dirlist);
1060 	ASYNC_API_FCT(create_dir);
1061 	ASYNC_API_FCT(delete_dir);
1062 	ASYNC_API_FCT(copy_dir);
1063 	//ASYNC_API_FCT(extract_zip); //TODO remove dependency to GuiEngine
1064 	ASYNC_API_FCT(get_version);
1065 	ASYNC_API_FCT(download_file);
1066 	ASYNC_API_FCT(get_modstore_details);
1067 	ASYNC_API_FCT(get_modstore_list);
1068 	//ASYNC_API_FCT(gettext); (gettext lib isn't threadsafe)
1069 }
1070