A corporation manager and dashboard for EVE Online
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

354 lines
9.8 KiB

  1. #!/usr/bin/env python3
  2. # -*- coding: utf-8 -*-
  3. """
  4. Import an EVE Online Static Data Export dump to Calefaction.
  5. """
  6. import gzip
  7. from pathlib import Path
  8. import shutil
  9. import sys
  10. import yaml
  11. _SHIP_CAT = 6
  12. _FIGHTER_CAT = 87
  13. _STRUCT_CATS = [22, 23, 40, 46, 65]
  14. _REGION = 3
  15. _CONSTELLATION = 4
  16. _SOLAR_SYSTEM = 5
  17. def _load_yaml(filename):
  18. with filename.open("rb") as fp:
  19. return yaml.load(fp, Loader=yaml.CLoader)
  20. def _save_yaml(filename, data):
  21. with filename.open("w") as fp:
  22. fp.write(yaml.dump(data, Dumper=yaml.CDumper))
  23. def _verify_categoryids(sde_dir):
  24. print("Verifying categoryIDs... ", end="", flush=True)
  25. data = _load_yaml(sde_dir / "fsd" / "categoryIDs.yaml")
  26. assert data[_SHIP_CAT]["name"]["en"] == "Ship"
  27. print("done.")
  28. def _load_groupids(sde_dir):
  29. print("Loading groupIDs... ", end="", flush=True)
  30. data = _load_yaml(sde_dir / "fsd" / "groupIDs.yaml")
  31. groups = {cid: {} for cid in [_SHIP_CAT, _FIGHTER_CAT] + _STRUCT_CATS}
  32. for gid, group in data.items():
  33. cat = group["categoryID"]
  34. if cat in groups:
  35. name = group["name"]["en"]
  36. assert isinstance(gid, int)
  37. assert isinstance(name, str)
  38. groups[cat][gid] = name
  39. print("done.")
  40. return groups
  41. def _load_typeids(sde_dir, groups):
  42. print("Loading typeIDs... ", end="", flush=True)
  43. data = _load_yaml(sde_dir / "fsd" / "typeIDs.yaml")
  44. assert data[_REGION]["groupID"] == _REGION
  45. assert data[_REGION]["name"]["en"] == "Region"
  46. assert data[_CONSTELLATION]["groupID"] == _CONSTELLATION
  47. assert data[_CONSTELLATION]["name"]["en"] == "Constellation"
  48. assert data[_SOLAR_SYSTEM]["groupID"] == _SOLAR_SYSTEM
  49. assert data[_SOLAR_SYSTEM]["name"]["en"] == "Solar System"
  50. types = {}
  51. killables = {"ships": {}, "structures": {}, "fighters": {}}
  52. cat_conv = {_SHIP_CAT: "ships", _FIGHTER_CAT: "fighters"}
  53. cat_conv.update({cid: "structures" for cid in _STRUCT_CATS})
  54. group_conv = {gid: cid for cid, gids in groups.items() for gid in gids}
  55. for tid, type_ in data.items():
  56. name = type_["name"].get("en", "Unknown")
  57. gid = type_["groupID"]
  58. assert isinstance(tid, int)
  59. assert isinstance(gid, int)
  60. assert tid >= 0
  61. assert gid >= 0
  62. types[tid] = {"name": name, "group_id": gid}
  63. if "marketGroupID" in type_:
  64. mgid = type_["marketGroupID"]
  65. assert isinstance(mgid, int)
  66. assert mgid >= 0
  67. types[tid]["market_group_id"] = mgid
  68. if gid in group_conv:
  69. cid = group_conv[gid]
  70. cname = cat_conv[cid]
  71. group = groups[cid][gid]
  72. killables[cname][tid] = {"name": name, "group": group}
  73. print("done.")
  74. return types, killables
  75. def _load_ids(sde_dir):
  76. print("Loading itemIDs... ", end="", flush=True)
  77. data = _load_yaml(sde_dir / "bsd" / "invItems.yaml")
  78. ids = {_REGION: [], _CONSTELLATION: [], _SOLAR_SYSTEM: []}
  79. for entry in data:
  80. if entry["typeID"] in ids:
  81. ids[entry["typeID"]].append(entry["itemID"])
  82. print("done.")
  83. return ids
  84. def _load_names(sde_dir):
  85. print("Loading itemNames... ", end="", flush=True)
  86. data = _load_yaml(sde_dir / "bsd" / "invNames.yaml")
  87. names = {}
  88. for entry in data:
  89. name = entry["itemName"]
  90. assert isinstance(name, str)
  91. names[entry["itemID"]] = name
  92. print("done.")
  93. return names
  94. def _build_galaxy_skeleton(ids, names):
  95. print("Building galaxy skeleton... ", end="", flush=True)
  96. galaxy = {"regions": {}, "constellations": {}, "systems": {}}
  97. d = galaxy["regions"]
  98. for rid in ids[_REGION]:
  99. assert isinstance(rid, int)
  100. d[rid] = {
  101. "name": names[rid]
  102. }
  103. d = galaxy["constellations"]
  104. for cid in ids[_CONSTELLATION]:
  105. assert isinstance(cid, int)
  106. d[cid] = {
  107. "name": names[cid],
  108. "region": -1
  109. }
  110. d = galaxy["systems"]
  111. for sid in ids[_SOLAR_SYSTEM]:
  112. assert isinstance(sid, int)
  113. d[sid] = {
  114. "name": names[sid],
  115. "constellation": -1,
  116. "region": -1,
  117. "security": 0.0
  118. }
  119. print("done.")
  120. return galaxy
  121. def _load_assoc_for_system(galaxy, system, rid, cid):
  122. data = _load_yaml(system / "solarsystem.staticdata")
  123. sid = data["solarSystemID"]
  124. sec = data["security"]
  125. coords = data["center"]
  126. assert isinstance(sid, int)
  127. assert isinstance(sec, float)
  128. assert sid >= 0
  129. assert sec >= -1.0 and sec <= 1.0
  130. assert len(coords) == 3 and all(isinstance(val, float) for val in coords)
  131. galaxy["systems"][sid]["constellation"] = cid
  132. galaxy["systems"][sid]["region"] = rid
  133. galaxy["systems"][sid]["security"] = sec
  134. galaxy["systems"][sid]["coords"] = coords
  135. if "factionID" in data:
  136. facid = data["factionID"]
  137. assert isinstance(facid, int)
  138. assert facid >= 0
  139. galaxy["systems"][sid]["faction"] = facid
  140. def _load_assoc_for_constellation(galaxy, constellation, rid):
  141. data = _load_yaml(constellation / "constellation.staticdata")
  142. cid = data["constellationID"]
  143. assert isinstance(cid, int)
  144. assert cid >= 0
  145. galaxy["constellations"][cid]["region"] = rid
  146. if "factionID" in data:
  147. facid = data["factionID"]
  148. assert isinstance(facid, int)
  149. assert facid >= 0
  150. galaxy["constellations"][cid]["faction"] = facid
  151. for system in constellation.iterdir():
  152. if not system.is_dir():
  153. continue
  154. _load_assoc_for_system(galaxy, system, rid, cid)
  155. def _load_assoc_for_region(galaxy, region):
  156. data = _load_yaml(region / "region.staticdata")
  157. rid = data["regionID"]
  158. assert isinstance(rid, int)
  159. assert rid >= 0
  160. if "factionID" in data:
  161. facid = data["factionID"]
  162. assert isinstance(facid, int)
  163. assert facid >= 0
  164. galaxy["regions"][rid]["faction"] = facid
  165. for constellation in region.iterdir():
  166. if not constellation.is_dir():
  167. continue
  168. _load_assoc_for_constellation(galaxy, constellation, rid)
  169. def _load_galaxy_associations(sde_dir, galaxy):
  170. print("Loading galaxy staticdata... ", end="", flush=True)
  171. univdir = sde_dir / "fsd" / "universe"
  172. for base in univdir.iterdir():
  173. if not base.is_dir():
  174. continue
  175. for region in base.iterdir():
  176. if not region.is_dir():
  177. continue
  178. _load_assoc_for_region(galaxy, region)
  179. print("done.")
  180. for cid, constellation in galaxy["constellations"].items():
  181. if constellation["region"] < 0:
  182. print("[WARNING] Orphaned constellation: %d=%s" % (
  183. cid, constellation["name"]))
  184. for sid, system in galaxy["systems"].items():
  185. if system["region"] < 0 or system["constellation"] < 0:
  186. print("[WARNING] Orphaned system: %d=%s" % (sid, system["name"]))
  187. def _load_factions(sde_dir):
  188. print("Loading factions... ", end="", flush=True)
  189. data = _load_yaml(sde_dir / "bsd" / "chrFactions.yaml")
  190. factions = {}
  191. for entry in data:
  192. fid = entry["factionID"]
  193. name = entry["factionName"]
  194. assert isinstance(fid, int)
  195. assert isinstance(name, str)
  196. assert fid >= 0
  197. factions[fid] = {"name": name}
  198. print("done.")
  199. return factions
  200. def _dump_types(out_dir, types):
  201. print("Dumping types... ", end="", flush=True)
  202. _save_yaml(out_dir / "types.yml", types)
  203. print("done.")
  204. def _dump_killables(out_dir, killables):
  205. print("Dumping killables... ", end="", flush=True)
  206. _save_yaml(out_dir / "killables.yml", killables)
  207. print("done.")
  208. def _dump_galaxy(out_dir, galaxy):
  209. print("Dumping galaxy... ", end="", flush=True)
  210. _save_yaml(out_dir / "galaxy.yml", galaxy)
  211. print("done.")
  212. def _dump_entities(out_dir, factions):
  213. print("Dumping entities... ", end="", flush=True)
  214. entities = {"factions": factions}
  215. _save_yaml(out_dir / "entities.yml", entities)
  216. print("done.")
  217. def _compress(out_dir):
  218. targets = ["types", "killables", "galaxy", "entities"]
  219. for basename in targets:
  220. print("Compressing %s... " % basename, end="", flush=True)
  221. fn_src = out_dir / (basename + ".yml")
  222. fn_dst = out_dir / (basename + ".yml.gz")
  223. with fn_src.open("rb") as f_in:
  224. with gzip.open(str(fn_dst), "wb") as f_out:
  225. shutil.copyfileobj(f_in, f_out)
  226. print("done.")
  227. def _cleanup(out_dir):
  228. print("Cleaning up... ", end="", flush=True)
  229. targets = ["types", "killables", "galaxy", "entities"]
  230. for basename in targets:
  231. (out_dir / (basename + ".yml")).unlink()
  232. print("done.")
  233. def import_sde(sde_dir, out_dir):
  234. """Import the SDE unzipped at sde_dir to out_dir."""
  235. print("EVE Online static data import")
  236. print("- from: %s" % sde_dir)
  237. print("- to: %s" % out_dir)
  238. _verify_categoryids(sde_dir)
  239. groups = _load_groupids(sde_dir)
  240. types, killables = _load_typeids(sde_dir, groups)
  241. _dump_types(out_dir, types)
  242. _dump_killables(out_dir, killables)
  243. del groups, types, killables
  244. ids = _load_ids(sde_dir)
  245. print("Counts: regions=%d, constellations=%d, systems=%d" % (
  246. len(ids[_REGION]), len(ids[_CONSTELLATION]), len(ids[_SOLAR_SYSTEM])))
  247. names = _load_names(sde_dir)
  248. galaxy = _build_galaxy_skeleton(ids, names)
  249. del ids, names
  250. _load_galaxy_associations(sde_dir, galaxy)
  251. _dump_galaxy(out_dir, galaxy)
  252. del galaxy
  253. factions = _load_factions(sde_dir)
  254. _dump_entities(out_dir, factions)
  255. del factions
  256. _compress(out_dir)
  257. _cleanup(out_dir)
  258. def main():
  259. if len(sys.argv) < 2:
  260. print("usage: %s <sde_directory>" % sys.argv[0])
  261. exit(1)
  262. sde_dir = Path(sys.argv[1]).resolve()
  263. out_dir = Path(__file__).resolve().parent.parent / "data" / "universe"
  264. import_sde(sde_dir, out_dir)
  265. if __name__ == "__main__":
  266. main()