From da23fcae8300a2b6f2be414af8f2b98cbf5497b3 Mon Sep 17 00:00:00 2001 From: Maxime Cauvin Date: Tue, 19 Mar 2019 23:15:56 +0900 Subject: [PATCH] Support for 0.13 --- .../DrinkWaterPanel.cs | 0 .../IngameInformationsCounter.cs | 0 ...iewControllerContinueButtonPressedPatch.cs | 0 ...ViewControllerRestartButtonPressedPatch.cs | 0 .../{BeatSaberDrinkWater => 0.12.2}/Plugin.cs | 0 .../Properties/AssemblyInfo.cs | 0 .../Settings/PluginConfig.cs | 0 .../Settings/SettingsUI.cs | 0 .../UniGif/UniGif.cs | 0 .../UniGif/UniGifConst.cs | 0 .../UniGif/UniGifDecoder.cs | 0 .../UniGif/UniGifExtension.cs | 0 .../UniGif/UniGifFormatter.cs | 0 .../UniGif/UniGifImage.cs | 0 .../Utility/UniGifImageAspectController.cs | 0 .../Utilities/ReflectionUtil.cs | 0 .../Utilities/SceneUtils.cs | 0 BeatSaberDrinkWater/0.13/DrinkWaterPanel.cs | 192 ++++++ .../0.13/IngameInformationsCounter.cs | 80 +++ ...iewControllerContinueButtonPressedPatch.cs | 23 + ...ViewControllerRestartButtonPressedPatch.cs | 23 + BeatSaberDrinkWater/0.13/Plugin.cs | 86 +++ .../0.13/Properties/AssemblyInfo.cs | 36 ++ .../0.13/Settings/PluginConfig.cs | 102 +++ .../0.13/Settings/SettingsUI.cs | 69 ++ BeatSaberDrinkWater/0.13/UniGif/UniGif.cs | 75 +++ .../0.13/UniGif/UniGifConst.cs | 278 ++++++++ .../0.13/UniGif/UniGifDecoder.cs | 582 +++++++++++++++++ .../0.13/UniGif/UniGifExtension.cs | 59 ++ .../0.13/UniGif/UniGifFormatter.cs | 606 ++++++++++++++++++ .../0.13/UniGif/UniGifImage.cs | 361 +++++++++++ .../Utility/UniGifImageAspectController.cs | 92 +++ .../0.13/Utilities/ReflectionUtil.cs | 204 ++++++ .../0.13/Utilities/SceneUtils.cs | 31 + 34 files changed, 2899 insertions(+) rename BeatSaberDrinkWater/{BeatSaberDrinkWater => 0.12.2}/DrinkWaterPanel.cs (100%) rename BeatSaberDrinkWater/{BeatSaberDrinkWater => 0.12.2}/IngameInformationsCounter.cs (100%) rename BeatSaberDrinkWater/{BeatSaberDrinkWater => 0.12.2}/Patches/HandleResultsViewControllerContinueButtonPressedPatch.cs (100%) rename BeatSaberDrinkWater/{BeatSaberDrinkWater => 0.12.2}/Patches/HandleResultsViewControllerRestartButtonPressedPatch.cs (100%) rename BeatSaberDrinkWater/{BeatSaberDrinkWater => 0.12.2}/Plugin.cs (100%) rename BeatSaberDrinkWater/{BeatSaberDrinkWater => 0.12.2}/Properties/AssemblyInfo.cs (100%) rename BeatSaberDrinkWater/{BeatSaberDrinkWater => 0.12.2}/Settings/PluginConfig.cs (100%) rename BeatSaberDrinkWater/{BeatSaberDrinkWater => 0.12.2}/Settings/SettingsUI.cs (100%) rename BeatSaberDrinkWater/{BeatSaberDrinkWater => 0.12.2}/UniGif/UniGif.cs (100%) rename BeatSaberDrinkWater/{BeatSaberDrinkWater => 0.12.2}/UniGif/UniGifConst.cs (100%) rename BeatSaberDrinkWater/{BeatSaberDrinkWater => 0.12.2}/UniGif/UniGifDecoder.cs (100%) rename BeatSaberDrinkWater/{BeatSaberDrinkWater => 0.12.2}/UniGif/UniGifExtension.cs (100%) rename BeatSaberDrinkWater/{BeatSaberDrinkWater => 0.12.2}/UniGif/UniGifFormatter.cs (100%) rename BeatSaberDrinkWater/{BeatSaberDrinkWater => 0.12.2}/UniGif/UniGifImage.cs (100%) rename BeatSaberDrinkWater/{BeatSaberDrinkWater => 0.12.2}/UniGif/Utility/UniGifImageAspectController.cs (100%) rename BeatSaberDrinkWater/{BeatSaberDrinkWater => 0.12.2}/Utilities/ReflectionUtil.cs (100%) rename BeatSaberDrinkWater/{BeatSaberDrinkWater => 0.12.2}/Utilities/SceneUtils.cs (100%) create mode 100644 BeatSaberDrinkWater/0.13/DrinkWaterPanel.cs create mode 100644 BeatSaberDrinkWater/0.13/IngameInformationsCounter.cs create mode 100644 BeatSaberDrinkWater/0.13/Patches/HandleResultsViewControllerContinueButtonPressedPatch.cs create mode 100644 BeatSaberDrinkWater/0.13/Patches/HandleResultsViewControllerRestartButtonPressedPatch.cs create mode 100644 BeatSaberDrinkWater/0.13/Plugin.cs create mode 100644 BeatSaberDrinkWater/0.13/Properties/AssemblyInfo.cs create mode 100644 BeatSaberDrinkWater/0.13/Settings/PluginConfig.cs create mode 100644 BeatSaberDrinkWater/0.13/Settings/SettingsUI.cs create mode 100644 BeatSaberDrinkWater/0.13/UniGif/UniGif.cs create mode 100644 BeatSaberDrinkWater/0.13/UniGif/UniGifConst.cs create mode 100644 BeatSaberDrinkWater/0.13/UniGif/UniGifDecoder.cs create mode 100644 BeatSaberDrinkWater/0.13/UniGif/UniGifExtension.cs create mode 100644 BeatSaberDrinkWater/0.13/UniGif/UniGifFormatter.cs create mode 100644 BeatSaberDrinkWater/0.13/UniGif/UniGifImage.cs create mode 100644 BeatSaberDrinkWater/0.13/UniGif/Utility/UniGifImageAspectController.cs create mode 100644 BeatSaberDrinkWater/0.13/Utilities/ReflectionUtil.cs create mode 100644 BeatSaberDrinkWater/0.13/Utilities/SceneUtils.cs diff --git a/BeatSaberDrinkWater/BeatSaberDrinkWater/DrinkWaterPanel.cs b/BeatSaberDrinkWater/0.12.2/DrinkWaterPanel.cs similarity index 100% rename from BeatSaberDrinkWater/BeatSaberDrinkWater/DrinkWaterPanel.cs rename to BeatSaberDrinkWater/0.12.2/DrinkWaterPanel.cs diff --git a/BeatSaberDrinkWater/BeatSaberDrinkWater/IngameInformationsCounter.cs b/BeatSaberDrinkWater/0.12.2/IngameInformationsCounter.cs similarity index 100% rename from BeatSaberDrinkWater/BeatSaberDrinkWater/IngameInformationsCounter.cs rename to BeatSaberDrinkWater/0.12.2/IngameInformationsCounter.cs diff --git a/BeatSaberDrinkWater/BeatSaberDrinkWater/Patches/HandleResultsViewControllerContinueButtonPressedPatch.cs b/BeatSaberDrinkWater/0.12.2/Patches/HandleResultsViewControllerContinueButtonPressedPatch.cs similarity index 100% rename from BeatSaberDrinkWater/BeatSaberDrinkWater/Patches/HandleResultsViewControllerContinueButtonPressedPatch.cs rename to BeatSaberDrinkWater/0.12.2/Patches/HandleResultsViewControllerContinueButtonPressedPatch.cs diff --git a/BeatSaberDrinkWater/BeatSaberDrinkWater/Patches/HandleResultsViewControllerRestartButtonPressedPatch.cs b/BeatSaberDrinkWater/0.12.2/Patches/HandleResultsViewControllerRestartButtonPressedPatch.cs similarity index 100% rename from BeatSaberDrinkWater/BeatSaberDrinkWater/Patches/HandleResultsViewControllerRestartButtonPressedPatch.cs rename to BeatSaberDrinkWater/0.12.2/Patches/HandleResultsViewControllerRestartButtonPressedPatch.cs diff --git a/BeatSaberDrinkWater/BeatSaberDrinkWater/Plugin.cs b/BeatSaberDrinkWater/0.12.2/Plugin.cs similarity index 100% rename from BeatSaberDrinkWater/BeatSaberDrinkWater/Plugin.cs rename to BeatSaberDrinkWater/0.12.2/Plugin.cs diff --git a/BeatSaberDrinkWater/BeatSaberDrinkWater/Properties/AssemblyInfo.cs b/BeatSaberDrinkWater/0.12.2/Properties/AssemblyInfo.cs similarity index 100% rename from BeatSaberDrinkWater/BeatSaberDrinkWater/Properties/AssemblyInfo.cs rename to BeatSaberDrinkWater/0.12.2/Properties/AssemblyInfo.cs diff --git a/BeatSaberDrinkWater/BeatSaberDrinkWater/Settings/PluginConfig.cs b/BeatSaberDrinkWater/0.12.2/Settings/PluginConfig.cs similarity index 100% rename from BeatSaberDrinkWater/BeatSaberDrinkWater/Settings/PluginConfig.cs rename to BeatSaberDrinkWater/0.12.2/Settings/PluginConfig.cs diff --git a/BeatSaberDrinkWater/BeatSaberDrinkWater/Settings/SettingsUI.cs b/BeatSaberDrinkWater/0.12.2/Settings/SettingsUI.cs similarity index 100% rename from BeatSaberDrinkWater/BeatSaberDrinkWater/Settings/SettingsUI.cs rename to BeatSaberDrinkWater/0.12.2/Settings/SettingsUI.cs diff --git a/BeatSaberDrinkWater/BeatSaberDrinkWater/UniGif/UniGif.cs b/BeatSaberDrinkWater/0.12.2/UniGif/UniGif.cs similarity index 100% rename from BeatSaberDrinkWater/BeatSaberDrinkWater/UniGif/UniGif.cs rename to BeatSaberDrinkWater/0.12.2/UniGif/UniGif.cs diff --git a/BeatSaberDrinkWater/BeatSaberDrinkWater/UniGif/UniGifConst.cs b/BeatSaberDrinkWater/0.12.2/UniGif/UniGifConst.cs similarity index 100% rename from BeatSaberDrinkWater/BeatSaberDrinkWater/UniGif/UniGifConst.cs rename to BeatSaberDrinkWater/0.12.2/UniGif/UniGifConst.cs diff --git a/BeatSaberDrinkWater/BeatSaberDrinkWater/UniGif/UniGifDecoder.cs b/BeatSaberDrinkWater/0.12.2/UniGif/UniGifDecoder.cs similarity index 100% rename from BeatSaberDrinkWater/BeatSaberDrinkWater/UniGif/UniGifDecoder.cs rename to BeatSaberDrinkWater/0.12.2/UniGif/UniGifDecoder.cs diff --git a/BeatSaberDrinkWater/BeatSaberDrinkWater/UniGif/UniGifExtension.cs b/BeatSaberDrinkWater/0.12.2/UniGif/UniGifExtension.cs similarity index 100% rename from BeatSaberDrinkWater/BeatSaberDrinkWater/UniGif/UniGifExtension.cs rename to BeatSaberDrinkWater/0.12.2/UniGif/UniGifExtension.cs diff --git a/BeatSaberDrinkWater/BeatSaberDrinkWater/UniGif/UniGifFormatter.cs b/BeatSaberDrinkWater/0.12.2/UniGif/UniGifFormatter.cs similarity index 100% rename from BeatSaberDrinkWater/BeatSaberDrinkWater/UniGif/UniGifFormatter.cs rename to BeatSaberDrinkWater/0.12.2/UniGif/UniGifFormatter.cs diff --git a/BeatSaberDrinkWater/BeatSaberDrinkWater/UniGif/UniGifImage.cs b/BeatSaberDrinkWater/0.12.2/UniGif/UniGifImage.cs similarity index 100% rename from BeatSaberDrinkWater/BeatSaberDrinkWater/UniGif/UniGifImage.cs rename to BeatSaberDrinkWater/0.12.2/UniGif/UniGifImage.cs diff --git a/BeatSaberDrinkWater/BeatSaberDrinkWater/UniGif/Utility/UniGifImageAspectController.cs b/BeatSaberDrinkWater/0.12.2/UniGif/Utility/UniGifImageAspectController.cs similarity index 100% rename from BeatSaberDrinkWater/BeatSaberDrinkWater/UniGif/Utility/UniGifImageAspectController.cs rename to BeatSaberDrinkWater/0.12.2/UniGif/Utility/UniGifImageAspectController.cs diff --git a/BeatSaberDrinkWater/BeatSaberDrinkWater/Utilities/ReflectionUtil.cs b/BeatSaberDrinkWater/0.12.2/Utilities/ReflectionUtil.cs similarity index 100% rename from BeatSaberDrinkWater/BeatSaberDrinkWater/Utilities/ReflectionUtil.cs rename to BeatSaberDrinkWater/0.12.2/Utilities/ReflectionUtil.cs diff --git a/BeatSaberDrinkWater/BeatSaberDrinkWater/Utilities/SceneUtils.cs b/BeatSaberDrinkWater/0.12.2/Utilities/SceneUtils.cs similarity index 100% rename from BeatSaberDrinkWater/BeatSaberDrinkWater/Utilities/SceneUtils.cs rename to BeatSaberDrinkWater/0.12.2/Utilities/SceneUtils.cs diff --git a/BeatSaberDrinkWater/0.13/DrinkWaterPanel.cs b/BeatSaberDrinkWater/0.13/DrinkWaterPanel.cs new file mode 100644 index 0000000..3fb5384 --- /dev/null +++ b/BeatSaberDrinkWater/0.13/DrinkWaterPanel.cs @@ -0,0 +1,192 @@ +using BeatSaberDrinkWater.Settings; +using BeatSaberDrinkWater.UniGif; +using BeatSaberDrinkWater.UniGif.Utility; +using BeatSaberDrinkWater.Utilities; +using CustomUI.BeatSaber; +using System; +using System.Collections; +using System.Linq; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using TMPro; +using UnityEngine; +using UnityEngine.UI; + +namespace BeatSaberDrinkWater +{ + class DrinkWaterPanel : MonoBehaviour + { + public enum DrinkWaterPanelMode : int + { + UNKNOWN = -1, + CONTINUE = 0, + RESTART = 1 + } + + private CustomMenu _CustomMenu = null; + private CustomViewController _CustomViewController = null; + private UniGifImage _UniGifImage = null; + private RawImage _RawImage = null; + private string[] _GifRotation; + + private DrinkWaterPanelMode _CurrentPanelMode; + private TextMeshProUGUI _TextContent; + private Button _ContinueButton; + + public bool Initialized = false; + public bool DisplayPanelNeeded = false; + + private static DrinkWaterPanel _Instance = null; + public static DrinkWaterPanel Instance + { + get + { + if (!_Instance) + { + _Instance = new GameObject("[BeatSaverDrinkWater] DrinkWaterPanel").AddComponent(); + DontDestroyOnLoad(_Instance.gameObject); + } + return _Instance; + } + private set + { + _Instance = value; + } + } + + public void OnLoad() + { + Initialized = false; + _GifRotation = new string[] { "https://media1.tenor.com/images/013d560bab2b0fc56a2bc43b8262b4ed/tenor.gif", "https://i.giphy.com/zWOnltJgKVlsc.gif", + "https://i.giphy.com/3ohhwF34cGDoFFhRfy.gif", "https://i.giphy.com/eRBa4tzlbNwE8.gif" }; + _SetupUI(); + } + + public void OnMapFinished() + { + IngameInformationsCounter.Instance.PlayerHasFinishedMap(); + if (PluginConfig.EnableByPlaytime && IngameInformationsCounter.Instance.IngameTimeSpent.TotalMinutes >= PluginConfig.PlaytimeBeforeWarning) + { + IngameInformationsCounter.Instance.ResetTimeSpent(); + DisplayPanelNeeded = true; + } + else if (PluginConfig.EnableByPlaycount && IngameInformationsCounter.Instance.CurrentPlaycount >= PluginConfig.PlaycountBeforeWarning) + { + IngameInformationsCounter.Instance.ResetPlaycount(); + DisplayPanelNeeded = true; + } + } + + private void _SetupDrinkWaterPanel() + { + if (_CustomMenu != null && _CustomViewController != null) + return; + + _CustomMenu = BeatSaberUI.CreateCustomMenu("Drink some water!"); + _CustomViewController = BeatSaberUI.CreateViewController(); + _CustomViewController.name = "DrinkWaterPanel"; + + if (_CustomMenu != null && _CustomViewController != null) + { + _CustomMenu.SetMainViewController(_CustomViewController, false, (firstActivation, type) => + { + if (firstActivation && type == VRUI.VRUIViewController.ActivationType.AddedToHierarchy) + { + _TextContent = _CustomViewController.CreateText("", new Vector2(0, 28f)); + _TextContent.alignment = TextAlignmentOptions.Center; + _TextContent.fontSize = 5; + _TextContent.enableWordWrapping = false; + _ContinueButton = _CustomViewController.CreateUIButton("CreditsButton", new Vector2(0, -28f), new Vector2(37f, 10f), + null, "I understand!"); + _ContinueButton.ToggleWordWrapping(false); + _ContinueButton.SetButtonTextSize(4); + _ContinueButton.onClick.AddListener(delegate () { _CustomMenu.Dismiss(); Destroy(_CustomViewController.gameObject, 1); _CustomMenu = null; _CustomViewController = null; }); + + _RefreshTextContent(_CurrentPanelMode); + + if (PluginConfig.ShowGIFs) + { + GameObject go = new GameObject("[BeatSaberDrinkWater] PreviewGif"); + _RawImage = go.AddComponent(); + _RawImage.material = Instantiate(Resources.FindObjectsOfTypeAll().Where(m => m.name == "UINoGlow").FirstOrDefault()); + go.transform.SetParent(_CustomViewController.transform, false); + go.GetComponent().anchoredPosition = new Vector2(0, 0); + go.GetComponent().sizeDelta = new Vector2(30, 30); + _UniGifImage = go.AddComponent(); + UniGifImageAspectController ugiac = go.AddComponent(); + _UniGifImage.SetPrivateField("m_imgAspectCtrl", ugiac); + StartCoroutine(_DisplayGifFromRotation()); + } + } + }); + } + else + Console.WriteLine("[BeatSaberCustomExit.MainMenuUIOverload]: '_CustomMenu' or '_CustomViewController' was null."); + } + + public void ShowDrinkWaterPanel(DrinkWaterPanelMode mode) + { + if (_CustomMenu != null && _CustomViewController != null) + { + _CurrentPanelMode = mode; + if (PluginConfig.ShowGIFs) + StartCoroutine(_DisplayGifFromRotation()); + _CustomMenu.Present(); + _RefreshTextContent(mode); + StartCoroutine(MakeButtonInteractableDelay(_ContinueButton, PluginConfig.WaitDuration, 0.1f, "0.0")); + DisplayPanelNeeded = false; + } + } + + public IEnumerator MakeButtonInteractableDelay(Button button, float duration, float delayStep = 1f, string format = "0", bool showInButton = true) + { + string buttonTextContent = button.GetComponentInChildren().text; + if (showInButton) + button.SetButtonText(buttonTextContent + ((duration > 0) ? (" (" + duration.ToString(format) + ")") : (""))); + button.interactable = false; + while (duration > 0) + { + yield return new WaitForSeconds(delayStep); + duration -= delayStep; + if (duration < 0) duration = 0; + if (showInButton) + button.SetButtonText(buttonTextContent + ((duration > 0) ? (" (" + duration.ToString(format) + ")") : (""))); + } + button.interactable = true; + } + + private IEnumerator _DisplayGifFromRotation() + { + if (_UniGifImage != null) + { + Coroutine gifCoroutine = null; + _RawImage.enabled = false; + try + { + gifCoroutine = StartCoroutine(_UniGifImage.SetGifFromUrlCoroutine(_GifRotation[UnityEngine.Random.Range(0, _GifRotation.Length)])); + } + catch (Exception e) + { + Console.WriteLine("Exception catched on displaying gif: " + e); + } + yield return gifCoroutine; + _RawImage.enabled = true; + } + } + + private void _RefreshTextContent(DrinkWaterPanelMode mode) + { + if (_TextContent != null) + _TextContent.text = ((_CurrentPanelMode == DrinkWaterPanelMode.RESTART) ? ("Before restarting this song") : ("Before browsing some new songs")) + ", drink some water, that's important for your body!"; + } + + private void _SetupUI() + { + if (Initialized) return; + + _SetupDrinkWaterPanel(); + Initialized = true; + } + } +} diff --git a/BeatSaberDrinkWater/0.13/IngameInformationsCounter.cs b/BeatSaberDrinkWater/0.13/IngameInformationsCounter.cs new file mode 100644 index 0000000..4e3af1b --- /dev/null +++ b/BeatSaberDrinkWater/0.13/IngameInformationsCounter.cs @@ -0,0 +1,80 @@ +using BeatSaberDrinkWater.Utilities; +using System; +using System.Collections; +using TMPro; +using UnityEngine; +using UnityEngine.SceneManagement; + +namespace BeatSaberDrinkWater +{ + public class IngameInformationsCounter : MonoBehaviour + { + public static IngameInformationsCounter Instance; + + public TimeSpan IngameTimeSpent; + public int CurrentPlaycount; + private Coroutine _CUpdateIngameTimeSpentClock; + + public static void OnLoad() + { + if (Instance != null) return; + new GameObject("IngameTimeSpentClock").AddComponent(); + } + + public void Awake() + { + if (Instance == null) + { + Instance = this; + SceneManager.activeSceneChanged += SceneManagerOnActiveSceneChanged; + DontDestroyOnLoad(gameObject); + CurrentPlaycount = 0; + IngameTimeSpent = new TimeSpan(0); + } + else + Destroy(this); + } + + public void SceneManagerOnActiveSceneChanged(Scene arg0, Scene scene) + { + if (SceneUtils.IsGameScene(scene)) + { + if (_CUpdateIngameTimeSpentClock != null) + StopCoroutine(_CUpdateIngameTimeSpentClock); + _CUpdateIngameTimeSpentClock = StartCoroutine(UpdateIngameTimeSpentClock()); + } + else if (SceneUtils.IsMenuScene(scene)) + { + if (_CUpdateIngameTimeSpentClock != null) + { + StopCoroutine(_CUpdateIngameTimeSpentClock); + _CUpdateIngameTimeSpentClock = null; + } + } + } + + public IEnumerator UpdateIngameTimeSpentClock() + { + while (IngameTimeSpent != null) + { + IngameTimeSpent = IngameTimeSpent.Add(new TimeSpan(0, 0, 1)); + yield return new WaitForSeconds(1f); + } + } + + public void PlayerHasFinishedMap() + { + CurrentPlaycount += 1; + } + + public void ResetTimeSpent() + { + IngameTimeSpent = new TimeSpan(0); + } + + public void ResetPlaycount() + { + CurrentPlaycount = 0; + } + } +} diff --git a/BeatSaberDrinkWater/0.13/Patches/HandleResultsViewControllerContinueButtonPressedPatch.cs b/BeatSaberDrinkWater/0.13/Patches/HandleResultsViewControllerContinueButtonPressedPatch.cs new file mode 100644 index 0000000..da8b9fa --- /dev/null +++ b/BeatSaberDrinkWater/0.13/Patches/HandleResultsViewControllerContinueButtonPressedPatch.cs @@ -0,0 +1,23 @@ +using Harmony; +using System; +using UnityEngine; +using UnityEngine.UI; +using VRUI; + +namespace BeatSaberDrinkWater.Patches +{ + [HarmonyPatch(typeof(SoloFreePlayFlowCoordinator), "HandleResultsViewControllerContinueButtonPressed", + new Type[] { typeof(VRUIViewController) })] + class HandleResultsViewControllerContinueButtonPressedPatch + { + public static bool Prefix(VRUIViewController viewController) + { + if (DrinkWaterPanel.Instance.DisplayPanelNeeded) + { + DrinkWaterPanel.Instance.ShowDrinkWaterPanel(DrinkWaterPanel.DrinkWaterPanelMode.CONTINUE); + return false; + } + return true; + } + } +} \ No newline at end of file diff --git a/BeatSaberDrinkWater/0.13/Patches/HandleResultsViewControllerRestartButtonPressedPatch.cs b/BeatSaberDrinkWater/0.13/Patches/HandleResultsViewControllerRestartButtonPressedPatch.cs new file mode 100644 index 0000000..41f1b4c --- /dev/null +++ b/BeatSaberDrinkWater/0.13/Patches/HandleResultsViewControllerRestartButtonPressedPatch.cs @@ -0,0 +1,23 @@ +using Harmony; +using System; +using UnityEngine; +using UnityEngine.UI; +using VRUI; + +namespace BeatSaberDrinkWater.Patches +{ + [HarmonyPatch(typeof(SoloFreePlayFlowCoordinator), "HandleResultsViewControllerRestartButtonPressed", + new Type[] { typeof(VRUIViewController) })] + class HandleResultsViewControllerRestartButtonPressedPatch + { + public static bool Prefix(VRUIViewController viewController) + { + if (DrinkWaterPanel.Instance.DisplayPanelNeeded) + { + DrinkWaterPanel.Instance.ShowDrinkWaterPanel(DrinkWaterPanel.DrinkWaterPanelMode.RESTART); + return false; + } + return true; + } + } +} \ No newline at end of file diff --git a/BeatSaberDrinkWater/0.13/Plugin.cs b/BeatSaberDrinkWater/0.13/Plugin.cs new file mode 100644 index 0000000..d7fb1b9 --- /dev/null +++ b/BeatSaberDrinkWater/0.13/Plugin.cs @@ -0,0 +1,86 @@ +using BeatSaberDrinkWater.Settings; +using BeatSaberDrinkWater.Utilities; +using Harmony; +using IllusionPlugin; +using System; +using System.Reflection; +using UnityEngine.SceneManagement; + +namespace BeatSaberDrinkWater +{ + public class Plugin : IPlugin + { + public string Name => "BeatSaberDrinkWater"; + public string Version => "0.10.0"; + public void OnApplicationStart() + { + IngameInformationsCounter.OnLoad(); + SceneManager.activeSceneChanged += SceneManagerOnActiveSceneChanged; + SceneManager.sceneLoaded += SceneManager_sceneLoaded; + PluginConfig.LoadOrCreateConfig(); + HarmonyInstance harmony = HarmonyInstance.Create("com.Shoko84.beatsaber.BeatSaberDrinkWater"); + harmony.PatchAll(Assembly.GetExecutingAssembly()); + } + + public void OnApplicationQuit() + { + SceneManager.activeSceneChanged -= SceneManagerOnActiveSceneChanged; + SceneManager.sceneLoaded -= SceneManager_sceneLoaded; + PluginConfig.SaveConfig(); + } + + private void SceneManagerOnActiveSceneChanged(Scene from, Scene to) + { + if (from.name == "EmptyTransition" && SceneUtils.IsMenuScene(to)) + { + try + { + SettingsUI.Instance.OnLoad(); + if (PluginConfig.EnablePlugin) + DrinkWaterPanel.Instance.OnLoad(); + } + catch (Exception e) + { + Console.WriteLine("Exception on scene change: " + e); + } + } + + if (SceneUtils.IsGameScene(from) && SceneUtils.IsMenuScene(to)) + { + try + { + if (PluginConfig.EnablePlugin) + { + DrinkWaterPanel.Instance.OnLoad(); + DrinkWaterPanel.Instance.OnMapFinished(); + } + } + catch (Exception e) + { + Console.WriteLine("Exception on scene change: " + e); + } + } + } + + private void SceneManager_sceneLoaded(Scene arg0, LoadSceneMode arg1) + { + } + + public void OnLevelWasLoaded(int level) + { + + } + + public void OnLevelWasInitialized(int level) + { + } + + public void OnUpdate() + { + } + + public void OnFixedUpdate() + { + } + } +} diff --git a/BeatSaberDrinkWater/0.13/Properties/AssemblyInfo.cs b/BeatSaberDrinkWater/0.13/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..6e4d506 --- /dev/null +++ b/BeatSaberDrinkWater/0.13/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("BeatSaberDrinkWater")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("BeatSaberDrinkWater")] +[assembly: AssemblyCopyright("Copyright © 2018")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("000f5e7b-9f74-43d8-b205-0b76b2a03f54")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("0.10.0")] +[assembly: AssemblyFileVersion("0.10.0")] diff --git a/BeatSaberDrinkWater/0.13/Settings/PluginConfig.cs b/BeatSaberDrinkWater/0.13/Settings/PluginConfig.cs new file mode 100644 index 0000000..0bb8c2c --- /dev/null +++ b/BeatSaberDrinkWater/0.13/Settings/PluginConfig.cs @@ -0,0 +1,102 @@ +using IllusionPlugin; +using Microsoft.Win32; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Xml.Serialization; + +namespace BeatSaberDrinkWater.Settings +{ + public class PluginConfig + { + public static bool EnablePlugin = true; + public static bool ShowGIFs = true; + public static int WaitDuration = 5; + public static bool EnableByPlaytime = true; + public static bool EnableByPlaycount = false; + public static int PlaytimeBeforeWarning = 5; + public static int PlaycountBeforeWarning = 2; + + public static void LoadOrCreateConfig() + { + if (!Directory.Exists("UserData")) + Directory.CreateDirectory("UserData"); + + //EnablePlugin property + if (!ModPrefs.HasKey("BeatSaberDrinkWater", "EnablePlugin")) + { + ModPrefs.SetBool("BeatSaberDrinkWater", "EnablePlugin", true); + Console.WriteLine("Created config"); + } + else + EnablePlugin = ModPrefs.GetBool("BeatSaberDrinkWater", "EnablePlugin", true, true); + + //ShowGIFs property + if (!ModPrefs.HasKey("BeatSaberDrinkWater", "ShowGIFs")) + { + ModPrefs.SetBool("BeatSaberDrinkWater", "ShowGIFs", true); + Console.WriteLine("Created config"); + } + else + EnablePlugin = ModPrefs.GetBool("BeatSaberDrinkWater", "ShowGIFs", true, true); + + //WaitDuration property + if (!ModPrefs.HasKey("BeatSaberDrinkWater", "WaitDuration")) + { + ModPrefs.SetInt("BeatSaberDrinkWater", "WaitDuration", 5); + Console.WriteLine("Created config"); + } + else + WaitDuration = ModPrefs.GetInt("BeatSaberDrinkWater", "WaitDuration", 5, true); + + //EnableByPlaytime property + if (!ModPrefs.HasKey("BeatSaberDrinkWater", "EnableByPlaytime")) + { + ModPrefs.SetBool("BeatSaberDrinkWater", "EnableByPlaytime", true); + Console.WriteLine("Created config"); + } + else + EnableByPlaytime = ModPrefs.GetBool("BeatSaberDrinkWater", "EnableByPlaytime", true, true); + + //EnableByPlaycount property + if (!ModPrefs.HasKey("BeatSaberDrinkWater", "EnableByPlaycount")) + { + ModPrefs.SetBool("BeatSaberDrinkWater", "EnableByPlaycount", false); + Console.WriteLine("Created config"); + } + else + EnableByPlaycount = ModPrefs.GetBool("BeatSaberDrinkWater", "EnableByPlaycount", false, true); + + //PlaytimeBeforeWarning property + if (!ModPrefs.HasKey("BeatSaberDrinkWater", "PlaytimeBeforeWarning")) + { + ModPrefs.SetInt("BeatSaberDrinkWater", "PlaytimeBeforeWarning", 5); + Console.WriteLine("Created config"); + } + else + PlaytimeBeforeWarning = ModPrefs.GetInt("BeatSaberDrinkWater", "PlaytimeBeforeWarning", 5, true); + + //PlaycountBeforeWarning property + if (!ModPrefs.HasKey("BeatSaberDrinkWater", "PlaycountBeforeWarning")) + { + ModPrefs.SetInt("BeatSaberDrinkWater", "PlaycountBeforeWarning", 2); + Console.WriteLine("Created config"); + } + else + PlaycountBeforeWarning = ModPrefs.GetInt("BeatSaberDrinkWater", "PlaycountBeforeWarning", 2, true); + } + + public static void SaveConfig() + { + ModPrefs.SetBool("BeatSaberDrinkWater", "EnablePlugin", EnablePlugin); + ModPrefs.SetBool("BeatSaberDrinkWater", "ShowGIFs", ShowGIFs); + ModPrefs.SetInt("BeatSaberDrinkWater", "WaitDuration", WaitDuration); + ModPrefs.SetBool("BeatSaberDrinkWater", "EnableByPlaytime", EnableByPlaytime); + ModPrefs.SetBool("BeatSaberDrinkWater", "EnableByPlaycount", EnableByPlaycount); + ModPrefs.SetInt("BeatSaberDrinkWater", "PlaytimeBeforeWarning", PlaytimeBeforeWarning); + ModPrefs.SetInt("BeatSaberDrinkWater", "PlaycountBeforeWarning", PlaycountBeforeWarning); + } + } +} \ No newline at end of file diff --git a/BeatSaberDrinkWater/0.13/Settings/SettingsUI.cs b/BeatSaberDrinkWater/0.13/Settings/SettingsUI.cs new file mode 100644 index 0000000..674c5ce --- /dev/null +++ b/BeatSaberDrinkWater/0.13/Settings/SettingsUI.cs @@ -0,0 +1,69 @@ +using System.Collections.Generic; +using System.Linq; +using UnityEngine; +using CustomUI.Utilities; +using CustomUI.MenuButton; +using CustomUI.Settings; + +namespace BeatSaberDrinkWater.Settings +{ + class SettingsUI : MonoBehaviour + { + public bool Initialized = false; + + private static SettingsUI _Instance = null; + public static SettingsUI Instance + { + get + { + if (!_Instance) + { + _Instance = new GameObject("[BeatSaberDrinkWater] SettingsUI").AddComponent(); + DontDestroyOnLoad(_Instance.gameObject); + } + return _Instance; + } + private set + { + _Instance = value; + } + } + + public void OnLoad() + { + Initialized = false; + SetupUI(); + } + + private void SetupUI() + { + if (Initialized) return; + + var customExitSubMenu = CustomUI.Settings.SettingsUI.CreateSubMenu("Drink Water"); + + var enablePlugin = customExitSubMenu.AddBool("Enable", "Enable or not the plugin"); + enablePlugin.GetValue += delegate { return PluginConfig.EnablePlugin; }; + enablePlugin.SetValue += delegate (bool value) { PluginConfig.EnablePlugin = value; PluginConfig.SaveConfig(); }; + var showGIFs = customExitSubMenu.AddBool("Show GIFs", "Display or not GIFs on the warning panel"); + showGIFs.GetValue += delegate { return PluginConfig.ShowGIFs; }; + showGIFs.SetValue += delegate (bool value) { PluginConfig.ShowGIFs = value; PluginConfig.SaveConfig(); }; + var waitDuration = customExitSubMenu.AddInt("Wait duration", "Waiting time in seconds before skipping the warning panel", 0, 30, 1); + waitDuration.GetValue += delegate { return PluginConfig.WaitDuration; }; + waitDuration.SetValue += delegate (int value) { PluginConfig.WaitDuration = value; PluginConfig.SaveConfig(); }; + var enableByPlaytime = customExitSubMenu.AddBool("Enable by playtime", "Enable the plugin depending of the playtime"); + enableByPlaytime.GetValue += delegate { return PluginConfig.EnableByPlaytime; }; + enableByPlaytime.SetValue += delegate (bool value) { PluginConfig.EnableByPlaytime = value; PluginConfig.SaveConfig(); }; + var enableByPlaycount = customExitSubMenu.AddBool("Enable by play count", "Enable the plugin depending of the play count"); + enableByPlaycount.GetValue += delegate { return PluginConfig.EnableByPlaycount; }; + enableByPlaycount.SetValue += delegate (bool value) { PluginConfig.EnableByPlaycount = value; PluginConfig.SaveConfig(); }; + var playtimeBeforeWarning = customExitSubMenu.AddInt("Playtime warning", "How much playtime before being warned in minutes ('Enable by playtime' must be true)", 1, 30, 1); + playtimeBeforeWarning.GetValue += delegate { return PluginConfig.PlaytimeBeforeWarning; }; + playtimeBeforeWarning.SetValue += delegate (int value) { PluginConfig.PlaytimeBeforeWarning = value; PluginConfig.SaveConfig(); }; + var playcountBeforeWarning = customExitSubMenu.AddInt("Playcount warning", "How much play count before being warned ('Enable by play count' must be true)", 1, 5, 1); + playcountBeforeWarning.GetValue += delegate { return PluginConfig.PlaycountBeforeWarning; }; + playcountBeforeWarning.SetValue += delegate (int value) { PluginConfig.PlaycountBeforeWarning = value; PluginConfig.SaveConfig(); }; + + Initialized = true; + } + } +} diff --git a/BeatSaberDrinkWater/0.13/UniGif/UniGif.cs b/BeatSaberDrinkWater/0.13/UniGif/UniGif.cs new file mode 100644 index 0000000..1649d0b --- /dev/null +++ b/BeatSaberDrinkWater/0.13/UniGif/UniGif.cs @@ -0,0 +1,75 @@ +/* +UniGif +Copyright (c) 2015 WestHillApps (Hironari Nishioka) +This software is released under the MIT License. +http://opensource.org/licenses/mit-license.php +*/ + +using System; +using System.Collections; +using System.Collections.Generic; +using UnityEngine; + +namespace BeatSaberDrinkWater.UniGif +{ + public static partial class UniGif + { + /// + /// Get GIF texture list Coroutine + /// + /// GIF file byte data + /// Callback method(param is GIF texture list, Animation loop count, GIF image width (px), GIF image height (px)) + /// Textures filter mode + /// Textures wrap mode + /// Debug Log Flag + /// IEnumerator + public static IEnumerator GetTextureListCoroutine( + byte[] bytes, + Action, int, int, int> callback, + FilterMode filterMode = FilterMode.Bilinear, + TextureWrapMode wrapMode = TextureWrapMode.Clamp, + bool debugLog = false) + { + int loopCount = -1; + int width = 0; + int height = 0; + + // Set GIF data + var gifData = new GifData(); + if (SetGifData(bytes, ref gifData, debugLog) == false) + { + Debug.LogError("GIF file data set error."); + if (callback != null) + { + callback(null, loopCount, width, height); + } + yield break; + } + + // Decode to textures from GIF data + List gifTexList = null; + yield return DecodeTextureCoroutine(gifData, result => gifTexList = result, filterMode, wrapMode); + + if (gifTexList == null || gifTexList.Count <= 0) + { + Debug.LogError("GIF texture decode error."); + if (callback != null) + { + callback(null, loopCount, width, height); + } + yield break; + } + + loopCount = gifData.m_appEx.loopCount; + width = gifData.m_logicalScreenWidth; + height = gifData.m_logicalScreenHeight; + + if (callback != null) + { + callback(gifTexList, loopCount, width, height); + } + + yield break; + } + } +} \ No newline at end of file diff --git a/BeatSaberDrinkWater/0.13/UniGif/UniGifConst.cs b/BeatSaberDrinkWater/0.13/UniGif/UniGifConst.cs new file mode 100644 index 0000000..7872d15 --- /dev/null +++ b/BeatSaberDrinkWater/0.13/UniGif/UniGifConst.cs @@ -0,0 +1,278 @@ +/* +UniGif +Copyright (c) 2015 WestHillApps (Hironari Nishioka) +This software is released under the MIT License. +http://opensource.org/licenses/mit-license.php +*/ + +using System; +using System.Collections.Generic; +using System.Text; +using UnityEngine; + +namespace BeatSaberDrinkWater.UniGif +{ + public static partial class UniGif + { + /// + /// Gif Texture + /// + public class GifTexture + { + // Texture + public Texture2D m_texture2d; + // Delay time until the next texture. + public float m_delaySec; + + public GifTexture(Texture2D texture2d, float delaySec) + { + m_texture2d = texture2d; + m_delaySec = delaySec; + } + } + + /// + /// GIF Data Format + /// + private struct GifData + { + // Signature + public byte m_sig0, m_sig1, m_sig2; + // Version + public byte m_ver0, m_ver1, m_ver2; + // Logical Screen Width + public ushort m_logicalScreenWidth; + // Logical Screen Height + public ushort m_logicalScreenHeight; + // Global Color Table Flag + public bool m_globalColorTableFlag; + // Color Resolution + public int m_colorResolution; + // Sort Flag + public bool m_sortFlag; + // Size of Global Color Table + public int m_sizeOfGlobalColorTable; + // Background Color Index + public byte m_bgColorIndex; + // Pixel Aspect Ratio + public byte m_pixelAspectRatio; + // Global Color Table + public List m_globalColorTable; + // ImageBlock + public List m_imageBlockList; + // GraphicControlExtension + public List m_graphicCtrlExList; + // Comment Extension + public List m_commentExList; + // Plain Text Extension + public List m_plainTextExList; + // Application Extension + public ApplicationExtension m_appEx; + // Trailer + public byte m_trailer; + + public string signature + { + get + { + char[] c = { (char)m_sig0, (char)m_sig1, (char)m_sig2 }; + return new string(c); + } + } + + public string version + { + get + { + char[] c = { (char)m_ver0, (char)m_ver1, (char)m_ver2 }; + return new string(c); + } + } + + public void Dump() + { + Debug.Log("GIF Type: " + signature + "-" + version); + Debug.Log("Image Size: " + m_logicalScreenWidth + "x" + m_logicalScreenHeight); + Debug.Log("Animation Image Count: " + m_imageBlockList.Count); + Debug.Log("Animation Loop Count (0 is infinite): " + m_appEx.loopCount); + if (m_graphicCtrlExList != null && m_graphicCtrlExList.Count > 0) + { + var sb = new StringBuilder("Animation Delay Time (1/100sec)"); + for (int i = 0; i < m_graphicCtrlExList.Count; i++) + { + sb.Append(", "); + sb.Append(m_graphicCtrlExList[i].m_delayTime); + } + Debug.Log(sb.ToString()); + } + Debug.Log("Application Identifier: " + m_appEx.applicationIdentifier); + Debug.Log("Application Authentication Code: " + m_appEx.applicationAuthenticationCode); + } + } + + /// + /// Image Block + /// + private struct ImageBlock + { + // Image Separator + public byte m_imageSeparator; + // Image Left Position + public ushort m_imageLeftPosition; + // Image Top Position + public ushort m_imageTopPosition; + // Image Width + public ushort m_imageWidth; + // Image Height + public ushort m_imageHeight; + // Local Color Table Flag + public bool m_localColorTableFlag; + // Interlace Flag + public bool m_interlaceFlag; + // Sort Flag + public bool m_sortFlag; + // Size of Local Color Table + public int m_sizeOfLocalColorTable; + // Local Color Table + public List m_localColorTable; + // LZW Minimum Code Size + public byte m_lzwMinimumCodeSize; + // Block Size & Image Data List + public List m_imageDataList; + + public struct ImageDataBlock + { + // Block Size + public byte m_blockSize; + // Image Data + public byte[] m_imageData; + } + } + + /// + /// Graphic Control Extension + /// + private struct GraphicControlExtension + { + // Extension Introducer + public byte m_extensionIntroducer; + // Graphic Control Label + public byte m_graphicControlLabel; + // Block Size + public byte m_blockSize; + // Disposal Mothod + public ushort m_disposalMethod; + // Transparent Color Flag + public bool m_transparentColorFlag; + // Delay Time + public ushort m_delayTime; + // Transparent Color Index + public byte m_transparentColorIndex; + // Block Terminator + public byte m_blockTerminator; + } + + /// + /// Comment Extension + /// + private struct CommentExtension + { + // Extension Introducer + public byte m_extensionIntroducer; + // Comment Label + public byte m_commentLabel; + // Block Size & Comment Data List + public List m_commentDataList; + + public struct CommentDataBlock + { + // Block Size + public byte m_blockSize; + // Image Data + public byte[] m_commentData; + } + } + + /// + /// Plain Text Extension + /// + private struct PlainTextExtension + { + // Extension Introducer + public byte m_extensionIntroducer; + // Plain Text Label + public byte m_plainTextLabel; + // Block Size + public byte m_blockSize; + // Block Size & Plain Text Data List + public List m_plainTextDataList; + + public struct PlainTextDataBlock + { + // Block Size + public byte m_blockSize; + // Plain Text Data + public byte[] m_plainTextData; + } + } + + /// + /// Application Extension + /// + private struct ApplicationExtension + { + // Extension Introducer + public byte m_extensionIntroducer; + // Extension Label + public byte m_extensionLabel; + // Block Size + public byte m_blockSize; + // Application Identifier + public byte m_appId1, m_appId2, m_appId3, m_appId4, m_appId5, m_appId6, m_appId7, m_appId8; + // Application Authentication Code + public byte m_appAuthCode1, m_appAuthCode2, m_appAuthCode3; + // Block Size & Application Data List + public List m_appDataList; + + public struct ApplicationDataBlock + { + // Block Size + public byte m_blockSize; + // Application Data + public byte[] m_applicationData; + } + + public string applicationIdentifier + { + get + { + char[] c = { (char)m_appId1, (char)m_appId2, (char)m_appId3, (char)m_appId4, (char)m_appId5, (char)m_appId6, (char)m_appId7, (char)m_appId8 }; + return new string(c); + } + } + + public string applicationAuthenticationCode + { + get + { + char[] c = { (char)m_appAuthCode1, (char)m_appAuthCode2, (char)m_appAuthCode3 }; + return new string(c); + } + } + + public int loopCount + { + get + { + if (m_appDataList == null || m_appDataList.Count < 1 || + m_appDataList[0].m_applicationData.Length < 3 || + m_appDataList[0].m_applicationData[0] != 0x01) + { + return 0; + } + return BitConverter.ToUInt16(m_appDataList[0].m_applicationData, 1); + } + } + } + } +} \ No newline at end of file diff --git a/BeatSaberDrinkWater/0.13/UniGif/UniGifDecoder.cs b/BeatSaberDrinkWater/0.13/UniGif/UniGifDecoder.cs new file mode 100644 index 0000000..841c4d9 --- /dev/null +++ b/BeatSaberDrinkWater/0.13/UniGif/UniGifDecoder.cs @@ -0,0 +1,582 @@ +/* +UniGif +Copyright (c) 2015 WestHillApps (Hironari Nishioka) +This software is released under the MIT License. +http://opensource.org/licenses/mit-license.php +*/ + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Text; +using UnityEngine; + +namespace BeatSaberDrinkWater.UniGif +{ + public static partial class UniGif + { + /// + /// Decode to textures from GIF data + /// + /// GIF data + /// Callback method(param is GIF texture list) + /// Textures filter mode + /// Textures wrap mode + /// IEnumerator + private static IEnumerator DecodeTextureCoroutine(GifData gifData, Action> callback, FilterMode filterMode, TextureWrapMode wrapMode) + { + if (gifData.m_imageBlockList == null || gifData.m_imageBlockList.Count < 1) + { + yield break; + } + + List gifTexList = new List(gifData.m_imageBlockList.Count); + List disposalMethodList = new List(gifData.m_imageBlockList.Count); + + int imgIndex = 0; + + for (int i = 0; i < gifData.m_imageBlockList.Count; i++) + { + byte[] decodedData = GetDecodedData(gifData.m_imageBlockList[i]); + + GraphicControlExtension? graphicCtrlEx = GetGraphicCtrlExt(gifData, imgIndex); + + int transparentIndex = GetTransparentIndex(graphicCtrlEx); + + disposalMethodList.Add(GetDisposalMethod(graphicCtrlEx)); + + Color32 bgColor; + List colorTable = GetColorTableAndSetBgColor(gifData, gifData.m_imageBlockList[i], transparentIndex, out bgColor); + + yield return 0; + + bool filledTexture; + Texture2D tex = CreateTexture2D(gifData, gifTexList, imgIndex, disposalMethodList, bgColor, filterMode, wrapMode, out filledTexture); + + yield return 0; + + // Set pixel data + int dataIndex = 0; + // Reverse set pixels. because GIF data starts from the top left. + for (int y = tex.height - 1; y >= 0; y--) + { + SetTexturePixelRow(tex, y, gifData.m_imageBlockList[i], decodedData, ref dataIndex, colorTable, bgColor, transparentIndex, filledTexture); + } + tex.Apply(); + + yield return 0; + + float delaySec = GetDelaySec(graphicCtrlEx); + + // Add to GIF texture list + gifTexList.Add(new GifTexture(tex, delaySec)); + + imgIndex++; + } + + if (callback != null) + { + callback(gifTexList); + } + + yield break; + } + + #region Call from DecodeTexture methods + + /// + /// Get decoded image data from ImageBlock + /// + private static byte[] GetDecodedData(ImageBlock imgBlock) + { + // Combine LZW compressed data + List lzwData = new List(); + for (int i = 0; i < imgBlock.m_imageDataList.Count; i++) + { + for (int k = 0; k < imgBlock.m_imageDataList[i].m_imageData.Length; k++) + { + lzwData.Add(imgBlock.m_imageDataList[i].m_imageData[k]); + } + } + + // LZW decode + int needDataSize = imgBlock.m_imageHeight * imgBlock.m_imageWidth; + byte[] decodedData = DecodeGifLZW(lzwData, imgBlock.m_lzwMinimumCodeSize, needDataSize); + + // Sort interlace GIF + if (imgBlock.m_interlaceFlag) + { + decodedData = SortInterlaceGifData(decodedData, imgBlock.m_imageWidth); + } + return decodedData; + } + + /// + /// Get color table and set background color (local or global) + /// + private static List GetColorTableAndSetBgColor(GifData gifData, ImageBlock imgBlock, int transparentIndex, out Color32 bgColor) + { + List colorTable = imgBlock.m_localColorTableFlag ? imgBlock.m_localColorTable : gifData.m_globalColorTableFlag ? gifData.m_globalColorTable : null; + + if (colorTable != null) + { + // Set background color from color table + byte[] bgRgb = colorTable[gifData.m_bgColorIndex]; + bgColor = new Color32(bgRgb[0], bgRgb[1], bgRgb[2], (byte)(transparentIndex == gifData.m_bgColorIndex ? 0 : 255)); + } + else + { + bgColor = Color.black; + } + + return colorTable; + } + + /// + /// Get GraphicControlExtension from GifData + /// + private static GraphicControlExtension? GetGraphicCtrlExt(GifData gifData, int imgBlockIndex) + { + if (gifData.m_graphicCtrlExList != null && gifData.m_graphicCtrlExList.Count > imgBlockIndex) + { + return gifData.m_graphicCtrlExList[imgBlockIndex]; + } + return null; + } + + /// + /// Get transparent color index from GraphicControlExtension + /// + private static int GetTransparentIndex(GraphicControlExtension? graphicCtrlEx) + { + int transparentIndex = -1; + if (graphicCtrlEx != null && graphicCtrlEx.Value.m_transparentColorFlag) + { + transparentIndex = graphicCtrlEx.Value.m_transparentColorIndex; + } + return transparentIndex; + } + + /// + /// Get delay seconds from GraphicControlExtension + /// + private static float GetDelaySec(GraphicControlExtension? graphicCtrlEx) + { + // Get delay sec from GraphicControlExtension + float delaySec = graphicCtrlEx != null ? graphicCtrlEx.Value.m_delayTime / 100f : (1f / 60f); + if (delaySec <= 0f) + { + delaySec = 0.1f; + } + return delaySec; + } + + /// + /// Get disposal method from GraphicControlExtension + /// + private static ushort GetDisposalMethod(GraphicControlExtension? graphicCtrlEx) + { + return graphicCtrlEx != null ? graphicCtrlEx.Value.m_disposalMethod : (ushort)2; + } + + /// + /// Create Texture2D object and initial settings + /// + private static Texture2D CreateTexture2D(GifData gifData, List gifTexList, int imgIndex, List disposalMethodList, Color32 bgColor, FilterMode filterMode, TextureWrapMode wrapMode, out bool filledTexture) + { + filledTexture = false; + + // Create texture + Texture2D tex = new Texture2D(gifData.m_logicalScreenWidth, gifData.m_logicalScreenHeight, TextureFormat.ARGB32, false); + tex.filterMode = filterMode; + tex.wrapMode = wrapMode; + + // Check dispose + ushort disposalMethod = imgIndex > 0 ? disposalMethodList[imgIndex - 1] : (ushort)2; + int useBeforeIndex = -1; + if (disposalMethod == 0) + { + // 0 (No disposal specified) + } + else if (disposalMethod == 1) + { + // 1 (Do not dispose) + useBeforeIndex = imgIndex - 1; + } + else if (disposalMethod == 2) + { + // 2 (Restore to background color) + filledTexture = true; + Color32[] pix = new Color32[tex.width * tex.height]; + for (int i = 0; i < pix.Length; i++) + { + pix[i] = bgColor; + } + tex.SetPixels32(pix); + tex.Apply(); + } + else if (disposalMethod == 3) + { + // 3 (Restore to previous) + for (int i = imgIndex - 1; i >= 0; i--) + { + if (disposalMethodList[i] == 0 || disposalMethodList[i] == 1) + { + useBeforeIndex = i; + break; + } + } + } + + if (useBeforeIndex >= 0) + { + filledTexture = true; + Color32[] pix = gifTexList[useBeforeIndex].m_texture2d.GetPixels32(); + tex.SetPixels32(pix); + tex.Apply(); + } + + return tex; + } + + /// + /// Set texture pixel row + /// + private static void SetTexturePixelRow(Texture2D tex, int y, ImageBlock imgBlock, byte[] decodedData, ref int dataIndex, List colorTable, Color32 bgColor, int transparentIndex, bool filledTexture) + { + // Row no (0~) + int row = tex.height - 1 - y; + + for (int x = 0; x < tex.width; x++) + { + // Line no (0~) + int line = x; + + // Out of image blocks + if (row < imgBlock.m_imageTopPosition || + row >= imgBlock.m_imageTopPosition + imgBlock.m_imageHeight || + line < imgBlock.m_imageLeftPosition || + line >= imgBlock.m_imageLeftPosition + imgBlock.m_imageWidth) + { + // Get pixel color from bg color + if (filledTexture == false) + { + tex.SetPixel(x, y, bgColor); + } + continue; + } + + // Out of decoded data + if (dataIndex >= decodedData.Length) + { + if (filledTexture == false) + { + tex.SetPixel(x, y, bgColor); + if (dataIndex == decodedData.Length) + { + Debug.LogError("dataIndex exceeded the size of decodedData. dataIndex:" + dataIndex + " decodedData.Length:" + decodedData.Length + " y:" + y + " x:" + x); + } + } + dataIndex++; + continue; + } + + // Get pixel color from color table + { + byte colorIndex = decodedData[dataIndex]; + if (colorTable == null || colorTable.Count <= colorIndex) + { + if (filledTexture == false) + { + tex.SetPixel(x, y, bgColor); + if (colorTable == null) + { + Debug.LogError("colorIndex exceeded the size of colorTable. colorTable is null. colorIndex:" + colorIndex); + } + else + { + Debug.LogError("colorIndex exceeded the size of colorTable. colorTable.Count:" + colorTable.Count + " colorIndex:" + colorIndex); + } + } + dataIndex++; + continue; + } + byte[] rgb = colorTable[colorIndex]; + + // Set alpha + byte alpha = transparentIndex >= 0 && transparentIndex == colorIndex ? (byte)0 : (byte)255; + + if (filledTexture == false || alpha != 0) + { + // Set color + Color32 col = new Color32(rgb[0], rgb[1], rgb[2], alpha); + tex.SetPixel(x, y, col); + } + } + + dataIndex++; + } + } + + #endregion + + #region Decode LZW & Sort interrace methods + + /// + /// GIF LZW decode + /// + /// LZW compressed data + /// LZW minimum code size + /// Need decoded data size + /// Decoded data array + private static byte[] DecodeGifLZW(List compData, int lzwMinimumCodeSize, int needDataSize) + { + int clearCode = 0; + int finishCode = 0; + + // Initialize dictionary + Dictionary dic = new Dictionary(); + int lzwCodeSize = 0; + InitDictionary(dic, lzwMinimumCodeSize, out lzwCodeSize, out clearCode, out finishCode); + + // Convert to bit array + byte[] compDataArr = compData.ToArray(); + var bitData = new BitArray(compDataArr); + + byte[] output = new byte[needDataSize]; + int outputAddIndex = 0; + + string prevEntry = null; + + bool dicInitFlag = false; + + int bitDataIndex = 0; + + // LZW decode loop + while (bitDataIndex < bitData.Length) + { + if (dicInitFlag) + { + InitDictionary(dic, lzwMinimumCodeSize, out lzwCodeSize, out clearCode, out finishCode); + dicInitFlag = false; + } + + int key = bitData.GetNumeral(bitDataIndex, lzwCodeSize); + + string entry = null; + + if (key == clearCode) + { + // Clear (Initialize dictionary) + dicInitFlag = true; + bitDataIndex += lzwCodeSize; + prevEntry = null; + continue; + } + else if (key == finishCode) + { + // Exit + Debug.LogWarning("early stop code. bitDataIndex:" + bitDataIndex + " lzwCodeSize:" + lzwCodeSize + " key:" + key + " dic.Count:" + dic.Count); + break; + } + else if (dic.ContainsKey(key)) + { + // Output from dictionary + entry = dic[key]; + } + else if (key >= dic.Count) + { + if (prevEntry != null) + { + // Output from estimation + entry = prevEntry + prevEntry[0]; + } + else + { + Debug.LogWarning("It is strange that come here. bitDataIndex:" + bitDataIndex + " lzwCodeSize:" + lzwCodeSize + " key:" + key + " dic.Count:" + dic.Count); + bitDataIndex += lzwCodeSize; + continue; + } + } + else + { + Debug.LogWarning("It is strange that come here. bitDataIndex:" + bitDataIndex + " lzwCodeSize:" + lzwCodeSize + " key:" + key + " dic.Count:" + dic.Count); + bitDataIndex += lzwCodeSize; + continue; + } + + // Output + // Take out 8 bits from the string. + byte[] temp = Encoding.Unicode.GetBytes(entry); + for (int i = 0; i < temp.Length; i++) + { + if (i % 2 == 0) + { + output[outputAddIndex] = temp[i]; + outputAddIndex++; + } + } + + if (outputAddIndex >= needDataSize) + { + // Exit + break; + } + + if (prevEntry != null) + { + // Add to dictionary + dic.Add(dic.Count, prevEntry + entry[0]); + } + + prevEntry = entry; + + bitDataIndex += lzwCodeSize; + + if (lzwCodeSize == 3 && dic.Count >= 8) + { + lzwCodeSize = 4; + } + else if (lzwCodeSize == 4 && dic.Count >= 16) + { + lzwCodeSize = 5; + } + else if (lzwCodeSize == 5 && dic.Count >= 32) + { + lzwCodeSize = 6; + } + else if (lzwCodeSize == 6 && dic.Count >= 64) + { + lzwCodeSize = 7; + } + else if (lzwCodeSize == 7 && dic.Count >= 128) + { + lzwCodeSize = 8; + } + else if (lzwCodeSize == 8 && dic.Count >= 256) + { + lzwCodeSize = 9; + } + else if (lzwCodeSize == 9 && dic.Count >= 512) + { + lzwCodeSize = 10; + } + else if (lzwCodeSize == 10 && dic.Count >= 1024) + { + lzwCodeSize = 11; + } + else if (lzwCodeSize == 11 && dic.Count >= 2048) + { + lzwCodeSize = 12; + } + else if (lzwCodeSize == 12 && dic.Count >= 4096) + { + int nextKey = bitData.GetNumeral(bitDataIndex, lzwCodeSize); + if (nextKey != clearCode) + { + dicInitFlag = true; + } + } + } + + return output; + } + + /// + /// Initialize dictionary + /// + /// Dictionary + /// LZW minimum code size + /// out LZW code size + /// out Clear code + /// out Finish code + private static void InitDictionary(Dictionary dic, int lzwMinimumCodeSize, out int lzwCodeSize, out int clearCode, out int finishCode) + { + int dicLength = (int)Math.Pow(2, lzwMinimumCodeSize); + + clearCode = dicLength; + finishCode = clearCode + 1; + + dic.Clear(); + + for (int i = 0; i < dicLength + 2; i++) + { + dic.Add(i, ((char)i).ToString()); + } + + lzwCodeSize = lzwMinimumCodeSize + 1; + } + + /// + /// Sort interlace GIF data + /// + /// Decoded GIF data + /// Pixel number of horizontal row + /// Sorted data + private static byte[] SortInterlaceGifData(byte[] decodedData, int xNum) + { + int rowNo = 0; + int dataIndex = 0; + var newArr = new byte[decodedData.Length]; + // Every 8th. row, starting with row 0. + for (int i = 0; i < newArr.Length; i++) + { + if (rowNo % 8 == 0) + { + newArr[i] = decodedData[dataIndex]; + dataIndex++; + } + if (i != 0 && i % xNum == 0) + { + rowNo++; + } + } + rowNo = 0; + // Every 8th. row, starting with row 4. + for (int i = 0; i < newArr.Length; i++) + { + if (rowNo % 8 == 4) + { + newArr[i] = decodedData[dataIndex]; + dataIndex++; + } + if (i != 0 && i % xNum == 0) + { + rowNo++; + } + } + rowNo = 0; + // Every 4th. row, starting with row 2. + for (int i = 0; i < newArr.Length; i++) + { + if (rowNo % 4 == 2) + { + newArr[i] = decodedData[dataIndex]; + dataIndex++; + } + if (i != 0 && i % xNum == 0) + { + rowNo++; + } + } + rowNo = 0; + // Every 2nd. row, starting with row 1. + for (int i = 0; i < newArr.Length; i++) + { + if (rowNo % 8 != 0 && rowNo % 8 != 4 && rowNo % 4 != 2) + { + newArr[i] = decodedData[dataIndex]; + dataIndex++; + } + if (i != 0 && i % xNum == 0) + { + rowNo++; + } + } + + return newArr; + } + + #endregion + } +} \ No newline at end of file diff --git a/BeatSaberDrinkWater/0.13/UniGif/UniGifExtension.cs b/BeatSaberDrinkWater/0.13/UniGif/UniGifExtension.cs new file mode 100644 index 0000000..7b89c4a --- /dev/null +++ b/BeatSaberDrinkWater/0.13/UniGif/UniGifExtension.cs @@ -0,0 +1,59 @@ +using UnityEngine; +using System.Collections; + +namespace BeatSaberDrinkWater.UniGif +{ + /// + /// Extension methods class + /// + public static class UniGifExtension + { + /// + /// Convert BitArray to int (Specifies the start index and bit length) + /// + /// Start index + /// Bit length + /// Converted int + public static int GetNumeral(this BitArray array, int startIndex, int bitLength) + { + var newArray = new BitArray(bitLength); + + for (int i = 0; i < bitLength; i++) + { + if (array.Length <= startIndex + i) + { + newArray[i] = false; + } + else + { + bool bit = array.Get(startIndex + i); + newArray[i] = bit; + } + } + + return newArray.ToNumeral(); + } + + /// + /// Convert BitArray to int + /// + /// Converted int + public static int ToNumeral(this BitArray array) + { + if (array == null) + { + Debug.LogError("array is nothing."); + return 0; + } + if (array.Length > 32) + { + Debug.LogError("must be at most 32 bits long."); + return 0; + } + + var result = new int[1]; + array.CopyTo(result, 0); + return result[0]; + } + } +} \ No newline at end of file diff --git a/BeatSaberDrinkWater/0.13/UniGif/UniGifFormatter.cs b/BeatSaberDrinkWater/0.13/UniGif/UniGifFormatter.cs new file mode 100644 index 0000000..4e78fa8 --- /dev/null +++ b/BeatSaberDrinkWater/0.13/UniGif/UniGifFormatter.cs @@ -0,0 +1,606 @@ +/* +UniGif +Copyright (c) 2015 WestHillApps (Hironari Nishioka) +This software is released under the MIT License. +http://opensource.org/licenses/mit-license.php +*/ + +using System; +using System.Collections.Generic; +using UnityEngine; + +namespace BeatSaberDrinkWater.UniGif +{ + public static partial class UniGif + { + /// + /// Set GIF data + /// + /// GIF byte data + /// ref GIF data + /// Debug log flag + /// Result + private static bool SetGifData(byte[] gifBytes, ref GifData gifData, bool debugLog) + { + if (debugLog) + { + Debug.Log("SetGifData Start."); + } + + if (gifBytes == null || gifBytes.Length <= 0) + { + Debug.LogError("bytes is nothing."); + return false; + } + + int byteIndex = 0; + + if (SetGifHeader(gifBytes, ref byteIndex, ref gifData) == false) + { + Debug.LogError("GIF header set error."); + return false; + } + + if (SetGifBlock(gifBytes, ref byteIndex, ref gifData) == false) + { + Debug.LogError("GIF block set error."); + return false; + } + + if (debugLog) + { + gifData.Dump(); + Debug.Log("SetGifData Finish."); + } + return true; + } + + private static bool SetGifHeader(byte[] gifBytes, ref int byteIndex, ref GifData gifData) + { + // Signature(3 Bytes) + // 0x47 0x49 0x46 (GIF) + if (gifBytes[0] != 'G' || gifBytes[1] != 'I' || gifBytes[2] != 'F') + { + Debug.LogError("This is not GIF image."); + return false; + } + gifData.m_sig0 = gifBytes[0]; + gifData.m_sig1 = gifBytes[1]; + gifData.m_sig2 = gifBytes[2]; + + // Version(3 Bytes) + // 0x38 0x37 0x61 (87a) or 0x38 0x39 0x61 (89a) + if ((gifBytes[3] != '8' || gifBytes[4] != '7' || gifBytes[5] != 'a') && + (gifBytes[3] != '8' || gifBytes[4] != '9' || gifBytes[5] != 'a')) + { + Debug.LogError("GIF version error.\nSupported only GIF87a or GIF89a."); + return false; + } + gifData.m_ver0 = gifBytes[3]; + gifData.m_ver1 = gifBytes[4]; + gifData.m_ver2 = gifBytes[5]; + + // Logical Screen Width(2 Bytes) + gifData.m_logicalScreenWidth = BitConverter.ToUInt16(gifBytes, 6); + + // Logical Screen Height(2 Bytes) + gifData.m_logicalScreenHeight = BitConverter.ToUInt16(gifBytes, 8); + + // 1 Byte + { + // Global Color Table Flag(1 Bit) + gifData.m_globalColorTableFlag = (gifBytes[10] & 128) == 128; // 0b10000000 + + // Color Resolution(3 Bits) + switch (gifBytes[10] & 112) + { + case 112: // 0b01110000 + gifData.m_colorResolution = 8; + break; + case 96: // 0b01100000 + gifData.m_colorResolution = 7; + break; + case 80: // 0b01010000 + gifData.m_colorResolution = 6; + break; + case 64: // 0b01000000 + gifData.m_colorResolution = 5; + break; + case 48: // 0b00110000 + gifData.m_colorResolution = 4; + break; + case 32: // 0b00100000 + gifData.m_colorResolution = 3; + break; + case 16: // 0b00010000 + gifData.m_colorResolution = 2; + break; + default: + gifData.m_colorResolution = 1; + break; + } + + // Sort Flag(1 Bit) + gifData.m_sortFlag = (gifBytes[10] & 8) == 8; // 0b00001000 + + // Size of Global Color Table(3 Bits) + int val = (gifBytes[10] & 7) + 1; + gifData.m_sizeOfGlobalColorTable = (int)Math.Pow(2, val); + } + + // Background Color Index(1 Byte) + gifData.m_bgColorIndex = gifBytes[11]; + + // Pixel Aspect Ratio(1 Byte) + gifData.m_pixelAspectRatio = gifBytes[12]; + + byteIndex = 13; + if (gifData.m_globalColorTableFlag) + { + // Global Color Table(0~255×3 Bytes) + gifData.m_globalColorTable = new List(); + for (int i = byteIndex; i < byteIndex + (gifData.m_sizeOfGlobalColorTable * 3); i += 3) + { + gifData.m_globalColorTable.Add(new byte[] { gifBytes[i], gifBytes[i + 1], gifBytes[i + 2] }); + } + byteIndex = byteIndex + (gifData.m_sizeOfGlobalColorTable * 3); + } + + return true; + } + + private static bool SetGifBlock(byte[] gifBytes, ref int byteIndex, ref GifData gifData) + { + try + { + int lastIndex = 0; + while (true) + { + int nowIndex = byteIndex; + + if (gifBytes[nowIndex] == 0x2c) + { + // Image Block(0x2c) + SetImageBlock(gifBytes, ref byteIndex, ref gifData); + + } + else if (gifBytes[nowIndex] == 0x21) + { + // Extension + switch (gifBytes[nowIndex + 1]) + { + case 0xf9: + // Graphic Control Extension(0x21 0xf9) + SetGraphicControlExtension(gifBytes, ref byteIndex, ref gifData); + break; + case 0xfe: + // Comment Extension(0x21 0xfe) + SetCommentExtension(gifBytes, ref byteIndex, ref gifData); + break; + case 0x01: + // Plain Text Extension(0x21 0x01) + SetPlainTextExtension(gifBytes, ref byteIndex, ref gifData); + break; + case 0xff: + // Application Extension(0x21 0xff) + SetApplicationExtension(gifBytes, ref byteIndex, ref gifData); + break; + default: + break; + } + } + else if (gifBytes[nowIndex] == 0x3b) + { + // Trailer(1 Byte) + gifData.m_trailer = gifBytes[byteIndex]; + byteIndex++; + break; + } + + if (lastIndex == nowIndex) + { + Debug.LogError("Infinite loop error."); + return false; + } + + lastIndex = nowIndex; + } + } + catch (Exception ex) + { + Debug.LogError(ex.Message); + return false; + } + + return true; + } + + private static void SetImageBlock(byte[] gifBytes, ref int byteIndex, ref GifData gifData) + { + ImageBlock ib = new ImageBlock(); + + // Image Separator(1 Byte) + // 0x2c + ib.m_imageSeparator = gifBytes[byteIndex]; + byteIndex++; + + // Image Left Position(2 Bytes) + ib.m_imageLeftPosition = BitConverter.ToUInt16(gifBytes, byteIndex); + byteIndex += 2; + + // Image Top Position(2 Bytes) + ib.m_imageTopPosition = BitConverter.ToUInt16(gifBytes, byteIndex); + byteIndex += 2; + + // Image Width(2 Bytes) + ib.m_imageWidth = BitConverter.ToUInt16(gifBytes, byteIndex); + byteIndex += 2; + + // Image Height(2 Bytes) + ib.m_imageHeight = BitConverter.ToUInt16(gifBytes, byteIndex); + byteIndex += 2; + + // 1 Byte + { + // Local Color Table Flag(1 Bit) + ib.m_localColorTableFlag = (gifBytes[byteIndex] & 128) == 128; // 0b10000000 + + // Interlace Flag(1 Bit) + ib.m_interlaceFlag = (gifBytes[byteIndex] & 64) == 64; // 0b01000000 + + // Sort Flag(1 Bit) + ib.m_sortFlag = (gifBytes[byteIndex] & 32) == 32; // 0b00100000 + + // Reserved(2 Bits) + // Unused + + // Size of Local Color Table(3 Bits) + int val = (gifBytes[byteIndex] & 7) + 1; + ib.m_sizeOfLocalColorTable = (int)Math.Pow(2, val); + + byteIndex++; + } + + if (ib.m_localColorTableFlag) + { + // Local Color Table(0~255×3 Bytes) + ib.m_localColorTable = new List(); + for (int i = byteIndex; i < byteIndex + (ib.m_sizeOfLocalColorTable * 3); i += 3) + { + ib.m_localColorTable.Add(new byte[] { gifBytes[i], gifBytes[i + 1], gifBytes[i + 2] }); + } + byteIndex = byteIndex + (ib.m_sizeOfLocalColorTable * 3); + } + + // LZW Minimum Code Size(1 Byte) + ib.m_lzwMinimumCodeSize = gifBytes[byteIndex]; + byteIndex++; + + // Block Size & Image Data List + while (true) + { + // Block Size(1 Byte) + byte blockSize = gifBytes[byteIndex]; + byteIndex++; + + if (blockSize == 0x00) + { + // Block Terminator(1 Byte) + break; + } + + var imageDataBlock = new ImageBlock.ImageDataBlock(); + imageDataBlock.m_blockSize = blockSize; + + // Image Data(? Bytes) + imageDataBlock.m_imageData = new byte[imageDataBlock.m_blockSize]; + for (int i = 0; i < imageDataBlock.m_imageData.Length; i++) + { + imageDataBlock.m_imageData[i] = gifBytes[byteIndex]; + byteIndex++; + } + + if (ib.m_imageDataList == null) + { + ib.m_imageDataList = new List(); + } + ib.m_imageDataList.Add(imageDataBlock); + } + + if (gifData.m_imageBlockList == null) + { + gifData.m_imageBlockList = new List(); + } + gifData.m_imageBlockList.Add(ib); + } + + private static void SetGraphicControlExtension(byte[] gifBytes, ref int byteIndex, ref GifData gifData) + { + GraphicControlExtension gcEx = new GraphicControlExtension(); + + // Extension Introducer(1 Byte) + // 0x21 + gcEx.m_extensionIntroducer = gifBytes[byteIndex]; + byteIndex++; + + // Graphic Control Label(1 Byte) + // 0xf9 + gcEx.m_graphicControlLabel = gifBytes[byteIndex]; + byteIndex++; + + // Block Size(1 Byte) + // 0x04 + gcEx.m_blockSize = gifBytes[byteIndex]; + byteIndex++; + + // 1 Byte + { + // Reserved(3 Bits) + // Unused + + // Disposal Mothod(3 Bits) + // 0 (No disposal specified) + // 1 (Do not dispose) + // 2 (Restore to background color) + // 3 (Restore to previous) + switch (gifBytes[byteIndex] & 28) + { // 0b00011100 + case 4: // 0b00000100 + gcEx.m_disposalMethod = 1; + break; + case 8: // 0b00001000 + gcEx.m_disposalMethod = 2; + break; + case 12: // 0b00001100 + gcEx.m_disposalMethod = 3; + break; + default: + gcEx.m_disposalMethod = 0; + break; + } + + // User Input Flag(1 Bit) + // Unknown + + // Transparent Color Flag(1 Bit) + gcEx.m_transparentColorFlag = (gifBytes[byteIndex] & 1) == 1; // 0b00000001 + + byteIndex++; + } + + // Delay Time(2 Bytes) + gcEx.m_delayTime = BitConverter.ToUInt16(gifBytes, byteIndex); + byteIndex += 2; + + // Transparent Color Index(1 Byte) + gcEx.m_transparentColorIndex = gifBytes[byteIndex]; + byteIndex++; + + // Block Terminator(1 Byte) + gcEx.m_blockTerminator = gifBytes[byteIndex]; + byteIndex++; + + if (gifData.m_graphicCtrlExList == null) + { + gifData.m_graphicCtrlExList = new List(); + } + gifData.m_graphicCtrlExList.Add(gcEx); + } + + private static void SetCommentExtension(byte[] gifBytes, ref int byteIndex, ref GifData gifData) + { + CommentExtension commentEx = new CommentExtension(); + + // Extension Introducer(1 Byte) + // 0x21 + commentEx.m_extensionIntroducer = gifBytes[byteIndex]; + byteIndex++; + + // Comment Label(1 Byte) + // 0xfe + commentEx.m_commentLabel = gifBytes[byteIndex]; + byteIndex++; + + // Block Size & Comment Data List + while (true) + { + // Block Size(1 Byte) + byte blockSize = gifBytes[byteIndex]; + byteIndex++; + + if (blockSize == 0x00) + { + // Block Terminator(1 Byte) + break; + } + + var commentDataBlock = new CommentExtension.CommentDataBlock(); + commentDataBlock.m_blockSize = blockSize; + + // Comment Data(n Byte) + commentDataBlock.m_commentData = new byte[commentDataBlock.m_blockSize]; + for (int i = 0; i < commentDataBlock.m_commentData.Length; i++) + { + commentDataBlock.m_commentData[i] = gifBytes[byteIndex]; + byteIndex++; + } + + if (commentEx.m_commentDataList == null) + { + commentEx.m_commentDataList = new List(); + } + commentEx.m_commentDataList.Add(commentDataBlock); + } + + if (gifData.m_commentExList == null) + { + gifData.m_commentExList = new List(); + } + gifData.m_commentExList.Add(commentEx); + } + + private static void SetPlainTextExtension(byte[] gifBytes, ref int byteIndex, ref GifData gifData) + { + PlainTextExtension plainTxtEx = new PlainTextExtension(); + + // Extension Introducer(1 Byte) + // 0x21 + plainTxtEx.m_extensionIntroducer = gifBytes[byteIndex]; + byteIndex++; + + // Plain Text Label(1 Byte) + // 0x01 + plainTxtEx.m_plainTextLabel = gifBytes[byteIndex]; + byteIndex++; + + // Block Size(1 Byte) + // 0x0c + plainTxtEx.m_blockSize = gifBytes[byteIndex]; + byteIndex++; + + // Text Grid Left Position(2 Bytes) + // Not supported + byteIndex += 2; + + // Text Grid Top Position(2 Bytes) + // Not supported + byteIndex += 2; + + // Text Grid Width(2 Bytes) + // Not supported + byteIndex += 2; + + // Text Grid Height(2 Bytes) + // Not supported + byteIndex += 2; + + // Character Cell Width(1 Bytes) + // Not supported + byteIndex++; + + // Character Cell Height(1 Bytes) + // Not supported + byteIndex++; + + // Text Foreground Color Index(1 Bytes) + // Not supported + byteIndex++; + + // Text Background Color Index(1 Bytes) + // Not supported + byteIndex++; + + // Block Size & Plain Text Data List + while (true) + { + // Block Size(1 Byte) + byte blockSize = gifBytes[byteIndex]; + byteIndex++; + + if (blockSize == 0x00) + { + // Block Terminator(1 Byte) + break; + } + + var plainTextDataBlock = new PlainTextExtension.PlainTextDataBlock(); + plainTextDataBlock.m_blockSize = blockSize; + + // Plain Text Data(n Byte) + plainTextDataBlock.m_plainTextData = new byte[plainTextDataBlock.m_blockSize]; + for (int i = 0; i < plainTextDataBlock.m_plainTextData.Length; i++) + { + plainTextDataBlock.m_plainTextData[i] = gifBytes[byteIndex]; + byteIndex++; + } + + if (plainTxtEx.m_plainTextDataList == null) + { + plainTxtEx.m_plainTextDataList = new List(); + } + plainTxtEx.m_plainTextDataList.Add(plainTextDataBlock); + } + + if (gifData.m_plainTextExList == null) + { + gifData.m_plainTextExList = new List(); + } + gifData.m_plainTextExList.Add(plainTxtEx); + } + + private static void SetApplicationExtension(byte[] gifBytes, ref int byteIndex, ref GifData gifData) + { + // Extension Introducer(1 Byte) + // 0x21 + gifData.m_appEx.m_extensionIntroducer = gifBytes[byteIndex]; + byteIndex++; + + // Extension Label(1 Byte) + // 0xff + gifData.m_appEx.m_extensionLabel = gifBytes[byteIndex]; + byteIndex++; + + // Block Size(1 Byte) + // 0x0b + gifData.m_appEx.m_blockSize = gifBytes[byteIndex]; + byteIndex++; + + // Application Identifier(8 Bytes) + gifData.m_appEx.m_appId1 = gifBytes[byteIndex]; + byteIndex++; + gifData.m_appEx.m_appId2 = gifBytes[byteIndex]; + byteIndex++; + gifData.m_appEx.m_appId3 = gifBytes[byteIndex]; + byteIndex++; + gifData.m_appEx.m_appId4 = gifBytes[byteIndex]; + byteIndex++; + gifData.m_appEx.m_appId5 = gifBytes[byteIndex]; + byteIndex++; + gifData.m_appEx.m_appId6 = gifBytes[byteIndex]; + byteIndex++; + gifData.m_appEx.m_appId7 = gifBytes[byteIndex]; + byteIndex++; + gifData.m_appEx.m_appId8 = gifBytes[byteIndex]; + byteIndex++; + + // Application Authentication Code(3 Bytes) + gifData.m_appEx.m_appAuthCode1 = gifBytes[byteIndex]; + byteIndex++; + gifData.m_appEx.m_appAuthCode2 = gifBytes[byteIndex]; + byteIndex++; + gifData.m_appEx.m_appAuthCode3 = gifBytes[byteIndex]; + byteIndex++; + + // Block Size & Application Data List + while (true) + { + // Block Size (1 Byte) + byte blockSize = gifBytes[byteIndex]; + byteIndex++; + + if (blockSize == 0x00) + { + // Block Terminator(1 Byte) + break; + } + + var appDataBlock = new ApplicationExtension.ApplicationDataBlock(); + appDataBlock.m_blockSize = blockSize; + + // Application Data(n Byte) + appDataBlock.m_applicationData = new byte[appDataBlock.m_blockSize]; + for (int i = 0; i < appDataBlock.m_applicationData.Length; i++) + { + appDataBlock.m_applicationData[i] = gifBytes[byteIndex]; + byteIndex++; + } + + if (gifData.m_appEx.m_appDataList == null) + { + gifData.m_appEx.m_appDataList = new List(); + } + gifData.m_appEx.m_appDataList.Add(appDataBlock); + } + } + } +} \ No newline at end of file diff --git a/BeatSaberDrinkWater/0.13/UniGif/UniGifImage.cs b/BeatSaberDrinkWater/0.13/UniGif/UniGifImage.cs new file mode 100644 index 0000000..cce53da --- /dev/null +++ b/BeatSaberDrinkWater/0.13/UniGif/UniGifImage.cs @@ -0,0 +1,361 @@ +/* +UniGif +Copyright (c) 2015 WestHillApps (Hironari Nishioka) +This software is released under the MIT License. +http://opensource.org/licenses/mit-license.php +*/ + +using System.Collections; +using System.Collections.Generic; +using System.IO; +using UnityEngine; +using UnityEngine.UI; +using UnityEngine.Networking; +using BeatSaberDrinkWater.UniGif.Utility; + +namespace BeatSaberDrinkWater.UniGif +{ + /// + /// Texture Animation from GIF image + /// + public class UniGifImage : MonoBehaviour + { + /// + /// This component state + /// + public enum State + { + None, + Loading, + Ready, + Playing, + Pause, + } + + // Target row image + [SerializeField] + private RawImage m_rawImage; + // Image Aspect Controller + [SerializeField] + private UniGifImageAspectController m_imgAspectCtrl; + // Textures filter mode + [SerializeField] + private FilterMode m_filterMode = FilterMode.Point; + // Textures wrap mode + [SerializeField] + private TextureWrapMode m_wrapMode = TextureWrapMode.Clamp; + // Load from url on start + [SerializeField] + private bool m_loadOnStart; + // GIF image url (WEB or StreamingAssets path) + [SerializeField] + private string m_loadOnStartUrl; + // Rotating on loading + [SerializeField] + private bool m_rotateOnLoading; + // Debug log flag + [SerializeField] + private bool m_outputDebugLog; + + // Decoded GIF texture list + private List m_gifTextureList; + // Delay time + private float m_delayTime; + // Texture index + private int m_gifTextureIndex; + // loop counter + private int m_nowLoopCount; + + /// + /// Now state + /// + public State nowState + { + get; + private set; + } + + /// + /// Animation loop count (0 is infinite) + /// + public int loopCount + { + get; + private set; + } + + /// + /// Texture width (px) + /// + public int width + { + get; + private set; + } + + /// + /// Texture height (px) + /// + public int height + { + get; + private set; + } + + private void Start() + { + if (m_rawImage == null) + { + m_rawImage = GetComponent(); + } + if (m_loadOnStart) + { + SetGifFromUrl(m_loadOnStartUrl); + } + } + + private void OnDestroy() + { + Clear(); + } + + private void Update() + { + switch (nowState) + { + case State.None: + break; + + case State.Loading: + if (m_rotateOnLoading) + { + transform.Rotate(0f, 0f, 30f * Time.deltaTime, Space.Self); + } + break; + + case State.Ready: + break; + + case State.Playing: + if (m_rawImage == null || m_gifTextureList == null || m_gifTextureList.Count <= 0) + { + return; + } + if (m_delayTime > Time.time) + { + return; + } + // Change texture + m_gifTextureIndex++; + if (m_gifTextureIndex >= m_gifTextureList.Count) + { + m_gifTextureIndex = 0; + + if (loopCount > 0) + { + m_nowLoopCount++; + if (m_nowLoopCount >= loopCount) + { + Stop(); + return; + } + } + } + m_rawImage.texture = m_gifTextureList[m_gifTextureIndex].m_texture2d; + m_delayTime = Time.time + m_gifTextureList[m_gifTextureIndex].m_delaySec; + break; + + case State.Pause: + break; + + default: + break; + } + } + + /// + /// Set GIF texture from url + /// + /// GIF image url (WEB or StreamingAssets path) + /// Auto play after decode + public void SetGifFromUrl(string url, bool autoPlay = true) + { + StartCoroutine(SetGifFromUrlCoroutine(url, autoPlay)); + } + + /// + /// Set GIF texture from url + /// + /// GIF image url (WEB or StreamingAssets path) + /// Auto play after decode + /// IEnumerator + public IEnumerator SetGifFromUrlCoroutine(string url, bool autoPlay = true) + { + if (string.IsNullOrEmpty(url)) + { + Debug.LogError("URL is nothing."); + yield break; + } + + if (nowState == State.Loading) + { + Debug.LogWarning("Already loading."); + yield break; + } + nowState = State.Loading; + + string path; + if (url.StartsWith("http")) + { + // from WEB + path = url; + } + else + { + // from StreamingAssets + path = Path.Combine("file:///" + Application.streamingAssetsPath, url); + } + + // Load file + using (UnityWebRequest www = new UnityWebRequest(path)) + { + yield return www; + + if (string.IsNullOrEmpty(www.error) == false) + { + Debug.LogError("File load error.\n" + www.error); + nowState = State.None; + yield break; + } + + Clear(); + nowState = State.Loading; + + // Get GIF textures + www.downloadHandler = new DownloadHandlerBuffer(); + yield return www.SendWebRequest(); + yield return StartCoroutine(UniGif.GetTextureListCoroutine(www.downloadHandler.data, (gifTexList, loopCount, width, height) => + { + if (gifTexList != null) + { + m_gifTextureList = gifTexList; + this.loopCount = loopCount; + this.width = width; + this.height = height; + nowState = State.Ready; + + m_imgAspectCtrl.FixAspectRatio(width, height); + + if (m_rotateOnLoading) + { + transform.localEulerAngles = Vector3.zero; + } + + if (autoPlay) + { + Play(); + } + } + else + { + Debug.LogError("Gif texture get error."); + nowState = State.None; + } + }, + m_filterMode, m_wrapMode, m_outputDebugLog)); + } + } + + /// + /// Clear GIF texture + /// + public void Clear() + { + if (m_rawImage != null) + { + m_rawImage.texture = null; + } + + if (m_gifTextureList != null) + { + for (int i = 0; i < m_gifTextureList.Count; i++) + { + if (m_gifTextureList[i] != null) + { + if (m_gifTextureList[i].m_texture2d != null) + { + Destroy(m_gifTextureList[i].m_texture2d); + m_gifTextureList[i].m_texture2d = null; + } + m_gifTextureList[i] = null; + } + } + m_gifTextureList.Clear(); + m_gifTextureList = null; + } + + nowState = State.None; + } + + /// + /// Play GIF animation + /// + public void Play() + { + if (nowState != State.Ready) + { + Debug.LogWarning("State is not READY."); + return; + } + if (m_rawImage == null || m_gifTextureList == null || m_gifTextureList.Count <= 0) + { + Debug.LogError("Raw Image or GIF Texture is nothing."); + return; + } + nowState = State.Playing; + m_rawImage.texture = m_gifTextureList[0].m_texture2d; + m_delayTime = Time.time + m_gifTextureList[0].m_delaySec; + m_gifTextureIndex = 0; + m_nowLoopCount = 0; + } + + /// + /// Stop GIF animation + /// + public void Stop() + { + if (nowState != State.Playing && nowState != State.Pause) + { + Debug.LogWarning("State is not Playing and Pause."); + return; + } + nowState = State.Ready; + } + + /// + /// Pause GIF animation + /// + public void Pause() + { + if (nowState != State.Playing) + { + Debug.LogWarning("State is not Playing."); + return; + } + nowState = State.Pause; + } + + /// + /// Resume GIF animation + /// + public void Resume() + { + if (nowState != State.Pause) + { + Debug.LogWarning("State is not Pause."); + return; + } + nowState = State.Playing; + } + } +} \ No newline at end of file diff --git a/BeatSaberDrinkWater/0.13/UniGif/Utility/UniGifImageAspectController.cs b/BeatSaberDrinkWater/0.13/UniGif/Utility/UniGifImageAspectController.cs new file mode 100644 index 0000000..74f86bf --- /dev/null +++ b/BeatSaberDrinkWater/0.13/UniGif/Utility/UniGifImageAspectController.cs @@ -0,0 +1,92 @@ +/* +UniGif +Copyright (c) 2015 WestHillApps (Hironari Nishioka) +This software is released under the MIT License. +http://opensource.org/licenses/mit-license.php +*/ + +using UnityEngine; + +namespace BeatSaberDrinkWater.UniGif.Utility +{ + [ExecuteInEditMode] + public class UniGifImageAspectController : MonoBehaviour + { + public int m_originalWidth; + public int m_originalHeight; + + public bool m_fixOnUpdate; + + private Vector2 m_lastSize = Vector2.zero; + private Vector2 m_newSize = Vector2.zero; + + private RectTransform m_rectTransform; + + public RectTransform rectTransform + { + get + { + return m_rectTransform != null ? m_rectTransform : (m_rectTransform = GetComponent()); + } + } + + private void Update() + { +#if UNITY_EDITOR + if (Application.isPlaying == false) + { + FixAspectRatio(); + return; + } +#endif + + if (m_fixOnUpdate) + { + FixAspectRatio(); + } + } + + public void FixAspectRatio(int originalWidth = -1, int originalHeight = -1) + { + bool forceUpdate = false; + if (originalWidth > 0 && originalHeight > 0) + { + m_originalWidth = originalWidth; + m_originalHeight = originalHeight; + forceUpdate = true; + } + if (m_originalWidth <= 0 || m_originalHeight <= 0) + { + return; + } + + bool changeX; + if (forceUpdate || m_lastSize.x != rectTransform.sizeDelta.x) + { + changeX = true; + } + else if (m_lastSize.y != rectTransform.sizeDelta.y) + { + changeX = false; + } + else + { + return; + } + + if (changeX) + { + float ratio = rectTransform.sizeDelta.x / m_originalWidth; + m_newSize.Set(rectTransform.sizeDelta.x, m_originalHeight * ratio); + } + else + { + float ratio = rectTransform.sizeDelta.y / m_originalHeight; + m_newSize.Set(m_originalWidth * ratio, rectTransform.sizeDelta.y); + } + rectTransform.sizeDelta = m_newSize; + + m_lastSize = rectTransform.sizeDelta; + } + } +} \ No newline at end of file diff --git a/BeatSaberDrinkWater/0.13/Utilities/ReflectionUtil.cs b/BeatSaberDrinkWater/0.13/Utilities/ReflectionUtil.cs new file mode 100644 index 0000000..52bff5d --- /dev/null +++ b/BeatSaberDrinkWater/0.13/Utilities/ReflectionUtil.cs @@ -0,0 +1,204 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using UnityEngine; + +/** + * Modified by Moon on 8/20/2018 + * Code originally sourced from xyonico's repo, modified to suit my needs + * (https://github.com/xyonico/BeatSaberSongLoader/blob/master/SongLoaderPlugin/ReflectionUtil.cs) + */ + +namespace BeatSaberDrinkWater.Utilities +{ + public static class ReflectionUtil + { + private const BindingFlags _allBindingFlags = BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic; + + //Sets the value of a (static?) field in object "obj" with name "fieldName" + public static void SetField(this object obj, string fieldName, object value) + { + (obj is Type ? (Type)obj : obj.GetType()) + .GetField(fieldName, _allBindingFlags) + .SetValue(obj, value); + } + + //Gets the value of a (static?) field in object "obj" with name "fieldName" + public static object GetField(this object obj, string fieldName) + { + return (obj is Type ? (Type)obj : obj.GetType()) + .GetField(fieldName, _allBindingFlags) + .GetValue(obj); + } + + //Gets the value of a (static?) field in object "obj" with name "fieldName" (TYPED) + public static T GetField(this object obj, string fieldName) => (T)GetField(obj, fieldName); + + //Sets the value of a (static?) Property specified by the object "obj" and the name "propertyName" + public static void SetProperty(this object obj, string propertyName, object value) + { + (obj is Type ? (Type)obj : obj.GetType()) + .GetProperty(propertyName, _allBindingFlags) + .SetValue(obj, value, null); + } + + //Gets the value of a (static?) Property specified by the object "obj" and the name "propertyName" + public static object GetProperty(this object obj, string propertyName) + { + return (obj is Type ? (Type)obj : obj.GetType()) + .GetProperty(propertyName, _allBindingFlags) + .GetValue(obj); + } + + //Gets the value of a (static?) Property specified by the object "obj" and the name "propertyName" (TYPED) + public static T GetProperty(this object obj, string propertyName) => (T)GetProperty(obj, propertyName); + + //Invokes a (static?) private method with name "methodName" and params "methodParams", returns an object of the specified type + public static T InvokeMethod(this object obj, string methodName, params object[] methodParams) => (T)InvokeMethod(obj, methodName, methodParams); + + //Invokes a (static?) private method with name "methodName" and params "methodParams" + public static object InvokeMethod(this object obj, string methodName, params object[] methodParams) + { + return (obj is Type ? (Type)obj : obj.GetType()) + .GetMethod(methodName, _allBindingFlags) + .Invoke(obj, methodParams); + } + + //Returns a constructor with the specified parameters to the specified type or object + public static object InvokeConstructor(this object obj, params object[] constructorParams) + { + Type[] types = new Type[constructorParams.Length]; + for (int i = 0; i < constructorParams.Length; i++) types[i] = constructorParams[i].GetType(); + return (obj is Type ? (Type)obj : obj.GetType()) + .GetConstructor(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, types, null) + .Invoke(constructorParams); + } + + //Returns a Type object which can be used to invoke static methods with the above helpers + public static Type GetStaticType(string clazz) + { + return Type.GetType(clazz); + } + + //Returns a list (of strings) of the names of all loaded assemblies + public static IEnumerable ListLoadedAssemblies() + { + return AppDomain.CurrentDomain.GetAssemblies(); + } + + //Returns a list of all loaded namespaces + //TODO: Check up on time complexity here, could potentially be parallelized + public static IEnumerable ListNamespacesInAssembly(Assembly assembly) + { + IEnumerable ret = Enumerable.Empty(); + ret = ret.Concat(assembly.GetTypes() + .Select(t => t.Namespace) + .Distinct() + .Where(n => n != null)); + return ret.Distinct(); + } + + //Returns a list of classes in a namespace + //TODO: Check up on time complexity here, could potentially be parallelized + public static IEnumerable ListClassesInNamespace(string ns) + { + //For each loaded assembly + foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies()) + { + //If the assembly contains the desired namespace + if (assembly.GetTypes().Where(t => t.Namespace == ns).Any()) + { + //Select the types we want from the namespace and return them + return assembly.GetTypes() + .Where(t => t.IsClass) + .Select(t => t.Name); + } + } + return null; + + //Code to list reflectable classes + /* + ReflectionUtil.ListLoadedAssemblies().ToList().ForEach(x => { + if (x.GetName().Name == "BeatSaberMultiplayer") + { + Logger.Success($"ASSEMBLY: {x.GetName().Name}"); + ReflectionUtil.ListNamespacesInAssembly(x).ToList().ForEach(y => + { + Logger.Warning($"NAMESPACE: {y}"); + ReflectionUtil.ListClassesInNamespace(y).ToList().ForEach(z => + { + Logger.Warning($"CLASS: {z} : {((ReflectionUtil.GetStaticType(y + "." + z + "," + x) != null) ? "REFLECTABLE" : "NOT")}"); + }); + }); + } + }); + */ + } + + //(Created by taz?) Copies a component to a destination object, keeping all its field values? + public static Behaviour CopyComponent(Behaviour original, Type originalType, Type overridingType, GameObject destination) + { + Behaviour copy = null; + + try + { + copy = destination.AddComponent(overridingType) as Behaviour; + } + catch (Exception) + { + + } + + copy.enabled = false; + + //Copy types of super classes as well as our class + Type type = originalType; + while (type != typeof(MonoBehaviour)) + { + CopyForType(type, original, copy); + type = type.BaseType; + } + + copy.enabled = true; + return copy; + } + + public static void SetPrivateField(this object obj, string fieldName, object value) + { + var field = obj.GetType().GetField(fieldName, BindingFlags.Instance | BindingFlags.NonPublic); + field.SetValue(obj, value); + } + + public static T GetPrivateField(this object obj, string fieldName) + { + var field = obj.GetType().GetField(fieldName, BindingFlags.Instance | BindingFlags.NonPublic); + var value = field.GetValue(obj); + return (T)value; + } + + public static object GetPrivateField(Type type, object obj, string fieldName) + { + var field = obj.GetType().GetField(fieldName, BindingFlags.Instance | BindingFlags.NonPublic); + var value = field.GetValue(obj); + return value; + } + + public static void InvokePrivateMethod(this object obj, string methodName, object[] methodParams) + { + var method = obj.GetType().GetMethod(methodName, BindingFlags.Instance | BindingFlags.NonPublic); + method.Invoke(obj, methodParams); + } + + //(Created by taz?) Copies a Component of Type type, and all its fields + private static void CopyForType(Type type, Component source, Component destination) + { + FieldInfo[] myObjectFields = type.GetFields(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance | BindingFlags.GetField | BindingFlags.SetField); + + foreach (FieldInfo fi in myObjectFields) + { + fi.SetValue(destination, fi.GetValue(source)); + } + } + } +} \ No newline at end of file diff --git a/BeatSaberDrinkWater/0.13/Utilities/SceneUtils.cs b/BeatSaberDrinkWater/0.13/Utilities/SceneUtils.cs new file mode 100644 index 0000000..e3ec23c --- /dev/null +++ b/BeatSaberDrinkWater/0.13/Utilities/SceneUtils.cs @@ -0,0 +1,31 @@ +using System; +using UnityEngine.SceneManagement; + +namespace BeatSaberDrinkWater.Utilities +{ + class SceneUtils + { + public static bool IsMenuScene(Scene scene) + { + return CheckSceneByName(scene, "MenuCore"); + } + + public static bool IsGameScene(Scene scene) + { + return CheckSceneByName(scene, "GameCore"); + } + + private static bool CheckSceneByName(Scene scene, String sceneName) + { + try + { + return (scene.name == sceneName); + } + catch (Exception e) + { + Console.WriteLine("Error getting " + sceneName + " scene:" + e); + } + return false; + } + } +} \ No newline at end of file