Utility.cs 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454
  1. using System;
  2. using System.Collections.Generic;
  3. using System.IO;
  4. using System.Linq;
  5. using System.Reflection;
  6. using System.Text.RegularExpressions;
  7. using Ionic.Zlib;
  8. using UnityEngine;
  9. namespace MeidoPhotoStudio.Plugin;
  10. public static class Utility
  11. {
  12. public enum ModKey { Control, Shift, Alt }
  13. private const BindingFlags bindingFlags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic
  14. | BindingFlags.Static;
  15. public static readonly BepInEx.Logging.ManualLogSource Logger =
  16. BepInEx.Logging.Logger.CreateLogSource(MeidoPhotoStudio.pluginName);
  17. public static string Timestamp =>
  18. $"{DateTime.Now:yyyyMMddHHmmss}";
  19. public static Vector3 MousePosition =>
  20. mousePosition.Position;
  21. internal static readonly byte[] pngHeader = { 137, 80, 78, 71, 13, 10, 26, 10 };
  22. internal static readonly byte[] pngEnd = System.Text.Encoding.ASCII.GetBytes("IEND");
  23. internal static readonly Regex guidRegEx = new(
  24. @"^[a-f0-9]{8}(\-[a-f0-9]{4}){3}\-[a-f0-9]{12}$", RegexOptions.IgnoreCase
  25. );
  26. internal static readonly GameObject mousePositionGo;
  27. internal static readonly MousePosition mousePosition;
  28. static Utility()
  29. {
  30. mousePositionGo = new();
  31. mousePosition = mousePositionGo.AddComponent<MousePosition>();
  32. }
  33. public static void LogInfo(object data) =>
  34. Logger.LogInfo(data);
  35. public static void LogMessage(object data) =>
  36. Logger.LogMessage(data);
  37. public static void LogWarning(object data) =>
  38. Logger.LogWarning(data);
  39. public static void LogError(object data) =>
  40. Logger.LogError(data);
  41. public static void LogDebug(object data) =>
  42. Logger.LogDebug(data);
  43. public static int Wrap(int value, int min, int max)
  44. {
  45. max--;
  46. return value < min ? max : value > max ? min : value;
  47. }
  48. public static int GetPix(int num) =>
  49. (int)((1f + (Screen.width / 1280f - 1f) * 0.6f) * num);
  50. public static float Bound(float value, float left, float right) =>
  51. left > (double)right ? Mathf.Clamp(value, right, left) : Mathf.Clamp(value, left, right);
  52. public static int Bound(int value, int left, int right) =>
  53. left > right ? Mathf.Clamp(value, right, left) : Mathf.Clamp(value, left, right);
  54. public static Texture2D MakeTex(int width, int height, Color color)
  55. {
  56. var colors = new Color32[width * height];
  57. for (var i = 0; i < colors.Length; i++)
  58. colors[i] = color;
  59. var texture2D = new Texture2D(width, height);
  60. texture2D.SetPixels32(colors);
  61. texture2D.Apply();
  62. return texture2D;
  63. }
  64. public static FieldInfo GetFieldInfo<T>(string field) =>
  65. typeof(T).GetField(field, bindingFlags);
  66. public static TValue GetFieldValue<TType, TValue>(TType instance, string field)
  67. {
  68. var fieldInfo = GetFieldInfo<TType>(field);
  69. return fieldInfo is null || !fieldInfo.IsStatic && instance == null
  70. ? default
  71. : (TValue)fieldInfo.GetValue(instance);
  72. }
  73. public static void SetFieldValue<TType, TValue>(TType instance, string name, TValue value) =>
  74. GetFieldInfo<TType>(name).SetValue(instance, value);
  75. public static PropertyInfo GetPropertyInfo<T>(string field) =>
  76. typeof(T).GetProperty(field, bindingFlags);
  77. public static TValue GetPropertyValue<TType, TValue>(TType instance, string property)
  78. {
  79. var propertyInfo = GetPropertyInfo<TType>(property);
  80. return propertyInfo is null
  81. ? default
  82. : (TValue)propertyInfo.GetValue(instance, null);
  83. }
  84. public static void SetPropertyValue<TType, TValue>(TType instance, string name, TValue value) =>
  85. GetPropertyInfo<TType>(name).SetValue(instance, value, null);
  86. public static bool AnyMouseDown() =>
  87. Input.GetMouseButtonDown(0) || Input.GetMouseButtonDown(1) || Input.GetMouseButtonDown(2);
  88. public static string ScreenshotFilename()
  89. {
  90. var screenShotDir = Path.Combine(
  91. GameMain.Instance.SerializeStorageManager.StoreDirectoryPath, "ScreenShot"
  92. );
  93. if (!Directory.Exists(screenShotDir))
  94. Directory.CreateDirectory(screenShotDir);
  95. return Path.Combine(screenShotDir, $"img{Timestamp}.png");
  96. }
  97. public static string TempScreenshotFilename() =>
  98. Path.Combine(Path.GetTempPath(), $"cm3d2_{Guid.NewGuid()}.png");
  99. public static void ShowMouseExposition(string text, float time = 2f)
  100. {
  101. var mouseExposition = MouseExposition.GetObject();
  102. mouseExposition.SetText(text, time);
  103. }
  104. public static bool IsGuidString(string guid) =>
  105. !string.IsNullOrEmpty(guid) && guid.Length is 36 && guidRegEx.IsMatch(guid);
  106. public static string HandItemToOdogu(string menu)
  107. {
  108. menu = menu.Substring(menu.IndexOf('_') + 1);
  109. menu = menu.Substring(0, menu.IndexOf("_i_.menu", StringComparison.OrdinalIgnoreCase));
  110. menu = $"odogu_{menu}";
  111. return menu;
  112. }
  113. public static void FixGameObjectScale(GameObject go)
  114. {
  115. var scale = go.transform.localScale;
  116. var largest = Mathf.Max(scale.x, Mathf.Max(scale.y, scale.z));
  117. go.transform.localScale = Vector3.one * (float)Math.Round(largest, 3);
  118. }
  119. public static string SanitizePathPortion(string path)
  120. {
  121. var invalid = Path.GetInvalidFileNameChars();
  122. path = path.Trim();
  123. path = string.Join("_", path.Split(invalid)).Replace(".", "").Trim('_');
  124. return path;
  125. }
  126. public static string GP01FbFaceHash(TMorph face, string hash)
  127. {
  128. if (face.bodyskin.PartsVersion >= 120 && hash is not "eyeclose3" && hash.StartsWith("eyeclose"))
  129. {
  130. if (hash is "eyeclose")
  131. hash += '1';
  132. hash += TMorph.crcFaceTypesStr[(int)face.GetFaceTypeGP01FB()];
  133. }
  134. return hash;
  135. }
  136. public static void ResizeToFit(Texture2D texture, int maxWidth, int maxHeight)
  137. {
  138. var width = texture.width;
  139. var height = texture.height;
  140. if (width == maxWidth && height == maxHeight)
  141. return;
  142. var scale = Mathf.Min(maxWidth / (float)width, maxHeight / (float)height);
  143. width = Mathf.RoundToInt(width * scale);
  144. height = Mathf.RoundToInt(height * scale);
  145. TextureScale.Bilinear(texture, width, height);
  146. }
  147. public static bool BytesEqual(byte[] buffer, byte[] other)
  148. {
  149. if (buffer.Length != other.Length)
  150. return false;
  151. for (var i = 0; i < buffer.Length; i++)
  152. if (buffer[i] != other[i])
  153. return false;
  154. return true;
  155. }
  156. public static bool IsPngFile(Stream stream)
  157. {
  158. var buffer = new byte[8];
  159. stream.Read(buffer, 0, 8);
  160. return BytesEqual(buffer, pngHeader);
  161. }
  162. public static bool SeekPngEnd(Stream stream)
  163. {
  164. var buffer = new byte[8];
  165. stream.Read(buffer, 0, 8);
  166. if (!BytesEqual(buffer, pngHeader))
  167. return false;
  168. buffer = new byte[4];
  169. do
  170. {
  171. stream.Read(buffer, 0, 4);
  172. if (BitConverter.IsLittleEndian)
  173. Array.Reverse(buffer);
  174. var length = BitConverter.ToUInt32(buffer, 0);
  175. stream.Read(buffer, 0, 4);
  176. stream.Seek(length + 4L, SeekOrigin.Current);
  177. } while (!BytesEqual(buffer, pngEnd));
  178. return true;
  179. }
  180. public static void WriteToFile(string name, IEnumerable<string> list)
  181. {
  182. if (Path.GetExtension(name) is not ".txt")
  183. name += ".txt";
  184. File.WriteAllLines(Path.Combine(Constants.configPath, name), list.ToArray());
  185. }
  186. public static void WriteToFile(string name, byte[] data) =>
  187. File.WriteAllBytes(Path.Combine(Constants.configPath, name), data);
  188. }
  189. public class MousePosition : MonoBehaviour
  190. {
  191. public Vector3 Position =>
  192. mousePosition;
  193. private Vector3 mousePosition;
  194. private void Awake()
  195. {
  196. DontDestroyOnLoad(this);
  197. mousePosition = Input.mousePosition;
  198. }
  199. private void Update()
  200. {
  201. if (Input.GetMouseButton(0))
  202. {
  203. mousePosition.x += Input.GetAxis("Mouse X") * 20;
  204. mousePosition.y += Input.GetAxis("Mouse Y") * 20;
  205. }
  206. else
  207. mousePosition = Input.mousePosition;
  208. }
  209. }
  210. public static class KeyValuePairExtensions
  211. {
  212. public static void Deconstruct<TKey, TValue>(this KeyValuePair<TKey, TValue> kvp, out TKey key, out TValue value)
  213. {
  214. key = kvp.Key;
  215. value = kvp.Value;
  216. }
  217. }
  218. public static class StreamExtensions
  219. {
  220. public static void CopyTo(this Stream stream, Stream outStream)
  221. {
  222. var buf = new byte[1024 * 32];
  223. int length;
  224. while ((length = stream.Read(buf, 0, buf.Length)) > 0)
  225. outStream.Write(buf, 0, length);
  226. }
  227. public static MemoryStream Decompress(this MemoryStream stream)
  228. {
  229. var dataMemoryStream = new MemoryStream();
  230. using var compressionStream = new DeflateStream(stream, CompressionMode.Decompress, true);
  231. compressionStream.CopyTo(dataMemoryStream);
  232. compressionStream.Flush();
  233. dataMemoryStream.Position = 0L;
  234. return dataMemoryStream;
  235. }
  236. public static DeflateStream GetCompressionStream(this MemoryStream stream) =>
  237. new(stream, CompressionMode.Compress);
  238. }
  239. public static class CameraUtility
  240. {
  241. public static CameraMain MainCamera =>
  242. GameMain.Instance.MainCamera;
  243. public static UltimateOrbitCamera UOCamera { get; } =
  244. GameMain.Instance.MainCamera.GetComponent<UltimateOrbitCamera>();
  245. public static void StopSpin()
  246. {
  247. Utility.SetFieldValue(UOCamera, "xVelocity", 0f);
  248. Utility.SetFieldValue(UOCamera, "yVelocity", 0f);
  249. }
  250. public static void StopMovement() =>
  251. MainCamera.SetTargetPos(MainCamera.GetTargetPos());
  252. public static void StopAll()
  253. {
  254. StopSpin();
  255. StopMovement();
  256. }
  257. public static void ForceCalcNearClip(this CameraMain camera)
  258. {
  259. camera.StopAllCoroutines();
  260. camera.m_bCalcNearClip = false;
  261. camera.camera.nearClipPlane = 0.01f;
  262. }
  263. public static void ResetCalcNearClip(this CameraMain camera)
  264. {
  265. if (camera.m_bCalcNearClip)
  266. return;
  267. camera.StopAllCoroutines();
  268. camera.m_bCalcNearClip = true;
  269. camera.Start();
  270. }
  271. }
  272. public static class BinaryExtensions
  273. {
  274. public static string ReadNullableString(this BinaryReader binaryReader) =>
  275. binaryReader.ReadBoolean()
  276. ? binaryReader.ReadString()
  277. : null;
  278. public static void WriteNullableString(this BinaryWriter binaryWriter, string str)
  279. {
  280. binaryWriter.Write(str is not null);
  281. if (str is not null)
  282. binaryWriter.Write(str);
  283. }
  284. public static void Write(this BinaryWriter binaryWriter, Vector3 vector3)
  285. {
  286. binaryWriter.Write(vector3.x);
  287. binaryWriter.Write(vector3.y);
  288. binaryWriter.Write(vector3.z);
  289. }
  290. public static void WriteVector3(this BinaryWriter binaryWriter, Vector3 vector3)
  291. {
  292. binaryWriter.Write(vector3.x);
  293. binaryWriter.Write(vector3.y);
  294. binaryWriter.Write(vector3.z);
  295. }
  296. public static Vector2 ReadVector2(this BinaryReader binaryReader) =>
  297. new(binaryReader.ReadSingle(), binaryReader.ReadSingle());
  298. public static Vector3 ReadVector3(this BinaryReader binaryReader) =>
  299. new(binaryReader.ReadSingle(), binaryReader.ReadSingle(), binaryReader.ReadSingle());
  300. public static Vector4 ReadVector4(this BinaryReader binaryReader) =>
  301. new(binaryReader.ReadSingle(), binaryReader.ReadSingle(), binaryReader.ReadSingle(),
  302. binaryReader.ReadSingle()
  303. );
  304. public static void Write(this BinaryWriter binaryWriter, Quaternion quaternion)
  305. {
  306. binaryWriter.Write(quaternion.x);
  307. binaryWriter.Write(quaternion.y);
  308. binaryWriter.Write(quaternion.z);
  309. binaryWriter.Write(quaternion.w);
  310. }
  311. public static void WriteQuaternion(this BinaryWriter binaryWriter, Quaternion quaternion)
  312. {
  313. binaryWriter.Write(quaternion.x);
  314. binaryWriter.Write(quaternion.y);
  315. binaryWriter.Write(quaternion.z);
  316. binaryWriter.Write(quaternion.w);
  317. }
  318. public static Quaternion ReadQuaternion(this BinaryReader binaryReader) =>
  319. new(binaryReader.ReadSingle(), binaryReader.ReadSingle(), binaryReader.ReadSingle(), binaryReader.ReadSingle());
  320. public static void Write(this BinaryWriter binaryWriter, Color colour)
  321. {
  322. binaryWriter.Write(colour.r);
  323. binaryWriter.Write(colour.g);
  324. binaryWriter.Write(colour.b);
  325. binaryWriter.Write(colour.a);
  326. }
  327. public static void WriteColour(this BinaryWriter binaryWriter, Color colour)
  328. {
  329. binaryWriter.Write(colour.r);
  330. binaryWriter.Write(colour.g);
  331. binaryWriter.Write(colour.b);
  332. binaryWriter.Write(colour.a);
  333. }
  334. public static Color ReadColour(this BinaryReader binaryReader) =>
  335. new(binaryReader.ReadSingle(), binaryReader.ReadSingle(), binaryReader.ReadSingle(), binaryReader.ReadSingle());
  336. public static Matrix4x4 ReadMatrix4x4(this BinaryReader binaryReader)
  337. {
  338. Matrix4x4 matrix = default;
  339. for (var i = 0; i < 16; i++)
  340. matrix[i] = binaryReader.ReadSingle();
  341. return matrix;
  342. }
  343. }