using System; using System.Collections.Generic; using System.Text.RegularExpressions; using System.IO; using System.Reflection; using UnityEngine; using System.Linq; using Ionic.Zlib; namespace COM3D2.MeidoPhotoStudio.Plugin { public static class Utility { private const BindingFlags bindingFlags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static; internal static readonly byte[] pngHeader = { 137, 80, 78, 71, 13, 10, 26, 10 }; internal static readonly byte[] pngEnd = System.Text.Encoding.ASCII.GetBytes("IEND"); internal static readonly Regex guidRegEx = new Regex( @"^[a-f0-9]{8}(\-[a-f0-9]{4}){3}\-[a-f0-9]{12}$", RegexOptions.IgnoreCase ); internal static readonly GameObject mousePositionGo; internal static readonly MousePosition mousePosition; public static readonly BepInEx.Logging.ManualLogSource Logger = BepInEx.Logging.Logger.CreateLogSource(MeidoPhotoStudio.pluginName); public enum ModKey { Control, Shift, Alt } public static string Timestamp => $"{DateTime.Now:yyyyMMddHHmmss}"; public static Vector3 MousePosition => mousePosition.Position; static Utility() { mousePositionGo = new GameObject(); mousePosition = mousePositionGo.AddComponent(); } public static void LogInfo(object data) => Logger.LogInfo(data); public static void LogMessage(object data) => Logger.LogMessage(data); public static void LogWarning(object data) => Logger.LogWarning(data); public static void LogError(object data) => Logger.LogError(data); public static void LogDebug(object data) => Logger.LogDebug(data); public static int Wrap(int value, int min, int max) { max--; return value < min ? max : value > max ? min : value; } public static int GetPix(int num) => (int)((1f + (((Screen.width / 1280f) - 1f) * 0.6f)) * num); public static float Bound(float value, float left, float right) { return left > (double)right ? Mathf.Clamp(value, right, left) : Mathf.Clamp(value, left, right); } public static int Bound(int value, int left, int right) { return left > right ? Mathf.Clamp(value, right, left) : Mathf.Clamp(value, left, right); } public static Texture2D MakeTex(int width, int height, Color color) { Color[] colors = new Color[width * height]; for (int i = 0; i < colors.Length; i++) { colors[i] = color; } Texture2D texture2D = new Texture2D(width, height); texture2D.SetPixels(colors); texture2D.Apply(); return texture2D; } public static FieldInfo GetFieldInfo(string field) => typeof(T).GetField(field, bindingFlags); public static TValue GetFieldValue(TType instance, string field) { FieldInfo fieldInfo = GetFieldInfo(field); if (fieldInfo == null || (!fieldInfo.IsStatic && instance == null)) return default; return (TValue)fieldInfo.GetValue(instance); } public static void SetFieldValue(TType instance, string name, TValue value) { GetFieldInfo(name).SetValue(instance, value); } public static PropertyInfo GetPropertyInfo(string field) => typeof(T).GetProperty(field, bindingFlags); public static TValue GetPropertyValue(TType instance, string property) { var propertyInfo = GetPropertyInfo(property); return propertyInfo == null ? default : (TValue) propertyInfo.GetValue(instance, null); } public static void SetPropertyValue(TType instance, string name, TValue value) => GetPropertyInfo(name).SetValue(instance, value, null); public static bool AnyMouseDown() { return Input.GetMouseButtonDown(0) || Input.GetMouseButtonDown(1) || Input.GetMouseButtonDown(2); } public static string ScreenshotFilename() { string screenShotDir = Path.Combine( GameMain.Instance.SerializeStorageManager.StoreDirectoryPath, "ScreenShot" ); if (!Directory.Exists(screenShotDir)) Directory.CreateDirectory(screenShotDir); return Path.Combine(screenShotDir, $"img{Timestamp}.png"); } public static string TempScreenshotFilename() { return Path.Combine(Path.GetTempPath(), $"cm3d2_{Guid.NewGuid()}.png"); } public static void ShowMouseExposition(string text, float time = 2f) { MouseExposition mouseExposition = MouseExposition.GetObject(); mouseExposition.SetText(text, time); } public static bool IsGuidString(string guid) { if (string.IsNullOrEmpty(guid) || guid.Length != 36) return false; return guidRegEx.IsMatch(guid); } public static string HandItemToOdogu(string menu) { menu = menu.Substring(menu.IndexOf('_') + 1); menu = menu.Substring(0, menu.IndexOf("_i_.menu")); menu = $"odogu_{menu}"; return menu; } public static void FixGameObjectScale(GameObject go) { Vector3 scale = go.transform.localScale; float largest = Mathf.Max(scale.x, Mathf.Max(scale.y, scale.z)); go.transform.localScale = Vector3.one * (float)Math.Round(largest, 3); } public static string SanitizePathPortion(string path) { char[] invalid = Path.GetInvalidFileNameChars(); path = path.Trim(); path = string.Join("_", path.Split(invalid)).Replace(".", "").Trim('_'); return path; } public static string GP01FbFaceHash(TMorph face, string hash) { if ((face.bodyskin.PartsVersion >= 120) && (hash != "eyeclose3") && hash.StartsWith("eyeclose")) { if (hash == "eyeclose") hash += '1'; hash += TMorph.crcFaceTypesStr[(int)face.GetFaceTypeGP01FB()]; } return hash; } public static void ResizeToFit(Texture2D texture, int maxWidth, int maxHeight) { int width = texture.width; int height = texture.height; if (width != maxWidth || height != maxHeight) { float scale = Mathf.Min(maxWidth / (float)width, maxHeight / (float)height); width = Mathf.RoundToInt(width * scale); height = Mathf.RoundToInt(height * scale); TextureScale.Bilinear(texture, width, height); } } public static bool BytesEqual(byte[] buffer, byte[] other) { if (buffer.Length != other.Length) return false; for (int i = 0; i < buffer.Length; i++) { if (buffer[i] != other[i]) return false; } return true; } public static bool IsPngFile(Stream stream) { byte[] buffer = new byte[8]; stream.Read(buffer, 0, 8); return BytesEqual(buffer, pngHeader); } public static bool SeekPngEnd(Stream stream) { byte[] buffer = new byte[8]; stream.Read(buffer, 0, 8); if (!BytesEqual(buffer, pngHeader)) return false; buffer = new byte[4]; do { stream.Read(buffer, 0, 4); if (BitConverter.IsLittleEndian) Array.Reverse(buffer); uint length = BitConverter.ToUInt32(buffer, 0); stream.Read(buffer, 0, 4); stream.Seek(length + 4L, SeekOrigin.Current); } while (!BytesEqual(buffer, pngEnd)); return true; } public static void WriteToFile(string name, System.Collections.Generic.IEnumerable list) { if (Path.GetExtension(name) != ".txt") name += ".txt"; File.WriteAllLines(Path.Combine(Constants.configPath, name), list.ToArray()); } public static void WriteToFile(string name, byte[] data) { File.WriteAllBytes(Path.Combine(Constants.configPath, name), data); } } public class MousePosition : MonoBehaviour { private Vector3 mousePosition; public Vector3 Position => mousePosition; private void Awake() { DontDestroyOnLoad(this); mousePosition = Input.mousePosition; } private void Update() { if (Input.GetMouseButton(0)) { mousePosition.x += Input.GetAxis("Mouse X") * 20; mousePosition.y += Input.GetAxis("Mouse Y") * 20; } else mousePosition = Input.mousePosition; } } public static class KeyValuePairExtensions { public static void Deconstruct( this KeyValuePair kvp, out TKey key, out TValue value ) { key = kvp.Key; value = kvp.Value; } } public static class StreamExtensions { public static void CopyTo(this Stream stream, Stream outStream) { var buf = new byte[1024 * 32]; int length; while ((length = stream.Read(buf, 0, buf.Length)) > 0) outStream.Write(buf, 0, length); } public static MemoryStream Decompress(this MemoryStream stream) { var dataMemoryStream = new MemoryStream(); using var compressionStream = new DeflateStream(stream, CompressionMode.Decompress, true); compressionStream.CopyTo(dataMemoryStream); compressionStream.Flush(); dataMemoryStream.Position = 0L; return dataMemoryStream; } public static DeflateStream GetCompressionStream(this MemoryStream stream) => new(stream, CompressionMode.Compress); } public static class CameraUtility { public static CameraMain MainCamera => GameMain.Instance.MainCamera; public static UltimateOrbitCamera UOCamera { get; } = GameMain.Instance.MainCamera.GetComponent(); public static void StopSpin() { Utility.SetFieldValue(UOCamera, "xVelocity", 0f); Utility.SetFieldValue(UOCamera, "yVelocity", 0f); } public static void StopMovement() => MainCamera.SetTargetPos(MainCamera.GetTargetPos()); public static void StopAll() { StopSpin(); StopMovement(); } public static void ForceCalcNearClip(this CameraMain camera) { camera.StopAllCoroutines(); camera.m_bCalcNearClip = false; camera.camera.nearClipPlane = 0.01f; } public static void ResetCalcNearClip(this CameraMain camera) { if (camera.m_bCalcNearClip) return; camera.StopAllCoroutines(); camera.m_bCalcNearClip = true; camera.Start(); } } public static class BinaryExtensions { public static string ReadNullableString(this BinaryReader binaryReader) { return binaryReader.ReadBoolean() ? binaryReader.ReadString() : null; } public static void WriteNullableString(this BinaryWriter binaryWriter, string str) { binaryWriter.Write(str != null); if (str != null) binaryWriter.Write(str); } public static void Write(this BinaryWriter binaryWriter, Vector3 vector3) { binaryWriter.Write(vector3.x); binaryWriter.Write(vector3.y); binaryWriter.Write(vector3.z); } public static void WriteVector3(this BinaryWriter binaryWriter, Vector3 vector3) { binaryWriter.Write(vector3.x); binaryWriter.Write(vector3.y); binaryWriter.Write(vector3.z); } public static Vector2 ReadVector2(this BinaryReader binaryReader) { return new Vector2(binaryReader.ReadSingle(), binaryReader.ReadSingle()); } public static Vector3 ReadVector3(this BinaryReader binaryReader) { return new Vector3( binaryReader.ReadSingle(), binaryReader.ReadSingle(), binaryReader.ReadSingle() ); } public static Vector4 ReadVector4(this BinaryReader binaryReader) { return new Vector4( binaryReader.ReadSingle(), binaryReader.ReadSingle(), binaryReader.ReadSingle(), binaryReader.ReadSingle() ); } public static void Write(this BinaryWriter binaryWriter, Quaternion quaternion) { binaryWriter.Write(quaternion.x); binaryWriter.Write(quaternion.y); binaryWriter.Write(quaternion.z); binaryWriter.Write(quaternion.w); } public static void WriteQuaternion(this BinaryWriter binaryWriter, Quaternion quaternion) { binaryWriter.Write(quaternion.x); binaryWriter.Write(quaternion.y); binaryWriter.Write(quaternion.z); binaryWriter.Write(quaternion.w); } public static Quaternion ReadQuaternion(this BinaryReader binaryReader) { return new Quaternion ( binaryReader.ReadSingle(), binaryReader.ReadSingle(), binaryReader.ReadSingle(), binaryReader.ReadSingle() ); } public static void Write(this BinaryWriter binaryWriter, Color colour) { binaryWriter.Write(colour.r); binaryWriter.Write(colour.g); binaryWriter.Write(colour.b); binaryWriter.Write(colour.a); } public static void WriteColour(this BinaryWriter binaryWriter, Color colour) { binaryWriter.Write(colour.r); binaryWriter.Write(colour.g); binaryWriter.Write(colour.b); binaryWriter.Write(colour.a); } public static Color ReadColour(this BinaryReader binaryReader) { return new Color ( binaryReader.ReadSingle(), binaryReader.ReadSingle(), binaryReader.ReadSingle(), binaryReader.ReadSingle() ); } public static Matrix4x4 ReadMatrix4x4(this BinaryReader binaryReader) { Matrix4x4 matrix = default; for (var i = 0; i < 16; i++) matrix[i] = binaryReader.ReadSingle(); return matrix; } } }