Decompiled source of FittingPaintings v1.0.2

FittingPaintings.dll

Decompiled a month ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Threading.Tasks;
using BepInEx;
using BepInEx.Logging;
using ExitGames.Client.Photon;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using Photon.Pun;
using Photon.Realtime;
using UnityEngine;
using UnityEngine.SceneManagement;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyVersion("0.0.0.0")]
[module: RefSafetyRules(11)]
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.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]
	[Microsoft.CodeAnalysis.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]
	[Microsoft.CodeAnalysis.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 FittingPaintings
{
	[BepInPlugin("ZeroTails.FittingPaintings", "FittingPaintings", "1.0.2")]
	public class FittingPaintings : BaseUnityPlugin
	{
		public class PaintingGroup
		{
			public string paintingType;

			public string paintingFolderName;

			public HashSet<string> targetMaterials;

			public List<Material> loadedMaterials;

			public PaintingGroup(string paintingType, string paintingFolderName, HashSet<string> targetMaterials)
			{
				this.paintingType = paintingType;
				this.paintingFolderName = paintingFolderName;
				this.targetMaterials = targetMaterials;
				loadedMaterials = new List<Material>();
			}
		}

		[HarmonyPatch(typeof(LoadingUI), "LevelAnimationComplete")]
		public class PaintingSwapPatch
		{
			private static void Postfix()
			{
				Task.Run(async delegate
				{
					int waited = 0;
					int interval = 50;
					while (!receivedSeed.HasValue && waited < maxWaitTimeMs)
					{
						await Task.Delay(interval);
						waited += interval;
					}
					if (receivedSeed.HasValue)
					{
						logger.LogInfo($"[Postfix] Client using received seed: {receivedSeed.Value}");
						FittingPaintingsSwap.ReceivedSeed = receivedSeed.Value;
						receivedSeed = null;
					}
					else
					{
						logger.LogWarning("[Postfix] Client did not receive seed in time. Proceeding without it.");
					}
					swapper.ReplacePaintings();
				});
			}

			private static void Prefix()
			{
				if (swapper.GetModState() == FittingPaintingsSwap.ModState.Client)
				{
					PhotonNetwork.AddCallbackTarget((object)sync);
				}
				if (swapper.GetModState() == FittingPaintingsSwap.ModState.Host)
				{
					FittingPaintingsSwap.HostSeed = Random.Range(0, int.MaxValue);
					logger.LogInfo($"Generated Hostseed: {FittingPaintingsSwap.HostSeed}");
					PhotonNetwork.AddCallbackTarget((object)sync);
					sync.SendSeed(FittingPaintingsSwap.HostSeed);
				}
			}
		}

		[HarmonyPatch(typeof(NetworkConnect), "TryJoiningRoom")]
		public class JoinLobbyPatch
		{
			private static void Prefix()
			{
				logger.LogInfo("JoinLobbyPatch Prefix called.");
				if (swapper.GetModState() == FittingPaintingsSwap.ModState.SinglePlayer)
				{
					swapper.SetState(FittingPaintingsSwap.ModState.Client);
				}
			}
		}

		[HarmonyPatch(typeof(SteamManager), "HostLobby")]
		public class HostLobbyPatch
		{
			private static bool Prefix()
			{
				logger.LogInfo("HostLobbyPatch Prefix called.");
				if (swapper.GetModState() != 0)
				{
					swapper.SetState(FittingPaintingsSwap.ModState.Host);
				}
				return true;
			}
		}

		[HarmonyPatch(typeof(SteamManager), "LeaveLobby")]
		public class LeaveLobbyPatch
		{
			private static void Postfix()
			{
				PhotonNetwork.RemoveCallbackTarget((object)sync);
				swapper.SetState(FittingPaintingsSwap.ModState.SinglePlayer);
			}
		}

		public static List<PaintingGroup> paintingGroups = new List<PaintingGroup>
		{
			new PaintingGroup("Landscape", "FittingSwapLandscapePaintings", new HashSet<string> { "Painting_H_Landscape" }),
			new PaintingGroup("Portrait", "FittingSwapPortraitPaintings", new HashSet<string> { "Painting_V_Furman", "painting teacher01", "painting teacher02", "painting teacher03", "painting teacher04", "Painting_S_Tree" }),
			new PaintingGroup("Square", "FittingSwapSquarePaintings", new HashSet<string> { "Painting_S_Creep", "Painting_S_Creep 2_0", "Painting_S_Creep 2", "Painting Wizard Class" })
		};

		private static Logger logger = null;

		private static FittingPaintingsLoader loader = null;

		private static FittingPaintingsSwap swapper = null;

		private static FittingPaintingsSync sync = null;

		public static int? receivedSeed = null;

		public static readonly int maxWaitTimeMs = 3000;

		private readonly Harmony harmony = new Harmony("ZeroTails.FittingPaintings");

		private void Awake()
		{
			logger = new Logger("FittingPaintings");
			logger.LogInfo("FittingPaintings mod initialized.");
			loader = new FittingPaintingsLoader(logger);
			loader.LoadImagesFromAllPlugins();
			swapper = new FittingPaintingsSwap(logger, loader);
			sync = new FittingPaintingsSync(logger);
			harmony.PatchAll();
		}
	}
	public class FittingPaintingsLoader
	{
		private readonly Logger logger;

		public List<Material> LoadedMaterials { get; } = new List<Material>();


		public FittingPaintingsLoader(Logger logger)
		{
			this.logger = logger;
		}

		public void LoadImagesFromAllPlugins()
		{
			string pluginPath = Paths.PluginPath;
			if (!Directory.Exists(pluginPath))
			{
				logger.LogWarning("Plugins directory not found: " + pluginPath);
				return;
			}
			foreach (FittingPaintings.PaintingGroup paintingGroup in FittingPaintings.paintingGroups)
			{
				string[] directories = Directory.GetDirectories(pluginPath, paintingGroup.paintingFolderName, SearchOption.AllDirectories);
				if (directories.Length == 0)
				{
					logger.LogWarning("No '" + paintingGroup.paintingFolderName + "' folders found in plugins.");
					continue;
				}
				string[] array = directories;
				foreach (string text in array)
				{
					logger.LogInfo("Loading images from: " + text);
					LoadImagesFromDirectory(paintingGroup, text);
				}
			}
		}

		private void LoadImagesFromDirectory(FittingPaintings.PaintingGroup paintingGroup, string directoryPath)
		{
			//IL_00d8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00dd: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e8: Expected O, but got Unknown
			if (!Directory.Exists(directoryPath))
			{
				logger.LogWarning("Directory does not exist: " + directoryPath);
				return;
			}
			string[] validExtensions = new string[4] { ".png", ".jpg", ".jpeg", ".bmp" };
			string[] array = (from file in Directory.EnumerateFiles(directoryPath, "*.*", SearchOption.AllDirectories)
				where validExtensions.Contains(Path.GetExtension(file).ToLower())
				select file).ToArray();
			if (array.Length == 0)
			{
				logger.LogWarning("No images found in " + directoryPath);
				return;
			}
			for (int i = 0; i < array.Length; i++)
			{
				string text = array[i];
				Texture2D val = LoadTextureFromFile(text);
				if ((Object)(object)val != (Object)null)
				{
					Material item = new Material(Shader.Find("Standard"))
					{
						mainTexture = (Texture)(object)val
					};
					paintingGroup.loadedMaterials.Add(item);
					logger.LogInfo($"Loaded image #{i + 1}: {Path.GetFileName(text)}");
				}
				else
				{
					logger.LogWarning($"Failed to load image #{i + 1}: {text}");
				}
			}
			logger.LogInfo($"Total images loaded: {paintingGroup.loadedMaterials.Count}");
		}

		private Texture2D? LoadTextureFromFile(string filePath)
		{
			//IL_000a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0010: Expected O, but got Unknown
			byte[] array = File.ReadAllBytes(filePath);
			Texture2D val = new Texture2D(2, 2);
			if (ImageConversion.LoadImage(val, array))
			{
				val.Apply();
				return val;
			}
			return null;
		}
	}
	public class FittingPaintingsSwap
	{
		public enum ModState
		{
			Host,
			Client,
			SinglePlayer
		}

		private readonly Logger logger;

		private readonly FittingPaintingsLoader loader;

		private static int randomSeed = 0;

		public static int HostSeed = 0;

		public static int ReceivedSeed = 0;

		public static int Seed = 0;

		private int paintingsChangedCount;

		private static ModState currentState = ModState.SinglePlayer;

		public FittingPaintingsSwap(Logger logger, FittingPaintingsLoader loader)
		{
			this.logger = logger;
			this.loader = loader;
			logger.LogInfo($"Initial ModState: {currentState}");
			if (randomSeed == 0)
			{
				randomSeed = Random.Range(0, int.MaxValue);
				logger.LogInfo($"Generated initial random seed: {randomSeed}");
			}
		}

		public ModState GetModState()
		{
			return currentState;
		}

		public void ReplacePaintings()
		{
			//IL_0049: Unknown result type (might be due to invalid IL or missing references)
			//IL_004e: Unknown result type (might be due to invalid IL or missing references)
			ModState modState = currentState;
			if (1 == 0)
			{
			}
			int seed = modState switch
			{
				ModState.SinglePlayer => randomSeed, 
				ModState.Host => HostSeed, 
				ModState.Client => ReceivedSeed, 
				_ => Seed, 
			};
			if (1 == 0)
			{
			}
			Seed = seed;
			Scene activeScene = SceneManager.GetActiveScene();
			logger.LogInfo($"Applying seed {Seed} for painting swaps in scene: {((Scene)(ref activeScene)).name}");
			logger.LogInfo("Replacing all materials containing 'painting' with custom images...");
			paintingsChangedCount = 0;
			int num = 0;
			GameObject[] rootGameObjects = ((Scene)(ref activeScene)).GetRootGameObjects();
			foreach (GameObject val in rootGameObjects)
			{
				MeshRenderer[] componentsInChildren = val.GetComponentsInChildren<MeshRenderer>();
				foreach (MeshRenderer val2 in componentsInChildren)
				{
					Material[] sharedMaterials = ((Renderer)val2).sharedMaterials;
					for (int k = 0; k < sharedMaterials.Length; k++)
					{
						num++;
						foreach (FittingPaintings.PaintingGroup paintingGroup in FittingPaintings.paintingGroups)
						{
							if ((Object)(object)sharedMaterials[k] != (Object)null && paintingGroup.targetMaterials.Contains(((Object)sharedMaterials[k]).name) && paintingGroup.loadedMaterials.Count > 0)
							{
								int index = Mathf.Abs((Seed + paintingsChangedCount) % paintingGroup.loadedMaterials.Count);
								sharedMaterials[k] = paintingGroup.loadedMaterials[index];
								paintingsChangedCount++;
							}
						}
					}
					((Renderer)val2).sharedMaterials = sharedMaterials;
				}
			}
			logger.LogInfo($"Total materials checked: {num}");
			logger.LogInfo($"Total paintings changed in this scene: {paintingsChangedCount}");
			logger.LogInfo($"RandomSeed = {randomSeed}");
			logger.LogInfo($"HostSeed = {HostSeed}");
			logger.LogInfo($"ReceivedSeed = {ReceivedSeed}");
		}

		public void SetState(ModState newState)
		{
			currentState = newState;
			logger.LogInfo($"Mod state set to: {currentState}");
		}
	}
	public class FittingPaintingsSync : MonoBehaviourPunCallbacks, IOnEventCallback
	{
		private readonly Logger logger;

		public const byte SeedEventCode = 1;

		public FittingPaintingsSync(Logger logger)
		{
			this.logger = logger;
		}

		public void SendSeed(int seed)
		{
			//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_0029: Unknown result type (might be due to invalid IL or missing references)
			//IL_002e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0030: Unknown result type (might be due to invalid IL or missing references)
			//IL_0036: Expected O, but got Unknown
			//IL_0039: Unknown result type (might be due to invalid IL or missing references)
			object[] array = new object[1] { seed };
			logger.LogInfo("Sharing seed with other clients");
			RaiseEventOptions val = new RaiseEventOptions
			{
				Receivers = (ReceiverGroup)1,
				CachingOption = (EventCaching)4
			};
			PhotonNetwork.RaiseEvent((byte)1, (object)array, val, SendOptions.SendReliable);
		}

		public void OnEvent(EventData photonEvent)
		{
			if (photonEvent.Code == 1)
			{
				int num = (int)((object[])photonEvent.CustomData)[0];
				logger.LogInfo($"Received seed: {num}");
				FittingPaintingsSwap.ReceivedSeed = num;
			}
		}
	}
	public class Logger
	{
		private readonly string logFilePath;

		private readonly ManualLogSource logSource;

		public Logger(string modName)
		{
			string directoryName = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
			logFilePath = Path.Combine(directoryName, modName + "_log.txt");
			if (File.Exists(logFilePath))
			{
				File.Delete(logFilePath);
			}
			logSource = Logger.CreateLogSource(modName);
		}

		public void LogInfo(string message)
		{
			WriteLog("INFO", message);
			logSource.LogInfo((object)message);
		}

		public void LogWarning(string message)
		{
			WriteLog("WARNING", message);
			logSource.LogWarning((object)message);
		}

		public void LogError(string message)
		{
			WriteLog("ERROR", message);
			logSource.LogError((object)message);
		}

		private void WriteLog(string level, string message)
		{
			string text = $"{DateTime.Now:yyyy-MM-dd HH:mm:ss} [{level}] {message}";
			File.AppendAllText(logFilePath, text + Environment.NewLine);
		}

		public void LogMaterial(Material material)
		{
			if ((Object)(object)material != (Object)null && ((Object)material).name.ToLower().Contains("painting"))
			{
				LogInfo("Material containing 'painting': " + ((Object)material).name);
			}
		}

		public void ClearLog()
		{
			if (File.Exists(logFilePath))
			{
				File.Delete(logFilePath);
			}
		}
	}
}
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}