DumpMonsterAndCharacterDat.cs 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286
  1. using System;
  2. using System.Collections.Concurrent;
  3. using System.Collections.Generic;
  4. using System.Globalization;
  5. using System.IO;
  6. using System.Linq;
  7. using System.Threading.Tasks;
  8. using System.Xml;
  9. using OpenVIII.Battle;
  10. using OpenVIII.Battle.Dat;
  11. using Abilities = OpenVIII.Battle.Dat.Abilities;
  12. namespace OpenVIII.Dat_Dump
  13. {
  14. internal static class DumpMonsterAndCharacterDat
  15. {
  16. #region Fields
  17. public static ConcurrentDictionary<int, DatFile> MonsterData = new ConcurrentDictionary<int, DatFile>();
  18. private static readonly ConcurrentDictionary<int, DatFile> CharacterData = new ConcurrentDictionary<int, DatFile>();
  19. #endregion Fields
  20. #region Properties
  21. private static string Ls => CultureInfo.CurrentCulture.TextInfo.ListSeparator;
  22. #endregion Properties
  23. #region Methods
  24. public static async Task LoadMonsters()
  25. {
  26. if (!MonsterData.IsEmpty) return;
  27. //one issue with this is animations aren't loaded. because it requires all the geometry and skeleton loaded...
  28. // so the sequence dump is probably less useful or broken.
  29. Task<bool> addMonster(int i)
  30. => Task.Run(() => MonsterData.TryAdd(i,
  31. MonsterDatFile.CreateInstance(i,
  32. Sections.AnimationSequences | Sections.Information)));
  33. await Task.WhenAll(Enumerable.Range(0, 200).Select(addMonster));
  34. }
  35. public static async Task Process()
  36. {
  37. var xmlWriterSettings = new XmlWriterSettings
  38. {
  39. Async = true, // Enable async operations
  40. Indent = true,
  41. IndentChars = "\t", // note: default is two spaces
  42. NewLineOnAttributes = true,
  43. OmitXmlDeclaration = false
  44. };
  45. using (var csv2File = new StreamWriter(new FileStream("MonsterAttacks.csv", FileMode.Create, FileAccess.Write, FileShare.ReadWrite), System.Text.Encoding.UTF8))
  46. {
  47. using (var csvFile = new StreamWriter(new FileStream("SequenceDump.csv", FileMode.Create, FileAccess.Write, FileShare.ReadWrite), System.Text.Encoding.UTF8))
  48. {
  49. await LoadMonsters();
  50. //header for monster attacks
  51. await csv2File.WriteLineAsync($"{nameof(Enemy)}{Ls}" +
  52. $"{nameof(Enemy.EII.Data.FileName)}{Ls}" +
  53. $"{nameof(Abilities)}{Ls}" +
  54. $"Number{Ls}" +
  55. $"{nameof(Abilities.Animation)}{Ls}" +
  56. $"Type{Ls}" +
  57. $"ID{Ls}" +
  58. $"Name{Ls}");
  59. //header for animation info
  60. await csvFile.WriteLineAsync($"Type{Ls}Type ID{Ls}Name{Ls}Animation Count{Ls}Sequence Count{Ls}Sequence ID{Ls}Offset{Ls}Bytes");
  61. using (var xmlWriter = XmlWriter.Create("SequenceDump.xml", xmlWriterSettings))
  62. {
  63. await xmlWriter.WriteStartDocumentAsync();
  64. await xmlWriter.WriteStartElementAsync(null, "dat", null);
  65. await XmlMonsterDataAsync(xmlWriter, csvFile, csv2File);
  66. await XmlCharacterDataAsync(xmlWriter, csvFile);
  67. await xmlWriter.WriteEndElementAsync();
  68. await xmlWriter.WriteEndDocumentAsync();
  69. }
  70. }
  71. }
  72. }
  73. private static async Task<string> XmlAnimationsAsync(XmlWriter xmlWriter, DatFile battleDatFile)
  74. {
  75. var count = $"{battleDatFile.Animations.Count}";
  76. await xmlWriter.WriteStartElementAsync(null, "animations", null);
  77. await xmlWriter.WriteAttributeStringAsync(null, "Count", null, count);
  78. await xmlWriter.WriteEndElementAsync();
  79. return count;
  80. }
  81. private static async Task XmlCharacterDataAsync(XmlWriter xmlWriter, TextWriter csvFile)
  82. {
  83. await xmlWriter.WriteStartElementAsync(null, "characters", null);
  84. for (var i = 0; i <= 10; i++)
  85. {
  86. DatFile test = CharacterDatFile.CreateInstance(i, 0);
  87. if (test != null && CharacterData.TryAdd(i, test))
  88. {
  89. // Character data added successfully
  90. }
  91. if (!CharacterData.TryGetValue(i, out var battleDat) || battleDat == null) continue;
  92. const string type = "character";
  93. await xmlWriter.WriteStartElementAsync(null, type, null);
  94. var id = i.ToString();
  95. await xmlWriter.WriteAttributeStringAsync(null, "ID", null, id);
  96. var name = Memory.Strings.GetName((Characters)i);
  97. await xmlWriter.WriteAttributeStringAsync(null, "name", null, name);
  98. var prefix0 = $"{type}{Ls}{id}{Ls}";
  99. var prefix1 = $"{name}";
  100. prefix1 += $"{Ls}{await XmlAnimationsAsync(xmlWriter, battleDat)}"; // Assuming XmlAnimations is async-safe
  101. await XmlSequencesAsync(xmlWriter, battleDat, csvFile, $"{prefix0}{prefix1}"); // Assuming XmlSequences is now async
  102. await XmlWeaponDataAsync(xmlWriter, i, battleDat, csvFile, prefix1); // Assuming XmlWeaponData is async
  103. await xmlWriter.WriteEndElementAsync(); // End of "character"
  104. }
  105. await xmlWriter.WriteEndElementAsync(); // End of "characters"
  106. }
  107. private static async Task XmlMonsterDataAsync(XmlWriter xmlWriter, StreamWriter csvFile, TextWriter csv2File)
  108. {
  109. await xmlWriter.WriteStartElementAsync(null, "monsters", null);
  110. for (var i = 0; i <= 200; i++)
  111. {
  112. if (!MonsterData.TryGetValue(i, out var battleDat) || battleDat == null) continue;
  113. const string type = "monster";
  114. var id = i.ToString();
  115. var name = battleDat.Information.Name ?? new FF8String("");
  116. var prefix = $"{type}{Ls}{id}{Ls}{name}";
  117. await xmlWriter.WriteStartElementAsync(null, type, null);
  118. await xmlWriter.WriteAttributeStringAsync(null, "ID", null, id);
  119. await xmlWriter.WriteAttributeStringAsync(null, "name", null, name);
  120. prefix += $"{Ls}{await XmlAnimationsAsync(xmlWriter, battleDat)}"; // Assuming XmlAnimations is async-safe
  121. await XmlSequencesAsync(xmlWriter, battleDat, csvFile, prefix); // Assuming XmlSequences is now async
  122. await xmlWriter.WriteEndElementAsync(); // End of "monster"
  123. var e = Enemy.Load(new EnemyInstanceInformation { Data = battleDat });
  124. async Task addAbilityAsync(string fieldName, Abilities a, int number)
  125. {
  126. await csv2File.WriteLineAsync($"{name}{Ls}" +
  127. $"{battleDat.FileName}{Ls}" +
  128. $"{fieldName}{Ls}" +
  129. $"{number}{Ls}" +
  130. $"{a.Animation}{Ls}" +
  131. $"{(a.Item != null ? nameof(a.Item) : a.Magic != null ? nameof(a.Magic) : a.Monster != null ? nameof(a.Monster) : "")}{Ls}" +
  132. $"{a.Item?.ID ?? (a.Magic?.MagicDataID ?? (a.Monster?.EnemyAttackID ?? 0))}{Ls}" +
  133. $"\"{(a.Item != null ? a.Item.Value.Name : a.Magic != null ? a.Magic.Name : a.Monster != null ? a.Monster.Name : new FF8String(""))}\"{Ls}");
  134. }
  135. async Task addAbilitiesAsync(string fieldName, IReadOnlyList<Abilities> abilities)
  136. {
  137. if (abilities == null) return;
  138. for (var number = 0; number < e.Info.AbilitiesLow.Length; number++)
  139. {
  140. var a = abilities[number];
  141. await addAbilityAsync(fieldName, a, number);
  142. }
  143. }
  144. await addAbilitiesAsync(nameof(e.Info.AbilitiesLow), e.Info.AbilitiesLow);
  145. await addAbilitiesAsync(nameof(e.Info.AbilitiesMed), e.Info.AbilitiesMed);
  146. await addAbilitiesAsync(nameof(e.Info.AbilitiesHigh), e.Info.AbilitiesHigh);
  147. }
  148. await xmlWriter.WriteEndElementAsync(); // End of "monsters"
  149. }
  150. private static async Task XmlSequencesAsync(XmlWriter xmlWriter, DatFile battleDatFile, TextWriter csvFile, string prefix)
  151. {
  152. await xmlWriter.WriteStartElementAsync(null, "sequences", null);
  153. var count = $"{battleDatFile.Sequences?.Count ?? 0}";
  154. await xmlWriter.WriteAttributeStringAsync(null, "Count", null, count);
  155. if (battleDatFile.Sequences != null)
  156. {
  157. foreach (var s in battleDatFile.Sequences)
  158. {
  159. await xmlWriter.WriteStartElementAsync(null, "sequence", null);
  160. var id = s.ID.ToString();
  161. var offset = s.Offset.ToString("X");
  162. var bytes = s.Count.ToString();
  163. await xmlWriter.WriteAttributeStringAsync(null, "ID", null, id);
  164. await xmlWriter.WriteAttributeStringAsync(null, "offset", null, offset);
  165. await xmlWriter.WriteAttributeStringAsync(null, "bytes", null, bytes);
  166. if (csvFile != null)
  167. {
  168. await csvFile.WriteAsync($"{prefix ?? ""}{Ls}{count}{Ls}{id}{Ls}{s.Offset}{Ls}{bytes}");
  169. }
  170. foreach (var b in s)
  171. {
  172. await xmlWriter.WriteStringAsync($"{b:X2} ");
  173. if (csvFile != null)
  174. {
  175. await csvFile.WriteAsync($"{Ls}{b}");
  176. }
  177. }
  178. if (csvFile != null)
  179. {
  180. await csvFile.WriteLineAsync();
  181. }
  182. await xmlWriter.WriteEndElementAsync(); // End of "sequence"
  183. }
  184. }
  185. if (csvFile != null)
  186. {
  187. await csvFile.FlushAsync(); // Ensure async flush
  188. }
  189. await xmlWriter.WriteEndElementAsync(); // End of "sequences"
  190. }
  191. private static async Task XmlWeaponDataAsync(XmlWriter xmlWriter, int characterID, DatFile r, TextWriter csvFile, string prefix1)
  192. {
  193. var weaponData = new ConcurrentDictionary<int, DatFile>();
  194. await xmlWriter.WriteStartElementAsync(null, "weapons", null);
  195. for (var i = 0; i <= 40; i++)
  196. {
  197. DatFile test;
  198. if (characterID == 1 || characterID == 9)
  199. test = WeaponDatFile.CreateInstance(characterID, i, r);
  200. else
  201. test = WeaponDatFile.CreateInstance(characterID, i);
  202. if (test != null && weaponData.TryAdd(i, test))
  203. {
  204. // Weapon data added successfully
  205. }
  206. if (!weaponData.TryGetValue(i, out var battleDat) || battleDat == null) continue;
  207. const string type = "weapon";
  208. var id = i.ToString();
  209. await xmlWriter.WriteStartElementAsync(null, type, null);
  210. await xmlWriter.WriteAttributeStringAsync(null, "ID", null, id);
  211. var index = ModuleBattleDebug.Weapons[(Characters)characterID]?.Select(((b, i1) => new { i, b }))
  212. .FirstOrDefault(v => v.b == i)?.i;
  213. if (!index.HasValue) continue;
  214. var currentWeaponData = Memory.KernelBin.WeaponsData.FirstOrDefault(v =>
  215. v.Character == (Characters)characterID && v.AltID == checked((byte)index.Value));
  216. if (currentWeaponData != default)
  217. {
  218. await xmlWriter.WriteAttributeStringAsync(null, "name", null, currentWeaponData.Name);
  219. var prefix = $"{type}{Ls}{id}{Ls}{currentWeaponData.Name}/{prefix1}"; // Bringing over name from character.
  220. await XmlAnimationsAsync(xmlWriter, battleDat); // Assuming XmlAnimations is now async
  221. await XmlSequencesAsync(xmlWriter, battleDat, csvFile, prefix); // Assuming XmlSequences is async
  222. }
  223. await xmlWriter.WriteEndElementAsync(); // End of "weapon"
  224. }
  225. await xmlWriter.WriteEndElementAsync(); // End of "weapons"
  226. }
  227. #endregion Methods
  228. }
  229. }