For usages of assert() that are meant to persist in Release builds (when NDEBUG is...
[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(!initializeWorld(path, games[gameidx].id)){
648                         lua_pushstring(L, "Failed to initialize world");
649
650                 }
651                 else {
652                 lua_pushnil(L);
653                 }
654         }
655         else {
656                 lua_pushstring(L, "Invalid game index");
657         }
658         return 1;
659 }
660
661 /******************************************************************************/
662 int ModApiMainMenu::l_delete_world(lua_State *L)
663 {
664         int worldidx    = luaL_checkinteger(L,1) -1;
665
666         std::vector<WorldSpec> worlds = getAvailableWorlds();
667
668         if ((worldidx >= 0) &&
669                 (worldidx < (int) worlds.size())) {
670
671                 WorldSpec spec = worlds[worldidx];
672
673                 std::vector<std::string> paths;
674                 paths.push_back(spec.path);
675                 fs::GetRecursiveSubPaths(spec.path, paths);
676
677                 // Delete files
678                 if (!fs::DeletePaths(paths)) {
679                         lua_pushstring(L, "Failed to delete world");
680                 }
681                 else {
682                         lua_pushnil(L);
683                 }
684         }
685         else {
686                 lua_pushstring(L, "Invalid world index");
687         }
688         return 1;
689 }
690
691 /******************************************************************************/
692 int ModApiMainMenu::l_set_topleft_text(lua_State *L)
693 {
694         GUIEngine* engine = getGuiEngine(L);
695         sanity_check(engine != NULL);
696
697         std::string text = "";
698
699         if (!lua_isnone(L,1) && !lua_isnil(L,1))
700                 text = luaL_checkstring(L, 1);
701
702         engine->setTopleftText(text);
703         return 0;
704 }
705
706 /******************************************************************************/
707 int ModApiMainMenu::l_get_mapgen_names(lua_State *L)
708 {
709         lua_newtable(L);
710
711         std::list<const char *> names;
712         EmergeManager::getMapgenNames(names);
713
714         int i = 1;
715         for (std::list<const char *>::const_iterator
716                         it = names.begin(); it != names.end(); ++it) {
717                 lua_pushstring(L, *it);
718                 lua_rawseti(L, -2, i++);
719         }
720
721         return 1;
722 }
723
724
725 /******************************************************************************/
726 int ModApiMainMenu::l_get_modpath(lua_State *L)
727 {
728         std::string modpath
729                         = fs::RemoveRelativePathComponents(porting::path_user + DIR_DELIM + "mods" + DIR_DELIM);
730         lua_pushstring(L, modpath.c_str());
731         return 1;
732 }
733
734 /******************************************************************************/
735 int ModApiMainMenu::l_get_gamepath(lua_State *L)
736 {
737         std::string gamepath
738                         = fs::RemoveRelativePathComponents(porting::path_user + DIR_DELIM + "games" + DIR_DELIM);
739         lua_pushstring(L, gamepath.c_str());
740         return 1;
741 }
742
743 /******************************************************************************/
744 int ModApiMainMenu::l_get_texturepath(lua_State *L)
745 {
746         std::string gamepath
747                         = fs::RemoveRelativePathComponents(porting::path_user + DIR_DELIM + "textures");
748         lua_pushstring(L, gamepath.c_str());
749         return 1;
750 }
751
752 int ModApiMainMenu::l_get_texturepath_share(lua_State *L)
753 {
754         std::string gamepath
755                         = fs::RemoveRelativePathComponents(porting::path_share + DIR_DELIM + "textures");
756         lua_pushstring(L, gamepath.c_str());
757         return 1;
758 }
759
760 /******************************************************************************/
761 int ModApiMainMenu::l_get_dirlist(lua_State *L)
762 {
763         const char *path        = luaL_checkstring(L, 1);
764         bool dironly            = lua_toboolean(L, 2);
765
766         std::vector<fs::DirListNode> dirlist = fs::GetDirListing(path);
767
768         unsigned int index = 1;
769         lua_newtable(L);
770         int table = lua_gettop(L);
771
772         for (unsigned int i=0;i< dirlist.size(); i++) {
773                 if ((dirlist[i].dir) || (dironly == false)) {
774                         lua_pushnumber(L,index);
775                         lua_pushstring(L,dirlist[i].name.c_str());
776                         lua_settable(L, table);
777                         index++;
778                 }
779         }
780
781         return 1;
782 }
783
784 /******************************************************************************/
785 int ModApiMainMenu::l_create_dir(lua_State *L) {
786         const char *path        = luaL_checkstring(L, 1);
787
788         if (ModApiMainMenu::isMinetestPath(path)) {
789                 lua_pushboolean(L,fs::CreateAllDirs(path));
790                 return 1;
791         }
792         lua_pushboolean(L,false);
793         return 1;
794 }
795
796 /******************************************************************************/
797 int ModApiMainMenu::l_delete_dir(lua_State *L)
798 {
799         const char *path        = luaL_checkstring(L, 1);
800
801         std::string absolute_path = fs::RemoveRelativePathComponents(path);
802
803         if (ModApiMainMenu::isMinetestPath(absolute_path)) {
804                 lua_pushboolean(L,fs::RecursiveDelete(absolute_path));
805                 return 1;
806         }
807         lua_pushboolean(L,false);
808         return 1;
809 }
810
811 /******************************************************************************/
812 int ModApiMainMenu::l_copy_dir(lua_State *L)
813 {
814         const char *source      = luaL_checkstring(L, 1);
815         const char *destination = luaL_checkstring(L, 2);
816
817         bool keep_source = true;
818
819         if ((!lua_isnone(L,3)) &&
820                         (!lua_isnil(L,3))) {
821                 keep_source = lua_toboolean(L,3);
822         }
823
824         std::string absolute_destination = fs::RemoveRelativePathComponents(destination);
825         std::string absolute_source = fs::RemoveRelativePathComponents(source);
826
827         if ((ModApiMainMenu::isMinetestPath(absolute_source)) &&
828                         (ModApiMainMenu::isMinetestPath(absolute_destination))) {
829                 bool retval = fs::CopyDir(absolute_source,absolute_destination);
830
831                 if (retval && (!keep_source)) {
832
833                         retval &= fs::RecursiveDelete(absolute_source);
834                 }
835                 lua_pushboolean(L,retval);
836                 return 1;
837         }
838         lua_pushboolean(L,false);
839         return 1;
840 }
841
842 /******************************************************************************/
843 int ModApiMainMenu::l_extract_zip(lua_State *L)
844 {
845         GUIEngine* engine = getGuiEngine(L);
846         sanity_check(engine != NULL);(engine != 0);
847
848         const char *zipfile     = luaL_checkstring(L, 1);
849         const char *destination = luaL_checkstring(L, 2);
850
851         std::string absolute_destination = fs::RemoveRelativePathComponents(destination);
852
853         if (ModApiMainMenu::isMinetestPath(absolute_destination)) {
854                 fs::CreateAllDirs(absolute_destination);
855
856                 io::IFileSystem* fs = engine->m_device->getFileSystem();
857
858                 if (!fs->addFileArchive(zipfile,true,false,io::EFAT_ZIP)) {
859                         lua_pushboolean(L,false);
860                         return 1;
861                 }
862
863                 sanity_check(fs->getFileArchiveCount() > 0);
864
865                 /**********************************************************************/
866                 /* WARNING this is not threadsafe!!                                   */
867                 /**********************************************************************/
868                 io::IFileArchive* opened_zip =
869                         fs->getFileArchive(fs->getFileArchiveCount()-1);
870
871                 const io::IFileList* files_in_zip = opened_zip->getFileList();
872
873                 unsigned int number_of_files = files_in_zip->getFileCount();
874
875                 for (unsigned int i=0; i < number_of_files; i++) {
876                         std::string fullpath = destination;
877                         fullpath += DIR_DELIM;
878                         fullpath += files_in_zip->getFullFileName(i).c_str();
879                         std::string fullpath_dir = fs::RemoveLastPathComponent(fullpath);
880
881                         if (!files_in_zip->isDirectory(i)) {
882                                 if (!fs::PathExists(fullpath_dir) && !fs::CreateAllDirs(fullpath_dir)) {
883                                         fs->removeFileArchive(fs->getFileArchiveCount()-1);
884                                         lua_pushboolean(L,false);
885                                         return 1;
886                                 }
887
888                                 io::IReadFile* toread = opened_zip->createAndOpenFile(i);
889
890                                 FILE *targetfile = fopen(fullpath.c_str(),"wb");
891
892                                 if (targetfile == NULL) {
893                                         fs->removeFileArchive(fs->getFileArchiveCount()-1);
894                                         lua_pushboolean(L,false);
895                                         return 1;
896                                 }
897
898                                 char read_buffer[1024];
899                                 long total_read = 0;
900
901                                 while (total_read < toread->getSize()) {
902
903                                         unsigned int bytes_read =
904                                                         toread->read(read_buffer,sizeof(read_buffer));
905                                         if ((bytes_read == 0 ) ||
906                                                 (fwrite(read_buffer, 1, bytes_read, targetfile) != bytes_read))
907                                         {
908                                                 fclose(targetfile);
909                                                 fs->removeFileArchive(fs->getFileArchiveCount()-1);
910                                                 lua_pushboolean(L,false);
911                                                 return 1;
912                                         }
913                                         total_read += bytes_read;
914                                 }
915
916                                 fclose(targetfile);
917                         }
918
919                 }
920
921                 fs->removeFileArchive(fs->getFileArchiveCount()-1);
922                 lua_pushboolean(L,true);
923                 return 1;
924         }
925
926         lua_pushboolean(L,false);
927         return 1;
928 }
929
930 /******************************************************************************/
931 int ModApiMainMenu::l_get_mainmenu_path(lua_State *L)
932 {
933         GUIEngine* engine = getGuiEngine(L);
934         sanity_check(engine != NULL);
935
936         lua_pushstring(L,engine->getScriptDir().c_str());
937         return 1;
938 }
939
940 /******************************************************************************/
941 bool ModApiMainMenu::isMinetestPath(std::string path)
942 {
943         if (fs::PathStartsWith(path,fs::TempPath()))
944                 return true;
945
946         /* games */
947         if (fs::PathStartsWith(path,fs::RemoveRelativePathComponents(porting::path_share + DIR_DELIM + "games")))
948                 return true;
949
950         /* mods */
951         if (fs::PathStartsWith(path,fs::RemoveRelativePathComponents(porting::path_user + DIR_DELIM + "mods")))
952                 return true;
953
954         /* worlds */
955         if (fs::PathStartsWith(path,fs::RemoveRelativePathComponents(porting::path_user + DIR_DELIM + "worlds")))
956                 return true;
957
958
959         return false;
960 }
961
962 /******************************************************************************/
963 int ModApiMainMenu::l_show_file_open_dialog(lua_State *L)
964 {
965         GUIEngine* engine = getGuiEngine(L);
966         sanity_check(engine != NULL);
967
968         const char *formname= luaL_checkstring(L, 1);
969         const char *title       = luaL_checkstring(L, 2);
970
971         GUIFileSelectMenu* fileOpenMenu =
972                 new GUIFileSelectMenu(engine->m_device->getGUIEnvironment(),
973                                                                 engine->m_parent,
974                                                                 -1,
975                                                                 engine->m_menumanager,
976                                                                 title,
977                                                                 formname);
978         fileOpenMenu->setTextDest(engine->m_buttonhandler);
979         fileOpenMenu->drop();
980         return 0;
981 }
982
983 /******************************************************************************/
984 int ModApiMainMenu::l_get_version(lua_State *L)
985 {
986         lua_pushstring(L, minetest_version_simple);
987         return 1;
988 }
989
990 /******************************************************************************/
991 int ModApiMainMenu::l_sound_play(lua_State *L)
992 {
993         GUIEngine* engine = getGuiEngine(L);
994
995         SimpleSoundSpec spec;
996         read_soundspec(L, 1, spec);
997         bool looped = lua_toboolean(L, 2);
998
999         u32 handle = engine->playSound(spec, looped);
1000
1001         lua_pushinteger(L, handle);
1002
1003         return 1;
1004 }
1005
1006 /******************************************************************************/
1007 int ModApiMainMenu::l_sound_stop(lua_State *L)
1008 {
1009         GUIEngine* engine = getGuiEngine(L);
1010
1011         u32 handle = luaL_checkinteger(L, 1);
1012         engine->stopSound(handle);
1013
1014         return 1;
1015 }
1016
1017 /******************************************************************************/
1018 int ModApiMainMenu::l_download_file(lua_State *L)
1019 {
1020         const char *url    = luaL_checkstring(L, 1);
1021         const char *target = luaL_checkstring(L, 2);
1022
1023         //check path
1024         std::string absolute_destination = fs::RemoveRelativePathComponents(target);
1025
1026         if (ModApiMainMenu::isMinetestPath(absolute_destination)) {
1027                 if (GUIEngine::downloadFile(url,absolute_destination)) {
1028                         lua_pushboolean(L,true);
1029                         return 1;
1030                 }
1031         } else {
1032                 errorstream << "DOWNLOAD denied: " << absolute_destination
1033                                 << " isn't a allowed path" << std::endl;
1034         }
1035         lua_pushboolean(L,false);
1036         return 1;
1037 }
1038
1039 /******************************************************************************/
1040 int ModApiMainMenu::l_get_video_drivers(lua_State *L)
1041 {
1042         std::vector<irr::video::E_DRIVER_TYPE> drivers
1043                 = porting::getSupportedVideoDrivers();
1044
1045         lua_newtable(L);
1046         for (u32 i = 0; i != drivers.size(); i++) {
1047                 const char *name  = porting::getVideoDriverName(drivers[i]);
1048                 const char *fname = porting::getVideoDriverFriendlyName(drivers[i]);
1049
1050                 lua_newtable(L);
1051                 lua_pushstring(L, name);
1052                 lua_setfield(L, -2, "name");
1053                 lua_pushstring(L, fname);
1054                 lua_setfield(L, -2, "friendly_name");
1055
1056                 lua_rawseti(L, -2, i + 1);
1057         }
1058
1059         return 1;
1060 }
1061
1062 /******************************************************************************/
1063 int ModApiMainMenu::l_gettext(lua_State *L)
1064 {
1065         std::wstring wtext = wstrgettext((std::string) luaL_checkstring(L, 1));
1066         lua_pushstring(L, wide_to_narrow(wtext).c_str());
1067
1068         return 1;
1069 }
1070
1071 /******************************************************************************/
1072 int ModApiMainMenu::l_get_screen_info(lua_State *L)
1073 {
1074         lua_newtable(L);
1075         int top = lua_gettop(L);
1076         lua_pushstring(L,"density");
1077         lua_pushnumber(L,porting::getDisplayDensity());
1078         lua_settable(L, top);
1079
1080         lua_pushstring(L,"display_width");
1081         lua_pushnumber(L,porting::getDisplaySize().X);
1082         lua_settable(L, top);
1083
1084         lua_pushstring(L,"display_height");
1085         lua_pushnumber(L,porting::getDisplaySize().Y);
1086         lua_settable(L, top);
1087
1088         lua_pushstring(L,"window_width");
1089         lua_pushnumber(L,porting::getWindowSize().X);
1090         lua_settable(L, top);
1091
1092         lua_pushstring(L,"window_height");
1093         lua_pushnumber(L,porting::getWindowSize().Y);
1094         lua_settable(L, top);
1095         return 1;
1096 }
1097
1098 /******************************************************************************/
1099 int ModApiMainMenu::l_get_min_supp_proto(lua_State *L)
1100 {
1101         lua_pushinteger(L, CLIENT_PROTOCOL_VERSION_MIN);
1102         return 1;
1103 }
1104
1105 int ModApiMainMenu::l_get_max_supp_proto(lua_State *L)
1106 {
1107         lua_pushinteger(L, CLIENT_PROTOCOL_VERSION_MAX);
1108         return 1;
1109 }
1110
1111 /******************************************************************************/
1112 int ModApiMainMenu::l_do_async_callback(lua_State *L)
1113 {
1114         GUIEngine* engine = getGuiEngine(L);
1115
1116         size_t func_length, param_length;
1117         const char* serialized_func_raw = luaL_checklstring(L, 1, &func_length);
1118
1119         const char* serialized_param_raw = luaL_checklstring(L, 2, &param_length);
1120
1121         sanity_check(serialized_func_raw != NULL);
1122         sanity_check(serialized_param_raw != NULL);
1123
1124         std::string serialized_func = std::string(serialized_func_raw, func_length);
1125         std::string serialized_param = std::string(serialized_param_raw, param_length);
1126
1127         lua_pushinteger(L, engine->queueAsync(serialized_func, serialized_param));
1128
1129         return 1;
1130 }
1131
1132 /******************************************************************************/
1133 void ModApiMainMenu::Initialize(lua_State *L, int top)
1134 {
1135         API_FCT(update_formspec);
1136         API_FCT(set_clouds);
1137         API_FCT(get_textlist_index);
1138         API_FCT(get_table_index);
1139         API_FCT(get_worlds);
1140         API_FCT(get_games);
1141         API_FCT(start);
1142         API_FCT(close);
1143         API_FCT(get_favorites);
1144         API_FCT(show_keys_menu);
1145         API_FCT(create_world);
1146         API_FCT(delete_world);
1147         API_FCT(delete_favorite);
1148         API_FCT(set_background);
1149         API_FCT(set_topleft_text);
1150         API_FCT(get_mapgen_names);
1151         API_FCT(get_modpath);
1152         API_FCT(get_gamepath);
1153         API_FCT(get_texturepath);
1154         API_FCT(get_texturepath_share);
1155         API_FCT(get_dirlist);
1156         API_FCT(create_dir);
1157         API_FCT(delete_dir);
1158         API_FCT(copy_dir);
1159         API_FCT(extract_zip);
1160         API_FCT(get_mainmenu_path);
1161         API_FCT(show_file_open_dialog);
1162         API_FCT(get_version);
1163         API_FCT(download_file);
1164         API_FCT(get_modstore_details);
1165         API_FCT(get_modstore_list);
1166         API_FCT(sound_play);
1167         API_FCT(sound_stop);
1168         API_FCT(gettext);
1169         API_FCT(get_video_drivers);
1170         API_FCT(get_screen_info);
1171         API_FCT(get_min_supp_proto);
1172         API_FCT(get_max_supp_proto);
1173         API_FCT(do_async_callback);
1174 }
1175
1176 /******************************************************************************/
1177 void ModApiMainMenu::InitializeAsync(AsyncEngine& engine)
1178 {
1179
1180         ASYNC_API_FCT(get_worlds);
1181         ASYNC_API_FCT(get_games);
1182         ASYNC_API_FCT(get_favorites);
1183         ASYNC_API_FCT(get_mapgen_names);
1184         ASYNC_API_FCT(get_modpath);
1185         ASYNC_API_FCT(get_gamepath);
1186         ASYNC_API_FCT(get_texturepath);
1187         ASYNC_API_FCT(get_texturepath_share);
1188         ASYNC_API_FCT(get_dirlist);
1189         ASYNC_API_FCT(create_dir);
1190         ASYNC_API_FCT(delete_dir);
1191         ASYNC_API_FCT(copy_dir);
1192         //ASYNC_API_FCT(extract_zip); //TODO remove dependency to GuiEngine
1193         ASYNC_API_FCT(get_version);
1194         ASYNC_API_FCT(download_file);
1195         ASYNC_API_FCT(get_modstore_details);
1196         ASYNC_API_FCT(get_modstore_list);
1197         //ASYNC_API_FCT(gettext); (gettext lib isn't threadsafe)
1198 }