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.
 
 
 
 
 

335 lines
9.0 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 = {"ships": {}, "structures": {}, "fighters": {}}
  51. cat_conv = {_SHIP_CAT: "ships", _FIGHTER_CAT: "fighters"}
  52. cat_conv.update({cid: "structures" for cid in _STRUCT_CATS})
  53. group_conv = {gid: cid for cid, gids in groups.items() for gid in gids}
  54. for tid, type_ in data.items():
  55. gid = type_["groupID"]
  56. if gid in group_conv:
  57. cid = group_conv[gid]
  58. cname = cat_conv[cid]
  59. name = type_["name"]["en"]
  60. group = groups[cid][gid]
  61. assert isinstance(tid, int)
  62. types[cname][tid] = {"name": name, "group": group}
  63. print("done.")
  64. return types
  65. def _load_ids(sde_dir):
  66. print("Loading itemIDs... ", end="", flush=True)
  67. data = _load_yaml(sde_dir / "bsd" / "invItems.yaml")
  68. ids = {_REGION: [], _CONSTELLATION: [], _SOLAR_SYSTEM: []}
  69. for entry in data:
  70. if entry["typeID"] in ids:
  71. ids[entry["typeID"]].append(entry["itemID"])
  72. print("done.")
  73. return ids
  74. def _load_names(sde_dir):
  75. print("Loading itemNames... ", end="", flush=True)
  76. data = _load_yaml(sde_dir / "bsd" / "invNames.yaml")
  77. names = {}
  78. for entry in data:
  79. name = entry["itemName"]
  80. assert isinstance(name, str)
  81. names[entry["itemID"]] = name
  82. print("done.")
  83. return names
  84. def _build_galaxy_skeleton(ids, names):
  85. print("Building galaxy skeleton... ", end="", flush=True)
  86. galaxy = {"regions": {}, "constellations": {}, "systems": {}}
  87. d = galaxy["regions"]
  88. for rid in ids[_REGION]:
  89. assert isinstance(rid, int)
  90. d[rid] = {
  91. "name": names[rid]
  92. }
  93. d = galaxy["constellations"]
  94. for cid in ids[_CONSTELLATION]:
  95. assert isinstance(cid, int)
  96. d[cid] = {
  97. "name": names[cid],
  98. "region": -1
  99. }
  100. d = galaxy["systems"]
  101. for sid in ids[_SOLAR_SYSTEM]:
  102. assert isinstance(sid, int)
  103. d[sid] = {
  104. "name": names[sid],
  105. "constellation": -1, "region": -1, "security": 0.0
  106. }
  107. print("done.")
  108. return galaxy
  109. def _load_assoc_for_system(galaxy, system, rid, cid):
  110. data = _load_yaml(system / "solarsystem.staticdata")
  111. sid = data["solarSystemID"]
  112. sec = data["security"]
  113. assert isinstance(sid, int)
  114. assert isinstance(sec, float)
  115. assert sid >= 0
  116. assert sec >= -1.0 and sec <= 1.0
  117. galaxy["systems"][sid]["constellation"] = cid
  118. galaxy["systems"][sid]["region"] = rid
  119. galaxy["systems"][sid]["security"] = sec
  120. if "factionID" in data:
  121. facid = data["factionID"]
  122. assert isinstance(facid, int)
  123. assert facid >= 0
  124. galaxy["systems"][sid]["faction"] = facid
  125. def _load_assoc_for_constellation(galaxy, constellation, rid):
  126. data = _load_yaml(constellation / "constellation.staticdata")
  127. cid = data["constellationID"]
  128. assert isinstance(cid, int)
  129. assert cid >= 0
  130. galaxy["constellations"][cid]["region"] = rid
  131. if "factionID" in data:
  132. facid = data["factionID"]
  133. assert isinstance(facid, int)
  134. assert facid >= 0
  135. galaxy["constellations"][cid]["faction"] = facid
  136. for system in constellation.iterdir():
  137. if not system.is_dir():
  138. continue
  139. _load_assoc_for_system(galaxy, system, rid, cid)
  140. def _load_assoc_for_region(galaxy, region):
  141. data = _load_yaml(region / "region.staticdata")
  142. rid = data["regionID"]
  143. assert isinstance(rid, int)
  144. assert rid >= 0
  145. if "factionID" in data:
  146. facid = data["factionID"]
  147. assert isinstance(facid, int)
  148. assert facid >= 0
  149. galaxy["regions"][rid]["faction"] = facid
  150. for constellation in region.iterdir():
  151. if not constellation.is_dir():
  152. continue
  153. _load_assoc_for_constellation(galaxy, constellation, rid)
  154. def _load_galaxy_associations(sde_dir, galaxy):
  155. print("Loading galaxy staticdata... ", end="", flush=True)
  156. univdir = sde_dir / "fsd" / "universe"
  157. for base in univdir.iterdir():
  158. if not base.is_dir():
  159. continue
  160. for region in base.iterdir():
  161. if not region.is_dir():
  162. continue
  163. _load_assoc_for_region(galaxy, region)
  164. print("done.")
  165. for cid, constellation in galaxy["constellations"].items():
  166. if constellation["region"] < 0:
  167. print("[WARNING] Orphaned constellation: %d=%s" % (
  168. cid, constellation["name"]))
  169. for sid, system in galaxy["systems"].items():
  170. if system["region"] < 0 or system["constellation"] < 0:
  171. print("[WARNING] Orphaned system: %d=%s" % (sid, system["name"]))
  172. def _load_factions(sde_dir):
  173. print("Loading factions... ", end="", flush=True)
  174. data = _load_yaml(sde_dir / "bsd" / "chrFactions.yaml")
  175. factions = {}
  176. for entry in data:
  177. fid = entry["factionID"]
  178. name = entry["factionName"]
  179. assert isinstance(fid, int)
  180. assert isinstance(name, str)
  181. assert fid >= 0
  182. factions[fid] = {"name": name}
  183. print("done.")
  184. return factions
  185. def _dump_types(out_dir, types):
  186. print("Dumping types... ", end="", flush=True)
  187. _save_yaml(out_dir / "types.yml", types)
  188. print("done.")
  189. def _dump_galaxy(out_dir, galaxy):
  190. print("Dumping galaxy... ", end="", flush=True)
  191. _save_yaml(out_dir / "galaxy.yml", galaxy)
  192. print("done.")
  193. def _dump_entities(out_dir, factions):
  194. print("Dumping entities... ", end="", flush=True)
  195. entities = {"factions": factions}
  196. _save_yaml(out_dir / "entities.yml", entities)
  197. print("done.")
  198. def _compress(out_dir):
  199. targets = ["types", "galaxy", "entities"]
  200. for basename in targets:
  201. print("Compressing %s... " % basename, end="", flush=True)
  202. fn_src = out_dir / (basename + ".yml")
  203. fn_dst = out_dir / (basename + ".yml.gz")
  204. with fn_src.open("rb") as f_in:
  205. with gzip.open(str(fn_dst), "wb") as f_out:
  206. shutil.copyfileobj(f_in, f_out)
  207. print("done.")
  208. def _cleanup(out_dir):
  209. print("Cleaning up... ", end="", flush=True)
  210. targets = ["types", "galaxy", "entities"]
  211. for basename in targets:
  212. (out_dir / (basename + ".yml")).unlink()
  213. print("done.")
  214. def import_sde(sde_dir, out_dir):
  215. """Import the SDE unzipped at sde_dir to out_dir."""
  216. print("EVE Online static data import")
  217. print("- from: %s" % sde_dir)
  218. print("- to: %s" % out_dir)
  219. _verify_categoryids(sde_dir)
  220. groups = _load_groupids(sde_dir)
  221. types = _load_typeids(sde_dir, groups)
  222. _dump_types(out_dir, types)
  223. del groups, types
  224. ids = _load_ids(sde_dir)
  225. print("Counts: regions=%d, constellations=%d, systems=%d" % (
  226. len(ids[_REGION]), len(ids[_CONSTELLATION]), len(ids[_SOLAR_SYSTEM])))
  227. names = _load_names(sde_dir)
  228. galaxy = _build_galaxy_skeleton(ids, names)
  229. del ids, names
  230. _load_galaxy_associations(sde_dir, galaxy)
  231. _dump_galaxy(out_dir, galaxy)
  232. del galaxy
  233. factions = _load_factions(sde_dir)
  234. _dump_entities(out_dir, factions)
  235. del factions
  236. _compress(out_dir)
  237. _cleanup(out_dir)
  238. def main():
  239. if len(sys.argv) < 2:
  240. print("usage: %s <sde_directory>" % sys.argv[0])
  241. exit(1)
  242. sde_dir = Path(sys.argv[1]).resolve()
  243. out_dir = Path(__file__).resolve().parent.parent / "data" / "universe"
  244. import_sde(sde_dir, out_dir)
  245. if __name__ == "__main__":
  246. main()