Due to update 2.4.3, some mods may no longer function. FixedConfig may be necessary.
Decompiled source of texturePackLoader v1.0.1
texturepack_loader.dll
Decompiled a day agousing System; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.IO.Compression; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using System.Text; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using Microsoft.CodeAnalysis; using UnityEngine; using UnityEngine.InputSystem; using UnityEngine.InputSystem.Controls; using UnityEngine.UI; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")] [assembly: AssemblyCompany("texturepack_loader")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyDescription("My first plugin")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0")] [assembly: AssemblyProduct("texturepack_loader")] [assembly: AssemblyTitle("texturepack_loader")] [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.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace texturepack_loader { internal class Patches { [HarmonyPatch(typeof(SpriteRenderer), "set_sprite")] private class SpriteRendererSetSpritePatch { private static void Postfix(SpriteRenderer __instance, Sprite value) { if (!((Object)(object)__instance == (Object)null) && !((Object)(object)value == (Object)null) && !((Object)(object)value.texture == (Object)null)) { Plugin.instance.RegisterSprite(((Object)value).name, value.texture, ui: false); } } } } [BepInPlugin("com.erwer.texturepack_loader", "texturepack_loader", "1.0.0")] public class Plugin : BaseUnityPlugin { private class ProcessableSprite { public string Name; public Texture2D Tex; public bool NeedsToChange; public bool uiSpace; public ProcessableSprite(string name, Texture2D tex, bool needsToChange, bool uispace) { Name = name; Tex = tex; NeedsToChange = needsToChange; uiSpace = uispace; } } public static class TextureMenu { public static Rect windowRect = new Rect(10f, 125f, 400f, 250f); } public const string PLUGIN_GUID = "com.erwer.texturepack_loader"; public const string PLUGIN_NAME = "texturepack_loader"; public const string PLUGIN_VERSION = "1.0.0"; public static ManualLogSource logger; public string PluginDirectory; private ConfigEntry<int> discoveryInterval; private ConfigEntry<bool> verbose; private ConfigEntry<bool> vanillaBasing; public static Plugin instance; private List<TexturePack> TexturePacks = new List<TexturePack>(); private Dictionary<Texture2D, ProcessableSprite> FullProcessed = new Dictionary<Texture2D, ProcessableSprite>(); private static uint frame; private bool hasHiddenBefore; private bool showTextureLoader = true; private string notificationMessage = ""; private Vector2 scrollPosition; private void Awake() { //IL_0021: Unknown result type (might be due to invalid IL or missing references) PluginDirectory = Path.GetDirectoryName(((BaseUnityPlugin)this).Info.Location); instance = this; new Harmony("com.erwer.texturepack_loader").PatchAll(typeof(Patches)); discoveryInterval = ((BaseUnityPlugin)this).Config.Bind<int>("Texture Pack Loader", "DiscoveryInterval", 4, "Higher values are better for preformance but cause visual artifacting the first frame a texture is loaded."); verbose = ((BaseUnityPlugin)this).Config.Bind<bool>("Texture Pack Loader Dev", "Verbose", false, "Enables \"additional\" logging. Useful for me developing this mod or for you creating your own texture pack."); vanillaBasing = ((BaseUnityPlugin)this).Config.Bind<bool>("Texture Pack Loader", "Vanilla Base", true, "Loads vanilla textures as a base before applying the active texture pack, allowing packs to include only the textures they change. Disabling this may cause missing textures or unexpected results. Disable this option when developing a texture pack."); CharacterSelectHandler_online.clientSideMods_you_can_increment_this_to_enable_matchmaking_for_your_mods__please_dont_use_it_to_cheat_thats_really_cringe_especially_if_its_desyncing_others___you_didnt_even_win_on_your_opponents_screen___I_cannot_imagine_a_sadder_existence++; ((BaseUnityPlugin)this).Logger.LogInfo((object)"Plugin com.erwer.texturepack_loader is loaded!"); logger = ((BaseUnityPlugin)this).Logger; if (!Directory.Exists(activeTexturePath())) { Directory.CreateDirectory(activeTexturePath()); } if (!Directory.Exists(texturePacksPath())) { Directory.CreateDirectory(texturePacksPath()); } if (!Directory.Exists(unpackPath())) { Directory.CreateDirectory(unpackPath()); } string text = Path.Combine(PluginDirectory, "vanilla.zip"); if (File.Exists(text)) { File.Move(text, Path.Combine(texturePacksPath(), "vanilla.zip")); } RefreshTexturePacks(); } private void Update() { if (((ButtonControl)Keyboard.current[(Key)21]).wasPressedThisFrame) { showTextureLoader = !showTextureLoader; hasHiddenBefore = true; } frame++; if (frame % discoveryInterval.Value == 0L) { SpriteRenderer[] array = Object.FindObjectsOfType<SpriteRenderer>(); foreach (SpriteRenderer val in array) { if (!((Object)(object)val == (Object)null) && !((Object)(object)((Component)val).gameObject == (Object)null) && !((Object)(object)val.sprite == (Object)null) && Object.op_Implicit((Object)(object)val.sprite.texture)) { Texture2D texture = val.sprite.texture; RegisterSprite(((Object)val.sprite).name, texture, ui: false); } } Image[] array2 = Object.FindObjectsOfType<Image>(); foreach (Image val2 in array2) { if (!((Object)(object)val2 == (Object)null) && !((Object)(object)val2.sprite == (Object)null) && !((Object)(object)val2.sprite.texture == (Object)null)) { Texture2D texture2 = val2.sprite.texture; RegisterSprite(((Object)val2.sprite).name, texture2, ui: true); } } } ApplyChangesToSprites(); } public void RegisterSprite(string name, Texture2D tex, bool ui) { if (!FullProcessed.ContainsKey(tex)) { FullProcessed.Add(tex, new ProcessableSprite(name, tex, needsToChange: true, ui)); } } private void ApplyChangesToSprites() { foreach (ProcessableSprite value in FullProcessed.Values) { if (value.NeedsToChange) { value.NeedsToChange = false; processImage(value.Tex, value.Name, value.uiSpace); } } } private void processImage(Texture2D tex, string spriteName, bool uiSpace = false) { if ((Object)(object)tex == (Object)null) { return; } string text = Translator.Translate(spriteName, uiSpace); string text2 = Path.Combine(activeTexturePath(), text + ".png"); if (File.Exists(text2)) { byte[] array = File.ReadAllBytes(text2); ImageConversion.LoadImage(tex, array); if (verbose.Value) { ((BaseUnityPlugin)this).Logger.LogMessage((object)("[Verbose] [Name: " + spriteName + "] replaced with -> [Path: " + text2 + "]")); } } else if (verbose.Value) { ((BaseUnityPlugin)this).Logger.LogMessage((object)("[Verbose] [Name: " + spriteName + "] [Path: " + text2 + "] was not found, so this texture has been skipped.")); } } public string GetFirstLineLimited(string filePath, int x) { if (!File.Exists(filePath)) { return string.Empty; } using StreamReader streamReader = new StreamReader(filePath); string text = streamReader.ReadLine(); if (string.IsNullOrEmpty(text)) { return string.Empty; } return (text.Length <= x) ? text : text.Substring(0, x); } private void RefreshTexturePacks() { TexturePacks.Clear(); ((BaseUnityPlugin)this).Logger.LogInfo((object)"@Checking textures"); if (!Directory.Exists(texturePacksPath())) { return; } string[] directories = Directory.GetDirectories(texturePacksPath()); foreach (string text in directories) { ((BaseUnityPlugin)this).Logger.LogInfo((object)text); string text2 = Path.Combine(text, "version"); if (File.Exists(text2)) { if (int.TryParse(GetFirstLineLimited(text2, 250), out var result)) { TexturePacks.Add(new TexturePack(Path.GetFileName(text), text, zipped: false, result)); } else { ((BaseUnityPlugin)this).Logger.LogWarning((object)("Failed to parse version as a number. Pack [" + text + "] rejected.")); } } else { ((BaseUnityPlugin)this).Logger.LogWarning((object)("No version file. Pack [" + text + "] rejected.")); } } directories = Directory.GetFiles(texturePacksPath()); foreach (string text3 in directories) { ((BaseUnityPlugin)this).Logger.LogInfo((object)("@" + text3)); if (!Path.GetExtension(text3).Equals(".zip", StringComparison.OrdinalIgnoreCase)) { continue; } try { string s = ""; int num = 0; using (ZipArchive zipArchive = ZipFile.Open(text3, ZipArchiveMode.Read)) { foreach (ZipArchiveEntry entry in zipArchive.Entries) { if (!(entry.Name == "version")) { continue; } if (entry.Length > 256) { ((BaseUnityPlugin)this).Logger.LogError((object)(text3 + " was rejected. Entry length > 256")); return; } using Stream stream = entry.Open(); using StreamReader streamReader = new StreamReader(stream, Encoding.UTF8); s = streamReader.ReadToEnd().Trim(); } } if (int.TryParse(s, out var result2)) { num = result2; TexturePacks.Add(new TexturePack(Path.GetFileNameWithoutExtension(text3), text3, zipped: true, num)); } else { ((BaseUnityPlugin)this).Logger.LogError((object)("Failed to parse version. " + text3 + " was rejected.")); notificationMessage += "\nFailed to parse version number. Check log for details."; } } catch (Exception arg) { ((BaseUnityPlugin)this).Logger.LogError((object)$"Failed to parse. {text3} was rejected. {arg}"); notificationMessage += "\nFailed to parse. Check log for details."; } } } private void ApplyPack(TexturePack pack, bool NoDelete = false) { ((BaseUnityPlugin)this).Logger.LogInfo((object)("Preparing Textures Folder for pack: " + pack.Name)); if (!NoDelete) { ClearFolder(activeTexturePath()); foreach (ProcessableSprite value in FullProcessed.Values) { value.NeedsToChange = true; } } if (vanillaBasing.Value && pack.Name != "vanilla") { TexturePack pack2 = TexturePacks.FirstOrDefault((TexturePack texturePack) => texturePack.Name.Equals("vanilla", StringComparison.OrdinalIgnoreCase)); if (!Utility.IsNullOrWhiteSpace(pack2.Name)) { ApplyPack(pack2, NoDelete: true); } else { notificationMessage = "Couldn't find 'vanilla' texture pack. Hotswapping textures with a vanilla pack may cause overlap."; } } string text = pack.Path; if (pack.Zipped) { string text2 = Path.Combine(unpackPath(), pack.Name); int num = -1; string text3 = Path.Combine(text2, "version"); if (File.Exists(text3)) { if (!int.TryParse(GetFirstLineLimited(text3, 250), out var result)) { ((BaseUnityPlugin)this).Logger.LogError((object)("Failed to parse version number for " + pack.Name + " | This pack was rejected.")); notificationMessage += "\nFailed to parse version number. Check log for details."; return; } num = result; } if (verbose.Value) { if (num >= pack.Version) { ((BaseUnityPlugin)this).Logger.LogMessage((object)$"[Verbose] The zip for {pack.Name}v{pack.Version} is not a higher version than {num} so it will not be extracted."); } else { ((BaseUnityPlugin)this).Logger.LogMessage((object)$"[Verbose] The zip for {pack.Name}v{pack.Version} is a higher version than {num} so it will replace the cache."); } } if (Directory.Exists(text2) && num >= pack.Version) { text = text2; } else { try { ClearFolder(text2); SafeZip.Code num2 = SafeZip.Extract(pack.Path, text2); text = text2; if (num2 == SafeZip.Code.TOO_LARGE) { notificationMessage += "The pack's size exceeded an expected value."; text = null; } if (num2 == SafeZip.Code.PARTIAL) { notificationMessage += "This pack has failed to load fully."; text = null; } if (num2 == SafeZip.Code.SLIP) { text = null; } } catch (Exception ex) { notificationMessage += ex.Message; } } } if (text == null) { ((BaseUnityPlugin)this).Logger.LogError((object)"Failed to load a texturepack."); return; } string[] files = Directory.GetFiles(text, "*.*", SearchOption.AllDirectories); for (int num3 = 0; num3 < files.Length; num3++) { string? fileNameWithoutExtension = Path.GetFileNameWithoutExtension(files[num3]); string extension = Path.GetExtension(files[num3]); string text4 = Translator.Translate(fileNameWithoutExtension, isUI: false); File.Copy(files[num3], Path.Combine(activeTexturePath(), text4 + extension), overwrite: true); } ((BaseUnityPlugin)this).Logger.LogInfo((object)$"Loaded texture pack {pack.Name} ({files.Length}/{files.Length})"); } private void ClearFolder(string folderName) { DirectoryInfo directoryInfo = new DirectoryInfo(folderName); if (directoryInfo.Exists) { FileInfo[] files = directoryInfo.GetFiles(); for (int i = 0; i < files.Length; i++) { files[i].Delete(); } DirectoryInfo[] directories = directoryInfo.GetDirectories(); foreach (DirectoryInfo directoryInfo2 in directories) { ClearFolder(directoryInfo2.FullName); directoryInfo2.Delete(); } } } private void OnGUI() { //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_0028: Expected O, but got Unknown //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_0028: Unknown result type (might be due to invalid IL or missing references) //IL_005f: Unknown result type (might be due to invalid IL or missing references) //IL_0097: Unknown result type (might be due to invalid IL or missing references) if (showTextureLoader) { TextureMenu.windowRect = GUI.Window(42069, TextureMenu.windowRect, new WindowFunction(TexturepackWin), "Texturepack Loader"); } if (!Utility.IsNullOrWhiteSpace(notificationMessage)) { Rect val = default(Rect); ((Rect)(ref val))..ctor(10f, 10f, (float)(15 * notificationMessage.Length), 100f); GUI.Box(val, notificationMessage); if (GUI.Button(new Rect(((Rect)(ref val)).x + ((Rect)(ref val)).width - 25f, ((Rect)(ref val)).y + 5f, 20f, 20f), "X")) { notificationMessage = ""; } } } private void TexturepackWin(int windowID) { //IL_0002: 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_002c: Unknown result type (might be due to invalid IL or missing references) scrollPosition = GUILayout.BeginScrollView(scrollPosition, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Width(376f), GUILayout.Height(180f) }); for (int i = 0; i < TexturePacks.Count; i++) { TexturePack pack = TexturePacks[i]; if (GUILayout.Button(pack.Name, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(22f) })) { ApplyPack(pack); } } GUILayout.EndScrollView(); GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>()); if (GUILayout.Button("REFRESH", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(22f) })) { RefreshTexturePacks(); } if (GUILayout.Button("Open Pack Folder", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(22f) }) && Directory.Exists(texturePacksPath())) { Process.Start(new ProcessStartInfo { FileName = texturePacksPath(), UseShellExecute = true, Verb = "open" }); } GUILayout.EndHorizontal(); if (!hasHiddenBefore) { GUILayout.Label("[G] Toggles Visibility", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(22f) }); } GUI.DragWindow(); } public static string textureloaderPath() { return instance.PluginDirectory; } public static string unpackPath() { return Path.Combine(textureloaderPath(), "unpacks"); } public static string activeTexturePath() { return Path.Combine(textureloaderPath(), "activeTextures"); } public static string texturePacksPath() { return Path.Combine(textureloaderPath(), "texturePacks"); } } internal struct TexturePack { public string Name { get; set; } public string Path { get; set; } public bool Zipped { get; set; } public int Version { get; set; } public TexturePack(string name, string path, bool zipped, int version) { Name = name; Path = path; Zipped = zipped; Version = version; } } internal class SafeZip { internal enum Code { PARTIAL, SLIP, SUCCESS, TOO_LARGE } internal static Code Extract(string zipPath, string toDirectory) { long num = 0L; bool flag = false; string text = Path.GetFullPath(toDirectory); string text2 = text; char directorySeparatorChar = Path.DirectorySeparatorChar; if (!text2.EndsWith(directorySeparatorChar.ToString())) { string text3 = text; directorySeparatorChar = Path.DirectorySeparatorChar; text = text3 + directorySeparatorChar; } using (ZipArchive zipArchive = ZipFile.OpenRead(zipPath)) { foreach (ZipArchiveEntry entry in zipArchive.Entries) { num += entry.Length; if (num > 524288000) { Plugin.logger.LogError((object)("[Rejected] Size of zip file during extraction exceeded 500MB. " + zipPath + " I: " + entry.FullName + ".")); return Code.TOO_LARGE; } string fullPath = Path.GetFullPath(Path.Combine(text, entry.FullName)); if (!fullPath.StartsWith(text)) { Plugin.logger.LogWarning((object)("[Rejected] Potential path traversal attack in " + zipPath + " I: " + entry.FullName + ".")); return Code.SLIP; } if (string.IsNullOrEmpty(entry.Name)) { Directory.CreateDirectory(fullPath); continue; } Directory.CreateDirectory(Path.GetDirectoryName(fullPath)); try { entry.ExtractToFile(fullPath, overwrite: false); } catch (Exception ex) { Plugin.logger.LogError((object)ex); flag = true; } } } if (!flag) { return Code.SUCCESS; } return Code.PARTIAL; } } internal static class Translator { internal static readonly Dictionary<string, string> TextureMappings = new Dictionary<string, string> { { "Mine", "FlammableObject3" }, { "SpikeAttack", "SpikeSpriteLongRounded" }, { "MagnetGun", "Timeline 1_0010" }, { "duplicatorTransform", "Timeline 1_0014" }, { "MachoThrow3", "Throw" } }; internal static string Translate(string key, bool isUI) { if (key.Contains("_")) { int num = key.LastIndexOf("_"); string text = key.Substring(0, num); if (int.TryParse(key.Substring(num + 1), out var _)) { key = text; } } if (TextureMappings.TryGetValue(key, out var value)) { return value; } if (isUI && key.Equals("circle")) { return "UIcircle"; } return key; } } public static class PluginInfo { public const string PLUGIN_GUID = "texturepack_loader"; public const string PLUGIN_NAME = "texturepack_loader"; public const string PLUGIN_VERSION = "1.0.0"; } }