|  | @@ -0,0 +1,248 @@
 | 
	
		
			
				|  |  | +using System;
 | 
	
		
			
				|  |  | +using System.Collections.Generic;
 | 
	
		
			
				|  |  | +using System.Diagnostics;
 | 
	
		
			
				|  |  | +using System.IO;
 | 
	
		
			
				|  |  | +using System.Reflection;
 | 
	
		
			
				|  |  | +using System.Reflection.Emit;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +namespace COM3D2.CacheEditMenu
 | 
	
		
			
				|  |  | +{
 | 
	
		
			
				|  |  | +    internal static class BinaryExtensions
 | 
	
		
			
				|  |  | +    {
 | 
	
		
			
				|  |  | +        public static string ReadNullableString(this BinaryReader br)
 | 
	
		
			
				|  |  | +        {
 | 
	
		
			
				|  |  | +            return br.ReadBoolean() ? br.ReadString() : null;
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        public static void WriteNullableString(this BinaryWriter bw, string value)
 | 
	
		
			
				|  |  | +        {
 | 
	
		
			
				|  |  | +            bw.Write(value != null);
 | 
	
		
			
				|  |  | +            if (value != null)
 | 
	
		
			
				|  |  | +                bw.Write(value);
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    public static class Hooks
 | 
	
		
			
				|  |  | +    {
 | 
	
		
			
				|  |  | +        private static readonly Dictionary<string, MenuInfo> infoCache = new Dictionary<string, MenuInfo>();
 | 
	
		
			
				|  |  | +        private static BinaryWriter cacheWriter;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        private static readonly Func<Dictionary<int, SceneEdit.SMenuItem>> getIdItemDic = () =>
 | 
	
		
			
				|  |  | +            new Dictionary<int, SceneEdit.SMenuItem>();
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        private static readonly Func<Dictionary<int, string>> getTexFileIDDic = () => new Dictionary<int, string>();
 | 
	
		
			
				|  |  | +        private static readonly Action<bool> setNowMenuFlg = b => { };
 | 
	
		
			
				|  |  | +        private static readonly Action<int> setNowStrMenuFileID = b => { };
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        static Hooks()
 | 
	
		
			
				|  |  | +        {
 | 
	
		
			
				|  |  | +            var quickEditAss =
 | 
	
		
			
				|  |  | +                TryLoadFrom(Path.Combine(Patcher.Patcher.SybarisPath, "COM3D2.QuickEditStart.Managed.dll"));
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +            if (quickEditAss == null)
 | 
	
		
			
				|  |  | +                return;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +            var mainType = quickEditAss.GetType("COM3D2.QuickEditStart.Managed.QuickEditStartManaged");
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +            MakeStaticGetter(mainType.GetField("texFileIDDic", BindingFlags.Public | BindingFlags.Static),
 | 
	
		
			
				|  |  | +                ref getTexFileIDDic);
 | 
	
		
			
				|  |  | +            MakeStaticGetter(mainType.GetField("idItemDic", BindingFlags.Public | BindingFlags.Static),
 | 
	
		
			
				|  |  | +                ref getIdItemDic);
 | 
	
		
			
				|  |  | +            MakeStaticSetter(mainType.GetField("nowMenuFlg", BindingFlags.NonPublic | BindingFlags.Static),
 | 
	
		
			
				|  |  | +                ref setNowMenuFlg);
 | 
	
		
			
				|  |  | +            MakeStaticSetter(mainType.GetField("nowStrMenuFileID", BindingFlags.NonPublic | BindingFlags.Static),
 | 
	
		
			
				|  |  | +                ref setNowStrMenuFileID);
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        private static void MakeStaticGetter<T>(FieldInfo field, ref T result) where T : Delegate
 | 
	
		
			
				|  |  | +        {
 | 
	
		
			
				|  |  | +            if (field == null)
 | 
	
		
			
				|  |  | +                return;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +            var dm = new DynamicMethod($"CacheEditMenu_Getter_{field.GetHashCode()}", field.FieldType, null,
 | 
	
		
			
				|  |  | +                typeof(Hooks), true);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +            var il = dm.GetILGenerator();
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +            il.Emit(OpCodes.Ldsfld, field);
 | 
	
		
			
				|  |  | +            il.Emit(OpCodes.Ret);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +            result = dm.CreateDelegate(typeof(T)) as T;
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        private static void MakeStaticSetter<T>(FieldInfo field, ref T result) where T : Delegate
 | 
	
		
			
				|  |  | +        {
 | 
	
		
			
				|  |  | +            if (field == null)
 | 
	
		
			
				|  |  | +                return;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +            var dm = new DynamicMethod($"CacheEditMenu_Getter_{field.GetHashCode()}", typeof(void),
 | 
	
		
			
				|  |  | +                new[] {field.FieldType}, typeof(Hooks), true);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +            var il = dm.GetILGenerator();
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +            il.Emit(OpCodes.Ldarg_0);
 | 
	
		
			
				|  |  | +            il.Emit(OpCodes.Stsfld, field);
 | 
	
		
			
				|  |  | +            il.Emit(OpCodes.Ret);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +            result = dm.CreateDelegate(typeof(T)) as T;
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        private static Assembly TryLoadFrom(string path)
 | 
	
		
			
				|  |  | +        {
 | 
	
		
			
				|  |  | +            try
 | 
	
		
			
				|  |  | +            {
 | 
	
		
			
				|  |  | +                return Assembly.LoadFrom(path);
 | 
	
		
			
				|  |  | +            }
 | 
	
		
			
				|  |  | +            catch (Exception)
 | 
	
		
			
				|  |  | +            {
 | 
	
		
			
				|  |  | +                return null;
 | 
	
		
			
				|  |  | +            }
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        public static bool Prefix(ref bool result, SceneEdit.SMenuItem mi, string menuFileName)
 | 
	
		
			
				|  |  | +        {
 | 
	
		
			
				|  |  | +            Init();
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +            if (menuFileName.Contains("_zurashi"))
 | 
	
		
			
				|  |  | +            {
 | 
	
		
			
				|  |  | +                result = false;
 | 
	
		
			
				|  |  | +                return false;
 | 
	
		
			
				|  |  | +            }
 | 
	
		
			
				|  |  | +            if (menuFileName.Contains("_mekure"))
 | 
	
		
			
				|  |  | +            {
 | 
	
		
			
				|  |  | +                result = false;
 | 
	
		
			
				|  |  | +                return false;
 | 
	
		
			
				|  |  | +            }
 | 
	
		
			
				|  |  | +            menuFileName = Path.GetFileName(menuFileName);
 | 
	
		
			
				|  |  | +            mi.m_strMenuFileName = menuFileName;
 | 
	
		
			
				|  |  | +            mi.m_nMenuFileRID = menuFileName.ToLower().GetHashCode();
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +            if (infoCache.TryGetValue(menuFileName, out var menuInfo))
 | 
	
		
			
				|  |  | +            {
 | 
	
		
			
				|  |  | +                menuInfo.CopyTo(mi);
 | 
	
		
			
				|  |  | +                result = true;
 | 
	
		
			
				|  |  | +                return false;
 | 
	
		
			
				|  |  | +            }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +            return true;
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        private static Stopwatch sw = null;
 | 
	
		
			
				|  |  | +        public static bool Postfix(bool result, SceneEdit.SMenuItem mi, string menuFileName)
 | 
	
		
			
				|  |  | +        {
 | 
	
		
			
				|  |  | +            if (!result)
 | 
	
		
			
				|  |  | +                return false;
 | 
	
		
			
				|  |  | +            
 | 
	
		
			
				|  |  | +            var menuInfo = new MenuInfo
 | 
	
		
			
				|  |  | +            {
 | 
	
		
			
				|  |  | +                mi = mi,
 | 
	
		
			
				|  |  | +                key = menuFileName
 | 
	
		
			
				|  |  | +            };
 | 
	
		
			
				|  |  | +            getTexFileIDDic().TryGetValue(mi.m_nMenuFileRID, out menuInfo.texName);
 | 
	
		
			
				|  |  | +            infoCache[menuFileName] = menuInfo;
 | 
	
		
			
				|  |  | +            
 | 
	
		
			
				|  |  | +            menuInfo.Serialize(cacheWriter);
 | 
	
		
			
				|  |  | +            
 | 
	
		
			
				|  |  | +            return true;
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        private static void Init()
 | 
	
		
			
				|  |  | +        {
 | 
	
		
			
				|  |  | +            if (cacheWriter != null)
 | 
	
		
			
				|  |  | +                return;
 | 
	
		
			
				|  |  | +            var cacheDir = Path.Combine(Patcher.Patcher.SybarisPath, "EditMenuCache");
 | 
	
		
			
				|  |  | +            var cachePath = Path.Combine(cacheDir, "EditMenuCache.dat");
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +            Directory.CreateDirectory(cacheDir);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +            if (File.Exists(cachePath))
 | 
	
		
			
				|  |  | +                using (var br = new BinaryReader(File.Open(cachePath, FileMode.Open, FileAccess.Read)))
 | 
	
		
			
				|  |  | +                {
 | 
	
		
			
				|  |  | +                    try
 | 
	
		
			
				|  |  | +                    {
 | 
	
		
			
				|  |  | +                        while (true)
 | 
	
		
			
				|  |  | +                        {
 | 
	
		
			
				|  |  | +                            var menuInfo = new MenuInfo();
 | 
	
		
			
				|  |  | +                            menuInfo.Deserialize(br);
 | 
	
		
			
				|  |  | +                            infoCache[menuInfo.key] = menuInfo;
 | 
	
		
			
				|  |  | +                        }
 | 
	
		
			
				|  |  | +                    }
 | 
	
		
			
				|  |  | +                    catch (EndOfStreamException)
 | 
	
		
			
				|  |  | +                    {
 | 
	
		
			
				|  |  | +                        // End of file, no need to read more
 | 
	
		
			
				|  |  | +                    }
 | 
	
		
			
				|  |  | +                }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +            cacheWriter = new BinaryWriter(File.OpenWrite(cachePath));
 | 
	
		
			
				|  |  | +            cacheWriter.BaseStream.Seek(0, SeekOrigin.End);
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        private class MenuInfo
 | 
	
		
			
				|  |  | +        {
 | 
	
		
			
				|  |  | +            public string key;
 | 
	
		
			
				|  |  | +            public SceneEdit.SMenuItem mi;
 | 
	
		
			
				|  |  | +            public string texName;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +            public void Deserialize(BinaryReader br)
 | 
	
		
			
				|  |  | +            {
 | 
	
		
			
				|  |  | +                key = br.ReadString();
 | 
	
		
			
				|  |  | +                texName = br.ReadNullableString();
 | 
	
		
			
				|  |  | +                mi = new SceneEdit.SMenuItem
 | 
	
		
			
				|  |  | +                {
 | 
	
		
			
				|  |  | +                    m_strMenuName = br.ReadString(),
 | 
	
		
			
				|  |  | +                    m_strInfo = br.ReadNullableString(),
 | 
	
		
			
				|  |  | +                    m_mpn = (MPN) br.ReadInt32(),
 | 
	
		
			
				|  |  | +                    m_strCateName = br.ReadString(),
 | 
	
		
			
				|  |  | +                    m_eColorSetMPN = (MPN) br.ReadInt32(),
 | 
	
		
			
				|  |  | +                    m_strMenuNameInColorSet = br.ReadNullableString(),
 | 
	
		
			
				|  |  | +                    m_pcMultiColorID = (MaidParts.PARTS_COLOR) br.ReadInt32(),
 | 
	
		
			
				|  |  | +                    m_boDelOnly = br.ReadBoolean(),
 | 
	
		
			
				|  |  | +                    m_fPriority = br.ReadSingle(),
 | 
	
		
			
				|  |  | +                    m_bMan = br.ReadBoolean()
 | 
	
		
			
				|  |  | +                };
 | 
	
		
			
				|  |  | +            }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +            public void Serialize(BinaryWriter bw)
 | 
	
		
			
				|  |  | +            {
 | 
	
		
			
				|  |  | +                bw.Write(key);
 | 
	
		
			
				|  |  | +                bw.WriteNullableString(texName);
 | 
	
		
			
				|  |  | +                bw.Write(mi.m_strMenuName);
 | 
	
		
			
				|  |  | +                bw.WriteNullableString(mi.m_strInfo);
 | 
	
		
			
				|  |  | +                bw.Write((int) mi.m_mpn);
 | 
	
		
			
				|  |  | +                bw.Write(mi.m_strCateName);
 | 
	
		
			
				|  |  | +                bw.Write((int) mi.m_eColorSetMPN);
 | 
	
		
			
				|  |  | +                bw.WriteNullableString(mi.m_strMenuNameInColorSet);
 | 
	
		
			
				|  |  | +                bw.Write((int) mi.m_pcMultiColorID);
 | 
	
		
			
				|  |  | +                bw.Write(mi.m_boDelOnly);
 | 
	
		
			
				|  |  | +                bw.Write(mi.m_fPriority);
 | 
	
		
			
				|  |  | +                bw.Write(mi.m_bMan);
 | 
	
		
			
				|  |  | +            }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +            public void CopyTo(SceneEdit.SMenuItem other)
 | 
	
		
			
				|  |  | +            {
 | 
	
		
			
				|  |  | +                other.m_strMenuName = mi.m_strMenuName;
 | 
	
		
			
				|  |  | +                other.m_strInfo = mi.m_strInfo;
 | 
	
		
			
				|  |  | +                other.m_mpn = mi.m_mpn;
 | 
	
		
			
				|  |  | +                other.m_strCateName = mi.m_strCateName;
 | 
	
		
			
				|  |  | +                other.m_eColorSetMPN = mi.m_eColorSetMPN;
 | 
	
		
			
				|  |  | +                other.m_strMenuNameInColorSet = mi.m_strMenuNameInColorSet;
 | 
	
		
			
				|  |  | +                other.m_pcMultiColorID = mi.m_pcMultiColorID;
 | 
	
		
			
				|  |  | +                other.m_boDelOnly = mi.m_boDelOnly;
 | 
	
		
			
				|  |  | +                other.m_fPriority = mi.m_fPriority;
 | 
	
		
			
				|  |  | +                other.m_bMan = mi.m_bMan;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +                if (string.IsNullOrEmpty(texName))
 | 
	
		
			
				|  |  | +                    return;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +                setNowMenuFlg(true);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +                setNowStrMenuFileID(other.m_nMenuFileRID);
 | 
	
		
			
				|  |  | +                getIdItemDic()[other.m_nMenuFileRID] = other;
 | 
	
		
			
				|  |  | +                other.m_texIcon = ImportCM.CreateTexture(texName);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +                setNowMenuFlg(false);
 | 
	
		
			
				|  |  | +            }
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +}
 |