Merger.cs 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358
  1. using System;
  2. using System.IO;
  3. using System.Collections.Generic;
  4. using System.Globalization;
  5. using YamlDotNet.RepresentationModel;
  6. namespace Content.Tools
  7. {
  8. public sealed class Merger
  9. {
  10. public Map MapOurs { get; }
  11. public Map MapBased { get; }
  12. public Map MapOther { get; }
  13. public Dictionary<uint, uint> TileMapFromOtherToOurs { get; } = new Dictionary<uint, uint>();
  14. public Dictionary<uint, uint> TileMapFromBasedToOurs { get; } = new Dictionary<uint, uint>();
  15. public Dictionary<uint, uint> EntityMapFromOtherToOurs { get; } = new Dictionary<uint, uint>();
  16. public List<uint> EntityListDirectMerge { get; } = new List<uint>();
  17. private const int ExpectedChunkSize = 16 * 16 * 4;
  18. public Merger(Map ours, Map based, Map other)
  19. {
  20. MapOurs = ours;
  21. MapBased = based;
  22. MapOther = other;
  23. }
  24. public bool Merge()
  25. {
  26. PlanTileMapping(TileMapFromOtherToOurs, MapOther);
  27. PlanTileMapping(TileMapFromBasedToOurs, MapBased);
  28. MergeTiles();
  29. PlanEntityMapping();
  30. return MergeEntities();
  31. }
  32. // -- Tiles --
  33. public void PlanTileMapping(Dictionary<uint, uint> relativeOtherToOurs, Map relativeOther)
  34. {
  35. var mapping = new Dictionary<string, uint>();
  36. uint nextAvailable = 0;
  37. foreach (var kvp in MapOurs.TilemapNode)
  38. {
  39. var k = uint.Parse(kvp.Key.ToString());
  40. var v = kvp.Value.ToString();
  41. mapping[v] = k;
  42. if (k >= nextAvailable)
  43. nextAvailable = k + 1;
  44. }
  45. foreach (var kvp in relativeOther.TilemapNode)
  46. {
  47. var k = uint.Parse(kvp.Key.ToString());
  48. var v = kvp.Value.ToString();
  49. if (mapping.ContainsKey(v))
  50. {
  51. relativeOtherToOurs[k] = mapping[v];
  52. }
  53. else
  54. {
  55. MapOurs.TilemapNode.Add(nextAvailable.ToString(CultureInfo.InvariantCulture), v);
  56. relativeOtherToOurs[k] = nextAvailable++;
  57. }
  58. }
  59. }
  60. public void MergeTiles()
  61. {
  62. var a = MapOurs.GridsNode.Children[0];
  63. var b = MapBased.GridsNode.Children[0];
  64. var c = MapOther.GridsNode.Children[0];
  65. var aChunks = a["chunks"];
  66. var bChunks = b["chunks"];
  67. var cChunks = c["chunks"];
  68. MergeTileChunks((YamlSequenceNode) aChunks, (YamlSequenceNode) bChunks, (YamlSequenceNode) cChunks);
  69. }
  70. public void MergeTileChunks(YamlSequenceNode aChunks, YamlSequenceNode bChunks, YamlSequenceNode cChunks)
  71. {
  72. var aMap = ConvertTileChunks(aChunks);
  73. var bMap = ConvertTileChunks(bChunks);
  74. var cMap = ConvertTileChunks(cChunks);
  75. var xMap = new HashSet<string>();
  76. foreach (var kvp in aMap)
  77. xMap.Add(kvp.Key);
  78. // don't include b because that would mess with chunk deletion
  79. foreach (var kvp in cMap)
  80. xMap.Add(kvp.Key);
  81. foreach (var ind in xMap)
  82. {
  83. using var a = new MemoryStream(GetChunkBytes(aMap, ind));
  84. using var b = new MemoryStream(GetChunkBytes(bMap, ind));
  85. using var c = new MemoryStream(GetChunkBytes(cMap, ind));
  86. using var aR = new BinaryReader(a);
  87. using var bR = new BinaryReader(b);
  88. using var cR = new BinaryReader(c);
  89. var outB = new byte[ExpectedChunkSize];
  90. {
  91. using var outS = new MemoryStream(outB);
  92. using var outW = new BinaryWriter(outS);
  93. for (var i = 0; i < ExpectedChunkSize; i += 4)
  94. {
  95. var aI = aR.ReadUInt32();
  96. var bI = MapTileId(bR.ReadUInt32(), TileMapFromBasedToOurs);
  97. var cI = MapTileId(cR.ReadUInt32(), TileMapFromOtherToOurs);
  98. // cI needs translation.
  99. var result = aI;
  100. if (aI == bI)
  101. {
  102. // If aI == bI then aI did not change anything, so cI always wins
  103. result = cI;
  104. }
  105. else if (bI != cI)
  106. {
  107. // If bI != cI then cI definitely changed something (conflict, but overrides aI)
  108. result = cI;
  109. Console.WriteLine("WARNING: Tile (" + ind + ")[" + i + "] was changed by both branches.");
  110. }
  111. outW.Write(result);
  112. }
  113. }
  114. // Actually output chunk
  115. if (!aMap.ContainsKey(ind))
  116. {
  117. var res = new YamlMappingNode();
  118. res.Children["ind"] = ind;
  119. aMap[ind] = res;
  120. }
  121. aMap[ind].Children["tiles"] = Convert.ToBase64String(outB);
  122. }
  123. }
  124. public static uint MapTileId(uint src, Dictionary<uint, uint> mapping)
  125. {
  126. return (src & 0xFFFF0000) | mapping[src & 0xFFFF];
  127. }
  128. public static Dictionary<string, YamlMappingNode> ConvertTileChunks(YamlSequenceNode chunks)
  129. {
  130. var map = new Dictionary<string, YamlMappingNode>();
  131. foreach (var chunk in chunks)
  132. map[chunk["ind"].ToString()] = (YamlMappingNode) chunk;
  133. return map;
  134. }
  135. public static byte[] GetChunkBytes(Dictionary<string, YamlMappingNode> chunks, string ind)
  136. {
  137. if (!chunks.ContainsKey(ind))
  138. return new byte[ExpectedChunkSize];
  139. return Convert.FromBase64String(chunks[ind]["tiles"].ToString());
  140. }
  141. // -- Entities --
  142. public void PlanEntityMapping()
  143. {
  144. // Ok, so here's how it works:
  145. // 1. Entities that do not exist in "based" are additions.
  146. // 2. Entities that exist in "based" but do not exist in the one map or the other are removals.
  147. // Find modifications and deletions
  148. foreach (var kvp in MapBased.Entities)
  149. {
  150. var deletedByOurs = !MapOurs.Entities.ContainsKey(kvp.Key);
  151. var deletedByOther = !MapOther.Entities.ContainsKey(kvp.Key);
  152. if (deletedByOther && !deletedByOurs)
  153. {
  154. // Delete
  155. MapOurs.Entities.Remove(kvp.Key);
  156. }
  157. else if (!(deletedByOurs || deletedByOther))
  158. {
  159. // Modify
  160. EntityMapFromOtherToOurs[kvp.Key] = kvp.Key;
  161. }
  162. }
  163. // Find additions
  164. foreach (var kvp in MapOther.Entities)
  165. {
  166. if (!MapBased.Entities.ContainsKey(kvp.Key))
  167. {
  168. // New
  169. var newId = MapOurs.NextAvailableEntityId++;
  170. EntityMapFromOtherToOurs[kvp.Key] = newId;
  171. }
  172. }
  173. }
  174. public bool MergeEntities()
  175. {
  176. var success = true;
  177. foreach (var kvp in EntityMapFromOtherToOurs)
  178. {
  179. // For debug use.
  180. // Console.WriteLine("Entity C/" + kvp.Key + " -> A/" + kvp.Value);
  181. YamlMappingNode oursEnt;
  182. if (MapOurs.Entities.ContainsKey(kvp.Value))
  183. {
  184. oursEnt = MapOurs.Entities[kvp.Value];
  185. if (!MapBased.Entities.TryGetValue(kvp.Value, out var basedEnt))
  186. {
  187. basedEnt = oursEnt;
  188. }
  189. if (!MergeEntityNodes(oursEnt, basedEnt, MapOther.Entities[kvp.Key]))
  190. {
  191. Console.WriteLine("Unable to successfully merge entity C/" + kvp.Key);
  192. success = false;
  193. }
  194. }
  195. else
  196. {
  197. oursEnt = (YamlMappingNode) YamlTools.CopyYamlNodes(MapOther.Entities[kvp.Key]);
  198. if (!MapEntity(oursEnt))
  199. {
  200. Console.WriteLine("Unable to successfully import entity C/" + kvp.Key);
  201. success = false;
  202. }
  203. else
  204. {
  205. MapOurs.Entities[kvp.Value] = oursEnt;
  206. }
  207. }
  208. oursEnt.Children["uid"] = kvp.Value.ToString(CultureInfo.InvariantCulture);
  209. }
  210. return success;
  211. }
  212. public bool MergeEntityNodes(YamlMappingNode ours, YamlMappingNode based, YamlMappingNode other)
  213. {
  214. // Copy to intermediate
  215. var otherMapped = (YamlMappingNode) YamlTools.CopyYamlNodes(other);
  216. if (!MapEntity(otherMapped))
  217. return false;
  218. // Merge stuff that isn't components
  219. var path = "Entity" + other["uid"];
  220. YamlTools.MergeYamlMappings(ours, based, otherMapped, path, new[] { "components" });
  221. // Components are special
  222. var ourComponents = new Dictionary<string, YamlMappingNode>();
  223. var basedComponents = new Dictionary<string, YamlMappingNode>();
  224. var ourComponentsNode = (YamlSequenceNode) ours["components"];
  225. var basedComponentsNode = (YamlSequenceNode) based["components"];
  226. var otherComponentsNode = (YamlSequenceNode) otherMapped["components"];
  227. foreach (var component in ourComponentsNode)
  228. {
  229. var name = component["type"].ToString();
  230. ourComponents[name] = (YamlMappingNode) component;
  231. }
  232. foreach (var component in basedComponentsNode)
  233. {
  234. var name = component["type"].ToString();
  235. basedComponents[name] = (YamlMappingNode) component;
  236. }
  237. foreach (var otherComponent in otherComponentsNode)
  238. {
  239. var name = otherComponent["type"].ToString();
  240. if (ourComponents.ContainsKey(name))
  241. {
  242. var ourComponent = ourComponents[name];
  243. if (!basedComponents.TryGetValue(name, out var basedComponent))
  244. basedComponent = new YamlMappingNode();
  245. YamlTools.MergeYamlNodes(ourComponent, basedComponent, otherComponent, path + "/components/" + name);
  246. }
  247. else
  248. {
  249. ourComponentsNode.Add(otherComponent);
  250. }
  251. }
  252. return true;
  253. }
  254. public bool MapEntity(YamlMappingNode other)
  255. {
  256. var path = "Entity" + other["uid"];
  257. if (other.Children.ContainsKey("components"))
  258. {
  259. var components = (YamlSequenceNode) other["components"];
  260. foreach (var component in components)
  261. {
  262. var type = component["type"].ToString();
  263. if (type == "Transform")
  264. {
  265. if (!MapEntityProperty((YamlMappingNode) component, "parent", path))
  266. return false;
  267. }
  268. else if (type == "ContainerContainer")
  269. {
  270. MapEntityRecursiveAndBadly(component, path);
  271. }
  272. }
  273. }
  274. return true;
  275. }
  276. public bool MapEntityProperty(YamlMappingNode node, string property, string path)
  277. {
  278. if (node.Children.ContainsKey(property))
  279. {
  280. var prop = node[property];
  281. if (prop is YamlScalarNode yamlProp)
  282. return MapEntityProperty(yamlProp, path + "/" + property);
  283. }
  284. return true;
  285. }
  286. public bool MapEntityProperty(YamlScalarNode node, string path)
  287. {
  288. if (uint.TryParse(node.ToString(), out var uid))
  289. {
  290. if (EntityMapFromOtherToOurs.ContainsKey(uid))
  291. {
  292. node.Value = EntityMapFromOtherToOurs[uid].ToString(CultureInfo.InvariantCulture);
  293. }
  294. else
  295. {
  296. Console.WriteLine($"Error finding UID in MapEntityRecursiveAndBadly {path}. To fix this, the merge driver needs to be improved.");
  297. return false;
  298. }
  299. }
  300. return true;
  301. }
  302. public bool MapEntityRecursiveAndBadly(YamlNode node, string path)
  303. {
  304. switch (node)
  305. {
  306. case YamlSequenceNode subSequence:
  307. var idx = 0;
  308. foreach (var val in subSequence)
  309. if (!MapEntityRecursiveAndBadly(val, path + "/" + idx++))
  310. return false;
  311. return true;
  312. case YamlMappingNode subMapping:
  313. foreach (var kvp in subMapping)
  314. if (!MapEntityRecursiveAndBadly(kvp.Key, path))
  315. return false;
  316. foreach (var kvp in subMapping)
  317. if (!MapEntityRecursiveAndBadly(kvp.Value, path + "/" + kvp.Key))
  318. return false;
  319. return true;
  320. case YamlScalarNode subScalar:
  321. return MapEntityProperty(subScalar, path);
  322. default:
  323. throw new ArgumentException($"Unrecognized YAML node type: {node.GetType()} at {path}");
  324. }
  325. }
  326. }
  327. }