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