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