2 from . import MinetestCheckError, ContentType
3 from .config import parse_conf
5 def get_base_dir(path):
6 if not os.path.isdir(path):
7 raise IOError("Expected dir")
9 root, subdirs, files = next(os.walk(path))
10 if len(subdirs) == 1 and len(files) == 0:
11 return get_base_dir(path + "/" + subdirs[0])
16 def detect_type(path):
17 if os.path.isfile(path + "/game.conf"):
18 return ContentType.GAME
19 elif os.path.isfile(path + "/init.lua"):
20 return ContentType.MOD
21 elif os.path.isfile(path + "/modpack.txt") or \
22 os.path.isfile(path + "/modpack.conf"):
23 return ContentType.MODPACK
24 elif os.path.isdir(path + "/mods"):
25 return ContentType.GAME
26 elif os.path.isfile(path + "/texture_pack.conf"):
27 return ContentType.TXP
29 return ContentType.UNKNOWN
32 class PackageTreeNode:
33 def __init__(self, baseDir, relative, author=None, repo=None, name=None):
34 self.baseDir = baseDir
35 self.relative = relative
43 self.type = detect_type(baseDir)
46 if self.type == ContentType.GAME:
47 if not os.path.isdir(baseDir + "/mods"):
48 raise MinetestCheckError(("game at {} does not have a mods/ folder").format(self.relative))
49 self.add_children_from_mod_dir(baseDir + "/mods")
50 elif self.type == ContentType.MODPACK:
51 self.add_children_from_mod_dir(baseDir)
53 def getMetaFilePath(self):
55 if self.type == ContentType.GAME:
56 filename = "game.conf"
57 elif self.type == ContentType.MOD:
59 elif self.type == ContentType.MODPACK:
60 filename = "modpack.conf"
61 elif self.type == ContentType.TXP:
62 filename = "texture_pack.conf"
66 return self.baseDir + "/" + filename
74 with open(self.getMetaFilePath() or "", "r") as myfile:
75 conf = parse_conf(myfile.read())
76 for key, value in conf.items():
82 if not "description" in result:
84 with open(self.baseDir + "/description.txt", "r") as myfile:
85 result["description"] = myfile.read()
91 pattern = re.compile("^([a-z0-9_]+)\??$")
92 if not "depends" in result and not "optional_depends" in result:
94 with open(self.baseDir + "/depends.txt", "r") as myfile:
95 contents = myfile.read()
98 for line in contents.split("\n"):
100 if pattern.match(line):
101 if line[len(line) - 1] == "?":
102 soft.append( line[:-1])
106 result["depends"] = hard
107 result["optional_depends"] = soft
113 if "depends" in result:
114 result["depends"] = [x.strip() for x in result["depends"].split(",")]
115 if "optional_depends" in result:
116 result["optional_depends"] = [x.strip() for x in result["optional_depends"].split(",")]
118 # Fix games using "name" as "title"
119 if self.type == ContentType.GAME:
120 result["title"] = result["name"]
124 if "name" in result and not "title" in result:
125 result["title"] = result["name"].replace("_", " ").title()
127 # Calculate short description
128 if "description" in result:
129 desc = result["description"]
130 idx = desc.find(".") + 1
131 cutIdx = min(len(desc), 200 if idx < 5 else idx)
132 result["short_description"] = desc[:cutIdx]
135 self.name = result["name"]
140 def add_children_from_mod_dir(self, dir):
141 for entry in next(os.walk(dir))[1]:
142 path = os.path.join(dir, entry)
143 if not entry.startswith('.') and os.path.isdir(path):
144 child = PackageTreeNode(path, self.relative + entry + "/", name=entry)
145 if not child.type.isModLike():
146 raise MinetestCheckError(("Expecting mod or modpack, found {} at {} inside {}") \
147 .format(child.type.value, child.relative, self.type.value))
149 self.children.append(child)
151 def getModNames(self):
152 return self.fold("name", type=ContentType.MOD)
154 # attr: Attribute name
155 # key: Key in attribute
156 # retval: Accumulator
157 # type: Filter to type
158 def fold(self, attr, key=None, retval=None, type=None):
162 # Iterate through children
163 for child in self.children:
164 child.fold(attr, key, retval, type)
167 if type and type != self.type:
171 at = getattr(self, attr)
176 value = at if key is None else at.get(key)
177 if isinstance(value, list):
185 return self.meta.get(key)
188 for child in self.children: