using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using System.Text; using System.Threading; using System.Threading.Tasks; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using IEVO.UI.uGUIDirectedNavigation; using Microsoft.CodeAnalysis; using Steamworks; using Steamworks.Data; using Steamworks.Ugc; using UnityEngine; using UnityEngine.Events; using UnityEngine.Networking; using UnityEngine.UI; using VGFunctions; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: AssemblyCompany("LessRam")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyDescription("title")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0+786e633056fcf309aed952b5636e8ad233e9f3d2")] [assembly: AssemblyProduct("LessRam")] [assembly: AssemblyTitle("LessRam")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("1.0.0.0")] [module: UnverifiableCode] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)] internal sealed class NullableAttribute : Attribute { public readonly byte[] NullableFlags; public NullableAttribute(byte P_0) { NullableFlags = new byte[1] { P_0 }; } public NullableAttribute(byte[] P_0) { NullableFlags = P_0; } } [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)] internal sealed class NullableContextAttribute : Attribute { public readonly byte Flag; public NullableContextAttribute(byte P_0) { Flag = P_0; } } [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace LessRam { [HarmonyPatch(typeof(GameManager))] public static class GameManagerPatch { [HarmonyPatch("LoadAudio")] [HarmonyPrefix] public static bool PreLoadAudio(ref IEnumerator __result, VGLevel _level) { if (!SingletonBase.Inst.IsArcade || Object.op_Implicit((Object)(object)_level.LevelMusic)) { return true; } __result = LoadAudio(SingletonBase.Inst.CurrentArcadeLevel); return false; } private static IEnumerator LoadAudio(VGLevel _level) { VGLevelWrapper valueOrDefault = LessRam.Levels.GetValueOrDefault(((Object)_level).name, null); Coroutine val = ((MonoBehaviour)DataManager.inst).StartCoroutine(LevelLoaderHelper.LoadAudio(_level, valueOrDefault.AudioPath)); Coroutine coroutine2 = ((MonoBehaviour)DataManager.inst).StartCoroutine(LevelLoaderHelper.LoadImage(_level, valueOrDefault.ImagePath)); yield return val; yield return coroutine2; SingletonBase.Inst.LevelAudio = _level.LevelMusic; SingletonBase.Inst.CurLoadingState.Audio = true; } [HarmonyPatch("PlayGame")] [HarmonyPostfix] private static void OnPlayGame() { ArcadeMenuPatch.CleanLevels(); } } public static class LevelLoaderHelper { public static IEnumerator LoadAudio(VGLevel level, string path) { DownloadHandlerAudioClip val = new DownloadHandlerAudioClip(path, (AudioType)14); val.streamAudio = true; UnityWebRequest audioUwr = new UnityWebRequest(path, "GET", (DownloadHandler)(object)val, (UploadHandler)null); try { yield return audioUwr.SendWebRequest(); if ((int)audioUwr.result != 1) { LessRam.Logger.LogError((object)audioUwr.error); } else if (Object.op_Implicit((Object)(object)level)) { AudioClip content = DownloadHandlerAudioClip.GetContent(audioUwr); if (!Object.op_Implicit((Object)(object)content)) { LessRam.Logger.LogError((object)("Remove invalid song from arcade. No audio file. [" + ((Object)level).name + "]")); yield break; } ArcadeMenuPatch.activeClips.Add(content); ((Object)content).name = ((Object)level).name; level.LevelMusic = content; } } finally { ((IDisposable)audioUwr)?.Dispose(); } } public static IEnumerator LoadImage(VGLevel level, string path) { Task spriteTask = Task.Run(() => InternalLoadImage(path)); while (!spriteTask.IsCompleted) { yield return null; } if (Object.op_Implicit((Object)(object)spriteTask.Result)) { Sprite result = spriteTask.Result; if (!Object.op_Implicit((Object)(object)level)) { Object.Destroy((Object)(object)result.texture); Object.Destroy((Object)(object)result); } else { ArcadeMenuPatch.activeSprites.Add(result); level.AlbumArt = result; } } } private static async Task InternalLoadImage(string path) { UnityWebRequest uwr = UnityWebRequestTexture.GetTexture(path, false); try { SimpleCoroutineAwaiter val = IEnumeratorAwaitExtensions.GetAwaiter((AsyncOperation)(object)uwr.SendWebRequest()); if (!val.IsCompleted) { await val; object obj = default(object); val = (SimpleCoroutineAwaiter)obj; } val.GetResult(); if ((int)uwr.result != 1) { LessRam.Logger.LogError((object)uwr.error); return null; } Texture2D content = DownloadHandlerTexture.GetContent(uwr); int width = ((Texture)content).width; int height = ((Texture)content).height; if (width <= 512 && height <= 512 && width == height) { return Sprite.Create(content, new Rect(0f, 0f, (float)width, (float)height), new Vector2(0.5f, 0.5f), 72f, 0u, (SpriteMeshType)0); } return null; } finally { ((IDisposable)uwr)?.Dispose(); } } } public static class VGLevelExtension { public static bool InitSteamInfoFix(this VGLevel level, ulong _id, string _folder) { //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_0021: Expected O, but got Unknown //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_0027: Unknown result type (might be due to invalid IL or missing references) //IL_0034: Unknown result type (might be due to invalid IL or missing references) //IL_0040: Expected O, but got Unknown if (string.IsNullOrEmpty(_folder)) { return false; } level.SteamInfo = new SteamData { ItemID = PublishedFileId.op_Implicit(_id) }; level.BaseLevelData = new LevelDataBase { LevelID = _id.ToString(), LocalFolder = _folder }; level.LevelData = level.BaseLevelData; return true; } } [BepInPlugin("me.ytarame.LessRam", "LessRam", "1.0.3")] [BepInDependency(/*Could not decode attribute arguments.*/)] public class LessRam : BaseUnityPlugin { private Harmony _harmony; private const string Guid = "me.ytarame.LessRam"; private const string Name = "LessRam"; private const string Version = "1.0.3"; internal static readonly Dictionary Levels = new Dictionary(); internal static ManualLogSource Logger; internal static int SemaphoreCount = 5; private void Awake() { //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Expected O, but got Unknown //IL_0036: Unknown result type (might be due to invalid IL or missing references) //IL_0042: Expected O, but got Unknown Logger = ((BaseUnityPlugin)this).Logger; _harmony = new Harmony("me.ytarame.LessRam"); _harmony.PatchAll(); SemaphoreCount = ((BaseUnityPlugin)this).Config.Bind(new ConfigDefinition("LessRam", "Semaphore count"), 5, (ConfigDescription)null).Value; Logger.LogInfo((object)"Plugin me.ytarame.LessRam is loaded!"); } } [HarmonyPatch(typeof(SteamWorkshopFacepunch))] public static class SteamWorkshopPatch { private static object _lockObject = new object(); [HarmonyPatch("DownloadLevels")] [HarmonyPrefix] public static bool PreStart(SteamWorkshopFacepunch __instance, ref Task __result) { __result = LoadLevels(__instance); return false; } private static async Task LoadLevels(SteamWorkshopFacepunch facepunch) { Stopwatch stopWatch = new Stopwatch(); stopWatch.Start(); facepunch.TotalSteamWorkshopSubscriptions = 0; facepunch.TotalSteamWorkshopSubscriptionsDone = 0; SteamWorkshopFacepunch.inst.isLoadingLevels = true; Query val = Query.ItemsReadyToUse; val = ((Query)(ref val)).WhereUserSubscribed(default(SteamId)); Query q = ((Query)(ref val)).SortByCreationDate(); ResultPage? val2 = await ((Query)(ref q)).GetPageAsync(1); if (!val2.HasValue) { stopWatch.Stop(); LessRam.Logger.LogError((object)$"Level loading failure [{stopWatch.ElapsedMilliseconds}]"); return; } facepunch.TotalSteamWorkshopSubscriptions = val2.Value.TotalCount; ResultPage value = val2.Value; foreach (Item entry in ((ResultPage)(ref value)).Entries) { Item current = entry; if (SingletonBase.Inst.skippedLoad) { return; } if (((Item)(ref current)).IsInstalled) { (VGLevel, VGLevelWrapper)? tuple = CreateEntry(facepunch, current); if (tuple.HasValue) { LessRam.Levels.Add(((Object)tuple.Value.Item1).name, tuple.Value.Item2); SingletonBase.Inst.ArcadeLevels.Add(tuple.Value.Item1); SteamWorkshopFacepunch obj = facepunch; obj.TotalSteamWorkshopSubscriptionsDone++; } } } int totalPages = Mathf.CeilToInt((float)val2.Value.TotalCount / (float)val2.Value.ResultCount); SemaphoreSlim semaphore = new SemaphoreSlim(LessRam.SemaphoreCount); try { List tasks = new List(); for (int i = 2; i < totalPages; i++) { await semaphore.WaitAsync(); LessRam.Logger.LogInfo((object)$"loading page {i}"); int iteration = i; tasks.Add(Task.Run(async delegate { try { ResultPage? val3 = await ((Query)(ref q)).GetPageAsync(iteration); if (!val3.HasValue) { LessRam.Logger.LogError((object)$"{iteration} no value"); return; } List<(VGLevel, VGLevelWrapper)> list = new List<(VGLevel, VGLevelWrapper)>(val3.Value.ResultCount); ResultPage value2 = val3.Value; foreach (Item entry2 in ((ResultPage)(ref value2)).Entries) { Item current2 = entry2; if (SingletonBase.Inst.skippedLoad) { return; } if (((Item)(ref current2)).IsInstalled) { (VGLevel, VGLevelWrapper)? tuple2 = CreateEntry(facepunch, current2); if (tuple2.HasValue) { list.Add(tuple2.Value); } } } lock (_lockObject) { foreach (var item in list) { LessRam.Levels.Add(((Object)item.Item1).name, item.Item2); SingletonBase.Inst.ArcadeLevels.Add(item.Item1); SteamWorkshopFacepunch obj2 = facepunch; obj2.TotalSteamWorkshopSubscriptionsDone++; } } } finally { LessRam.Logger.LogError((object)$"{iteration} Release"); semaphore.Release(); } })); } await Task.WhenAll(tasks); stopWatch.Stop(); SteamWorkshopFacepunch.inst.isLoadingLevels = false; LessRam.Logger.LogInfo((object)$"Time to load levels [{stopWatch.ElapsedMilliseconds}ms]"); } finally { if (semaphore != null) { ((IDisposable)semaphore).Dispose(); } } } private static (VGLevel, VGLevelWrapper)? CreateEntry(SteamWorkshopFacepunch facepunch, Item entry) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0047: Unknown result type (might be due to invalid IL or missing references) //IL_0062: Unknown result type (might be due to invalid IL or missing references) //IL_0067: Unknown result type (might be due to invalid IL or missing references) VGLevelWrapper vGLevelWrapper = MakeLevelRealmObject(((object)((Item)(ref entry)).Id/*cast due to .constrained prefix*/).ToString(), ((Item)(ref entry)).Directory); if (vGLevelWrapper == null) { return null; } VGLevel val = ScriptableObject.CreateInstance(); if (val.InitArcadeData(((Item)(ref entry)).Directory) && val.InitSteamInfoFix(PublishedFileId.op_Implicit(((Item)(ref entry)).Id), ((Item)(ref entry)).Directory)) { ((Object)val).name = ((object)((Item)(ref entry)).Id/*cast due to .constrained prefix*/).ToString(); return (val, vGLevelWrapper); } return null; } private static VGLevelWrapper? MakeLevelRealmObject(string id, string directory) { if (string.IsNullOrEmpty(directory)) { return null; } VGLevelWrapper obj = new VGLevelWrapper { LevelPath = directory }; FileTypeDefinition fileTypeInfo = DataManager.inst.GetFileTypeInfo((FileType)5); string text = directory + "\\" + fileTypeInfo.CurrentFile; if (!LSFile.FileExists(text)) { text = directory + "\\" + fileTypeInfo.LegacyFile; } obj.ImagePath = text; FileTypeDefinition fileTypeInfo2 = DataManager.inst.GetFileTypeInfo((FileType)2); string text2 = directory + "\\" + fileTypeInfo2.CurrentFile; if (!LSFile.FileExists(text2)) { text2 = directory + "\\" + fileTypeInfo2.LegacyFile; } obj.AudioPath = text2; return obj; } } [HarmonyPatch(typeof(ArcadeMenu))] public static class ArcadeMenuPatch { private static List activeCoroutines = new List(); public static readonly List activeSprites = new List(); public static readonly List activeClips = new List(); [HarmonyPatch("SelectPage", new Type[] { typeof(int), typeof(bool) })] [HarmonyPrefix] private static bool PreSelectPage(int _page, bool _forceButton, ArcadeMenu __instance) { ((MonoBehaviour)__instance).StartCoroutine(SelectPage(_page, _forceButton, __instance)); return false; } public static void CleanLevels() { foreach (Coroutine activeCoroutine in activeCoroutines) { if (activeCoroutine != null) { ((MonoBehaviour)SingletonBase.Inst).StopCoroutine(activeCoroutine); } } activeCoroutines.Clear(); int num = 0; while (num < activeClips.Count) { if (Object.op_Implicit((Object)(object)activeClips[num]) && (Object)(object)SingletonBase.Inst.CurrentAudioClip != (Object)(object)activeClips[num] && (!Object.op_Implicit((Object)(object)SingletonBase.Inst.CurrentArcadeLevel) || (Object)(object)SingletonBase.Inst.CurrentArcadeLevel.LevelMusic != (Object)(object)activeClips[num])) { Object.Destroy((Object)(object)activeClips[num]); activeClips.RemoveAt(num); } else { num++; } } int num2 = 0; while (num2 < activeSprites.Count) { if (Object.op_Implicit((Object)(object)activeSprites[num2]) && (!Object.op_Implicit((Object)(object)SingletonBase.Inst.CurrentArcadeLevel) || (Object)(object)SingletonBase.Inst.CurrentArcadeLevel.AlbumArt != (Object)(object)activeSprites[num2])) { Object.Destroy((Object)(object)activeSprites[num2].texture); Object.Destroy((Object)(object)activeSprites[num2]); activeSprites.RemoveAt(num2); } else { num2++; } } } private static IEnumerator SelectPage(int _page, bool _forceButton, ArcadeMenu arcadeMenu) { CleanLevels(); if (arcadeMenu.Page != _page) { SingletonBase.Inst.PlaySound("PageSwap", 1f); } if (arcadeMenu.SearchedLevels.Count <= 0) { if (arcadeMenu.SearchedLevels.Count > 0) { arcadeMenu.ShowNoResults(); } else { arcadeMenu.ShowNoLevels(); } } else { if (_page < 0 || (arcadeMenu.SearchedLevels.Count > 0 && _page * 12 >= arcadeMenu.SearchedLevels.Count)) { yield break; } if (((UIElement)arcadeMenu.NoResults).IsVisible) { ((UIElement)arcadeMenu.NoResults).Hide((Action)null); } if (((UIElement)arcadeMenu.NoLevels).IsVisible) { ((UIElement)arcadeMenu.NoLevels).Hide((Action)null); } if (((UIElement)arcadeMenu.KeyboardButtons[0].UIButton).IsVisible) { arcadeMenu.HideAllKeyboardButtons(); } arcadeMenu.PageIsChanging = true; ((MonoBehaviour)arcadeMenu).StartCoroutine(arcadeMenu.DelayAnim(_forceButton)); arcadeMenu.Page = _page; int num = _page * 12; int num2 = Mathf.Clamp(num + 12, 0, arcadeMenu.SearchedLevels.Count - 1) - num; arcadeMenu.PageSlider.UpdateValueAndRange((float)_page, new Vector2(0f, Mathf.Ceil((float)((arcadeMenu.SearchedLevels.Count - 1) / 12))), new Vector2(0f, 9f)); arcadeMenu.PageSlider.UpdateValue(false); ((Selectable)arcadeMenu.PageSlider.LeftButton).interactable = !arcadeMenu.IsFirstPage(); ((Selectable)arcadeMenu.PageSlider.RightButton).interactable = !arcadeMenu.IsLastPage(); ((Component)arcadeMenu.LeftPageTrigger).gameObject.SetActive(true); ((Component)arcadeMenu.RightPageTrigger).gameObject.SetActive(true); ((Selectable)arcadeMenu.LeftPageTrigger).interactable = !arcadeMenu.IsFirstPage(); ((Selectable)arcadeMenu.RightPageTrigger).interactable = !arcadeMenu.IsLastPage(); int num3 = 0; foreach (ArcadeButtonRefs button in arcadeMenu.LevelButtons) { if (num3 > num2) { button.Button.ClearActions(); button.Button.LockButtonState(true); ((UnityEventBase)((Button)button.Button).onClick).RemoveAllListeners(); if (((UIElement)button.LevelButton).IsVisible) { LSHelpers.Delay(Random.Range(0f, 0.025f), (Action)delegate { ((UIElement)button.LevelButton).Hide((Action)null); }); } } else { activeCoroutines.Add(((MonoBehaviour)SingletonBase.Inst).StartCoroutine(CreateButton(button, arcadeMenu.SearchedLevels[num + num3], arcadeMenu))); } num3++; } } } private static IEnumerator CreateButton(ArcadeButtonRefs button, VGLevel level, ArcadeMenu arcadeMenu) { VGLevelWrapper valueOrDefault = LessRam.Levels.GetValueOrDefault(((Object)level).name, null); if (valueOrDefault != null) { Coroutine val = ((MonoBehaviour)arcadeMenu).StartCoroutine(LevelLoaderHelper.LoadAudio(level, valueOrDefault.AudioPath)); Coroutine coroutine2 = ((MonoBehaviour)arcadeMenu).StartCoroutine(LevelLoaderHelper.LoadImage(level, valueOrDefault.ImagePath)); yield return val; yield return coroutine2; } DifficultySetting difficulty = arcadeMenu.GetDifficulty(level.Difficulty); ((Component)button.Button).GetComponent().Active = false; button.Button.LockButtonState(true); ((UnityEventBase)((Button)button.Button).onClick).RemoveAllListeners(); button.Button.ClearActions(); button.Button.OnSelectButton += delegate { arcadeMenu.LastSelectedButtonOffset = 0; arcadeMenu.LastSelectedButton = ((Component)button.Button).gameObject; arcadeMenu.selectedLevelGO = ((Component)button.Button).gameObject; arcadeMenu._songPreviewDebounce.Run((Action)delegate { arcadeMenu.PlaySongPreview(level); }, 0.6f, (MonoBehaviour)(object)arcadeMenu); }; button.Button.OnHoverEnterButton += delegate { arcadeMenu.LastSelectedButtonOffset = 0; arcadeMenu.LastSelectedButton = ((Component)button.Button).gameObject; arcadeMenu.selectedLevelGO = ((Component)button.Button).gameObject; arcadeMenu._songPreviewDebounce.Run((Action)delegate { arcadeMenu.PlaySongPreview(level); }, 0.3f, (MonoBehaviour)(object)arcadeMenu); }; ((UnityEvent)((Button)button.Button).onClick).AddListener((UnityAction)delegate { arcadeMenu.LastSelectedButtonOffset = 0; arcadeMenu.LastSelectedButton = ((Component)button.Button).gameObject; arcadeMenu.selectedLevelGO = ((Component)button.Button).gameObject; if (SingletonBase.Inst.currentSongGroup == level.TrackName) { arcadeMenu._songPreviewDebounce.ResetTime((MonoBehaviour)(object)arcadeMenu); } arcadeMenu.SetupSongMenu(level); ((Selectable)arcadeMenu.LeftPageTrigger).interactable = false; ((Selectable)arcadeMenu.RightPageTrigger).interactable = false; arcadeMenu.SetSelectedGO((GameObject)null); arcadeMenu.ViewManager.SwapPage("Song Menu"); }); button.LevelButton.UpdateTitle(LSText.ClampString(level.TrackName, 20, " -")); button.LevelButton.UpdateDifficulty(difficulty); button.LevelButton.UpdateAlbumArt(level.AlbumArt); button.LevelButton.UpdateContent(arcadeMenu.getLevelButtonContent(level)); button.LevelButton.UpdateRank(SingletonBase.Inst.FetchArcadeSave(level.LevelData.LevelID, true).LevelRank); ((UIElement)button.LevelButton).Stutter(true, (Action)null); ((UIElement)button.LevelButton).Show(0f, 0.1f); ((Component)button.Button).GetComponent().Active = true; button.Button.LockButtonState(false); } } [HarmonyPatch(typeof(ArcadeLevelDataManager))] public static class ArcadeDataPatch { [HarmonyPatch("GetLocalCustomLevel")] [HarmonyPostfix] private static void preGetLevel(ArcadeLevelDataManager __instance, string _id, ref VGLevel __result) { if (Object.op_Implicit((Object)(object)__result) && !Object.op_Implicit((Object)(object)__result.LevelMusic)) { VGLevelWrapper valueOrDefault = LessRam.Levels.GetValueOrDefault(_id, null); if (valueOrDefault != null) { ((MonoBehaviour)__instance).StartCoroutine(LevelLoaderHelper.LoadAudio(__result, valueOrDefault.AudioPath)); ((MonoBehaviour)__instance).StartCoroutine(LevelLoaderHelper.LoadImage(__result, valueOrDefault.ImagePath)); } } } } [HarmonyPatch(typeof(LSText))] public static class LSTextPatch { [HarmonyPatch("ClampString")] [HarmonyPrefix] private static bool PreClamp(ref string __result, string _inputStr, int _maxLength, string _end) { if (!SteamWorkshopFacepunch.inst.isLoadingLevels) { return true; } if (string.IsNullOrEmpty(_inputStr) || _inputStr.Length <= _maxLength) { __result = _inputStr; return false; } StringBuilder stringBuilder = new StringBuilder(); if (_end == null) { stringBuilder.Append(_inputStr.Substring(0, _maxLength)); } else { stringBuilder.Append(_inputStr.Substring(0, _maxLength - (_end.Length - 1))); stringBuilder.Append(_end); } __result = stringBuilder.ToString(); return false; } } public class VGLevelWrapper { public string ImagePath { get; set; } public string AudioPath { get; set; } public string LevelPath { get; set; } } public static class MyPluginInfo { public const string PLUGIN_GUID = "LessRam"; public const string PLUGIN_NAME = "LessRam"; public const string PLUGIN_VERSION = "1.0.0"; } }