]> git.lizzy.rs Git - dragonfireclient.git/blob - builtin/mainmenu/modmgr.lua
Support for scalable font and gui elements
[dragonfireclient.git] / builtin / mainmenu / modmgr.lua
1 --Minetest
2 --Copyright (C) 2013 sapier
3 --
4 --This program is free software; you can redistribute it and/or modify
5 --it under the terms of the GNU Lesser General Public License as published by
6 --the Free Software Foundation; either version 2.1 of the License, or
7 --(at your option) any later version.
8 --
9 --This program is distributed in the hope that it will be useful,
10 --but WITHOUT ANY WARRANTY; without even the implied warranty of
11 --MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12 --GNU Lesser General Public License for more details.
13 --
14 --You should have received a copy of the GNU Lesser General Public License along
15 --with this program; if not, write to the Free Software Foundation, Inc.,
16 --51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
17
18 --------------------------------------------------------------------------------
19 function get_mods(path,retval,modpack)
20
21         local mods = core.get_dirlist(path,true)
22         for i=1,#mods,1 do
23                 local toadd = {}
24                 local modpackfile = nil
25
26                 toadd.name = mods[i]
27                 toadd.path = path .. DIR_DELIM .. mods[i] .. DIR_DELIM
28                 if modpack ~= nil and
29                         modpack ~= "" then
30                         toadd.modpack = modpack
31                 else
32                         local filename = path .. DIR_DELIM .. mods[i] .. DIR_DELIM .. "modpack.txt"
33                         local error = nil
34                         modpackfile,error = io.open(filename,"r")
35                 end
36
37                 if modpackfile ~= nil then
38                         modpackfile:close()
39                         toadd.is_modpack = true
40                         table.insert(retval,toadd)
41                         get_mods(path .. DIR_DELIM .. mods[i],retval,mods[i])
42                 else
43                         table.insert(retval,toadd)
44                 end
45         end
46 end
47
48 --modmanager implementation
49 modmgr = {}
50
51 --------------------------------------------------------------------------------
52 function modmgr.extract(modfile)
53         if modfile.type == "zip" then
54                 local tempfolder = os.tempfolder()
55
56                 if tempfolder ~= nil and
57                         tempfolder ~= "" then
58                         core.create_dir(tempfolder)
59                         if core.extract_zip(modfile.name,tempfolder) then
60                                 return tempfolder
61                         end
62                 end
63         end
64         return nil
65 end
66
67 -------------------------------------------------------------------------------
68 function modmgr.getbasefolder(temppath)
69
70         if temppath == nil then
71                 return {
72                 type = "invalid",
73                 path = ""
74                 }
75         end
76
77         local testfile = io.open(temppath .. DIR_DELIM .. "init.lua","r")
78         if testfile ~= nil then
79                 testfile:close()
80                 return {
81                                 type="mod",
82                                 path=temppath
83                                 }
84         end
85
86         testfile = io.open(temppath .. DIR_DELIM .. "modpack.txt","r")
87         if testfile ~= nil then
88                 testfile:close()
89                 return {
90                                 type="modpack",
91                                 path=temppath
92                                 }
93         end
94
95         local subdirs = core.get_dirlist(temppath,true)
96
97         --only single mod or modpack allowed
98         if #subdirs ~= 1 then
99                 return {
100                         type = "invalid",
101                         path = ""
102                         }
103         end
104
105         testfile =
106         io.open(temppath .. DIR_DELIM .. subdirs[1] ..DIR_DELIM .."init.lua","r")
107         if testfile ~= nil then
108                 testfile:close()
109                 return {
110                         type="mod",
111                         path= temppath .. DIR_DELIM .. subdirs[1]
112                         }
113         end
114
115         testfile =
116         io.open(temppath .. DIR_DELIM .. subdirs[1] ..DIR_DELIM .."modpack.txt","r")
117         if testfile ~= nil then
118                 testfile:close()
119                 return {
120                         type="modpack",
121                         path=temppath ..  DIR_DELIM .. subdirs[1]
122                         }
123         end
124
125         return {
126                 type = "invalid",
127                 path = ""
128                 }
129 end
130
131 --------------------------------------------------------------------------------
132 function modmgr.isValidModname(modpath)
133         if modpath:find("-") ~= nil then
134                 return false
135         end
136
137         return true
138 end
139
140 --------------------------------------------------------------------------------
141 function modmgr.parse_register_line(line)
142         local pos1 = line:find("\"")
143         local pos2 = nil
144         if pos1 ~= nil then
145                 pos2 = line:find("\"",pos1+1)
146         end
147
148         if pos1 ~= nil and pos2 ~= nil then
149                 local item = line:sub(pos1+1,pos2-1)
150
151                 if item ~= nil and
152                         item ~= "" then
153                         local pos3 = item:find(":")
154
155                         if pos3 ~= nil then
156                                 local retval = item:sub(1,pos3-1)
157                                 if retval ~= nil and
158                                         retval ~= "" then
159                                         return retval
160                                 end
161                         end
162                 end
163         end
164         return nil
165 end
166
167 --------------------------------------------------------------------------------
168 function modmgr.parse_dofile_line(modpath,line)
169         local pos1 = line:find("\"")
170         local pos2 = nil
171         if pos1 ~= nil then
172                 pos2 = line:find("\"",pos1+1)
173         end
174
175         if pos1 ~= nil and pos2 ~= nil then
176                 local filename = line:sub(pos1+1,pos2-1)
177
178                 if filename ~= nil and
179                         filename ~= "" and
180                         filename:find(".lua") then
181                         return modmgr.identify_modname(modpath,filename)
182                 end
183         end
184         return nil
185 end
186
187 --------------------------------------------------------------------------------
188 function modmgr.identify_modname(modpath,filename)
189         local testfile = io.open(modpath .. DIR_DELIM .. filename,"r")
190         if testfile ~= nil then
191                 local line = testfile:read()
192
193                 while line~= nil do
194                         local modname = nil
195
196                         if line:find("minetest.register_tool") then
197                                 modname = modmgr.parse_register_line(line)
198                         end
199
200                         if line:find("minetest.register_craftitem") then
201                                 modname = modmgr.parse_register_line(line)
202                         end
203
204
205                         if line:find("minetest.register_node") then
206                                 modname = modmgr.parse_register_line(line)
207                         end
208
209                         if line:find("dofile") then
210                                 modname = modmgr.parse_dofile_line(modpath,line)
211                         end
212
213                         if modname ~= nil then
214                                 testfile:close()
215                                 return modname
216                         end
217
218                         line = testfile:read()
219                 end
220                 testfile:close()
221         end
222
223         return nil
224 end
225 --------------------------------------------------------------------------------
226 function modmgr.render_modlist(render_list)
227         local retval = ""
228
229         if render_list == nil then
230                 if modmgr.global_mods == nil then
231                         modmgr.refresh_globals()
232                 end
233                 render_list = modmgr.global_mods
234         end
235
236         local list = render_list:get_list()
237         local last_modpack = nil
238
239         for i,v in ipairs(list) do
240                 if retval ~= "" then
241                         retval = retval ..","
242                 end
243
244                 local color = ""
245
246                 if v.is_modpack then
247                         local rawlist = render_list:get_raw_list()
248
249                         local all_enabled = true
250                         for j=1,#rawlist,1 do
251                                 if rawlist[j].modpack == list[i].name and
252                                         rawlist[j].enabled ~= true then
253                                                 all_enabled = false
254                                                 break
255                                 end
256                         end
257
258                         if all_enabled == false then
259                                 color = mt_color_grey
260                         else
261                                 color = mt_color_dark_green
262                         end
263                 end
264
265                 if v.typ == "game_mod" then
266                         color = mt_color_blue
267                 else
268                         if v.enabled then
269                                 color = mt_color_green
270                         end
271                 end
272
273                 retval = retval .. color
274                 if v.modpack  ~= nil then
275                         retval = retval .. "    "
276                 end
277                 retval = retval .. v.name
278         end
279
280         return retval
281 end
282
283 --------------------------------------------------------------------------------
284 function modmgr.get_dependencies(modfolder)
285         local toadd = ""
286         if modfolder ~= nil then
287                 local filename = modfolder ..
288                                         DIR_DELIM .. "depends.txt"
289
290                 local dependencyfile = io.open(filename,"r")
291
292                 if dependencyfile then
293                         local dependency = dependencyfile:read("*l")
294                         while dependency do
295                                 if toadd ~= "" then
296                                         toadd = toadd .. ","
297                                 end
298                                 toadd = toadd .. dependency
299                                 dependency = dependencyfile:read()
300                         end
301                         dependencyfile:close()
302                 end
303         end
304
305         return toadd
306 end
307
308 --------------------------------------------------------------------------------
309 function modmgr.get_worldconfig(worldpath)
310         local filename = worldpath ..
311                                 DIR_DELIM .. "world.mt"
312
313         local worldfile = Settings(filename)
314
315         local worldconfig = {}
316         worldconfig.global_mods = {}
317         worldconfig.game_mods = {}
318
319         for key,value in pairs(worldfile:to_table()) do
320                 if key == "gameid" then
321                         worldconfig.id = value
322                 else
323                         worldconfig.global_mods[key] = core.is_yes(value)
324                 end
325         end
326
327         --read gamemods
328         local gamespec = gamemgr.find_by_gameid(worldconfig.id)
329         gamemgr.get_game_mods(gamespec, worldconfig.game_mods)
330
331         return worldconfig
332 end
333
334 --------------------------------------------------------------------------------
335 function modmgr.installmod(modfilename,basename)
336         local modfile = modmgr.identify_filetype(modfilename)
337         local modpath = modmgr.extract(modfile)
338
339         if modpath == nil then
340                 gamedata.errormessage = fgettext("Install Mod: file: \"$1\"", modfile.name) ..
341                         fgettext("\nInstall Mod: unsupported filetype \"$1\" or broken archive", modfile.type)
342                 return
343         end
344
345         local basefolder = modmgr.getbasefolder(modpath)
346
347         if basefolder.type == "modpack" then
348                 local clean_path = nil
349
350                 if basename ~= nil then
351                         clean_path = "mp_" .. basename
352                 end
353
354                 if clean_path == nil then
355                         clean_path = get_last_folder(cleanup_path(basefolder.path))
356                 end
357
358                 if clean_path ~= nil then
359                         local targetpath = core.get_modpath() .. DIR_DELIM .. clean_path
360                         if not core.copy_dir(basefolder.path,targetpath) then
361                                 gamedata.errormessage = fgettext("Failed to install $1 to $2", basename, targetpath)
362                         end
363                 else
364                         gamedata.errormessage = fgettext("Install Mod: unable to find suitable foldername for modpack $1", modfilename)
365                 end
366         end
367
368         if basefolder.type == "mod" then
369                 local targetfolder = basename
370
371                 if targetfolder == nil then
372                         targetfolder = modmgr.identify_modname(basefolder.path,"init.lua")
373                 end
374
375                 --if heuristic failed try to use current foldername
376                 if targetfolder == nil then
377                         targetfolder = get_last_folder(basefolder.path)
378                 end
379
380                 if targetfolder ~= nil and modmgr.isValidModname(targetfolder) then
381                         local targetpath = core.get_modpath() .. DIR_DELIM .. targetfolder
382                         core.copy_dir(basefolder.path,targetpath)
383                 else
384                         gamedata.errormessage = fgettext("Install Mod: unable to find real modname for: $1", modfilename)
385                 end
386         end
387
388         core.delete_dir(modpath)
389
390         modmgr.refresh_globals()
391
392 end
393
394 --------------------------------------------------------------------------------
395 function modmgr.preparemodlist(data)
396         local retval = {}
397
398         local global_mods = {}
399         local game_mods = {}
400
401         --read global mods
402         local modpath = core.get_modpath()
403
404         if modpath ~= nil and
405                 modpath ~= "" then
406                 get_mods(modpath,global_mods)
407         end
408
409         for i=1,#global_mods,1 do
410                 global_mods[i].typ = "global_mod"
411                 table.insert(retval,global_mods[i])
412         end
413
414         --read game mods
415         local gamespec = gamemgr.find_by_gameid(data.gameid)
416         gamemgr.get_game_mods(gamespec, game_mods)
417
418         for i=1,#game_mods,1 do
419                 game_mods[i].typ = "game_mod"
420                 table.insert(retval,game_mods[i])
421         end
422
423         if data.worldpath == nil then
424                 return retval
425         end
426
427         --read world mod configuration
428         local filename = data.worldpath ..
429                                 DIR_DELIM .. "world.mt"
430
431         local worldfile = Settings(filename)
432
433         for key,value in pairs(worldfile:to_table()) do
434                 if key:sub(1, 9) == "load_mod_" then
435                         key = key:sub(10)
436                         local element = nil
437                         for i=1,#retval,1 do
438                                 if retval[i].name == key and
439                                         not retval[i].is_modpack then
440                                         element = retval[i]
441                                         break
442                                 end
443                         end
444                         if element ~= nil then
445                                 element.enabled = core.is_yes(value)
446                         else
447                                 core.log("info", "Mod: " .. key .. " " .. dump(value) .. " but not found")
448                         end
449                 end
450         end
451
452         return retval
453 end
454
455 --------------------------------------------------------------------------------
456 function modmgr.comparemod(elem1,elem2)
457         if elem1 == nil or elem2 == nil then
458                 return false
459         end
460         if elem1.name ~= elem2.name then
461                 return false
462         end
463         if elem1.is_modpack ~= elem2.is_modpack then
464                 return false
465         end
466         if elem1.typ ~= elem2.typ then
467                 return false
468         end
469         if elem1.modpack ~= elem2.modpack then
470                 return false
471         end
472
473         if elem1.path ~= elem2.path then
474                 return false
475         end
476
477         return true
478 end
479
480 --------------------------------------------------------------------------------
481 function modmgr.mod_exists(basename)
482
483         if modmgr.global_mods == nil then
484                 modmgr.refresh_globals()
485         end
486
487         if modmgr.global_mods:raw_index_by_uid(basename) > 0 then
488                 return true
489         end
490
491         return false
492 end
493
494 --------------------------------------------------------------------------------
495 function modmgr.get_global_mod(idx)
496
497         if modmgr.global_mods == nil then
498                 return nil
499         end
500
501         if idx == nil or idx < 1 or
502                 idx > modmgr.global_mods:size() then
503                 return nil
504         end
505
506         return modmgr.global_mods:get_list()[idx]
507 end
508
509 --------------------------------------------------------------------------------
510 function modmgr.refresh_globals()
511         modmgr.global_mods = filterlist.create(
512                                         modmgr.preparemodlist, --refresh
513                                         modmgr.comparemod, --compare
514                                         function(element,uid) --uid match
515                                                 if element.name == uid then
516                                                         return true
517                                                 end
518                                         end,
519                                         nil, --filter
520                                         {}
521                                         )
522         modmgr.global_mods:add_sort_mechanism("alphabetic", sort_mod_list)
523         modmgr.global_mods:set_sortmode("alphabetic")
524 end
525
526 --------------------------------------------------------------------------------
527 function modmgr.identify_filetype(name)
528
529         if name:sub(-3):lower() == "zip" then
530                 return {
531                                 name = name,
532                                 type = "zip"
533                                 }
534         end
535
536         if name:sub(-6):lower() == "tar.gz" or
537                 name:sub(-3):lower() == "tgz"then
538                 return {
539                                 name = name,
540                                 type = "tgz"
541                                 }
542         end
543
544         if name:sub(-6):lower() == "tar.bz2" then
545                 return {
546                                 name = name,
547                                 type = "tbz"
548                                 }
549         end
550
551         if name:sub(-2):lower() == "7z" then
552                 return {
553                                 name = name,
554                                 type = "7z"
555                                 }
556         end
557
558         return {
559                 name = name,
560                 type = "ukn"
561         }
562 end