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