Hooks.cs 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Diagnostics;
  4. using System.IO;
  5. using System.Reflection;
  6. using System.Reflection.Emit;
  7. namespace COM3D2.CacheEditMenu
  8. {
  9. internal static class BinaryExtensions
  10. {
  11. public static string ReadNullableString(this BinaryReader br)
  12. {
  13. return br.ReadBoolean() ? br.ReadString() : null;
  14. }
  15. public static void WriteNullableString(this BinaryWriter bw, string value)
  16. {
  17. bw.Write(value != null);
  18. if (value != null)
  19. bw.Write(value);
  20. }
  21. }
  22. public static class Hooks
  23. {
  24. private static readonly Dictionary<string, MenuInfo> infoCache = new Dictionary<string, MenuInfo>();
  25. private static BinaryWriter cacheWriter;
  26. private static readonly Func<Dictionary<int, SceneEdit.SMenuItem>> getIdItemDic = () =>
  27. new Dictionary<int, SceneEdit.SMenuItem>();
  28. private static readonly Func<Dictionary<int, string>> getTexFileIDDic = () => new Dictionary<int, string>();
  29. private static readonly Action<bool> setNowMenuFlg = b => { };
  30. private static readonly Action<int> setNowStrMenuFileID = b => { };
  31. static Hooks()
  32. {
  33. var quickEditAss =
  34. TryLoadFrom(Path.Combine(Patcher.Patcher.SybarisPath, "COM3D2.QuickEditStart.Managed.dll"));
  35. if (quickEditAss == null)
  36. return;
  37. var mainType = quickEditAss.GetType("COM3D2.QuickEditStart.Managed.QuickEditStartManaged");
  38. MakeStaticGetter(mainType.GetField("texFileIDDic", BindingFlags.Public | BindingFlags.Static),
  39. ref getTexFileIDDic);
  40. MakeStaticGetter(mainType.GetField("idItemDic", BindingFlags.Public | BindingFlags.Static),
  41. ref getIdItemDic);
  42. MakeStaticSetter(mainType.GetField("nowMenuFlg", BindingFlags.NonPublic | BindingFlags.Static),
  43. ref setNowMenuFlg);
  44. MakeStaticSetter(mainType.GetField("nowStrMenuFileID", BindingFlags.NonPublic | BindingFlags.Static),
  45. ref setNowStrMenuFileID);
  46. }
  47. private static void MakeStaticGetter<T>(FieldInfo field, ref T result) where T : Delegate
  48. {
  49. if (field == null)
  50. return;
  51. var dm = new DynamicMethod($"CacheEditMenu_Getter_{field.GetHashCode()}", field.FieldType, null,
  52. typeof(Hooks), true);
  53. var il = dm.GetILGenerator();
  54. il.Emit(OpCodes.Ldsfld, field);
  55. il.Emit(OpCodes.Ret);
  56. result = dm.CreateDelegate(typeof(T)) as T;
  57. }
  58. private static void MakeStaticSetter<T>(FieldInfo field, ref T result) where T : Delegate
  59. {
  60. if (field == null)
  61. return;
  62. var dm = new DynamicMethod($"CacheEditMenu_Getter_{field.GetHashCode()}", typeof(void),
  63. new[] {field.FieldType}, typeof(Hooks), true);
  64. var il = dm.GetILGenerator();
  65. il.Emit(OpCodes.Ldarg_0);
  66. il.Emit(OpCodes.Stsfld, field);
  67. il.Emit(OpCodes.Ret);
  68. result = dm.CreateDelegate(typeof(T)) as T;
  69. }
  70. private static Assembly TryLoadFrom(string path)
  71. {
  72. try
  73. {
  74. return Assembly.LoadFrom(path);
  75. }
  76. catch (Exception)
  77. {
  78. return null;
  79. }
  80. }
  81. public static bool Prefix(ref bool result, SceneEdit.SMenuItem mi, string menuFileName)
  82. {
  83. Init();
  84. if (menuFileName.Contains("_zurashi"))
  85. {
  86. result = false;
  87. return false;
  88. }
  89. if (menuFileName.Contains("_mekure"))
  90. {
  91. result = false;
  92. return false;
  93. }
  94. menuFileName = Path.GetFileName(menuFileName);
  95. mi.m_strMenuFileName = menuFileName;
  96. mi.m_nMenuFileRID = menuFileName.ToLower().GetHashCode();
  97. if (infoCache.TryGetValue(menuFileName, out var menuInfo))
  98. {
  99. menuInfo.CopyTo(mi);
  100. result = true;
  101. return false;
  102. }
  103. return true;
  104. }
  105. private static Stopwatch sw = null;
  106. public static bool Postfix(bool result, SceneEdit.SMenuItem mi, string menuFileName)
  107. {
  108. if (!result)
  109. return false;
  110. var menuInfo = new MenuInfo
  111. {
  112. mi = mi,
  113. key = menuFileName
  114. };
  115. getTexFileIDDic().TryGetValue(mi.m_nMenuFileRID, out menuInfo.texName);
  116. infoCache[menuFileName] = menuInfo;
  117. menuInfo.Serialize(cacheWriter);
  118. return true;
  119. }
  120. private static void Init()
  121. {
  122. if (cacheWriter != null)
  123. return;
  124. var cacheDir = Path.Combine(Patcher.Patcher.SybarisPath, "EditMenuCache");
  125. var cachePath = Path.Combine(cacheDir, "EditMenuCache.dat");
  126. Directory.CreateDirectory(cacheDir);
  127. if (File.Exists(cachePath))
  128. using (var br = new BinaryReader(File.Open(cachePath, FileMode.Open, FileAccess.Read)))
  129. {
  130. try
  131. {
  132. while (true)
  133. {
  134. var menuInfo = new MenuInfo();
  135. menuInfo.Deserialize(br);
  136. infoCache[menuInfo.key] = menuInfo;
  137. }
  138. }
  139. catch (EndOfStreamException)
  140. {
  141. // End of file, no need to read more
  142. }
  143. }
  144. cacheWriter = new BinaryWriter(File.OpenWrite(cachePath));
  145. cacheWriter.BaseStream.Seek(0, SeekOrigin.End);
  146. }
  147. private class MenuInfo
  148. {
  149. public string key;
  150. public SceneEdit.SMenuItem mi;
  151. public string texName;
  152. public void Deserialize(BinaryReader br)
  153. {
  154. key = br.ReadString();
  155. texName = br.ReadNullableString();
  156. mi = new SceneEdit.SMenuItem
  157. {
  158. m_strMenuName = br.ReadString(),
  159. m_strInfo = br.ReadNullableString(),
  160. m_mpn = (MPN) br.ReadInt32(),
  161. m_strCateName = br.ReadString(),
  162. m_eColorSetMPN = (MPN) br.ReadInt32(),
  163. m_strMenuNameInColorSet = br.ReadNullableString(),
  164. m_pcMultiColorID = (MaidParts.PARTS_COLOR) br.ReadInt32(),
  165. m_boDelOnly = br.ReadBoolean(),
  166. m_fPriority = br.ReadSingle(),
  167. m_bMan = br.ReadBoolean()
  168. };
  169. }
  170. public void Serialize(BinaryWriter bw)
  171. {
  172. bw.Write(key);
  173. bw.WriteNullableString(texName);
  174. bw.Write(mi.m_strMenuName);
  175. bw.WriteNullableString(mi.m_strInfo);
  176. bw.Write((int) mi.m_mpn);
  177. bw.Write(mi.m_strCateName);
  178. bw.Write((int) mi.m_eColorSetMPN);
  179. bw.WriteNullableString(mi.m_strMenuNameInColorSet);
  180. bw.Write((int) mi.m_pcMultiColorID);
  181. bw.Write(mi.m_boDelOnly);
  182. bw.Write(mi.m_fPriority);
  183. bw.Write(mi.m_bMan);
  184. }
  185. public void CopyTo(SceneEdit.SMenuItem other)
  186. {
  187. other.m_strMenuName = mi.m_strMenuName;
  188. other.m_strInfo = mi.m_strInfo;
  189. other.m_mpn = mi.m_mpn;
  190. other.m_strCateName = mi.m_strCateName;
  191. other.m_eColorSetMPN = mi.m_eColorSetMPN;
  192. other.m_strMenuNameInColorSet = mi.m_strMenuNameInColorSet;
  193. other.m_pcMultiColorID = mi.m_pcMultiColorID;
  194. other.m_boDelOnly = mi.m_boDelOnly;
  195. other.m_fPriority = mi.m_fPriority;
  196. other.m_bMan = mi.m_bMan;
  197. if (string.IsNullOrEmpty(texName))
  198. return;
  199. setNowMenuFlg(true);
  200. setNowStrMenuFileID(other.m_nMenuFileRID);
  201. getIdItemDic()[other.m_nMenuFileRID] = other;
  202. other.m_texIcon = ImportCM.CreateTexture(texName);
  203. setNowMenuFlg(false);
  204. }
  205. }
  206. }
  207. }