Ver Fonte

Implement asset emulation

Bepis há 7 anos atrás
pai
commit
9f9b97c540

+ 5 - 0
BepInEx.Common/Utility.cs

@@ -14,5 +14,10 @@ namespace BepInEx.Common
 
         //shamelessly stolen from Rei
         public static string CombinePaths(params string[] parts) => parts.Aggregate(Path.Combine);
+
+        public static string ConvertToWWWFormat(string path)
+        {
+            return $"file://{path.Replace('\\', '/')}";
+        }
     }
 }

+ 49 - 0
Plugins/ResourceRedirector/AssetLoader.cs

@@ -0,0 +1,49 @@
+using BepInEx.Common;
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Text;
+using UnityEngine;
+
+namespace ResourceRedirector
+{
+    public static class AssetLoader
+    {
+        public static AudioClip LoadAudioClip(string path, AudioType type)
+        {
+            using (WWW loadGachi = new WWW(Utility.ConvertToWWWFormat(path)))
+            {
+                AudioClip clip = loadGachi.GetAudioClipCompressed(false, type);
+
+                //force single threaded loading instead of using a coroutine
+                while (!clip.isReadyToPlay) { }
+
+                return clip;
+            }
+        }
+
+        public static Texture2D LoadTexture(string path)
+        {
+            byte[] data = File.ReadAllBytes(path);
+
+            Texture2D tex = new Texture2D(2, 2);
+
+            //DDS method
+            //tex.LoadRawTextureData
+
+            tex.LoadImage(data);
+
+            return tex;
+        }
+
+        public static TextAsset LoadTextAsset(string path)
+        {
+            string newPath = $"{Application.dataPath}\\Resources\\{Path.GetFileName(path)}";
+
+            File.Copy(path, newPath, true);
+
+            return Resources.Load<TextAsset>(Path.GetFileNameWithoutExtension(path));
+        }
+    }
+}

+ 123 - 0
Plugins/ResourceRedirector/Hooks.cs

@@ -0,0 +1,123 @@
+using BepInEx;
+using Harmony;
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Reflection;
+using System.Text;
+using UnityEngine;
+
+namespace ResourceRedirector
+{
+    static class Hooks
+    {
+        public static void InstallHooks()
+        {
+            var harmony = HarmonyInstance.Create("com.bepis.bepinex.resourceredirector");
+
+
+            MethodInfo original = AccessTools.Method(typeof(AssetBundleManager), "LoadAsset", new[] { typeof(string), typeof(string), typeof(Type), typeof(string) });
+
+            HarmonyMethod postfix = new HarmonyMethod(typeof(Hooks).GetMethod("LoadAssetPostHook"));
+
+            harmony.Patch(original, null, postfix);
+
+
+            original = AccessTools.Method(typeof(AssetBundleManager), "LoadAssetBundle", new[] { typeof(string), typeof(bool), typeof(string) });
+
+            postfix = new HarmonyMethod(typeof(Hooks).GetMethod("LoadAssetBundlePostHook"));
+
+            harmony.Patch(original, null, postfix);
+
+
+
+            original = AccessTools.Method(typeof(AssetBundleManager), "LoadAssetAsync", new[] { typeof(string), typeof(string), typeof(Type), typeof(string) });
+
+            postfix = new HarmonyMethod(typeof(Hooks).GetMethod("LoadAssetAsyncPostHook"));
+
+            harmony.Patch(original, null, postfix);
+
+
+
+            original = AccessTools.Method(typeof(AssetBundleManager), "LoadAllAsset", new[] { typeof(string), typeof(Type), typeof(string) });
+
+            postfix = new HarmonyMethod(typeof(Hooks).GetMethod("LoadAllAssetPostHook"));
+
+            harmony.Patch(original, null, postfix);
+
+            //Singleton<Manager.Character>.Instance.chaListCtrl.LoadListInfoAll();
+        }
+
+        public static void LoadAssetPostHook(ref AssetBundleLoadAssetOperation __result, string assetBundleName, string assetName, Type type, string manifestAssetBundleName)
+        {
+            //Console.WriteLine($"{assetBundleName} : {assetName} : {type.FullName} : {manifestAssetBundleName ?? ""}");
+
+            __result = ResourceRedirector.HandleAsset(assetBundleName, assetName, type, manifestAssetBundleName, ref __result);
+        }
+
+        public static void LoadAssetBundlePostHook(string assetBundleName, bool isAsync, string manifestAssetBundleName)
+        {
+            //Console.WriteLine($"{assetBundleName} : {manifestAssetBundleName} : {isAsync}");
+        }
+
+        public static void LoadAssetAsyncPostHook(ref AssetBundleLoadAssetOperation __result, string assetBundleName, string assetName, Type type, string manifestAssetBundleName)
+        {
+            //Chainloader.Log($"{assetBundleName} : {assetName} : {type.FullName} : {manifestAssetBundleName ?? ""}", true);
+
+            __result = ResourceRedirector.HandleAsset(assetBundleName, assetName, type, manifestAssetBundleName, ref __result);
+        }
+
+        public static void LoadAllAssetPostHook(ref AssetBundleLoadAssetOperation __result, string assetBundleName, Type type, string manifestAssetBundleName = null)
+        {
+            Chainloader.Log($"{assetBundleName} : {type.FullName} : {manifestAssetBundleName ?? ""}");
+
+            if (assetBundleName == "sound/data/systemse/brandcall/00.unity3d" ||
+                assetBundleName == "sound/data/systemse/titlecall/00.unity3d")
+            {
+                string dir = $"{BepInEx.Common.Utility.PluginsDirectory}\\introclips";
+
+                if (!Directory.Exists(dir))
+                    Directory.CreateDirectory(dir);
+
+                var files = Directory.GetFiles(dir, "*.wav");
+
+                if (files.Length == 0)
+                    return;
+
+                List<UnityEngine.Object> loadedClips = new List<UnityEngine.Object>();
+
+                foreach (string path in files)
+                    loadedClips.Add(AssetLoader.LoadAudioClip(path, AudioType.WAV));
+
+                __result = new AssetBundleLoadAssetOperationSimulation(loadedClips.ToArray());
+            }
+
+            if (type == typeof(TextAsset))
+            {
+                string dir = Path.Combine(ResourceRedirector.EmulatedDir, assetBundleName.Replace('/', '\\').Replace(".unity3d", ""));
+
+                List<UnityEngine.Object> go = new List<UnityEngine.Object>();
+
+                foreach (TextAsset t in __result.GetAllAssets<TextAsset>())
+                {
+                    string path = Path.Combine(dir, $"{t.name}.txt");
+
+                    //Chainloader.Log(path);
+
+                    if (File.Exists(path))
+                    {
+                        Chainloader.Log($"Loading emulated asset {path}");
+                        go.Add(AssetLoader.LoadTextAsset(path));
+                    }
+                    else
+                    {
+                        go.Add(t);
+                    }
+                }
+
+                __result = new AssetBundleLoadAssetOperationSimulation(go.ToArray());
+            }
+        }
+    }
+}

+ 46 - 0
Plugins/ResourceRedirector/Loaders/BGMLoader.cs

@@ -0,0 +1,46 @@
+using BepInEx;
+using Illusion.Game;
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Text;
+using UnityEngine;
+
+namespace ResourceRedirector
+{
+    static class BGMLoader
+    {
+        public static bool HandleAsset(string assetBundleName, string assetName, Type type, string manifestAssetBundleName, out AssetBundleLoadAssetOperation result)
+        {
+            if (assetName.StartsWith("bgm"))
+            {
+                string path;
+
+                switch ((BGM)int.Parse(assetName.Remove(0, 4)))
+                {
+                    case BGM.Title:
+                    default:
+                        path = $"{BepInEx.Common.Utility.PluginsDirectory}\\title.wav";
+                        break;
+                    case BGM.Custom:
+                        path = $"{BepInEx.Common.Utility.PluginsDirectory}\\custom.wav";
+                        break;
+                }
+
+                if (File.Exists(path))
+                {
+
+                    Chainloader.Log($"Loading {path}");
+
+                    result = new AssetBundleLoadAssetOperationSimulation(AssetLoader.LoadAudioClip(path, AudioType.WAV));
+
+                    return true;
+                }
+            }
+
+            result = null;
+            return false;
+        }
+    }
+}

+ 42 - 0
Plugins/ResourceRedirector/Loaders/IntroVoiceClipLoader.cs

@@ -0,0 +1,42 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+namespace ResourceRedirector
+{
+    public static class IntroVoiceClipLoader
+    {
+        //public static bool HandleAsset(string assetBundleName, string assetName, Type type, string manifestAssetBundleName, out AssetBundleLoadAssetOperation result)
+        //{
+        //    if (assetName.StartsWith("bgm") && type == typeof(AudioClip))
+        //    {
+        //        string path;
+
+        //        switch ((BGM)int.Parse(assetName.Remove(0, 4)))
+        //        {
+        //            case BGM.Title:
+        //            default:
+        //                path = $"{BepInEx.Common.Utility.PluginsDirectory}\\title.wav";
+        //                break;
+        //            case BGM.Custom:
+        //                path = $"{BepInEx.Common.Utility.PluginsDirectory}\\custom.wav";
+        //                break;
+        //        }
+
+        //        if (File.Exists(path))
+        //        {
+
+        //            Chainloader.Log($"Loading {path}");
+
+        //            result = new AssetBundleLoadAssetOperationSimulation(AssetLoader.LoadAudioClip(path, AudioType.WAV));
+
+        //            return true;
+        //        }
+        //    }
+
+        //    result = null;
+        //    return false;
+        //}
+    }
+}

+ 45 - 67
Plugins/ResourceRedirector/ResourceRedirector.cs

@@ -1,8 +1,10 @@
 using BepInEx;
+using BepInEx.Common;
 using Harmony;
 using Illusion.Game;
 using System;
 using System.Collections;
+using System.Collections.Generic;
 using System.IO;
 using System.Reflection;
 using UnityEngine;
@@ -12,104 +14,80 @@ namespace ResourceRedirector
 {
     public class ResourceRedirector : BaseUnityPlugin
     {
-        public override string Name => "Resource Redirector";
-
-        public ResourceRedirector()
-        {
-            var harmony = HarmonyInstance.Create("com.bepis.bepinex.resourceredirector");
-
-
-
-            MethodInfo original = AccessTools.Method(typeof(AssetBundleManager), "LoadAsset", new[] { typeof(string), typeof(string), typeof(Type), typeof(string) });
-
-            HarmonyMethod postfix = new HarmonyMethod(typeof(ResourceRedirector).GetMethod("LoadAssetPostHook"));
-
-            harmony.Patch(original, null, postfix);
+        public override string Name => "Asset Emulator";
 
+        public static string EmulatedDir => Path.Combine(Utility.ExecutingDirectory, "abdata-emulated");
 
+        public static bool EmulationEnabled;
 
 
-            original = AccessTools.Method(typeof(AssetBundleManager), "LoadAllAsset", new[] { typeof(string), typeof(Type), typeof(string) });
 
-            postfix = new HarmonyMethod(typeof(ResourceRedirector).GetMethod("LoadAllAssetPostHook"));
+        public delegate bool AssetHandler(string assetBundleName, string assetName, Type type, string manifestAssetBundleName, out AssetBundleLoadAssetOperation result);
 
-            harmony.Patch(original, null, postfix);
+        public static List<AssetHandler> AssetResolvers = new List<AssetHandler>();
 
 
 
-            original = AccessTools.Method(typeof(Manager.Sound), "Bind");
+        public ResourceRedirector()
+        {
+            Hooks.InstallHooks();
 
-            var prefix = new HarmonyMethod(typeof(ResourceRedirector).GetMethod("BindPreHook"));
+            EmulationEnabled = Directory.Exists(EmulatedDir);
 
-            harmony.Patch(original, prefix, null);
+            AssetResolvers.Add(BGMLoader.HandleAsset);
         }
 
-        public static void LoadAssetPostHook(string assetBundleName, string assetName, Type type, string manifestAssetBundleName)
+        
+        public static AssetBundleLoadAssetOperation HandleAsset(string assetBundleName, string assetName, Type type, string manifestAssetBundleName, ref AssetBundleLoadAssetOperation __result)
         {
-            //Console.WriteLine($"{assetBundleName} : {assetName} : {type.FullName} : {manifestAssetBundleName ?? ""}");
-        }
-
-        public static void LoadAllAssetPostHook(string assetBundleName, Type type, string manifestAssetBundleName = null)
-        {
-            //Console.WriteLine($"{assetBundleName} : {type.FullName} : {manifestAssetBundleName ?? ""}");
-        }
-
-        public static FieldInfo f_typeObjects = typeof(Manager.Sound).GetField("typeObjects", BindingFlags.Instance | BindingFlags.NonPublic);
-        public static FieldInfo f_settingObjects = typeof(Manager.Sound).GetField("settingObjects", BindingFlags.Instance | BindingFlags.NonPublic);
-        public static PropertyInfo f_clip = typeof(LoadAudioBase).GetProperty("clip", BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public);
+            foreach (var handler in AssetResolvers)
+            {
+                if (handler.Invoke(assetBundleName, assetName, type, manifestAssetBundleName, out AssetBundleLoadAssetOperation result))
+                    return result;
+            }
 
+            //emulate asset load
+            string dir = Path.Combine(EmulatedDir, assetBundleName.Replace('/', '\\').Replace(".unity3d", ""));
 
-        public static void BindPreHook(ref LoadSound script)
-        {
-            if (script.clip.name.StartsWith("bgm"))
+            if (Directory.Exists(dir))
             {
-                string path;
-
-                switch ((BGM)int.Parse(script.clip.name.Remove(0, 4)))
+                if (type == typeof(Texture2D))
                 {
-                    case BGM.Title:
-                    default:
-                        path = $"{BepInEx.Common.Utility.PluginsDirectory}\\title.wav";
-                        break;
-                    case BGM.Custom:
-                        path = $"{BepInEx.Common.Utility.PluginsDirectory}\\custom.wav";
-                        break;
-                }
-
-                if (!File.Exists(path))
-                    return;
-
-
-                Chainloader.Log($"Loading {path}");
+                    string path = Path.Combine(dir, $"{assetName}.png");
 
-                path = $"file://{path.Replace('\\', '/')}";
+                    if (!File.Exists(path))
+                        return __result;
 
+                    Chainloader.Log($"Loading emulated asset {path}");
 
-                if (script.audioSource == null)
+                    return new AssetBundleLoadAssetOperationSimulation(AssetLoader.LoadTexture(path));
+                }
+                else if (type == typeof(AudioClip))
                 {
-                    int type = (int)script.type;
-
-                    Transform[] typeObjects = (Transform[])f_typeObjects.GetValue(Manager.Game.Instance);
-                    GameObject[] settingObjects = (GameObject[])f_settingObjects.GetValue(Manager.Game.Instance);
+                    string path = Path.Combine(dir, $"{assetName}.wav");
 
-                    Manager.Sound.Instance.SetParent(typeObjects[type], script, settingObjects[type]);
-                }
+                    if (!File.Exists(path))
+                        return __result;
 
+                    Chainloader.Log($"Loading emulated asset {path}");
 
-                using (WWW loadGachi = new WWW(path))
+                    return new AssetBundleLoadAssetOperationSimulation(AssetLoader.LoadAudioClip(path, AudioType.WAV));
+                }
+                else if (type == typeof(TextAsset))
                 {
-                    AudioClip clip = loadGachi.GetAudioClipCompressed(false, AudioType.WAV);
+                    string path = Path.Combine(dir, $"{assetName}.bytes");
 
+                    if (!File.Exists(path))
+                        return __result;
 
-                    //force single threaded loading instead of using a coroutine
-                    while (!clip.isReadyToPlay) { }
+                    Chainloader.Log($"Loading emulated asset {path}");
 
-
-                    script.audioSource.clip = clip;
-
-                    f_clip.SetValue(script, clip, null);
+                    return new AssetBundleLoadAssetOperationSimulation(AssetLoader.LoadTextAsset(path));
                 }
             }
+
+            //otherwise return normal asset
+            return __result;
         }
     }
 }

+ 4 - 0
Plugins/ResourceRedirector/ResourceRedirector.csproj

@@ -50,6 +50,10 @@
     </Reference>
   </ItemGroup>
   <ItemGroup>
+    <Compile Include="AssetLoader.cs" />
+    <Compile Include="Loaders\BGMLoader.cs" />
+    <Compile Include="Hooks.cs" />
+    <Compile Include="Loaders\IntroVoiceClipLoader.cs" />
     <Compile Include="ResourceRedirector.cs" />
     <Compile Include="Properties\AssemblyInfo.cs" />
   </ItemGroup>